เมื่ออ่านคู่มือนี้จบ คุณจะสามารถกำหนดเครื่องมือ ส่งไปยัง OpenAI อ่าน tool call ที่โมเดลส่งกลับมา แยกวิเคราะห์อาร์กิวเมนต์ JSON แล้วเรียกใช้ฟังก์ชันของคุณเองได้อย่างปลอดภัย จากนั้นเปิดใช้ strict mode และ parallel calls รวมถึงตรวจสอบและจำลองฝั่ง API ด้วย Apidog ก่อนนำไปใช้งานจริง แนะนำให้เปิด เอกสาร function calling ของ OpenAI ไว้อ้างอิง และดูภาพรวมเพิ่มเติมจากบทความ สร้างเอเจนต์ด้วย OpenAI Agents SDK
สิ่งที่คุณต้องมีก่อนเริ่มต้น
Function calling หรือ tool calling คือกลไกที่ให้โมเดลเลือก “ฟังก์ชันที่ควรเรียก” และส่งอาร์กิวเมนต์กลับมาเป็น JSON ให้แอปของคุณนำไปใช้ต่อ โมเดลไม่ได้รันโค้ดเอง หน้าที่ของโมเดลคือเลือก intent และจัดรูปแบบพารามิเตอร์ ส่วนโค้ดของคุณยังเป็นผู้ตรวจสอบและรันฟังก์ชันจริง
ตัวอย่างเช่น ผู้ใช้ถามว่า:
What is the weather in Paris right now?
โมเดลอาจส่ง tool call กลับมาเป็น:
get_weather({"location": "Paris, France"})
แทนที่คุณจะต้องแยกข้อความธรรมชาติเอง คุณจะได้ชื่อฟังก์ชันและอาร์กิวเมนต์ที่มีโครงสร้างชัดเจน
ก่อนเริ่ม คุณควรมี:
- OpenAI API key
- ฟังก์ชันในแอปของคุณที่ต้องการให้โมเดลเรียกได้
- JSON Schema สำหรับอาร์กิวเมนต์ของฟังก์ชัน
- API หรือ service ปลายทางที่ฟังก์ชันของคุณจะเรียกใช้
- เครื่องมือทดสอบ เช่น Apidog สำหรับตรวจ payload และสร้าง mock API
คุณสามารถใช้ได้ทั้ง Chat Completions API และ Responses API ความสามารถคล้ายกัน แต่รูปแบบ request/response ต่างกันเล็กน้อย บทความนี้จะแสดงทั้งสองแนวทางในจุดที่จำเป็น
ขั้นตอนที่ 1: กำหนดเครื่องมือของคุณ
เครื่องมือคือคำอธิบายฟังก์ชันที่โมเดลอ่านได้ โดยประกอบด้วย:
-
name: ชื่อฟังก์ชัน -
description: คำอธิบายว่าเมื่อใดควรใช้ฟังก์ชันนี้ -
parameters: JSON Schema ของอาร์กิวเมนต์
คำอธิบายสำคัญมาก เพราะโมเดลใช้ตัดสินใจว่าจะเรียกฟังก์ชันนี้หรือไม่ ให้เขียนเป็น instruction มากกว่าป้ายกำกับสั้นๆ
ตัวอย่าง tool definition สำหรับ Chat Completions:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city. Use when the user asks about temperature or conditions.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and country, e.g. Bogotá, Colombia"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"],
"additionalProperties": false
}
}
}
ใน Responses API รูปแบบจะ flat กว่า โดย name, description, parameters และ strict อยู่ที่ระดับบนสุดของ object เครื่องมือ ไม่ต้องซ้อนใต้คีย์ function
ถ้าคุณมี OpenAPI spec อยู่แล้ว สามารถนำ schema ของ request body หรือ parameters มาใช้เป็นฐานได้เกือบโดยตรง ดูแนวทางเพิ่มเติมได้จากบทความ สร้างชุดการทดสอบจาก OpenAPI specs
ขั้นตอนที่ 2: ส่งคำขอแรกพร้อม tools
ส่งข้อความผู้ใช้พร้อมรายการเครื่องมือที่โมเดลสามารถเลือกใช้ได้ ตัวอย่าง Chat Completions request:
curl https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4.1",
"messages": [
{
"role": "user",
"content": "What is the weather in Paris right now?"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string"
}
},
"required": ["location"],
"additionalProperties": false
}
}
}
]
}'
ใน request นี้:
-
messagesคือบริบทการสนทนา -
toolsคือฟังก์ชันที่คุณเปิดให้โมเดลเรียก - โมเดลจะตัดสินใจเองว่าจะตอบเป็นข้อความปกติหรือส่ง tool call กลับมา
ถ้าโมเดลเลือกเครื่องมือ คุณจะไม่ได้คำตอบสุดท้ายทันที แต่จะได้ข้อมูลการเรียกฟังก์ชันเพื่อให้แอปของคุณนำไปดำเนินการต่อ
ขั้นตอนที่ 3: อ่าน tool call ที่โมเดลส่งกลับมา
เมื่อโมเดลต้องการเรียกฟังก์ชัน มันจะส่ง tool call กลับมาแทนข้อความธรรมดา
ใน Chat Completions ให้ดูที่ tool_calls ของ assistant message:
{
"id": "call_12345xyz",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"Paris, France\"}"
}
}
ใน Responses API tool call จะอยู่ใน output และมีรูปแบบ flat กว่า:
{
"type": "function_call",
"call_id": "call_12345xyz",
"name": "get_weather",
"arguments": "{\"location\":\"Paris, France\"}"
}
จุดสำคัญคือ arguments เป็น string ที่ encode JSON ไว้ ไม่ใช่ object ที่ parse แล้ว ดังนั้นต้อง parse เองทุกครั้ง
ตัวอย่าง Node.js:
const toolCall = response.choices[0].message.tool_calls[0];
const functionName = toolCall.function.name;
const args = JSON.parse(toolCall.function.arguments);
if (functionName === "get_weather") {
const result = await getWeather(args.location, args.unit);
}
อย่าเรียกใช้ฟังก์ชันทันทีโดยไม่ตรวจสอบค่า เพราะแม้ schema จะช่วยจัดรูปแบบ แต่ business rule ยังต้องตรวจเอง เช่น เมืองนั้นอยู่ในพื้นที่ให้บริการหรือไม่
ขั้นตอนที่ 4: รันฟังก์ชันและส่งผลลัพธ์กลับไปยังโมเดล
หลังจาก parse อาร์กิวเมนต์และรันฟังก์ชันแล้ว ให้ส่งผลลัพธ์กลับไปยังโมเดลเพื่อให้สร้างคำตอบสุดท้าย
ใน Chat Completions ให้เพิ่ม message ที่มี role เป็น tool และผูกกับ tool_call_id:
{
"role": "tool",
"tool_call_id": "call_12345xyz",
"content": "{\"temperature\":18,\"condition\":\"Cloudy\"}"
}
ตัวอย่าง flow แบบย่อ:
const messages = [
{
role: "user",
content: "What is the weather in Paris right now?"
}
];
// 1. ส่ง request แรก
const firstResponse = await client.chat.completions.create({
model: "gpt-4.1",
messages,
tools
});
// 2. อ่าน tool call
const assistantMessage = firstResponse.choices[0].message;
const toolCall = assistantMessage.tool_calls[0];
const args = JSON.parse(toolCall.function.arguments);
// 3. รันฟังก์ชันจริงของคุณ
const weather = await getWeather(args.location);
// 4. ส่งผลลัพธ์กลับไปให้โมเดล
messages.push(assistantMessage);
messages.push({
role: "tool",
tool_call_id: toolCall.id,
content: JSON.stringify(weather)
});
const finalResponse = await client.chat.completions.create({
model: "gpt-4.1",
messages,
tools
});
console.log(finalResponse.choices[0].message.content);
ใน Responses API แนวคิดเหมือนกัน แต่ส่งผลลัพธ์กลับด้วย item ประเภท function_call_output ที่อ้างอิง call_id
วงจรคือ:
- โมเดลเลือกฟังก์ชัน
- แอปของคุณ parse arguments
- แอปของคุณรันฟังก์ชัน
- แอปส่งผลลัพธ์กลับ
- โมเดลสร้างคำตอบสุดท้าย
ขั้นตอนที่ 5: เปิด parallel calls และ strict mode
เมื่อ flow พื้นฐานทำงานแล้ว ให้ปรับสองเรื่องนี้เพื่อเพิ่มความน่าเชื่อถือและควบคุมพฤติกรรม
Parallel tool calls
โดยค่าเริ่มต้น โมเดลสามารถส่ง tool call หลายรายการใน response เดียวได้ เช่น ผู้ใช้ถามสภาพอากาศของสามเมือง โมเดลอาจส่งการเรียก get_weather สามครั้งพร้อมกัน
ถ้าฟังก์ชันเป็นอิสระต่อกัน คุณสามารถรันพร้อมกันได้:
const results = await Promise.all(
toolCalls.map(async (call) => {
const args = JSON.parse(call.function.arguments);
return runTool(call.function.name, args);
})
);
แต่ถ้าการเรียกต้องทำตามลำดับ หรือผลลัพธ์หนึ่งเป็น input ของอีกอัน ให้ตั้งค่า:
{
"parallel_tool_calls": false
}
Strict mode
ตั้งค่า strict: true เพื่อบังคับให้อาร์กิวเมนต์ตรงกับ JSON Schema มากขึ้น แทนที่จะเป็น best effort
ตัวอย่าง schema แบบ strict:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string"
},
"unit": {
"type": ["string", "null"],
"enum": ["celsius", "fahrenheit", null]
}
},
"required": ["location", "unit"],
"additionalProperties": false
}
}
}
กฎสำคัญของ strict mode:
- ทุก object ต้องมี
additionalProperties: false - ทุก field ใน
propertiesต้องอยู่ในrequired - ถ้าต้องการ optional field ให้ทำเป็น nullable เช่น
["string", "null"] - โมเดลอาจส่ง
nullแต่ไม่ควรละคีย์นั้นไป
| การตั้งค่า | สิ่งที่ควบคุม | ค่าเริ่มต้น | เมื่อใดควรเปลี่ยน |
|---|---|---|---|
parallel_tool_calls |
อนุญาตให้มี tool call หลายรายการใน response เดียวหรือไม่ | true |
ตั้งเป็น false เมื่อการเรียกขึ้นต่อกันหรือต้องรันตามลำดับ |
strict |
บังคับให้อาร์กิวเมนต์ตรงกับ schema หรือไม่ | best effort ถ้าไม่ตั้งค่า | ควรเปิดเมื่อคุณต้อง parse และใช้ payload ต่อโดยตรง |
tool_choice |
ควบคุมว่าโมเดลเรียก tool ได้หรือไม่ และเรียกตัวใด | auto |
ใช้ required เพื่อบังคับเรียก, none เพื่อปิด, หรือระบุชื่อ tool เพื่อบังคับฟังก์ชันเดียว |
Strict mode ช่วยลด payload ที่ผิดรูปแบบ แต่ไม่ได้รับประกัน business correctness เช่น location อาจเป็น string ที่ถูกต้องตาม schema แต่เป็นเมืองที่ระบบคุณไม่รองรับ ดังนั้นยังต้อง validate ฝั่งแอปและทดสอบปลายทางจริงหรือ mock เสมอ
วิธีทดสอบใน Apidog
ก่อนต่อ tool call เข้ากับ production flow คุณควรตรวจสองเรื่อง:
- อาร์กิวเมนต์จากโมเดลตรงกับ schema ที่ฟังก์ชันคาดหวังหรือไม่
- API ปลายทางที่ฟังก์ชันจะเรียกทำงานตามที่คาดหรือไม่
Apidog ช่วยตรวจและจำลองฝั่ง API ได้ แต่ไม่ได้รันฟังก์ชันในแอปของคุณเอง หน้าที่ของ Apidog คือช่วยตรวจ contract รอบๆ ฟังก์ชันเหล่านั้น
1. ตรวจโครงสร้าง arguments
นำค่า arguments จาก tool call จริงมาใช้เป็น request body แล้วตรวจใน Apidog เช่น:
{
"location": "Paris, France",
"unit": "celsius"
}
สิ่งที่ควร assert:
-
locationมีอยู่จริง -
locationเป็น string -
unitอยู่ใน enum ที่อนุญาต - ไม่มี field แปลกปลอมถ้า schema ใช้
additionalProperties: false - field ที่จำเป็นมีครบ
คุณสามารถใช้ JSONPath เพื่อดึงค่าเฉพาะจาก payload และใช้ JSON Schema validation เพื่อตรวจ schema เดียวกับที่ส่งให้ OpenAI
แนวทางที่ดีคือใช้ schema เดียวกันทั้งสามจุด:
- tool definition ที่ส่งให้ OpenAI
- validation ใน Apidog
- validation ใน application code
2. จำลอง API ปลายทางที่ฟังก์ชันจะเรียก
สมมติฟังก์ชัน get_weather ของคุณต้องเรียก weather provider จริง ระหว่างพัฒนา provider อาจมี rate limit, มีค่าใช้จ่าย หรือยังไม่พร้อมใช้งาน
ให้สร้าง mock API ใน Apidog แล้วชี้ฟังก์ชันของคุณไปที่ mock แทน
ตัวอย่าง mock response:
{
"location": "Paris, France",
"temperature": 18,
"unit": "celsius",
"condition": "Cloudy"
}
จากนั้นทดสอบ flow ทั้งหมด:
- ส่ง prompt ไปยัง OpenAI
- รับ tool call
- parse และ validate arguments
- เรียก
get_weather - ให้
get_weatherเรียก Apidog mock - ส่งผลลัพธ์กลับไปยังโมเดล
- ตรวจคำตอบสุดท้าย
ข้อดีคือคุณสามารถจำลอง error case ได้ เช่น:
- timeout
- HTTP 429
- response body ผิดรูปแบบ
- field บางตัวหายไป
- provider ส่ง error message กลับมา
กรณีเหล่านี้มักทำซ้ำได้ยากกับ live API แต่จำเป็นต่อการทดสอบ production readiness
คำถามที่พบบ่อย
Function calling ใช้งานได้ทั้ง Chat Completions และ Responses API หรือไม่?
ได้ ทั้งสอง endpoint รองรับ function calling ความต่างหลักคือรูปแบบข้อมูล Chat Completions ซ้อนรายละเอียดไว้ใต้ function และส่งคืน tool_calls ส่วน Responses API ใช้ tool definition ที่ flat กว่า และส่งคืนรายการ function_call ใน output
ทำไม arguments เป็น string ไม่ใช่ object?
เพราะ API ส่ง arguments เป็น JSON-encoded string คุณต้อง parse เองเสมอ เช่น JSON.parse(...) และควร validate ผลลัพธ์ก่อนใช้จริง การตรวจด้วย JSON Schema validation ช่วยดัก payload ที่ผิดรูปแบบก่อนถึง business logic
Strict mode รับประกันว่าฟังก์ชันจะสำเร็จหรือไม่?
ไม่ Strict mode รับประกันเรื่องโครงสร้างตาม JSON Schema เท่านั้น ไม่ได้ตรวจ business rule และไม่ได้รันฟังก์ชันให้คุณ คุณยังต้องตรวจค่าด้วยตัวเอง และต้องจัดการ error จาก API ปลายทาง เช่น timeout, 404 หรือ 429
Apidog รันฟังก์ชันจริงของฉันได้หรือไม่?
ไม่ Apidog ไม่ได้รัน application function ของคุณ หน้าที่ของ Apidog คือช่วยตรวจ arguments ที่โมเดลสร้างขึ้น และจำลอง API ที่ฟังก์ชันของคุณพึ่งพา แอปของคุณยังเป็นผู้รันฟังก์ชันจริง
สรุป
Function calling คือ pattern ที่แบ่งหน้าที่ชัดเจน: โมเดลเลือกฟังก์ชันและสร้าง arguments ส่วนแอปของคุณ parse, validate, execute และส่งผลลัพธ์กลับไปให้โมเดลตอบผู้ใช้
ขั้นตอนใช้งานจริงคือ:
- กำหนด tool ด้วยชื่อ คำอธิบาย และ JSON Schema
- ส่ง tool พร้อมข้อความผู้ใช้ไปยัง OpenAI
- อ่าน
tool_callsหรือfunction_call - parse
arguments - validate payload
- รันฟังก์ชันของคุณ
- ส่งผลลัพธ์กลับไปยังโมเดล
- เปิด
strictและตั้งค่าparallel_tool_callsให้เหมาะกับ flow - ใช้ Apidog ตรวจ schema และ mock API ปลายทางก่อนขึ้น production
ถ้าต้องการทดสอบด้าน API contract ให้ครบในที่เดียว ให้ ดาวน์โหลด Apidog เพื่อ validate tool call arguments กับ schema ของคุณ และจำลอง API ที่ฟังก์ชันของคุณต้องพึ่งพา

Top comments (0)