Chapter 1 — Setup & Ollama API Deep Dive
ทำไมต้องชื่อนี้และเรียนไปเพื่ออะไร?
ก่อนจะสร้าง Agent ที่คิดและลงมือทำแทนเราได้ เราต้องมี "ท่อส่งกระแสประสาท (Inference Channel)" ที่คุยกับโมเดลภาษาในเครื่องได้ตรง ๆ ถ้าเราไม่เข้าใจว่า Python ส่ง Request เข้าพอร์ต 11434 อย่างไร รับ Payload กลับมาแบบไหน และจัดการข้อผิดพลาดระดับเครือข่ายท้องถิ่นอย่างไร เราจะไม่มีทางคุม Agent ระดับ Production ได้เลย Day 1 จึงเป็นการวางโครงสร้างรากฐานโปรเจกต์และเชื่อมต่อ API ของ Ollama ให้พร้อมใช้งานจริง
โค้ดทั้งหมดของ Day 1 จะอยู่ในโฟลเดอร์ se_agent/ โดยมีตำแหน่งไฟล์จริงบนดิสก์และหน้าที่เฉพาะเจาะจงดังนี้:
ทำหน้าที่เป็น Wrapper Class ชื่อ OllamaLLMClient ครอบไลบรารี ollama จัดการพารามิเตอร์โมเดล ตรวจสอบการเชื่อมต่อ และแปลงผลลัพธ์เป็นข้อความหรือโครงสร้างที่พร้อมส่งต่อให้ Engine
เป็น sanity check script สำหรับรันใน Terminal เพื่อยืนยันว่าพอร์ต 11434 พร้อมตอบสนองและโมเดล qwen2.5-coder:7b ถูกโหลดลง RAM ของ Mac M3 เรียบร้อยแล้ว
- Zero Cloud Leak: ไม่มีการส่งข้อมูลผ่านอินเทอร์เน็ตภายนอกแม้แต่ไบต์เดียว ปลอดภัย 100% สำหรับโค้ดภายในบริษัท
- Unified Memory Advantage: บน Mac M3 (16GB RAM) โมเดล 7B Q4_K_M กินแรมประมาณ 4.7 GB ทำให้เหลือแรมอีกกว่า 10 GB ให้ IDE และ Docker ทำงานได้สบาย
ollama>=0.4.0
ทำไมถึงใช้ตัวนี้? ollama เป็น Python SDK อย่างเป็นทางการที่ต่อเข้า REST API ของ Ollama daemon โดยตรง ไม่ต้องพึ่งพา LangChain หรือ Framework ขนาดใหญ่ ทำให้ไม่มี overhead และคุมพฤติกรรมได้ 100%
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 เพื่อให้ผลลัพธ์คำตอบเป็นไปตามหลักตรรกะ ไม่เพ้อเจ้อ
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()
เปิด Terminal บน MacBook M3 แล้วรันคำสั่งตามลำดับ 5 ขั้นตอนนี้ได้ทันที:
ollama pull qwen2.5-coder:7b
ขนาดไฟล์ประมาณ 4.7 GB จะถูกเก็บไว้ใน cache ท้องถิ่นของ macOS
mkdir -p ~/se_agent/core
cd ~/se_agent
touch requirements.txt test_connection.py core/__init__.py core/client.py
python3 -m venv .venv
source .venv/bin/activate
echo "ollama>=0.4.0" > requirements.txt
pip install -r requirements.txt
นำโค้ดใน ส่วนที่ 4 ไปวางใน core/client.py และ test_connection.py
python test_connection.py
คุณจะเห็นข้อความตอบกลับพร้อมโค้ด Python จากโมเดลภายในเวลาไม่ถึง 1-2 วินาทีบนชิป M3
ข้อที่ 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 สั่งงานต่อได้แล้วครับ!