สำหรับทุกคนที่กำลังสร้างระบบอัตโนมัติส่วนตัว (Personal Automation) หรือพัฒนา Local AI Agent ในเครื่อง ปัญหาคลาสสิกที่สร้างความปวดหัวให้มากที่สุดไม่ใช่เรื่องความฉลาดของโมเดล แต่เป็น "ความไม่แน่นอนของรูปแบบข้อมูล" (Format Inconsistency)
ลองจินตนาการว่าคุณเขียนสคริปต์ Python ให้โมเดล Local LLM (อย่าง Llama 3 หรือ Qwen 2.5) สกัดข้อมูลสำคัญจากข้อความบันทึกส่วนตัว เช่น ดึงรายการค่าใช้จ่ายเพื่อบันทึกเข้าฐานข้อมูล SQLite ในเครื่อง สิ่งที่คุณต้องการคือโครงสร้าง JSON บริสุทธิ์ ที่มีคีย์ตรงตามที่โค้ดต้องการ แต่สิ่งที่มักได้รับกลับมาคือ:
- AI ตอบทักทายเกริ่นนำ: "ได้เลยครับ! นี่คือผลการวิเคราะห์ข้อมูลที่คุณขอ..."
- ปิดหัวท้ายด้วย Markdown Code Block:
json ... - สะกดชื่อคีย์ผิดบ้าง หรือลืมใส่เครื่องหมายคำพูด (Quotes)
- หรือบางครั้งตอบข้อมูลชนิดตัวเลขกลายเป็นสตริง
ผลลัพธ์คือคำสั่ง json.loads() พังพินาศ เกิด JSONDecodeError และทำให้ไปป์ไลน์การทำงานอัตโนมัติหยุดชะงักทันที แม้เราจะพยายามเน้นย้ำใน System Prompt ว่า "ตอบเฉพาะ JSON เท่านั้น ห้ามพูดสิ่งอื่นเด็ดขาด" โมเดลก็ยังคงมีโอกาสหลุดได้อยู่เสมอ
แต่วันนี้ปัญหานั้นจบลงอย่างสมบูรณ์ ด้วยฟีเจอร์ Structured Outputs บน Ollama ร่วมกับไลบรารี Pydantic ในภาษา Python ที่ช่วยการันตีว่าผลลัพธ์ทุกตัวอักษรจะตรงตาม Schema ที่กำหนดไว้ 100% โดยทำงานออฟไลน์ในเครื่อง ไม่ต้องเสียค่า API และข้อมูลส่วนตัวไม่รั่วไหลแม้แต่ไบต์เดียว
เบื้องหลังความลับ: ทำไม Structured Outputs ถึงไม่มีวันตอบหลุด?
ในอดีต การขอให้ LLM ตอบเป็น JSON ทำงานผ่านการ "ขอร้อง" (Prompting) แล้วโมเดลจะพยายามสร้างประโยคตามความน่าจะเป็น ซึ่งย่อมมีโอกาสพลาดเสมอ
แต่ฟีเจอร์ Structured Outputs ของ Ollama ทำงานในระดับเครื่องยนต์ประมวลผล (Inference Engine) ด้วยเทคนิคที่เรียกว่า Constrained Decoding (หรือ Grammar-constrained Sampling)
หลักการทำงานคือ: 1. เราแปลงโมเดลข้อมูลใน Python (Pydantic Model) ให้กลายเป็น JSON Schema 2. Ollama นำ Schema นั้นไปสร้างเป็นกฎไวยากรณ์ (Grammar Rules) ระดับ Token 3. ในทุกจังหวะที่โมเดลกำลังจะสร้างคำถัดไป (Next Token Prediction) ระบบจะคัดกรองเฉพาะ Token ที่ถูกต้องตามไวยากรณ์ JSON และ Schema เท่านั้น Token ใดที่ทำให้โครงสร้างผิดกฎจะถูกตัดความน่าจะเป็นทิ้งเหลือ 0% ทันที
นั่นหมายความว่า โมเดลไม่มีโอกาสตอบคำเกริ่นนำ ไม่สามารถลืมปิดวงเล็บปีกกา และไม่สามารถเปลี่ยนชื่อคีย์ได้เลยแม้แต่ครั้งเดียว!
ขั้นตอนเตรียมความพร้อม
ก่อนเริ่มเขียนโค้ด ตรวจสอบให้แน่ใจว่าคุณติดตั้ง Ollama เวอร์ชันล่าสุด และมีไลบรารี ollama กับ pydantic ในสภาพแวดล้อม Python เรียบร้อยแล้ว:
# อัปเดตแพ็กเกจใน Python
pip install ollama pydantic
ในตัวอย่างนี้ เราจะใช้โมเดล Qwen 2.5 (7B) หรือ Llama 3.1 / 3.2 ซึ่งรองรับการสร้าง Structured Outputs ได้อย่างรวดเร็วและแม่นยำสูง ดึงโมเดลลงเครื่องด้วยคำสั่ง:
ollama pull qwen2.5:7b
ตัวอย่างการใช้งานจริง: สกัดข้อมูลค่าใช้จ่ายส่วนตัว (Expense Extraction)
สมมติว่าคุณคัดลอกข้อความบันทึกประจำวัน หรือข้อความแชตแจ้งเตือนการโอนเงิน แล้วต้องการให้ AI ดึงข้อมูลธุรกรรมออกมาเป็นโครงสร้างที่นำไปบันทึกต่อได้ทันที
เราจะเริ่มจากการกำหนด Pydantic Model เพื่อระบุว่าข้อมูลที่ต้องการมีฟิลด์อะไร ชนิดข้อมูลเป็นแบบไหน และจำกัดตัวเลือกด้วย Enum:
from enum import Enum
from typing import List, Optional
from pydantic import BaseModel, Field
import ollama
# 1. กำหนดหมวดหมู่ค่าใช้จ่ายที่ยอมรับได้
class ExpenseCategory(str, Enum):
FOOD = "อาหารและเครื่องดื่ม"
TRANSPORT = "การเดินทาง"
UTILITIES = "ค่าน้ำค่าไฟและบริการ"
ENTERTAINMENT = "ความบันเทิง"
HEALTH = "สุขภาพ"
OTHER = "อื่นๆ"
# 2. กำหนดโครงสร้างข้อมูลของแต่ละรายการ
class ExpenseItem(BaseModel):
merchant: str = Field(description="ชื่อร้านค้าหรือผู้รับเงิน")
amount: float = Field(description="จำนวนเงิน (บาท)")
category: ExpenseCategory = Field(description="หมวดหมู่ค่าใช้จ่าย")
is_subscription: bool = Field(description="เป็นการจ่ายแบบรายเดือน/สมัครสมาชิกหรือไม่")
# 3. กำหนดโครงสร้างภาพรวมของผลลัพธ์
class ExpenseReport(BaseModel):
summary: str = Field(description="สรุปภาพรวมสั้นๆ ของรายการที่พบ")
total_amount: float = Field(description="ยอดรวมเงินทั้งหมด")
items: List[ExpenseItem] = Field(description="รายการค่าใช้จ่ายทั้งหมด")
เรียกใช้ Ollama และรับผลลัพธ์แบบ Type-Safe
เมื่อได้ Schema เรียบร้อย เราสามารถส่งเข้าไปในพารามิเตอร์ format ของฟังก์ชัน ollama.chat() ได้ทันที โดยใช้เมธอด .model_json_schema() ของ Pydantic:
raw_note = """
เมื่อวานตอนเที่ยงไปกินข้าวหมูกรอบร้านป้าสมศรี จ่ายไป 85 บาท
แล้วตอนเย็นนั่ง BTS กลับบ้าน 45 บาท
พอดึกๆ มีตัดบัตรเครดิตค่าสมาชิก Netflix รายเดือนอีก 419 บาท
"""
response = ollama.chat(
model="qwen2.5:7b",
messages=[
{
"role": "user",
"content": f"สกัดรายการค่าใช้จ่ายจากข้อความต่อไปนี้:\n{raw_note}"
}
],
format=ExpenseReport.model_json_schema(), # ส่ง JSON Schema เพื่อบังคับผลลัพธ์
options={"temperature": 0.0} # ใช้ 0 เพื่อความแม่นยำสูงสุด
)
# แปลงผลลัพธ์กลับเป็น Pydantic Object ได้ทันทีโดยไม่ต้องกลัว Error
report = ExpenseReport.model_validate_json(response['message']['content'])
# ตรวจสอบผลลัพธ์
print(f"📊 สรุป: {report.summary}")
print(f"💰 ยอดรวม: {report.total_amount:.2f} บาท\n")
for item in report.items:
print(f"- ร้าน: {item.merchant} | จำนวน: {item.amount} บาท | หมวด: {item.category.value} | รายเดือน: {'ใช่' if item.is_subscription else 'ไม่ใช่'}")
ผลลัพธ์ที่ได้จากการรันสคริปต์
เมื่อรันโค้ดด้านบน สิ่งที่คุณจะได้รับคือข้อมูลที่ถูกแปลงเป็น Python Object ที่เข้าถึงฟิลด์ต่างๆ ผ่าน Dot Notation ได้ทันที ไร้ข้อความรบกวน:
📊 สรุป: ค่าใช้จ่ายประจำวันประกอบด้วยค่าอาหาร ค่าเดินทาง และค่าสมาชิกสตรีมมิ่ง
💰 ยอดรวม: 549.00 บาท
- ร้าน: ป้าสมศรี | จำนวน: 85.0 บาท | หมวด: อาหารและเครื่องดื่ม | รายเดือน: ไม่ใช่
- ร้าน: BTS | จำนวน: 45.0 บาท | หมวด: การเดินทาง | รายเดือน: ไม่ใช่
- ร้าน: Netflix | จำนวน: 419.0 บาท | หมวด: ความบันเทิง | รายเดือน: ใช่
โมเดลคำนวณยอดรวมได้อย่างถูกต้อง จำแนกหมวดหมู่ตาม ExpenseCategory ที่เรากำหนดไว้ และตั้งค่าแฟล็ก is_subscription ของ Netflix เป็น True อย่างแม่นยำ โดยที่เราไม่ต้องเขียน Regex หรือจัดการสตริงเองเลย
3 ข้อได้เปรียบของการใช้ Structured Outputs ในระบบ Private Brain OS
- ปลอดภัยต่อข้อมูลส่วนบุคคล 100% (Zero Data Leak): ข้อมูลอ่อนไหว เช่น สลิปโอนเงิน บันทึกการเงิน หรือไดอารี่สุขภาพ จะถูกประมวลผลอยู่เฉพาะบนฮาร์ดแวร์ในบ้านของคุณ ไม่มีความเสี่ยงที่ข้อมูลจะถูกส่งไปยังเซิร์ฟเวอร์ภายนอก
- ระบบอัตโนมัติไม่พัง (Deterministic Reliability): ช่วยให้คุณสามารถนำผลลัพธ์ไปเขียนลงฐานข้อมูล SQLite, บันทึกลงไฟล์ Markdown ใน Obsidian หรือส่งผ่าน Webhook ไปยังอุปกรณ์ในบ้านได้อย่างมั่นใจ
- ประหยัดทรัพยากร (Compute Efficiency): เมื่อโมเดลถูกจำกัดให้ตอบเฉพาะโทเคนตาม Schema โมเดลจะไม่ต้องเสียเวลาเจนคำพูดเกริ่นนำยาวๆ ช่วยให้ระยะเวลาในการประมวลผลเร็วขึ้นอย่างเห็นได้ชัด
สรุป
การรัน AI ในเครื่องตัวเองจะก้าวข้ามจากการเป็นแค่ "ของเล่นแชตบอต" สู่การเป็น "เครื่องยนต์ขับเคลื่อนระบบคิดอัตโนมัติ" (Cognitive Engine) ได้ ก็ต่อเมื่อเราสามารถควบคุมผลลัพธ์ให้ออกมาเป็นโครงสร้างที่เสถียรและแน่นอน
การผสานพลังระหว่าง Ollama และ Pydantic Structured Outputs คือจิ๊กซอว์ชิ้นสำคัญที่เปลี่ยนคอมพิวเตอร์ของคุณให้กลายเป็นระบบประมวลผลข้อมูลส่วนตัวที่ทั้งปลอดภัย ฉลาด และไว้ใจได้ในระยะยาว