สร้าง Agent Tools จาก OpenAPI โดยไม่ต้องดูแล Schema ด้วยมือ
โค้ดเบสของเอเจนต์ส่วนใหญ่มักมีไฟล์ที่ไม่มีใครอยากดูแล: คำจำกัดความของเครื่องมือ (tool definitions) หลายสิบรายการที่เขียนเป็น JSON Schema ด้วยมือ ทั้งที่ endpoint และ schema มีอยู่แล้วในเอกสาร OpenAPI เมื่อ API เพิ่มฟิลด์บังคับใหม่ สเปกและเอกสารอาจอัปเดตแล้ว แต่เอเจนต์ยังส่ง payload แบบเดิมจนเกิดข้อผิดพลาด 400
OpenAPI คือคำอธิบาย endpoint ที่เครื่องอ่านได้อยู่แล้ว งานของเราคือแปลงมันเป็นเครื่องมือที่โมเดลเรียกใช้ได้ และทำให้ทั้งสองส่วนซิงก์กันอัตโนมัติ แทนที่จะต้องแก้ไขหลายจุดด้วยมือ
หากต้องการบริบทเพิ่มเติม ลองอ่าน คุณยังจำเป็นต้องมีเครื่องมือ API เมื่อเอเจนต์เขียนโค้ดหรือไม่
ทำไมไม่ควรเขียน Tool Definitions ด้วยมือ
การเขียนด้วยมือพอใช้ได้เมื่อมี endpoint ไม่กี่รายการ แต่จะเริ่มสร้างปัญหาเมื่อมีประมาณ 20 รายการขึ้นไป:
- Schema drift: ทีม API ดูแลสเปก แต่ทีมเอเจนต์ดูแลไฟล์เครื่องมือ ไม่มีสิ่งใดเชื่อมทั้งสองเข้าด้วยกัน จึงเกิดความต่างโดยเงียบ ๆ
- คำอธิบายคุณภาพต่ำ: เมื่อเขียน schema จำนวนมาก คำอธิบายมักเหลือเพียงบรรทัดเดียว ทั้งที่โมเดลใช้ข้อความนี้ในการเลือกเครื่องมือ ดูแนวทางเพิ่มเติมใน การออกแบบ tool schema สำหรับเอเจนต์
-
พบข้อผิดพลาดตอนรันจริง: หาก schema ระบุ
stringแต่ API ต้องการintegerเอเจนต์จะพบ 422 ก็ต่อเมื่อเรียกใช้เครื่องมือนั้นในงานจริง
การสร้างจากสเปกแก้ปัญหาเหล่านี้ด้วยแหล่งข้อมูลจริงเพียงแหล่งเดียว: คำอธิบายมาจากเอกสารเดียวกัน และประเภทข้อมูลมาจาก schema ที่เซิร์ฟเวอร์ใช้ตรวจสอบ
แมป OpenAPI Operation เป็น Tool Schema
ตัวอย่าง OpenAPI operation:
paths:
/orders/{orderId}/refund:
post:
operationId: refundOrder
summary: Refund an order
description: >
Issues a full or partial refund against a completed order.
Refunds are irreversible. Partial refunds require an amount
no greater than the remaining refundable balance.
parameters:
- name: orderId
in: path
required: true
schema: { type: string }
description: The order to refund.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [reason]
properties:
amount:
type: integer
description: Amount in cents. Omit for a full refund.
reason:
type: string
enum: [duplicate, fraudulent, requested_by_customer]
แปลงเป็น tool definition ได้ดังนี้:
{
"name": are irreversible. Partial refunds require an amount no greater than the remaining refundable balance.",
"input_schema": {
"type": "object",
"required": ["orderId", "reason"],
"properties": {
"orderId": {
"type": "string",
"description": "The order to refund."
},
"amount": {
"type": "integer",
"description": "Amount in cents. Omit for a full refund."
},
"reason": {
"type": "string",
"enum": ["duplicate", "fraudulent", "requested_by_customer"]
}
}
}
}
กฎสำคัญมี 4 ข้อ:
-
operationIdเป็นชื่อเครื่องมือ หากไม่มี ให้สร้างชื่อที่เสถียรจาก HTTP method และ path แล้วเพิ่มกลับเข้าไปในสเปก - รวม path, query และ body parameters ไว้ใน
propertiesเดียว โมเดลไม่จำเป็นต้องรู้ตำแหน่งบน HTTP request แต่ runtime ต้องรู้ - รวม
summaryและdescriptionเป็นคำอธิบายเครื่องมือ เพราะsummaryเพียงอย่างเดียวมักสั้นเกินไปสำหรับการเลือก tool - รวมฟิลด์บังคับทั้งหมดไว้ใน
requiredเดียว ทั้ง path parameters และ request body fields
ตัวเรียกใช้งานสามารถเรียบง่ายได้:
def execute(tool_name, args, spec_index, http):
op = spec_index[tool_name] # method, path template, param locations
path = op.path
query, body = {}, {}
for name, value in args.items():
location = op.locations[name] # "path" | "query" | "header" | "body"
if location == "path":
path = path.replace("{" + name + "}", str(value))
elif location == "query":
query[name] = value
elif location == "body":
body[name] = value
return http.request(op.method, path, params=query, json=body or None)
เก็บ metadata ของตำแหน่ง parameter แยกไว้ใน spec_index แล้ว runtime จะประกอบ HTTP request จาก arguments ของโมเดลได้อย่างถูกต้อง
สิ่งที่ Generator ต้องจัดการ
การแปลงสเปกแบบตรงตัวมักให้ tool schema ที่โมเดลใช้งานได้ไม่ดี ควรจัดการอย่างน้อย 5 เรื่อง:
-
แก้
$ref: Tool API หลายตัวรองรับ JSON Schema เพียงบางส่วนและไม่ตาม reference ในcomponentsให้ inline schema ที่อ้างอิง แต่ต้องจำกัดความลึกของ recursive schema และอธิบายโครงสร้างส่วนลึกด้วยข้อความ -
ลดคีย์เวิร์ดที่ไม่รองรับ:
oneOf,allOf,discriminatorและnullableมักรองรับได้ไม่ดี รวมallOfด้วยการ merge properties; สำหรับoneOfให้เลือกแบบที่เด่นที่สุด หรือแยกเป็นหลาย tools ตามแต่ละรูปแบบ -
ลดความซ้อนของ payload: โมเดลกรอก body ที่ซ้อนหลายระดับได้ยาก เช่น
customer.address.postal_codeอาจควรเป็น input แบบแบน แล้วให้ runtime ประกอบโครงสร้างกลับ - ตัด response schemas ออก: Tool definition อธิบาย input ไม่ใช่ output การใส่ response schema เต็มรูปแบบสิ้นเปลือง context window อ่านต่อเรื่องนี้ได้ที่ การรักษาการตอบกลับ API ภายใน context window ของเอเจนต์
-
ติดธงการอนุมัติ: Write operations ควรถูกระบุเพื่อให้ runtime ส่งผ่านขั้นตอนอนุมัติ เช่น extension
x-agent-requires-approvalและใช้ร่วมกับแนวทาง AI agent guardrails
อย่าเปิดให้โมเดลเห็นทั้ง 200 Endpoints
ปัญหาหลักของ API ขนาดใหญ่ไม่ใช่การแปลง แต่คือจำนวนเครื่องมือ การส่งหลายร้อย tools ให้โมเดลพร้อมกันทำให้ context ถูกใช้จนหมด และลดความแม่นยำในการเลือกเครื่องมือที่คล้ายกัน
ลด tool surface ตามลำดับนี้:
กรองด้วย tags
OpenAPI tags มักตรงกับพื้นที่ผลิตภัณฑ์ เอเจนต์คืนเงินควรเห็นเฉพาะordersและpaymentsไม่ใช่adminหรือanalyticsใช้ allowlist ตาม
operationId
ระบุ endpoint ที่เอเจนต์ได้รับอนุญาตให้เรียก และสร้างเฉพาะเครื่องมือเหล่านั้น วิธีนี้เป็นมาตรการความปลอดภัยด้วย เพราะ endpoint ที่ไม่มี tool จะไม่ถูกเรียกโดยไม่ตั้งใจ ดูเหตุผลเพิ่มเติมใน การป้องกันเอเจนต์ทำลาย API ของคุณโหลด tools ตามความจำเป็น
สำหรับ API ที่ใหญ่มาก ให้จัดทำดัชนี operation และเลือกเฉพาะ tools ที่เกี่ยวข้องกับงานปัจจุบัน วิธีนี้เพิ่มขั้นตอนและ failure mode จึงควรใช้เมื่อ tags และ allowlist ไม่เพียงพอ
อีกทางเลือกคือ Model Context Protocol หรือ MCP ซึ่งกำหนดมาตรฐานให้เซิร์ฟเวอร์เปิดเผย tools แก่ไคลเอนต์ อ่านแนวคิดได้ใน MCP คืออะไร และดูวิธีใช้งานใน การสร้างเซิร์ฟเวอร์ MCP ด้วย Apidog
ตรวจสอบสเปกจากมุมมองของเอเจนต์ก่อนสร้าง
Generator จะสะท้อนคุณภาพของ OpenAPI specification โดยตรง หากคำอธิบายคลุมเครือ โมเดลจะเลือก endpoint ผิด หาก field ที่ต้องใช้จริงถูกทำเป็น optional เอเจนต์จะล้มเหลวตอนรัน
ตรวจสอบสเปกตามรายการนี้:
- ทุก operation มี
operationIdที่อ่านเหมือนกริยาและคำนาม - ทุก operation มีคำอธิบายว่า ทำอะไร เปลี่ยนแปลงอะไร และไม่ควรใช้เมื่อใด
- ทุก parameter มีคำอธิบายที่ระบุหน่วยและรูปแบบ เช่น “จำนวนเงินเป็นเซ็นต์ ขั้นต่ำ 50”
- กำหนด enums ใน schema แทนการอธิบายเป็นข้อความ
- ระบุ
requiredให้แม่นยำ ไม่ควรทำทุก field เป็น optional เพื่อเลื่อนปัญหา validation ไปยัง runtime
ใน Apidog สเปก เอกสาร mock server และการทดสอบใช้ข้อมูลจากโปรเจกต์เดียวกัน การปรับคำอธิบายหรือ schema ให้ดีขึ้นจึงช่วยทุกส่วนพร้อมกัน ส่วนการรักษาความถูกต้องเมื่อ API เปลี่ยน ดูได้ใน จัดการเวอร์ชัน API ใน Apidog
แชร์ Toolset แทนการคัดลอก
Toolset ที่สร้างแล้วเป็น configuration ดังนั้นควร version-control ร่วมกับสเปกต้นทาง โดยรวมสิ่งต่อไปนี้ไว้ด้วยกัน:
- เวอร์ชัน OpenAPI ที่ปักหมุดไว้
- tag filters
- operation allowlists
- การตั้งค่า flattening หรือ
$refresolution - flags สำหรับ approval ของ write operations
บางแพลตฟอร์มทำให้ configuration นี้เป็นหน่วยที่แชร์ได้ตั้งแต่แรก เช่น Sharkly ซึ่งบันทึกคำสั่ง runtime skills repository และ run settings ไว้กับ agent ทำให้ชุดเครื่องมือที่ผ่านการใช้งานจริงแล้วสามารถใช้ซ้ำข้าม Space ได้ โดยยังใช้ runtime อย่าง Claude Code, Codex หรือเครื่องมือเดิมของทีมได้
ทดสอบ Tools ที่สร้างขึ้นมา
ทดสอบทั้งการสร้างและการเรียกใช้ เพราะ generator อาจสร้างข้อผิดพลาดคนละแบบกับ schema ที่เขียนด้วยมือ
1. ทดสอบ Schema Round-Trip
สำหรับแต่ละ tool ให้สร้างตัวอย่าง input ที่ถูกต้องตาม schema แล้วส่งไปยัง API หากตอบกลับ 400 หรือ 422 แปลว่า tool schema กับ server contract ไม่ตรงกัน และควรแก้ที่ OpenAPI specification
2. ทดสอบการเลือก Tool
สร้างชุด prompt งานขนาดเล็กที่ทราบชื่อ tool ที่ถูกต้อง แล้วบันทึกว่าโมเดลเลือกอะไร ใช้เป็น regression suite เพื่อจับกรณีที่มีคนเปลี่ยนชื่อ operation หรือย่อคำอธิบายมากเกินไป
เนื่องจากผลลัพธ์ของโมเดลไม่ deterministic ให้ยืนยันชื่อเครื่องมือที่เลือก แทนการยืนยัน arguments แบบตายตัว อ่านวิธีออกแบบการทดสอบลักษณะนี้ได้ใน ทดสอบเอเจนต์ที่ไม่สามารถคาดเดาได้
3. ทดสอบกับ Mock ก่อน Production
ชี้เอเจนต์ไปที่ mock server ที่สร้างจากสเปกเดียวกันก่อน deploy จริง คุณจะได้ response ที่สมจริงโดยไม่มีผลข้างเคียง และสามารถจำลอง 500 errors หรือ timeouts เพื่อทดสอบ retry logic ได้
สรุป
OpenAPI specification คือสัญญา ส่วน tool list ควรเป็นภาพสะท้อนของสัญญานั้น ไม่ใช่สำเนาอีกชุดที่ต้องดูแลด้วยมือ
แนวทางที่ใช้งานได้จริงคือ:
- สร้าง tools จาก OpenAPI
- กรอง tools ให้เหลือเฉพาะที่เอเจนต์ต้องใช้
- เขียน operation และ parameter descriptions ให้ชัดเจน
- ทดสอบทั้ง schema validity และ tool selection
- ใช้ mock ก่อนเปิดให้เรียก production
เริ่มต้นจากการ export OpenAPI แล้วนับ operations ที่ไม่มีคำอธิบาย ตัวเลขนั้นคือปริมาณงานระหว่างระบบปัจจุบันของคุณกับชุดเครื่องมือเอเจนต์ที่เชื่อถือได้ หากต้องการรวมสเปก mocks และการทดสอบไว้ที่เดียว ดาวน์โหลด Apidog
คำถามที่พบบ่อย
ฉันสร้าง tools จาก Swagger 2.0 ได้หรือไม่?
ได้ แต่ควรแปลงเป็น OpenAPI 3.x ก่อน เพราะ body model ของ Swagger 2.0 แตกต่างมากพอที่จะทำให้ generator จัดการได้ไม่สม่ำเสมอ ปัจจุบันเครื่องมือส่วนใหญ่มุ่งรองรับ OpenAPI 3.x ดูความแตกต่างได้จาก OpenAPI Specification repository
โมเดลจัดการ tools ได้กี่รายการพร้อมกัน?
ความแม่นยำมักลดลงก่อนถึงขีดจำกัดเชิงเทคนิค โดยเพดานเชิงปฏิบัติมักอยู่ที่ประมาณไม่กี่สิบรายการ หากเกินกว่านั้น ควรกรองด้วย tags หรือ allowlist แทนการเพิ่ม tools ต่อไป
ชื่อ tool ควรตรงกับ operationId หรือไม่?
ควรตรงกัน เมื่อ operationId อ่านเข้าใจได้ คุณสามารถตามจาก tool call กลับไปยัง operation ในสเปกได้โดยตรง ทำให้ tracing และ debugging ง่ายขึ้น หากชื่อไม่ดี ให้แก้ในสเปก ไม่ใช่ใน generator
แล้ว GraphQL API ล่ะ?
ใช้แนวคิดเดียวกันได้: ตรวจสอบ schema และสร้าง tool หนึ่งรายการต่อ query หรือ mutation แต่ GraphQL มักเปิดเผย API surface กว้างกว่า จึงต้องกรองเครื่องมืออย่างเข้มงวดมากขึ้น
ยังต้องเขียน tools ด้วยมือหรือไม่?
ยังมีบ้าง โดยเฉพาะ composite tools ที่เชื่อมหลาย API calls เป็นการกระทำเดียว หรือ tools ที่ไม่ใช่ HTTP แต่ wrapper แบบหนึ่ง endpoint ต่อหนึ่ง tool ไม่ควรเป็นงานที่ต้องเขียนและดูแลด้วยมืออีกต่อไป
จะป้องกันไม่ให้เอเจนต์เรียก write endpoints ระหว่างทดสอบได้อย่างไร?
สร้าง toolset แบบ read-only สำหรับ test runs ด้วยการกรองตาม HTTP method และชี้ write operations ไปที่ mock server ดูรายละเอียดเพิ่มเติมใน เหตุใดเอเจนต์จึงควรเรียกใช้ mocks ไม่ใช่ production


Top comments (0)