LLM ไม่ได้เจาะเข้าไปในโค้ดของคุณ – พวกมันแค่ส่งคำขอมาให้ และคุณเป็นคนรันฟังก์ชันนั้นเอง ข้อเท็จจริงง่ายๆ นี้ช่วยล้างความเชื่อผิดๆ ที่ว่า “โมเดลสามารถเรียกใช้รูทีน Python ของฉันได้อย่างน่าอัศจรรย์” และบีบให้เหล่านักพัฒนาต้องกลับมาคิดทบทวนเรื่องการดีบั๊ก (debugging) และความปลอดภัยใหม่
ขั้นตอนของลูปการส่งคำสั่ง (dispatch loop) ทีละขั้นตอน
เมื่อโมเดลภาษา (LLM) ต้องการใช้เครื่องมือ มันจะทำตามลำดับขั้นตอนที่แน่นอนดังนี้:
- Planning (การวางแผน) – โมเดลตัดสินใจว่าจำเป็นต้องมีการดำเนินการบางอย่าง (เช่น “คืนเงินค่าชำระ”)
- Generating a request (การสร้างคำขอ) – โมเดลจะส่งข้อความที่มีโครงสร้างออกมา ซึ่งมักจะเป็น JSON โดยจะระบุชื่อเครื่องมือ (tool) และส่งอาร์กิวเมนต์ (arguments) มาให้
- Parsing (การแยกส่วนข้อมูล) – แอปพลิเคชันหรือเฟรมเวิร์กที่สนับสนุนจะอ่านข้อความนั้น
- Matching (การจับคู่) – เฟรมเวิร์กจะค้นหาชื่อในทะเบียนของฟังก์ชันจริงที่คุณเปิดใช้งานไว้
- Validating (การตรวจสอบความถูกต้อง) – ตรวจสอบว่าอาร์กิวเมนต์ตรงกับ schema ของฟังก์ชัน และผู้เรียกใช้งานได้รับอนุญาตหรือไม่
- Executing (การประมวลผล) – ฟังก์ชันที่จับคู่ได้จะทำงานในสภาพแวดล้อมของคุณเพื่อดำเนินการตามงานนั้น
- Returning (การส่งผลลัพธ์กลับ) – ผลลัพธ์จะถูกจัดแพ็กเกจและส่งกลับไปยังโมเดลเพื่อใช้ในการคิดวิเคราะห์ขั้นต่อไป
ให้ลองนึกภาพว่า LLM คือผู้วางแผน, เฟรมเวิร์กคือผู้ส่งคำสั่ง (dispatcher) และฟังก์ชันคือคนทำงานที่เคลื่อนย้ายข้อมูลหรือเงินจริงๆ
ทำไมความเชื่อเรื่อง “เวทมนตร์” ถึงยังคงอยู่
นักพัฒนาส่วนใหญ่เห็นผลลัพธ์จากโมเดลเพียงบรรทัดเดียวที่ดูเหมือนการเรียกฟังก์ชัน แล้วก็ทึกทักเอาเองว่าโมเดลเป็นคนดำเนินการนั้นด้วยตัวเอง คำว่า “tool calling” ในเอกสารของผู้ให้บริการ (provider) ฟังดูเหมือนว่าโมเดลกำลังเรียกใช้โค้ดโดยตรง
ในความเป็นจริง โมเดลเพียงแค่ สร้างข้อความ ที่ อธิบาย ถึงการเรียกใช้งานเท่านั้น กระบวนการของคุณต่างหากที่เป็นคนทำงานหนัก ทั้งการค้นหา (lookup), การตรวจสอบประเภทข้อมูล (type checking), การบังคับใช้สิทธิ์ (permission enforcement) และการจัดการข้อผิดพลาด (error handling)
เฟรมเวิร์กที่ช่วยซ่อนความซับซ้อนเบื้องหลัง
ไลบรารีอย่าง PydanticAI และ LangChain ช่วยลดความซับซ้อนของลูปนี้ เพื่อให้คุณสามารถโฟกัสไปที่ business logic ได้ โดยพวกมันจะทำสิ่งเหล่านี้โดยอัตโนมัติ:
- Validate arguments ตาม schema (เช่น Pydantic model)
- Enforce permissions เพื่อให้แน่ใจว่าผู้ใช้มีสิทธิ์เรียกใช้เครื่องมือนั้น
- Retry on failure โดยจะวนลูปกลับไปหาโมเดลเมื่อเครื่องมือส่งข้อผิดพลาดกลับมา
- Guard against runaway loops โดยการจำกัดจำนวนการเรียกใช้เครื่องมือที่ต่อเนื่องกัน
- Maintain conversation state โดยการนำผลลัพธ์จากเครื่องมือมาประกอบเข้ากับบทสนทนา
แม้จะมีตัวช่วยเหล่านี้ แต่รูปแบบก็ยังคงเดิม: โมเดลไม่เคยรันโค้ดด้วยตัวเอง
การรองรับการเรียกใช้เครื่องมือแบบ Native จากผู้ให้บริการ
ผู้ให้บริการบางรายมาพร้อมกับอินเทอร์เฟซการเรียกใช้เครื่องมือแบบ “native” ที่ช่วยกำหนดมาตรฐานการนิยามเครื่องมือและรูปแบบคำขอ ซึ่งช่วยให้การรวมระบบ (integration) ราบรื่นขึ้น แต่ไม่ได้ตัดขั้นตอนการส่งคำสั่ง (dispatch) ออกไป คุณยังคงต้องเขียน (หรือนำเข้า) โค้ดที่ทำหน้าที่รันการดำเนินการที่ถูกร้องขอจริงๆ
การดีบั๊กจะง่ายขึ้นเมื่อคุณเปลี่ยนชื่อเรียกปัญหา
แทนที่จะโทษว่า “agent สับสน” ให้บอกว่าปัญหาคือ “คำตอบของโมเดลไม่มีการเรียกใช้เครื่องมือ” การแยกแยะแบบนี้มีความสำคัญ:
- No tool call – โมเดลตอบกลับมาโดยตรง หรือไม่สามารถสร้างคำขอที่มีรูปแบบถูกต้องได้
- Malformed request – JSON มีไวยากรณ์ผิดหรือขาดฟิลด์ที่จำเป็น ทำให้ตัวส่งคำสั่ง (dispatcher) ปฏิเสธคำขอนั้น
- Validation failure – อาร์กิวเมนต์ไม่ตรงกับ schema ซึ่งจะทำให้เกิดข้อผิดพลาดก่อนที่จะมีการประมวลผล
การจัดหมวดหมู่ความล้มเหลวช่วยให้คุณสามารถบันทึก log ในแต่ละขั้นตอนของลูป และระบุจุดที่เกิดปัญหาได้อย่างแม่นยำ
เคล็ดลับเชิงปฏิบัติสำหรับ Pipeline ที่เชื่อถือได้
- Treat model output as untrusted input. ปฏิบัติต่อผลลัพธ์ของโมเดลเหมือนเป็นข้อมูลนำเข้าที่ไม่น่าเชื่อถือ โดยต้องผ่านการตรวจสอบแบบ deterministic validation ทุกครั้งก่อนที่จะเรียกใช้โค้ดที่มีผลกระทบต่อระบบ (side-effecting code)
- Log the raw request และผลลัพธ์ของแต่ละขั้นตอนการตรวจสอบ เพื่อสร้างร่องรอยที่สามารถนำมาเล่นซ้ำ (replay) ได้เมื่อเกิดปัญหา
- Set explicit limits สำหรับการเรียกใช้เครื่องมือที่ต่อเนื่องกัน เพราะลูปที่ทำงานไม่หยุดอาจทำให้ทรัพยากรหมดหรือติดข้อจำกัดเรื่อง rate limits
- Wrap each function in a try/except block ที่ส่งคืนออบเจกต์ข้อผิดพลาดที่มีโครงสร้างซึ่งโมเดลสามารถเข้าใจได้ เพื่อกระตุ้นให้เกิดการลองใหม่ (retry) หรือการถอยกลับอย่างเหมาะสม (graceful fallback)
- Separate permission checks from business logic. แยกการตรวจสอบสิทธิ์ออกจาก business logic โดยตรวจสอบสิทธิ์ของผู้เรียกใช้งานก่อนที่ฟังก์ชันจะทำงาน โดยเฉพาะอย่างยิ่งสำหรับการดำเนินการที่สำคัญ เช่น “delete user”
- Use schema-driven definitions (เช่น Pydantic models) เพื่อให้เฟรมเวิร์กสามารถสร้าง JSON schema ที่โมเดลต้องปฏิบัติตามได้โดยอัตโนมัติ
สิ่งที่ควรจับตามองต่อไป
เมื่อผู้ให้บริการปรับปรุง API การเรียกใช้เครื่องมือแบบ native ให้ดียิ่งขึ้น ให้คาดหวังว่าจะมีการกำหนดข้อตกลง (contracts) ที่เข้มงวดขึ้นเกี่ยวกับรูปแบบคำขอและรหัสข้อผิดพลาด (error codes) ที่ละเอียดขึ้น การเปลี่ยนแปลงเหล่านี้จะทำให้การตรวจสอบความถูกต้องง่ายขึ้น และช่วยให้นักพัฒนาสามารถสร้างกำแพงความปลอดภัยที่เข้มงวดขึ้นได้ คอยติดตามการอัปเดตของไลบรารีต่างๆ เพราะหลายแห่งกำลังเพิ่มการรองรับฟีเจอร์ใหม่ล่าสุดของผู้ให้บริการแบบ built-in
บทสรุป
LLM คือเครื่องมือสร้างข้อความที่มีความซับซ้อน ไม่ใช่ผู้ดำเนินการ (executor) โค้ดของคุณยังคงเป็นผู้มีอำนาจเพียงหนึ่งเดียวในการดำเนินการต่าง ๆ และ dispatcher ที่คุณสร้างขึ้น (หรือนำเข้า) คือผู้ควบคุม (gatekeeper) ที่ทำหน้าที่ตรวจสอบความถูกต้อง อนุญาต และรันการดำเนินการเหล่านั้น การปรับเปลี่ยนมุมมองของเวิร์กโฟลว์จะช่วยขจัดความเชื่อผิด ๆ เรื่อง "เวทมนตร์" (magic) ช่วยให้การดีบั๊กแม่นยำยิ่งขึ้น และช่วยบังคับใช้ระเบียบวินัยด้านความปลอดภัยที่ทุกระบบโปรดักชันจำเป็นต้องมี
