DEV Community

Cover image for การออกแบบ Tool Schema: พัฒนา AI Agent ให้เลือก Endpoint ได้อย่างแม่นยำ
Thanawat Wongchai
Thanawat Wongchai

Posted on Originally published at apidog.com

การออกแบบ Tool Schema: พัฒนา AI Agent ให้เลือก Endpoint ได้อย่างแม่นยำ

ออกแบบคำอธิบายเครื่องมือให้ AI agent เลือกได้ถูกต้อง

คุณมอบเครื่องมือสองชิ้นให้เอเจนต์: updateUser และ deactivateUser ตั๋วสนับสนุนระบุว่า “ปิดบัญชีนี้” เอเจนต์เรียก deactivateUser เมื่อสัปดาห์ที่แล้ว แต่ตั๋วที่คล้ายกันเกือบทำให้เรียก updateUser ด้วย status: "closed" ซึ่ง API ยอมรับและมีความหมายต่างกันในขั้นตอนถัดไป

ลองใช้ Apidog วันนี้

ไม่มีอะไรเสียที่ตัวโมเดลเอง โมเดลกำลังเลือกระหว่างตัวเลือกที่เป็นไปได้สองแบบ ซึ่งคำอธิบายไม่ได้บอกชัดว่าควรใช้ตัวใด การเลือกเครื่องมือจึงเป็นโหมดความล้มเหลวที่มักถูกโยนความผิดให้โมเดล ทั้งที่จุดแก้ไขอยู่ใน schema ซึ่งเป็นข้อมูลหลักที่โมเดลใช้ตัดสินใจ

คู่มือนี้อธิบาย:

  • โมเดลอ่านอะไรเมื่อเลือกเครื่องมือ
  • วิธีตั้งชื่อและเขียนคำอธิบายให้แยกแยะได้
  • วิธีออกแบบพารามิเตอร์เพื่อลดข้อผิดพลาด
  • วิธีทดสอบการเลือกเครื่องมือไม่ให้ถดถอย
  • สิ่งที่ควรใส่ไว้ใน API spec เมื่อสร้างเครื่องมือจากข้อกำหนด เช่นในแนวทาง เปลี่ยน OpenAPI spec ให้เป็นเครื่องมือของเอเจนต์

หากเครื่องมือสร้างจากคำจำกัดความ API ของคุณ Apidog คือจุดที่ควรแก้คำอธิบาย เพราะการปรับปรุงเอกสารจะปรับปรุงเครื่องมือไปพร้อมกัน

ภาพประกอบการออกแบบเครื่องมือสำหรับ AI agent

โมเดลเห็นอะไรจริง ๆ

ขณะเลือกเครื่องมือ โมเดลจะได้รับ:

  • บทสนทนา
  • system prompt
  • รายการคำจำกัดความเครื่องมือ

แต่ละคำจำกัดความประกอบด้วยชื่อ คำอธิบาย และ schema ของพารามิเตอร์ โมเดลไม่ได้เห็นเอกสาร API ความคิดเห็นในโค้ด หรือความรู้ภายในว่า updateUser เป็น legacy

ดังนั้นความแตกต่างที่สำคัญทั้งหมดต้องอยู่ในคำจำกัดความเครื่องมือเอง ทั้ง คู่มือ Function Calling ของ OpenAI และ เอกสาร Tool Use ของ Anthropic เน้นตรงกันว่า description เป็นส่วนที่สำคัญที่สุด และควรละเอียดพอ ไม่ใช่สั้นที่สุด

ข้อผิดพลาดจากการเลือกมักมี 4 แบบ:

  1. เลือกเครื่องมือที่คล้ายกันผิดตัว — ระบุในคำอธิบายว่าเมื่อใดไม่ควรใช้ และให้ชื่อเครื่องมือพี่น้องที่ควรใช้แทน
  2. ไม่เลือกเครื่องมือใดเลย — ใช้คำศัพท์ที่ผู้ใช้ใช้จริง ไม่ใช่เฉพาะศัพท์ใน API
  3. เลือกถูกเครื่องมือ แต่ส่งอาร์กิวเมนต์ผิด — ใช้ enum หน่วย และรูปแบบข้อมูลที่ชัดเจน
  4. เลือกผิดเพราะไม่รู้ลำดับขั้นตอน — ระบุเงื่อนไขเบื้องต้นและเครื่องมือที่ต้องเรียกก่อน

