DEV Community

Cover image for Cách sử dụng OpenAI Function Calling
Sebastian Petrus
Sebastian Petrus

Posted on • Originally published at apidog.com

Cách sử dụng OpenAI Function Calling

Đến cuối hướng dẫn này, bạn sẽ triển khai được vòng lặp gọi hàm với OpenAI: định nghĩa công cụ, gửi công cụ vào request, đọc tool_calls hoặc function_call, parse đối số JSON, chạy hàm trong ứng dụng của bạn, rồi trả kết quả về cho mô hình. Bạn cũng sẽ bật strict, kiểm soát parallel_tool_calls, và dùng Apidog để xác thực schema và mô phỏng API phía sau trước khi đưa vào production. Khi cần đối chiếu chi tiết API, hãy mở tài liệu gọi hàm của OpenAI; nếu bạn đang xây dựng tác nhân, xem thêm bài về xây dựng tác nhân với OpenAI Agents SDK.

Dùng thử Apidog hôm nay

Những gì bạn cần trước khi bắt đầu

Gọi hàm hay tool calling là cơ chế để mô hình tạo ra một yêu cầu có cấu trúc cho mã của bạn. Mô hình không tự chạy hàm. Nó chỉ trả về tên hàm và chuỗi JSON chứa đối số. Ứng dụng của bạn chịu trách nhiệm parse, xác thực, thực thi và trả kết quả lại cho mô hình.

Ví dụ, người dùng hỏi:

Lấy thời tiết ở Paris.
Enter fullscreen mode Exit fullscreen mode

Mô hình có thể trả về lời gọi:

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

Thay vì phải phân tích một đoạn văn tự do, bạn nhận được dữ liệu có cấu trúc để xử lý trong code.

Bạn cần chuẩn bị:

  • API key của OpenAI.
  • Một hàm thật trong ứng dụng, ví dụ get_weather.
  • JSON Schema mô tả đối số mà hàm chấp nhận.
  • Quyết định dùng endpoint nào:
    • Chat Completions API: cấu trúc cũ hơn, dùng toolstool_calls.
    • Responses API: cấu trúc mới hơn, dùng tool definition phẳng hơn và trả về function_call trong output.

Các bước dưới đây minh họa cả hai cấu trúc khi cần.

Bước 1: Định nghĩa công cụ của bạn

Một công cụ gồm 3 phần chính:

  • name: tên hàm mà ứng dụng của bạn sẽ map tới.
  • description: mô tả khi nào mô hình nên dùng hàm.
  • parameters: JSON Schema cho đối số.

Với Chat Completions, định nghĩa tool có dạng:

{
  "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

Với Responses API, các trường name, description, parametersstrict nằm trực tiếp ở cấp tool, không lồng trong khóa function.

Nếu bạn đã có OpenAPI spec cho API phía sau, hãy tái sử dụng phần schema đó để định nghĩa tool. Cách này giúp schema phục vụ cả API testing lẫn function calling. Xem thêm hướng dẫn tạo bộ sưu tập kiểm thử từ đặc tả OpenAPI.

Bước 2: Gửi request đầu tiên

Gửi tin nhắn người dùng cùng danh sách tools cho mô hình. Ví dụ với Chat Completions:

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

Ở lượt này, mô hình sẽ quyết định:

  • Trả lời bằng text thông thường.
  • Hoặc trả về một tool call nếu thấy cần gọi get_weather.

Bạn không nên giả định mô hình luôn gọi hàm, trừ khi bạn cấu hình tool_choice để ép hành vi đó.

Bước 3: Đọc tool call từ phản hồi

Khi mô hình chọn gọi hàm, phản hồi sẽ chứa lời gọi công cụ thay vì câu trả lời cuối cùng.

Trong Chat Completions, tool call nằm trong message.tool_calls:

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

Trong Responses API, lời gọi nằm trong mảng output:

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

Điểm quan trọng: argumentschuỗi JSON, không phải object đã được parse. Bạn cần parse thủ công trước khi truyền vào hàm.

Ví dụ 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);
}
Enter fullscreen mode Exit fullscreen mode

Nên luôn validate args trước khi chạy hàm thật, kể cả khi bạn bật strict mode.

Bước 4: Chạy hàm và trả kết quả về mô hình

Sau khi parse đối số, ứng dụng của bạn gọi hàm nội bộ hoặc API phía sau.

Ví dụ:

