นักพัฒนาสามารถรับประกันได้แล้วว่า JSON ที่ LLM ส่งกลับมานั้นมีรูปแบบ (shape) ตามที่กำหนดไว้ล่วงหน้า โดยการเชื่อมต่อ Zod schemas เข้ากับ Vercel AI SDK หรือ Anthropic’s tool-use API ซึ่งจะช่วยกำจัดปัญหา runtime crashes ที่เกิดขึ้นเมื่อโมเดลเพิ่มฟิลด์ที่ไม่ได้คาดคิดเข้ามา
ความจำเป็นในการมีระบบป้องกันที่ชัดเจนเริ่มเห็นได้ชัดในเดือนมกราคม เมื่อ classifier ที่ถูกนำไปใช้งานจริง (production) เริ่มส่งคีย์ “explanation” ตัวที่สองกลับมา หลังจากที่ทำงานได้อย่างไร้ที่ติมานานถึงสามสัปดาห์ เนื่องจากโค้ดคาดหวังเพียงฟิลด์เดียว คีย์ที่เกินมาจึงทำให้เกิด exception ขึ้นมาทันที ทั้งที่ไม่มีการ deploy โค้ดใหม่เลยแม้แต่นิดเดียว เหตุการณ์นี้สะท้อนให้เห็นถึงปัญหาที่กว้างกว่านั้น นั่นคือบทเรียนส่วนใหญ่มักจะหยุดอยู่แค่ที่ JSON.parse(response) โดยสันนิษฐานว่าโมเดลจะปฏิบัติตาม schema ใน prompt แต่ในความเป็นจริง LLM มักจะออกนอกลู่นอกทางบ่อยครั้ง เช่น เปลี่ยนการใช้ตัวพิมพ์เล็ก-ใหญ่ (casing), เพิ่มฟิลด์ใหม่ หรือห่อหุ้ม output ด้วย markdown fences ซึ่งนำไปสู่การเกิดข้อมูลเสียหายแบบเงียบๆ (silent data corruption) หรือความล้มเหลวของระบบโดยสิ้นเชิง
ทำไมการ parse JSON แบบดิบๆ ถึงไม่ปลอดภัย
LLM ถูกฝึกมาเพื่อให้ความช่วยเหลือ ไม่ใช่เพื่อให้เชื่อฟัง prompt ที่ขอให้ทำ
{ "category": "string" }
ไม่ได้เป็นการผูกมัดโมเดลไว้กับโครงสร้างนั้นอย่างตายตัว แม้แต่ prompt ที่เขียนมาอย่างดีก็อาจถูกแทนที่ด้วย heuristics ภายในของโมเดล โดยเฉพาะอย่างยิ่งเมื่อการตั้งค่า temperature กระตุ้นให้เกิดความคิดสร้างสรรค์ หรือเมื่อคำสั่งถัดมา (downstream instruction) กระตุ้นให้โมเดลอธิบายรายละเอียดมากขึ้น ผลลัพธ์ที่ได้คือข้อความที่ดูเหมือน JSON แต่มีความเบี่ยงเบนมากพอที่จะทำให้ parser ที่คาดหวังโครงสร้างที่เข้มงวดพังลงได้
เมื่อความไม่สอดคล้องกันดังกล่าวหลุดเข้าไปถึงโค้ดใน production ผลกระทบจะเกิดขึ้นทันที ไม่ว่าจะเป็นการเกิด exception, request ที่ล้มเหลว และอาจลามไปสู่ข้อผิดพลาดอื่นๆ ในระบบ (cascade of downstream errors) สำหรับบริการขนาดใหญ่ ช่วงเวลาที่ระบบล่ม (downtime) เพียงไม่กี่นาทีหมายถึงรายได้ที่สูญเสียไปและความเชื่อมั่นของผู้ใช้ที่ลดลง
Zod + Vercel AI SDK: ตาข่ายนิรภัย 3 ขั้นตอน
Zod คือ schema validator ที่เน้น TypeScript เป็นหลัก ซึ่งสามารถระบุรูปแบบข้อมูล (data shape) ที่แม่นยำที่โมเดลควรจะส่งออกมา เมื่อใช้งานร่วมกับ helper Output.object ของ Vercel AI SDK การตรวจสอบความถูกต้อง (validation) จะเกิดขึ้นโดยอัตโนมัติหลังจากที่โมเดลสร้าง response เสร็จสิ้น
- กำหนด schema – เขียน Zod object ที่เลียนแบบ JSON ที่ต้องการ สำหรับ classifier ง่ายๆ อาจจะเป็น
z.object({ category: z.string() })ส่วนสำหรับตัวดึงข้อมูลใบแจ้งหนี้ (invoice extractor) ที่ซับซ้อน schema สามารถซ้อน objects, arrays และ discriminated unions ได้ - ส่งค่าไปยัง SDK – ห่อหุ้ม schema ด้วย
Output.object(schema)โดย SDK จะทำการแทรก prompt เพื่อบอกให้โมเดลส่ง output เป็น JSON block ที่ตรงตาม schema และทำการ parse ผลลัพธ์ด้วยsafeParseของ Zod - จัดการความล้มเหลว –
safeParseจะคืนค่าเป็น result object แทนที่จะเป็นการ throw error หากการ parse ล้มเหลว ให้ส่ง error กลับไปที่โมเดลแล้วลองใหม่ (retry) โมเดลสามารถถูกสั่งให้แก้ไข output ตามข้อความ validation ที่ระบุอย่างชัดเจน ซึ่งจะเปลี่ยนกรณีขอบเขต (edge cases) ส่วนใหญ่ให้กลายเป็นลูปการแก้ไขตัวเอง (self-healing loop)
เนื่องจาก SDK จัดการทั้งเรื่อง prompting, parsing และ logic การ retry ไว้ในที่เดียว นักพัฒนาจึงสามารถเปลี่ยนจากการจัดการ string แบบ ad-hoc หลายๆ จุด มาเป็นการเรียกใช้งานเพียงครั้งเดียวที่ผ่านการตรวจสอบ type แล้ว
Anthropic tool use: การบังคับให้ได้ structured output
เมื่อทำงานกับ Anthropic’s API โดยตรง เราสามารถรับประกันผลลัพธ์แบบเดียวกันได้ผ่าน “tool use” โดย tool จะถูกกำหนดให้เป็นฟังก์ชันที่มี input schema แสดงอยู่ในรูปแบบ JSON Schema ซึ่งโมเดลของ Anthropic จะเรียกใช้ tool ก็ต่อเมื่อมันสามารถทำตาม schema ได้เท่านั้น การตั้งค่า tool_choice เป็น "any" (หรือชื่อ tool ที่ระบุเจาะจง) จะเป็นการบังคับให้โมเดลส่งคืน structured block แทนที่จะเป็นข้อความแบบอิสระ (free-form text)
ขั้นตอนการทำงานจะคล้ายกับแนวทางของ Vercel:
- เขียน Zod schema
- แปลงเป็น JSON Schema payload สำหรับการนิยาม tool
- ใส่ tool ลงใน request และกำหนดให้โมเดลต้องเรียกใช้งาน (invoke) มัน
- parse ผลลัพธ์ของ tool ด้วย
zod.safeParse
หากโมเดลยังคงสร้างข้อมูลที่ผิดรูปแบบ (malformed data) ก็สามารถใช้รูปแบบการ retry-with-feedback แบบเดียวกันได้
เมื่อการ validation ยังคงล้มเหลว
แม้จะมีการบังคับใช้ schema แต่ความไม่สอดคล้องกันก็ยังเกิดขึ้นได้เป็นครั้งคราว โดยมีสาเหตุมาจาก:
- Model hallucination: โมเดลอาจสร้าง string ที่ดูเหมือน JSON แต่มีข้อผิดพลาดทางไวยากรณ์ (syntax errors)
- Prompt leakage: บทสนทนาก่อนหน้าอาจมีคำสั่งด้านการจัดรูปแบบหลุดออกมา ซึ่งไปทับซ้อนกับคำสั่ง schema request
- Version differences: โมเดลเวอร์ชันใหม่ๆ บางครั้งอาจเปลี่ยนวิธีการตีความการเรียกใช้ tool
วิธีการบรรเทาปัญหาที่แนะนำคือการใช้ retry loop แบบเบาๆ เมื่อการ parse ล้มเหลว โค้ดจะส่ง prompt ติดตามไป เช่น “Your last output was not valid JSON. It contained … Please return only the fields defined in the schema.” เนื่องจากข้อผิดพลาดในการ validation นั้นชัดเจน โมเดลจึงสามารถแก้ไขตัวเองได้โดยไม่ต้องอาศัยการแทรกแซงจากมนุษย์
ข้อควรพิจารณาด้านประสิทธิภาพและค่าใช้จ่าย
การเพิ่ม Zod validation ทำให้เกิดภาระการทำงานของ CPU (CPU overhead) เพียงเล็กน้อยเท่านั้น โดยการทำงานของ safeParse ใช้เวลาเพียงระดับไมโครวินาทีสำหรับ payloads ทั่วไป ค่าความหน่วงของเครือข่าย (Network latency) ยังคงเดิม การส่งข้อมูลซ้ำ (round-trip) เพิ่มเติมจะเกิดขึ้นเฉพาะในกรณีที่เกิดความผิดพลาดซึ่งเกิดขึ้นได้ยากเท่านั้น ในทางปฏิบัติ มูลค่าของการป้องกัน exception ได้เพียงครั้งเดียวมีค่ามากกว่าเวลาที่เพิ่มขึ้นเพียงเล็กน้อยในการส่งคำขอ (request time) อย่างมาก
ข้อโต้แย้ง: การบังคับใช้ schema เป็นเรื่องที่เกินความจำเป็นหรือไม่?
นักพัฒนาบางคนแย้งว่า schema ที่เข้มงวดจะจำกัดความยืดหยุ่นของโมเดล โดยเฉพาะอย่างยิ่งเมื่อฟิลด์ใหม่ๆ อาจให้บริบท (context) ที่มีค่า การแลกเปลี่ยน (trade-off) คือเรื่องระหว่างความปลอดภัยและความเปิดกว้าง ในบริการที่มีความสำคัญสูง (mission-critical services) เช่น การประมวลผลการชำระเงิน การยืนยันตัวตน และการรายงานการปฏิบัติตามกฎระเบียบ (compliance reporting) ความสามารถในการคาดเดาผลลัพธ์ (predictability) คือสิ่งที่สำคัญที่สุด สำหรับต้นแบบที่อยู่ในขั้นสำรวจ (exploratory prototypes) แนวทางที่ผ่อนปรนกว่าอาจเป็นสิ่งที่ยอมรับได้ แต่ถึงอย่างนั้น การมีเกราะป้องกันขั้นต่ำ (เช่น z.object({}).passthrough()) ก็สามารถช่วยดักจับข้อผิดพลาดในการ parse ที่รุนแรงได้ โดยไม่จำเป็นต้องตัดส่วนขยายที่มีประโยชน์ทิ้งไป
สิ่งที่ควรจับตามองต่อไป
- วิวัฒนาการของ SDK: แผนงาน (roadmap) ของ Vercel’s AI SDK จะรวมถึงนโยบายการ retry ในตัวและการรายงานข้อผิดพลาดที่ละเอียดขึ้น ซึ่งจะช่วยให้กระบวนการแก้ไข (repair loop) มีความคล่องตัวยิ่งขึ้น
- การสร้างมาตรฐานของเครื่องมือ (Tooling standardization): เมื่อผู้ให้บริการรายต่างๆ เริ่มนำข้อกำหนดการใช้งานเครื่องมือ (tool-use conventions) มาใช้มากขึ้น อาจเกิดตัวตรวจสอบ schema ที่ใช้งานข้ามผู้ให้บริการได้ (cross-provider schema validators) ซึ่งจะช่วยลดความจำเป็นในการใช้ adapter เฉพาะสำหรับผู้ให้บริการแต่ละราย
- รูปแบบของชุมชน (Community patterns): ไลบรารีโอเพนซอร์สเริ่มมีการรวม Zod schemas เข้ากับ prompt templates ทำให้เวิร์กโฟลว์แบบ “schema-first” กลายเป็นสินทรัพย์ที่นำกลับมาใช้ใหม่ได้
บทสรุป
การปฏิบัติกับ Zod schema เสมือนเป็นสัญญา (contract) ที่โมเดลไม่สามารถละเมิดได้ ช่วยให้นักพัฒนาเปลี่ยนจากการใช้เทคนิค JSON.parse ที่เปราะบาง ไปสู่ pipeline ที่มีความแน่นอน (deterministic) ซึ่งฟิลด์ที่ไม่คาดคิดจะทำให้เกิดความล้มเหลวในการตรวจสอบ (validation failure) ที่ควบคุมได้ แทนที่จะทำให้ระบบล่ม (production crash) การผสมผสานระหว่างตัวช่วย Output.object ของ Vercel และกลไก tool-use ของ Anthropic จะเปลี่ยน LLMs จากเครื่องมือสร้างข้อความที่คาดเดาไม่ได้ ให้กลายเป็นผู้ให้บริการข้อมูลที่เชื่อถือได้ ช่วยให้ทีมสามารถมุ่งเน้นไปที่ business logic แทนที่จะต้องเสียเวลาไปกับการแก้บั๊กในกรณีขอบเขต (edge-case) ที่ไม่มีที่สิ้นสุด