ตั้งชื่อเครื่องมือตามสิ่งที่ทำ

โมเดลอ่านชื่อก่อน description ดังนั้นชื่อจึงสำคัญกว่าความยาวที่แสดงผล

ใช้รูปแบบ verbNoun ให้สม่ำเสมอ:

createOrder
refundOrder
getOrderStatus
Enter fullscreen mode Exit fullscreen mode

อย่าผสมรูปแบบอย่าง order_create, getOrder และ refund ในชุดเดียวกัน เพราะความไม่สม่ำเสมอทำให้การเปรียบเทียบยากขึ้น

ระบุวัตถุและวิธีการให้ชัด:

searchCustomersByEmail
Enter fullscreen mode Exit fullscreen mode

ดีกว่า:

search
Enter fullscreen mode Exit fullscreen mode

หลีกเลี่ยงศัพท์เฉพาะภายใน หาก API เรียกผู้ใช้ว่า entity และการสมัครสมาชิกว่า instrument โมเดลอาจไม่เชื่อมโยงกับข้อความที่พูดถึง “customer” และ “plan” ตั้งชื่อด้วยภาษาของงาน ไม่ใช่ภาษาภายใน schema

อย่าใช้ชื่อซ้ำในบริบทต่างกัน เครื่องมือชื่อ list สองตัวจะกลายเป็นความกำกวมทันทีเมื่ออยู่ในรายการเดียวกัน

เขียนคำอธิบายที่แยกแยะได้

คำอธิบายที่ดีควรตอบ 4 คำถาม:

  1. เครื่องมือนี้ทำอะไร
  2. เปลี่ยนแปลงอะไร
  3. ควรใช้เมื่อใด
  4. ไม่ควรใช้เมื่อใด

คู่ที่อ่อนแอ:

{ "name": "updateUser", "description": "Updates a user." }
{ "name": "deactivateUser", "description": "Deactivates a user." }
Enter fullscreen mode Exit fullscreen mode

คู่ที่แยกแยะได้จริง:

{
  "name": "updateUser",
  "description": "อัปเดตฟิลด์โปรไฟล์ของผู้ใช้ที่ใช้งานอยู่ เช่น ชื่อ อีเมล หรือเขตเวลา ใช้สำหรับการแก้ไขและการแก้ไขโปรไฟล์ที่ผู้ใช้ร้องขอ ไม่เปลี่ยนสถานะบัญชี หากต้องการปิดใช้งานบัญชี ให้ใช้ deactivateUser แทน ห้ามใช้เพื่อปิดหรือยกเลิกบัญชี"
}
{
  "name": "deactivateUser",
  "description": "ปิดใช้งานบัญชีผู้ใช้ เพิกถอนเซสชันทั้งหมดและบล็อกการเข้าสู่ระบบ สามารถย้อนกลับได้ด้วย reactivateUser ใช้เมื่อลูกค้าขอปิด ยกเลิก พัก หรือระงับบัญชีของตน ไม่ลบข้อมูล สำหรับการลบถาวร ให้ใช้ deleteUser ซึ่งไม่สามารถย้อนกลับได้"
}
Enter fullscreen mode Exit fullscreen mode

เทคนิคสำคัญในตัวอย่างนี้คือ:

  • ระบุเครื่องมือพี่น้อง — “ให้ใช้ deactivateUser แทน” ช่วยแก้ความกำกวมโดยตรง
  • ใช้คำศัพท์ของผู้ใช้ — เช่น “ปิด”, “ยกเลิก”, “พัก” และ “ระงับ”
  • บอกสิ่งที่เครื่องมือไม่ทำ — ข้อความเชิงลบมักแยกแยะได้ดีกว่าคำอธิบายเชิงบวกที่คล้ายกัน
  • ระบุว่าย้อนกลับได้หรือไม่ — โมเดลจะประเมินความเสี่ยงได้ดีขึ้นเมื่อเห็นผลกระทบชัดเจน