async function getWeather({ location }) {
  // Ví dụ: gọi API thời tiết thật hoặc mock server
  return {
    location,
    temperature: 18,
    unit: "celsius",
    condition: "cloudy"
  };
}
Enter fullscreen mode Exit fullscreen mode

Với Chat Completions, bạn gửi tiếp lịch sử hội thoại, bao gồm:

  1. Tin nhắn user ban đầu.
  2. Tin nhắn assistant chứa tool_calls.
  3. Tin nhắn tool chứa kết quả, gắn với tool_call_id.

Ví dụ dạng message:

{
  "role": "tool",
  "tool_call_id": "call_12345xyz",
  "content": "{\"location\":\"Paris, France\",\"temperature\":18,\"unit\":\"celsius\",\"condition\":\"cloudy\"}"
}
Enter fullscreen mode Exit fullscreen mode

Sau đó gọi model lần nữa để mô hình tạo câu trả lời cuối cùng cho người dùng.

Với Responses API, bạn gửi item function_call_output được gắn bằng call_id.

Luồng tổng quát luôn là:

User request
→ Model trả tool call
→ App parse arguments
→ App chạy hàm
→ App trả output cho model
→ Model trả câu trả lời cuối
Enter fullscreen mode Exit fullscreen mode

Bước 5: Bật strict mode và kiểm soát parallel calls

Khi luồng cơ bản đã chạy được, hãy thêm hai cấu hình để tăng độ tin cậy.

parallel_tool_calls

Mặc định, mô hình có thể trả về nhiều tool call trong cùng một lượt. Ví dụ người dùng hỏi thời tiết ở Paris, Tokyo và Berlin, bạn có thể nhận 3 lời gọi get_weather.

Nếu các lời gọi độc lập, bạn có thể chạy song song:

const results = await Promise.all(
  toolCalls.map(async (call) => {
    const args = JSON.parse(call.function.arguments);
    return getWeather(args);
  })
);
Enter fullscreen mode Exit fullscreen mode

Nếu các lời gọi phụ thuộc thứ tự hoặc bạn chỉ muốn xử lý từng cái một, đặt:

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

strict

Đặt strict: true để buộc đối số mô hình tạo ra khớp với JSON Schema.

Ví dụ định nghĩa tool chặt chẽ hơn:

{
  "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

Khi dùng strict mode, cần lưu ý:

  • Mỗi object nên có additionalProperties: false.
  • Tất cả field trong properties phải nằm trong required.
  • Field “tùy chọn” nên cho phép null, thay vì bỏ khỏi required.

Ví dụ unit là tùy chọn về mặt nghiệp vụ, nhưng trong strict schema bạn vẫn khai báo trong required và cho phép null. Nhờ vậy code của bạn luôn biết key nào sẽ xuất hiện.

Cài đặt Nó kiểm soát điều gì Mặc định Khi nào nên thay đổi
parallel_tool_calls Liệu nhiều lệnh gọi công cụ có thể quay lại trong một lượt true Đặt false khi các lệnh gọi phụ thuộc vào nhau hoặc phải chạy theo thứ tự
strict Liệu các đối số có bị buộc phải khớp với schema cố gắng hết sức trừ khi được đặt; khuyến nghị bật Bật cho bất kỳ lệnh gọi nào bạn phân tích mà không cần mã phòng thủ
tool_choice Liệu mô hình có thể gọi hàm nào và có gọi hay không auto required để buộc gọi, none để tắt, hoặc đặt tên để ghim nó

Strict mode giúp giảm lỗi cấu trúc JSON, nhưng không thay thế validation nghiệp vụ. Ví dụ location có thể là string hợp lệ nhưng lại nằm ngoài khu vực bạn hỗ trợ. Bạn vẫn cần kiểm tra trong code.

Cách kiểm thử trong Apidog

Trước khi nối tool call vào hàm production, hãy kiểm thử hai phần:

  1. Đối số mô hình tạo ra có khớp schema không.
  2. API phía sau mà hàm gọi có phản hồi đúng như ứng dụng kỳ vọng không.

Apidog phù hợp cho phần hợp đồng API: validate request/response, kiểm tra schema và mock API phụ thuộc. Apidog không chạy thay hàm trong ứng dụng của bạn; ứng dụng của bạn vẫn là nơi thực thi business logic.

Apidog

1. Xác thực cấu trúc arguments

Lấy chuỗi arguments từ phản hồi OpenAI:

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

Parse thành JSON:

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

Sau đó dùng Apidog để validate như một request body:

  • location tồn tại.
  • location là string.
  • unit chỉ nhận celsius, fahrenheit hoặc null.
  • Không có field ngoài schema nếu bạn dùng additionalProperties: false.

Bạn có thể dùng biểu thức JSONPath để kiểm tra field cụ thể, hoặc dùng xác thực dựa trên JSON Schema để kiểm tra toàn bộ payload.

2. Mô phỏng API phía sau

Giả sử get_weather gọi một API thời tiết. Trong quá trình phát triển, API thật có thể:

  • Bị rate limit.
  • Tốn phí.
  • Chưa sẵn sàng.
  • Khó tạo các lỗi như timeout hoặc HTTP 429.

Bạn có thể tạo một API giả lập trong Apidog trả về payload thời tiết mẫu:

{
  "location": "Paris, France",
  "temperature": 18,
  "unit": "celsius",
  "condition": "cloudy"
}
Enter fullscreen mode Exit fullscreen mode

Sau đó trỏ hàm get_weather vào mock endpoint trong môi trường dev:

async function getWeather({ location, unit }) {
  const res = await fetch(`${process.env.WEATHER_API_BASE_URL}/weather`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ location, unit })
  });

  if (!res.ok) {
    throw new Error(`Weather API failed: ${res.status}`);
  }

  return res.json();
}
Enter fullscreen mode Exit fullscreen mode

