DEV Community

Cover image for วิธีใช้ OpenAI Function Calling
Thanawat Wongchai
Thanawat Wongchai

Posted on • Originally published at apidog.com

วิธีใช้ OpenAI Function Calling

เมื่ออ่านคู่มือนี้จบ คุณจะสามารถกำหนดเครื่องมือ ส่งไปยัง OpenAI อ่าน tool call ที่โมเดลส่งกลับมา แยกวิเคราะห์อาร์กิวเมนต์ JSON แล้วเรียกใช้ฟังก์ชันของคุณเองได้อย่างปลอดภัย จากนั้นเปิดใช้ strict mode และ parallel calls รวมถึงตรวจสอบและจำลองฝั่ง API ด้วย Apidog ก่อนนำไปใช้งานจริง แนะนำให้เปิด เอกสาร function calling ของ OpenAI ไว้อ้างอิง และดูภาพรวมเพิ่มเติมจากบทความ สร้างเอเจนต์ด้วย OpenAI Agents SDK

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

สิ่งที่คุณต้องมีก่อนเริ่มต้น

Function calling หรือ tool calling คือกลไกที่ให้โมเดลเลือก “ฟังก์ชันที่ควรเรียก” และส่งอาร์กิวเมนต์กลับมาเป็น JSON ให้แอปของคุณนำไปใช้ต่อ โมเดลไม่ได้รันโค้ดเอง หน้าที่ของโมเดลคือเลือก intent และจัดรูปแบบพารามิเตอร์ ส่วนโค้ดของคุณยังเป็นผู้ตรวจสอบและรันฟังก์ชันจริง

ตัวอย่างเช่น ผู้ใช้ถามว่า:

What is the weather in Paris right now?
Enter fullscreen mode Exit fullscreen mode

โมเดลอาจส่ง tool call กลับมาเป็น:

get_weather({"location": "Paris, France"})
Enter fullscreen mode Exit fullscreen mode

แทนที่คุณจะต้องแยกข้อความธรรมชาติเอง คุณจะได้ชื่อฟังก์ชันและอาร์กิวเมนต์ที่มีโครงสร้างชัดเจน

ก่อนเริ่ม คุณควรมี:

  • 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
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

ใน 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
          }
        }
      }
    ]
  }'
Enter fullscreen mode Exit fullscreen mode

ใน 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\"}"
  }
}
Enter fullscreen mode Exit fullscreen mode

ใน Responses API tool call จะอยู่ใน output และมีรูปแบบ flat กว่า:

{
  "type": "function_call",
  "call_id": "call_12345xyz",
  "name": "get_weather",
  "arguments": "{\"location\":\"Paris, France\"}"
}
Enter fullscreen mode Exit fullscreen mode

จุดสำคัญคือ 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);
}
Enter fullscreen mode Exit fullscreen mode

อย่าเรียกใช้ฟังก์ชันทันทีโดยไม่ตรวจสอบค่า เพราะแม้ 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\"}"
}
Enter fullscreen mode Exit fullscreen mode

ตัวอย่าง 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);
Enter fullscreen mode Exit fullscreen mode

ใน Responses API แนวคิดเหมือนกัน แต่ส่งผลลัพธ์กลับด้วย item ประเภท function_call_output ที่อ้างอิง call_id

วงจรคือ:

  1. โมเดลเลือกฟังก์ชัน
  2. แอปของคุณ parse arguments
  3. แอปของคุณรันฟังก์ชัน
  4. แอปส่งผลลัพธ์กลับ
  5. โมเดลสร้างคำตอบสุดท้าย

ขั้นตอนที่ 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);
  })
);
Enter fullscreen mode Exit fullscreen mode

แต่ถ้าการเรียกต้องทำตามลำดับ หรือผลลัพธ์หนึ่งเป็น input ของอีกอัน ให้ตั้งค่า:

{
  "parallel_tool_calls": false
}
Enter fullscreen mode Exit fullscreen mode

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
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

กฎสำคัญของ 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 คุณควรตรวจสองเรื่อง:

  1. อาร์กิวเมนต์จากโมเดลตรงกับ schema ที่ฟังก์ชันคาดหวังหรือไม่
  2. API ปลายทางที่ฟังก์ชันจะเรียกทำงานตามที่คาดหรือไม่

Apidog ช่วยตรวจและจำลองฝั่ง API ได้ แต่ไม่ได้รันฟังก์ชันในแอปของคุณเอง หน้าที่ของ Apidog คือช่วยตรวจ contract รอบๆ ฟังก์ชันเหล่านั้น

Apidog interface

1. ตรวจโครงสร้าง arguments

นำค่า arguments จาก tool call จริงมาใช้เป็น request body แล้วตรวจใน Apidog เช่น:

{
  "location": "Paris, France",
  "unit": "celsius"
}
Enter fullscreen mode Exit fullscreen mode

สิ่งที่ควร 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"
}
Enter fullscreen mode Exit fullscreen mode

จากนั้นทดสอบ flow ทั้งหมด:

  1. ส่ง prompt ไปยัง OpenAI
  2. รับ tool call
  3. parse และ validate arguments
  4. เรียก get_weather
  5. ให้ get_weather เรียก Apidog mock
  6. ส่งผลลัพธ์กลับไปยังโมเดล
  7. ตรวจคำตอบสุดท้าย

ข้อดีคือคุณสามารถจำลอง 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 และส่งผลลัพธ์กลับไปให้โมเดลตอบผู้ใช้

ขั้นตอนใช้งานจริงคือ:

  1. กำหนด tool ด้วยชื่อ คำอธิบาย และ JSON Schema
  2. ส่ง tool พร้อมข้อความผู้ใช้ไปยัง OpenAI
  3. อ่าน tool_calls หรือ function_call
  4. parse arguments
  5. validate payload
  6. รันฟังก์ชันของคุณ
  7. ส่งผลลัพธ์กลับไปยังโมเดล
  8. เปิด strict และตั้งค่า parallel_tool_calls ให้เหมาะกับ flow
  9. ใช้ Apidog ตรวจ schema และ mock API ปลายทางก่อนขึ้น production

ถ้าต้องการทดสอบด้าน API contract ให้ครบในที่เดียว ให้ ดาวน์โหลด Apidog เพื่อ validate tool call arguments กับ schema ของคุณ และจำลอง API ที่ฟังก์ชันของคุณต้องพึ่งพา

Top comments (0)