คำอธิบาย 2–5 ประโยคเป็นจุดเริ่มต้นที่ดี แต่ความยาวไม่ใช่ปัญหา หากข้อความช่วยป้องกันการเรียกปลายทางที่ทำลายข้อมูลได้

คำอธิบายควรทำงานร่วมกับ guardrails และการบังคับใช้จริงใน runtime ไม่ควรพึ่งคำพูดเพียงอย่างเดียว ดูแนวทางเพิ่มเติมได้จาก AI agent guardrails

ออกแบบพารามิเตอร์ให้ส่งค่าได้ยาก

เมื่อเลือกเครื่องมือถูกต้องแล้ว อาร์กิวเมนต์คือจุดที่ผิดพลาดถัดไป JSON Schema ให้ข้อจำกัดส่วนใหญ่ที่จำเป็น และสามารถอ้างอิง JSON Schema Validation vocabulary ได้

ใช้ enum กับชุดค่าที่ปิด

พารามิเตอร์ status ที่เป็น string เปิดโอกาสให้โมเดลสร้างค่าใหม่เอง แต่ enum จะจำกัดค่าให้ตรงกับ API:

"status": {
  "type": "string",
  "enum": ["pending", "paid", "refunded", "cancelled"],
  "description": "สถานะคำสั่งซื้อ 'cancelled' หมายถึงไม่เคยดำเนินการ; 'refunded' หมายถึงดำเนินการแล้วถูกยกเลิก"
}
Enter fullscreen mode Exit fullscreen mode

ใส่หน่วยในชื่อ

ชื่อ amount คลุมเครือว่าเป็นดอลลาร์หรือเซนต์ ใช้ชื่อที่สื่อหน่วยโดยตรง:

amount_cents
timeout_seconds
distance_meters
duration_ms
Enter fullscreen mode Exit fullscreen mode

ระบุรูปแบบพร้อมตัวอย่าง

"description": "Start date in ISO 8601 format, for example 2026-08-26"
Enter fullscreen mode Exit fullscreen mode

ตัวอย่างรูปแบบวันที่ช่วยให้โมเดลสร้างค่าที่ถูกต้องได้บ่อยกว่าคำว่า “start date” เพียงอย่างเดียว

รักษารายการ required ให้ตรงกับ API

  • ทำเครื่องหมายสิ่งที่ API บังคับเป็น required
  • อย่าทำทุกอย่างเป็น optional แล้วปล่อยให้ runtime จัดการข้อผิดพลาด
  • อย่าทำเครื่องหมายค่าที่ API ตั้งค่าเริ่มต้นให้อย่างชาญฉลาดเป็น required โดยไม่จำเป็น

การตรวจสอบความถูกต้องที่ชัดเจนควรทำงานร่วมกับข้อความแสดงข้อผิดพลาดที่ออกแบบมาสำหรับเอเจนต์ ดู การออกแบบข้อความแสดงข้อผิดพลาดของ API สำหรับเอเจนต์

ชอบ schema แบบแบน

โครงสร้างซ้อนกันสร้างข้อผิดพลาดทางโครงสร้างได้ง่าย:

{
  "customer": {
    "address": {
      "postal_code": "..."
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

หากทำได้ ให้ใช้ customer_postal_code ที่ขอบเขตเครื่องมือ แล้วประกอบกลับใน operator ของคุณ

แยกเครื่องมือที่ทำหลายความหมาย

เครื่องมือที่มี mode ซึ่งเปลี่ยนความหมายของฟิลด์อื่นทั้งหมด มักเป็นเครื่องมือสองตัวที่ถูกบีบรวมกัน การแยกออกช่วยให้:

  • โมเดลเลือกได้แม่นยำขึ้น
  • schema แต่ละตัวง่ายขึ้น
  • การตรวจ:
{
  "name": "captureCharge",
  "description": "บันทึกการเรียกเก็บเงินที่ได้รับอนุญาตไว้ก่อนหน้านี้ ต้องมี authorization_id จาก authorizeCharge เรียก authorizeCharge ก่อนหากคุณยังไม่มี ไม่สามารถบันทึกได้เกินกว่าจำนวนที่ได้รับอนุญาต"
}
Enter fullscreen mode Exit fullscreen mode

สองบรรทัดนี้ระบุทั้ง dependency และข้อจำกัดที่สำคัญ ใช้แนวคิดเดียวกันกับ:

  • สร้างก่อนอัปเดต
  • อัปโหลดก่อนประมวลผล
  • อนุญาตก่อนบันทึก
  • เรียกขั้นตอนก่อนหน้าก่อนใช้ ID ที่ได้จากขั้นตอนนั้น

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

ทดสอบการเลือกเหมือนพฤติกรรมอื่น

คำอธิบายคือโค้ด และสามารถถดถอยได้ การย่อข้อความเพียงเล็กน้อยอาจทำให้เอเจนต์เลือกปลายทางผิดในวันถัดไป

สร้างชุดทดสอบขนาดเล็กประมาณ 20–50 prompt โดยแต่ละรายการระบุเครื่องมือที่คาดหวังไว้ จากนั้น:

  1. รัน prompt
  2. บันทึกชื่อเครื่องมือที่โมเดลเลือก
  3. ตรวจเฉพาะชื่อเครื่องมือก่อน ไม่ต้องตัดสินอาร์กิวเมนต์
  4. รันแต่ละ prompt หลายครั้ง
  5. นำชุดทดสอบไปรันใน CI

เริ่มจากกรณีที่มีโอกาสผิดสูงสุด:

  • เครื่องมือสองตัวที่คล้ายกันที่สุด พร้อม prompt ที่ควรเลือกแต่ละตัว
  • prompt ที่ใช้ศัพท์ของลูกค้าแทนศัพท์ API
  • prompt ที่ไม่ควรตรงกับเครื่องมือใด และพฤติกรรมที่ถูกต้องคือถามกลับ
  • เครื่องมือทำลายข้อมูลที่การเลือกผิดมีต้นทุนจริง

หากเลือกถูก 4 จาก 5 ครั้ง นั่นยังใกล้เคียงการโยนเหรียญในการใช้งานจริง ควรปรับปรุงคำอธิบายต่อ

แนวทางนี้สอดคล้องกับ การทดสอบ AI agent ที่ไม่เป็นแบบกำหนด

ชี้การรันไปยัง mock เพื่อไม่ให้การทดสอบแตะข้อมูลจริง Apidog สามารถให้บริการ mock จากคำจำกัดความเดียวกับที่ใช้สร้างเครื่องมือ ทำให้ schema และพฤติกรรมยังสอดคล้องกัน

ภาพประกอบการทดสอบ AI agent ด้วย mock

ตัวอย่างชุดเครื่องมือที่มักทำให้เกิดข้อผิดพลาด

ชุด CRUD

API ที่เติบโตมาหลายปีอาจเผยแพร่ getUser, listUsers, searchUsers และ queryUsers ซึ่งสำหรับโมเดลดูเหมือนชื่อ 4 แบบของแนวคิดเดียวกัน

การแก้ไขไม่จำเป็นต้องเขียน description ที่ยาวขึ้นทั้ง 4 ตัว แต่อาจเลือกเปิดเผยเพียงเครื่องมือเดียวต่อเอเจนต์ และซ่อนเครื่องมือที่เหลือไว้

ชุดเครื่องมือที่คัดสรรมาอย่างดีมักดีกว่าชุดที่ครบทุก endpoint

ชุดแอดมิน

เครื่องมืออ่านและเครื่องมือทำลายล้างอาจอยู่ติดกันด้วยน้ำเสียงเดียวกัน:

getInvoice
voidInvoice
deleteInvoice
Enter fullscreen mode Exit fullscreen mode

เพิ่มผลกระทบลงใน description ระบุว่าเครื่องมือใดต้องขออนุมัติ และบังคับใช้นโยบายใน operator อย่าพึ่งพาคำอธิบายเพียงอย่างเดียว ดูแนวทาง ป้องกันไม่ให้เอเจนต์ทำลาย API ของคุณ

ชุด legacy

หาก endpoint สองตัวทำงานเดียวกัน แต่ตัวหนึ่งเลิกใช้แล้ว การมีทั้งคู่ใน spec จะทำให้ generator สร้างเครื่องมือทั้งสอง และเอเจนต์อาจเลือกตัวเก่าประมาณครึ่งหนึ่งของเวลา

ทางเลือกคือ:

  • นำ endpoint ที่เลิกใช้ออกจากชุดเครื่องมือที่สร้างขึ้น
  • หรือเริ่ม description ด้วยข้อความที่ชัดเจน เช่น Deprecated. ใช้ createOrderV2 แทน

โมเดลให้ความสำคัญกับคำเตือนที่อยู่ต้น description มากกว่าข้อความเดียวกันที่ฝังไว้ตอนท้าย

ถือว่า description เป็น configuration ที่ใช้ร่วมกัน

เมื่อ description ขับเคลื่อนพฤติกรรม คำถามถัดไปคือใครเป็นเจ้าของมัน อย่าปล่อยให้เป็นไฟล์ท้องถิ่นของคนที่ตั้งค่าเอเจนต์เป็นคนแรก

ให้ถือชุดเครื่องมือเป็น artifact ที่ใช้ร่วมกัน และตรวจสอบเหมือน interface อื่น ๆ:

  • เก็บไว้ในระบบที่ทีมเข้าถึงได้
  • review การเปลี่ยนแปลง description
  • รันชุดทดสอบการเลือกทุกครั้งที่แก้ไข
  • ทำให้การเปลี่ยนแปลงส่งผลกับเอเจนต์ทุกคนอย่างโปร่งใส

แพลตฟอร์มที่สร้างรอบงานเอเจนต์มักรองรับแนวคิดนี้โดยตรง เช่น Sharkly Agent ซึ่งบันทึกคำแนะนำ Runtime Skills และ repositories ไว้ใน configuration เดียว การแชร์ภายใน Space ทำให้ทีมใช้การตั้งค่าร่วมกันได้

คุณค่าที่แท้จริงไม่ใช่แค่การจัดเก็บ แต่คือการทำให้การเปลี่ยน description กลายเป็นการแก้ไขที่ตรวจสอบได้ แทนที่จะเป็นการปรับแต่งเงียบ ๆ บนเครื่องของนักพัฒนาคนใดคนหนึ่ง

เก็บคำศัพท์จากผู้ใช้จริง

ช่องว่างที่พบบ่อยคือศัพท์ API ไม่ตรงกับภาษาของลูกค้า:

API ใช้คำว่า ลูกค้าอาจใช้คำว่า
subscription plan, membership, billing
deactivate cancel, close, turn off

รวบรวมภาษาจริงจาก:

  • ตั๋วสนับสนุน
  • บันทึกการค้นหา
  • log ของเอเจนต์ที่ล้มเหลว
  • prompt ที่ผู้ใช้ส่งเข้าระบบ

จากนั้นใส่วลีเหล่านั้นลงใน description ของเครื่องมือที่ควรตรงกัน การปรับศัพท์มักเพิ่มความแม่นยำได้มากกว่าการปรับ schema และใช้เวลาไม่นาน

หากเอเจนต์ไม่เลือกเครื่องมือใดแล้วตอบจากความรู้ทั่วไป นั่นอาจไม่ใช่ปัญหาการให้เหตุผล แต่เป็นปัญหาคำศัพท์: ภาษาของงานไม่เคยทับซ้อนกับข้อความในเครื่องมือ ทำให้เครื่องมือนั้นมองไม่เห็น

Checklist สำหรับชุดเครื่องมือ

  • [ ] ชื่อใช้รูปแบบ verbNoun และระบุวัตถุอย่างเฉพาะเจาะจง
  • [ ] ทุก description บอกสิ่งที่เปลี่ยนแปลง ควรใช้เมื่อใด และไม่ควรใช้เมื่อใด
  • [ ] เครื่องมือที่ทับซ้อนกันอ้างชื่อเครื่องมือพี่น้องอย่างชัดเจน
  • [ ] description รวมคำที่ผู้ใช้ใช้จริง
  • [ ] การดำเนินการที่ทำลายล้างหรือย้อนกลับไม่ได้ระบุผลกระทบ
  • [ ] ชุดค่าที่ปิดใช้ enum
  • [ ] หน่วยและรูปแบบอยู่ในชื่อพารามิเตอร์หรือ description พร้อมตัวอย่าง
  • [ ] required ตรงกับข้อบังคับจริงของ API
  • [ ] เครื่องมือที่พึ่งพาระบุเงื่อนไขเบื้องต้น
  • [ ] ชุดทดสอบการเลือกทำงานใน CI ด้วย mock

โมเดลกำลังจับคู่รูปแบบกับข้อความที่คุณเขียน เมื่อเลือกผิด จุดแรกที่ควรตรวจคือชื่อ description และ schema ของเครื่องมือ ไม่ใช่ prompt เพียงอย่างเดียว

หากต้องการจัดการคำอธิบาย mock และการทดสอบไว้ในโปรเจกต์เดียวกัน สามารถ ดาวน์โหลด Apidog ได้

คำถามที่พบบ่อย

Description ควรยาวเท่าใด?

ยาวพอที่จะแยกความกำกวมได้ โดยทั่วไปประมาณ 2–5 ประโยค เครื่องมือที่ไม่กำกวมใช้ข้อความสั้นได้ ส่วนเครื่องมือที่อยู่ติดกับตัวเลือกคล้ายกันควรใช้พื้นที่อธิบายความแตกต่าง

ควรใส่ตัวอย่างใน description หรือไม่?

ควรใส่สำหรับรูปแบบและหน่วย ตัวอย่างช่วยลดข้อผิดพลาดได้มาก ข้ามตัวอย่างการใช้งานที่ยาว เพราะใช้ context มากและมักไม่ช่วยเรื่องการเลือก

ควรมีเครื่องมือเฉพาะทางจำนวนมาก หรือเครื่องมือยืดหยุ่นไม่กี่ตัว?

เครื่องมือเฉพาะทางมักเลือกได้แม่นกว่า เพราะแต่ละตัวทำงานเดียว แต่เมื่อมีจำนวนมากเกินไป รายการเครื่องมือเองจะกลายเป็นปัญหา อาจต้องกรองหรือเรียกค้นก่อน แนวคิดนี้ครอบคลุมอยู่ใน การสร้างเครื่องมือเอเจนต์จาก OpenAPI

แก้การเลือกใน system prompt แทนได้หรือไม่?

ทำได้บางส่วน และเหมาะเป็นทางออกชั่วคราวสำหรับกรณีที่ทราบอยู่หนึ่งหรือสองกรณี แต่ขยายได้ยาก เพราะ system prompt ใช้ร่วมกันกับเครื่องมือทั้งหมด ขณะที่ description เดินทางไปพร้อมกับเครื่องมือที่ต้องการ

ถ้าโมเดลยังสร้างค่าพารามิเตอร์ขึ้นมาเองอยู่ล่ะ?

จำกัดประเภท เพิ่ม enum และระบุว่าค่าต้องมาจากการเรียกก่อนหน้า ไม่ใช่ค่าที่สร้างขึ้นเอง หากยังเกิดขึ้น ให้ตรวจสอบใน wrapper และส่งคืนข้อผิดพลาดที่ระบุค่าที่อนุญาตอย่างชัดเจน

กฎเหล่านี้ใช้กับ MCP server ด้วยหรือไม่?

ใช้ได้ MCP server เผยแพร่ชื่อ description และ schema ในรูปแบบเดียวกัน ดังนั้นหลักการด้านการตั้งชื่อ คำศัพท์ เงื่อนไขเบื้องต้น และข้อจำกัดจึงใช้เหมือนกัน อ่านเพิ่มเติมได้จาก MCP คืออะไร

Top comments (0)