← Phase 1 Phase 1 • Day 1 Reboot

Local SE Agent Masterclass

MacBook M3 16GB • Complete Implementation Standard
📍 ส่วนที่ 1: หมุดตั้งโจทย์ & วัตถุประสงค์หลัก (Title Connection)

Chapter 1 — Setup & Ollama API Deep Dive

ทำไมต้องชื่อนี้และเรียนไปเพื่ออะไร?

ก่อนจะสร้าง Agent ที่คิดและลงมือทำแทนเราได้ เราต้องมี "ท่อส่งกระแสประสาท (Inference Channel)" ที่คุยกับโมเดลภาษาในเครื่องได้ตรง ๆ ถ้าเราไม่เข้าใจว่า Python ส่ง Request เข้าพอร์ต 11434 อย่างไร รับ Payload กลับมาแบบไหน และจัดการข้อผิดพลาดระดับเครือข่ายท้องถิ่นอย่างไร เราจะไม่มีทางคุม Agent ระดับ Production ได้เลย Day 1 จึงเป็นการวางโครงสร้างรากฐานโปรเจกต์และเชื่อมต่อ API ของ Ollama ให้พร้อมใช้งานจริง

Day 1: Setup & Ollama API Day 2: JSON Mode & Structured Output Day 3-5: Base Tools Day 6+: Autonomous ReAct Loop
📂 ส่วนที่ 2: ผังโครงสร้างโปรเจกต์จริง & ตำแหน่งไฟล์ (Project File Structure)

โค้ดทั้งหมดของ Day 1 จะอยู่ในโฟลเดอร์ se_agent/ โดยมีตำแหน่งไฟล์จริงบนดิสก์และหน้าที่เฉพาะเจาะจงดังนี้:

se_agent/
├── requirements.txt               # ระบุเวอร์ชันของ library ภายนอก (ollama)
├── test_connection.py             # สคริปต์ทดสอบ ping ตรวจสอบว่า daemon เปิดอยู่ไหม
├── core/
│   ├── __init__.py                 # ประกาศว่า core เป็น Python package
│   └── client.py                   # [ไฟล์หลักของ Day 1] ตัวห่อหุ้มการเรียก Ollama API พร้อม error handling
└── main.py                        # จุดเริ่มต้นการรันโปรแกรมที่จะมาต่อในวันถัดไป
📍 core/client.py ทำหน้าที่อะไร?

ทำหน้าที่เป็น Wrapper Class ชื่อ OllamaLLMClient ครอบไลบรารี ollama จัดการพารามิเตอร์โมเดล ตรวจสอบการเชื่อมต่อ และแปลงผลลัพธ์เป็นข้อความหรือโครงสร้างที่พร้อมส่งต่อให้ Engine

📍 test_connection.py ทำหน้าที่อะไร?

เป็น sanity check script สำหรับรันใน Terminal เพื่อยืนยันว่าพอร์ต 11434 พร้อมตอบสนองและโมเดล qwen2.5-coder:7b ถูกโหลดลง RAM ของ Mac M3 เรียบร้อยแล้ว

🏛️ ส่วนที่ 3: สถาปัตยกรรมการสื่อสารระดับ Process (Mental Model)
🐍
1. Python Client Process
`core/client.py` ส่ง HTTP POST payload ผ่าน socket ภายในเครื่อง
Loopback Network
http://localhost:11434
⇄
Latency < 2 ms
🦙
2. Ollama Daemon (C++)
ประมวลผลบนชิป M3 Unified Memory ดึงโมเดล `qwen2.5-coder:7b` รันบน GPU
สาระสำคัญของการสื่อสารแบบนี้:
  • Zero Cloud Leak: ไม่มีการส่งข้อมูลผ่านอินเทอร์เน็ตภายนอกแม้แต่ไบต์เดียว ปลอดภัย 100% สำหรับโค้ดภายในบริษัท
  • Unified Memory Advantage: บน Mac M3 (16GB RAM) โมเดล 7B Q4_K_M กินแรมประมาณ 4.7 GB ทำให้เหลือแรมอีกกว่า 10 GB ให้ IDE และ Docker ทำงานได้สบาย
💻 ส่วนที่ 4: ซอร์สโค้ดจริง & คำอธิบายทีละบรรทัด (Line-by-Line Implementation)
📄 1. requirements.txt ไฟล์ระบุ dependencies
ollama>=0.4.0

ทำไมถึงใช้ตัวนี้? ollama เป็น Python SDK อย่างเป็นทางการที่ต่อเข้า REST API ของ Ollama daemon โดยตรง ไม่ต้องพึ่งพา LangChain หรือ Framework ขนาดใหญ่ ทำให้ไม่มี overhead และคุมพฤติกรรมได้ 100%

📄 2. core/client.py คลาสติดต่อสมองกล Ollama
import ollama
from typing import List, Dict, Any, Optional