Với mock API, bạn có thể chủ động test:

  • Response thành công.
  • Thiếu field.
  • Sai kiểu dữ liệu.
  • HTTP 429.
  • HTTP 500.
  • Timeout.

Quy trình thực tế nên là:

Capture tool call từ OpenAI
→ Parse arguments
→ Validate arguments bằng schema trong Apidog
→ Chạy hàm với mock API của Apidog
→ Kiểm tra output
→ Chỉ sau đó mới nối API thật
Enter fullscreen mode Exit fullscreen mode

Câu hỏi thường gặp

Gọi hàm có hoạt động trong cả Chat Completions và Responses API không?

Có. Cả hai endpoint đều hỗ trợ function calling.

Khác biệt chính:

  • Chat Completions lồng định nghĩa hàm trong khóa function và trả về tool_calls.
  • Responses API dùng định nghĩa tool phẳng hơn và trả về item function_call trong mảng output.

Tại sao arguments là chuỗi thay vì object?

arguments là JSON được mã hóa dưới dạng text. Bạn phải parse nó:

const args = JSON.parse(toolCall.function.arguments);
Enter fullscreen mode Exit fullscreen mode

Không nên truyền thẳng chuỗi này vào hàm. Hãy parse, validate, rồi mới thực thi. Nếu muốn bắt lỗi payload sớm, dùng xác thực JSON Schema trước khi gọi business logic.

Strict mode có đảm bảo hàm chạy thành công không?

Không. Strict mode chỉ đảm bảo cấu trúc đối số khớp JSON Schema. Nó không đảm bảo:

  • Giá trị hợp lệ về nghiệp vụ.
  • API phía sau hoạt động.
  • Hàm của bạn không ném lỗi.

Bạn vẫn cần validation nghiệp vụ, xử lý lỗi và retry nếu cần.

Apidog có chạy hàm thật của tôi không?

Không. Apidog không thay thế runtime của ứng dụng. Nó giúp bạn:

  • Xác thực payload mà mô hình tạo ra.
  • Kiểm tra schema.
  • Mock API mà hàm của bạn phụ thuộc.
  • Test các response thành công và lỗi.

Ứng dụng của bạn vẫn là nơi parse tool call, chạy hàm và trả kết quả lại cho OpenAI.

Tổng kết

Bạn đã có luồng triển khai function calling đầy đủ:

  1. Định nghĩa tool bằng JSON Schema.
  2. Gửi tool cùng request tới OpenAI.
  3. Đọc tool_calls hoặc function_call.
  4. Parse arguments.
  5. Validate đối số.
  6. Chạy hàm trong ứng dụng.
  7. Trả output lại cho mô hình.
  8. Bật strict và cấu hình parallel_tool_calls theo nhu cầu.
  9. Dùng Apidog để validate schema và mock API phụ thuộc trước khi lên production.

Nếu bạn muốn kiểm thử phần hợp đồng API, hãy Tải Apidog để xác thực đối số gọi công cụ theo schema và mô phỏng các API mà hàm của bạn phụ thuộc.

Top comments (0)