🇬🇧 มีฉบับภาษาอังกฤษ: Read this article in English →

คู่มือใช้งาน Ollama Structured Outputs: ควบคุม Local LLM ให้ตอบ JSON ตรง Schema 100% ด้วย Pydantic แบบออฟไลน์

คู่มือใช้งาน Ollama Structured Outputs: ควบคุม Local LLM ให้ตอบ JSON ตรง Schema 100% ด้วย Pydantic แบบออฟไลน์

สำหรับทุกคนที่กำลังสร้างระบบอัตโนมัติส่วนตัว (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

  1. ปลอดภัยต่อข้อมูลส่วนบุคคล 100% (Zero Data Leak): ข้อมูลอ่อนไหว เช่น สลิปโอนเงิน บันทึกการเงิน หรือไดอารี่สุขภาพ จะถูกประมวลผลอยู่เฉพาะบนฮาร์ดแวร์ในบ้านของคุณ ไม่มีความเสี่ยงที่ข้อมูลจะถูกส่งไปยังเซิร์ฟเวอร์ภายนอก
  2. ระบบอัตโนมัติไม่พัง (Deterministic Reliability): ช่วยให้คุณสามารถนำผลลัพธ์ไปเขียนลงฐานข้อมูล SQLite, บันทึกลงไฟล์ Markdown ใน Obsidian หรือส่งผ่าน Webhook ไปยังอุปกรณ์ในบ้านได้อย่างมั่นใจ
  3. ประหยัดทรัพยากร (Compute Efficiency): เมื่อโมเดลถูกจำกัดให้ตอบเฉพาะโทเคนตาม Schema โมเดลจะไม่ต้องเสียเวลาเจนคำพูดเกริ่นนำยาวๆ ช่วยให้ระยะเวลาในการประมวลผลเร็วขึ้นอย่างเห็นได้ชัด

สรุป

การรัน AI ในเครื่องตัวเองจะก้าวข้ามจากการเป็นแค่ "ของเล่นแชตบอต" สู่การเป็น "เครื่องยนต์ขับเคลื่อนระบบคิดอัตโนมัติ" (Cognitive Engine) ได้ ก็ต่อเมื่อเราสามารถควบคุมผลลัพธ์ให้ออกมาเป็นโครงสร้างที่เสถียรและแน่นอน

การผสานพลังระหว่าง Ollama และ Pydantic Structured Outputs คือจิ๊กซอว์ชิ้นสำคัญที่เปลี่ยนคอมพิวเตอร์ของคุณให้กลายเป็นระบบประมวลผลข้อมูลส่วนตัวที่ทั้งปลอดภัย ฉลาด และไว้ใจได้ในระยะยาว

🛒 TREND DEE STORE • ร้านเทรนด์ดี

🛠️ เครื่องสำหรับรัน Autonomous Agent ตลอด 24 ชม.

คัดสรรอุปกรณ์ที่ตอบโจทย์และใช้งานจริงตามเนื้อหาในบทความนี้