class OllamaLLMClient:
    """
    Wrapper สำหรับควบคุมการสื่อสารกับโมเดลภาษาในเครื่องผ่าน Ollama Daemon
    """
    def __init__(self, model_name: str = "qwen2.5-coder:7b", host: Optional[str] = None):
        self.model_name = model_name
        # ถ้าระบุ host จะชี้ไป endpoint ที่กำหนด เช่น http://localhost:11434
        self.client = ollama.Client(host=host) if host else ollama.Client()

    def check_health(self) -> bool:
        """ตรวจสอบว่า daemon พร้อมใช้งานและมีโมเดลเป้าหมายอยู่หรือไม่"""
        try:
            models_response = self.client.list()
            # ดึงรายชื่อโมเดลทั้งหมดที่มีอยู่ในเครื่อง
            available_models = [m.model for m in models_response.models]
            # ตรวจว่าโมเดลเป้าหมายมีอยู่ในเครื่องหรือไม่ (เช่น qwen2.5-coder:7b หรือ qwen2.5-coder:7b-latest)
            return any(self.model_name in name for name in available_models)
        except Exception:
            return False

    def chat(self, messages: List[Dict[str, str]], temperature: float = 0.2) -> str:
        """
        ส่งประวัติบทสนทนาและรับข้อความตอบกลับจากโมเดล
        :param messages: ลิสต์ของข้อความในรูปแบบ [{'role': 'user', 'content': '...'}]
        :param temperature: ค่าความสุ่ม ยิ่งต่ำยิ่งคงเส้นคงวาและเป็นเหตุเป็นผล (เหมาะกับงานโค้ด)
        """
        try:
            response = self.client.chat(
                model=self.model_name,
                messages=messages,
                options={"temperature": temperature}
            )
            # ดึงเฉพาะข้อความคำตอบที่โมเดลส่งออกมา
            return response.message.content
        except ollama.ResponseError as e:
            raise RuntimeError(f"Ollama Response Error (Status {e.status_code}): {e.error}")
        except Exception as e:
            raise ConnectionError(f"ไม่สามารถเชื่อมต่อ Ollama daemon ที่ localhost:11434 ได้: {str(e)}")
ชำแหละไวยากรณ์สำคัญใน core/client.py
ollama.Client(host=...)

ใช้ Client instance แทนฟังก์ชัน global ollama.chat() เพื่อให้สามารถเปลี่ยน endpoint หรือทำ mock ตอนเขียนเทสได้ง่ายในอนาคต

check_health() -> bool

ใช้ self.client.list() เช็คสถานะ daemon ล่วงหน้า ป้องกันไม่ให้โปรแกรมแครชกลางคันถ้ารันคำสั่งตอนที่ Ollama ยังไม่ได้เปิด

options={"temperature": 0.2}

คุมอุณหภูมิการสุ่มให้อยู่ในระดับต่ำ (0.1 - 0.2) สำหรับงาน Software Engineering เพื่อให้ผลลัพธ์คำตอบเป็นไปตามหลักตรรกะ ไม่เพ้อเจ้อ

📄 3. test_connection.py สคริปต์ทดสอบระบบ
from core.client import OllamaLLMClient

def main():
    print("🔍 กำลังตรวจสอบการเชื่อมต่อกับ Ollama Daemon บน Mac M3...")
    client = OllamaLLMClient(model_name="qwen2.5-coder:7b")
    
    if not client.check_health():
        print("❌ ข้อผิดพลาด: ไม่พบ Ollama Daemon หรือยังไม่ได้ดึงโมเดล 'qwen2.5-coder:7b'")
        print("👉 กรุณารันคำสั่ง: ollama run qwen2.5-coder:7b ใน Terminal อีกหน้าต่างหนึ่ง")
        return

    print("✅ การเชื่อมต่อสมบูรณ์! กำลังทดสอบส่ง Prompt ไปประมวลผล...")
    messages = [
        {"role": "system", "content": "You are a concise SE Assistant."},
        {"role": "user", "content": "เขียนฟังก์ชัน Python สั้นๆ สำหรับกลับข้อความ (reverse string) 1 บรรทัด"}
    ]
    
    answer = client.chat(messages, temperature=0.1)
    print("\n--- ผลลัพธ์จากโมเดล ---")
    print(answer)
    print("------------------------")
    print("🎉 ยินดีด้วยครับ! ท่อส่งข้อมูล Day 1 ทำงานได้สมบูรณ์แบบ 100%")

if __name__ == "__main__":
    main()
⚡ ส่วนที่ 5: ปฏิบัติการจริงบน Terminal Mac M3 (Step-by-Step Lab)

เปิด Terminal บน MacBook M3 แล้วรันคำสั่งตามลำดับ 5 ขั้นตอนนี้ได้ทันที:

ขั้นตอนที่ 1: ดึงโมเดลภาษาลงเครื่อง (ถ้ายังไม่มี)
ollama pull qwen2.5-coder:7b

ขนาดไฟล์ประมาณ 4.7 GB จะถูกเก็บไว้ใน cache ท้องถิ่นของ macOS

ขั้นตอนที่ 2: เตรียมโครงสร้างโฟลเดอร์โปรเจกต์
mkdir -p ~/se_agent/core
cd ~/se_agent
touch requirements.txt test_connection.py core/__init__.py core/client.py
ขั้นตอนที่ 3: ตั้งค่า Virtual Environment และติดตั้ง dependencies
python3 -m venv .venv
source .venv/bin/activate
echo "ollama>=0.4.0" > requirements.txt
pip install -r requirements.txt
ขั้นตอนที่ 4: คัดลอกโค้ดลงในไฟล์

นำโค้ดใน ส่วนที่ 4 ไปวางใน core/client.py และ test_connection.py

ขั้นตอนที่ 5: รันสคริปต์ทดสอบผลลัพธ์
python test_connection.py

คุณจะเห็นข้อความตอบกลับพร้อมโค้ด Python จากโมเดลภายในเวลาไม่ถึง 1-2 วินาทีบนชิป M3

📝 ส่วนที่ 6: ชุดควิซประเมินความรู้เชิงวิศวกรรม 15 ข้อ (ตอบปุ๊บรู้ผลทันที)

ข้อที่ 1: วัตถุประสงค์หลักของการเขียน Wrapper Class ใน `core/client.py` แทนการเรียกคำสั่งใน Terminal ตรงๆ คืออะไร?

ข้อที่ 2: พอร์ตเน็ตเวิร์กมาตรฐานที่ Ollama daemon ใช้เปิดรับ HTTP requests บน localhost คือพอร์ตใด?

ข้อที่ 3: ไฟล์ `core/__init__.py` มีบทบาทอย่างไรในโปรเจกต์ Python ของเรา?

ข้อที่ 4: ในการส่งคำสั่งโค้ดให้โมเดลประมวลผล เหตุใดเราจึงตั้งค่า `temperature=0.1` หรือ `0.2`?

ข้อที่ 5: หากรันสคริปต์แล้วเจอ Error: `ConnectionError: ไม่สามารถเชื่อมต่อ Ollama daemon ที่ localhost:11434 ได้` สาเหตุที่เป็นไปได้มากที่สุดคืออะไร?

ข้อที่ 6: บนเครื่อง Mac M3 สถาปัตยกรรม "Unified Memory" ส่งผลดีต่อการรันโมเดล Local อย่างไร?

ข้อที่ 7: พารามิเตอร์ `messages` ใน `client.chat()` มีโครงสร้างข้อมูลมาตรฐานแบบใด?

ข้อที่ 8: โมเดล `qwen2.5-coder:7b` มีความเหมาะสมกับการสร้าง SE Agent เหนือกว่าโมเดลสนทนาทั่วไปอย่างไร?

ข้อที่ 9: ในเมธอด `check_health()` เราใช้คำสั่งใดของ SDK ในการตรวจสอบรายชื่อโมเดลที่มีในเครื่อง?

ข้อที่ 10: การรันคำสั่ง `source .venv/bin/activate` บน macOS มีจุดประสงค์เพื่ออะไร?

ข้อที่ 11: คำว่า "role": "system" ในประวัติบทสนทนามีหน้าที่เฉพาะเจาะจงอย่างไร?

ข้อที่ 12: การสร้าง instance `ollama.Client()` แยกใน class แทนที่จะใช้ฟังก์ชันระดับ module ส่งผลดีต่อการออกแบบสถาปัตยกรรมอย่างไร?

ข้อที่ 13: ค่า Exception ชนิดใดของไลบรารี `ollama` ที่บ่งชี้ว่าตัว daemon ตอบกลับมาด้วย status code ที่ผิดปกติ (เช่น 404 หาโมเดลไม่เจอ)?

ข้อที่ 14: หลังจากที่เราสร้าง `core/client.py` ได้สำเร็จใน Day 1 แล้ว ใน Day 2 เราจะต่อยอดไปสู่เรื่องใด?

ข้อที่ 15: ในฐานะวิศวกรซอฟต์แวร์ เมื่อคุณเห็นโครงสร้าง `core/client.py` และสามารถรันคุยกับโมเดลบน Mac M3 ของตัวเองได้สำเร็จ รู้สึกอย่างไร?

🏁 สรุปหมุดเชื่อมโยง Day 1 (Chapter Wrap-up)

ใน Day 1 — Setup & Ollama API Deep Dive คุณได้สร้างโครงสร้างโปรเจกต์ se_agent/ ของจริง วางไฟล์ core/client.py เข้าใจพารามิเตอร์ของ ollama.Client ทุกตัว และทดสอบการยิง Prompt รับผลลัพธ์ผ่าน loopback port 11434 บนชิป Apple Silicon M3 ได้อย่างสมบูรณ์ พร้อมก้าวสู่ Day 2: JSON Mode & Structured Output เพื่อล็อกผลลัพธ์ให้อยู่ในรูปที่ Python สั่งงานต่อได้แล้วครับ!