Bỏ qua để đến nội dung

Tool Calling (gọi hàm)

Tool Calling (còn gọi là Function Calling) cho phép các mô hình vượt ra ngoài việc tạo văn bản bằng cách trả về một “yêu cầu gọi hàm” có cấu trúc khi cần thiết. Chương trình của bạn thực thi logic thực tế (kiểm tra thời tiết, truy vấn cơ sở dữ liệu, đặt hàng), trả kết quả về cho mô hình, và mô hình tạo phản hồi cuối cùng dựa trên nó. RouteAPI hỗ trợ Tool Calling trên cả /v1/chat/completions (định dạng OpenAI) và /v1/messages (định dạng Claude).

Tool Calling là một cuộc trao đổi nhiều vòng:

  1. Bạn khai báo một tập “công cụ” trong yêu cầu (mỗi công cụ có tên, mô tả và schema tham số).
  2. Mô hình xác định liệu có cần công cụ hay không; nếu có, nó trả về một yêu cầu gọi với tham số thay vì trả lời trực tiếp.
  3. Mã của bạn phân tích tham số, thực thi logic thực tế và lấy kết quả.
  4. Bạn truyền kết quả trở lại cho mô hình, mô hình tạo phản hồi ngôn ngữ tự nhiên cho người dùng.

Bản thân mô hình không thực thi bất kỳ mã nào; nó chỉ quyết định “gọi công cụ nào” và “truyền tham số gì”. Việc thực thi thực tế luôn xảy ra ở phía bạn, có nghĩa là các ranh giới bảo mật (kiểm tra quyền, lọc tham số) nằm dưới sự kiểm soát của bạn.

Kịch bảnMô tả
Truy vấn dữ liệu thời gian thựcThời tiết, tỷ giá hối đoái, giá cổ phiếu, tồn kho — thông tin không có trong dữ liệu huấn luyện của mô hình
Tích hợp hệ thống nội bộTruy vấn cơ sở dữ liệu, gọi API nội bộ, đọc trạng thái đơn hàng
Thực thi hành độngĐặt hàng, gửi email, tạo ticket — các thao tác có tác dụng phụ
Trích xuất có cấu trúcBuộc mô hình xuất ra theo schema cố định, tương đương với phương pháp xuất có cấu trúc
Điều phối agentCác framework agent sử dụng Tool Calling để điều khiển các tác vụ nhiều bước

Khả năng Tool Calling phụ thuộc vào mô hình được chọn. Hầu hết các mô hình chính thống (dòng OpenAI GPT, Claude, Gemini, v.v.) đều hỗ trợ, nhưng các chi tiết như số lượng công cụ tối đa và hỗ trợ gọi song song khác nhau. Endpoint danh sách mô hình không trả về trường biểu thị khả năng Tool Calling; hãy tham khảo tài liệu của nhà cung cấp mô hình, hoặc gửi trực tiếp một request thực tế có kèm tools tới mô hình mục tiêu để xác minh, xem thêm Models.

2. Định dạng định nghĩa công cụ (OpenAI)

Phần tiêu đề “2. Định dạng định nghĩa công cụ (OpenAI)”

Trong /v1/chat/completions, các công cụ được khai báo qua mảng cấp cao nhất tools, trong đó mỗi công cụ là một đối tượng lồng nhau với type: "function".

{
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Truy vấn thời tiết hiện tại cho thành phố được chỉ định. Sử dụng tên đầy đủ của thành phố bằng tiếng Trung.",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "Tên thành phố, ví dụ: Bắc Kinh" },
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Đơn vị nhiệt độ, mặc định là độ C"
}
},
"required": ["city"]
}
}
}
]
}
TrườngKiểuBắt buộcMô tả
typestringCóCố định là "function"
function.namestringCóTên công cụ, chỉ có thể chứa chữ cái, số, gạch dưới và gạch ngang
function.descriptionstringKhuyến nghịMô tả mục đích công cụ, mô hình sử dụng điều này để quyết định có gọi hay không
function.parametersobjectKhôngĐịnh nghĩa tham số, JSON Schema tiêu chuẩn

Chất lượng của description trực tiếp quyết định liệu mô hình có chọn đúng công cụ hay không. Mô tả rõ ràng mục đích, ý nghĩa của từng tham số, phạm vi giá trị và điều kiện biên hiệu quả hơn việc điều chỉnh bất kỳ tham số lấy mẫu nào.

parameters sử dụng JSON Schema tiêu chuẩn để mô tả cấu trúc tham số:

  • type: Thường là "object".
  • properties: Kiểu, mô tả, giá trị liệt kê cho từng tham số.
  • required: Danh sách tên tham số bắt buộc.
  • enum: Hạn chế phạm vi giá trị, giảm đáng kể xác suất mô hình truyền giá trị sai.

Các công cụ không có tham số vẫn phải cung cấp schema rỗng: "parameters": { "type": "object", "properties": {} }.

3. Định dạng định nghĩa công cụ (Claude)

Phần tiêu đề “3. Định dạng định nghĩa công cụ (Claude)”

Trong /v1/messages, định nghĩa công cụ là cấu trúc phẳng không có wrapper bên ngoài type và function, và trường tham số được gọi là input_schema.

{
"tools": [
{
"name": "get_weather",
"description": "Truy vấn thời tiết hiện tại cho thành phố được chỉ định. Sử dụng tên đầy đủ của thành phố bằng tiếng Trung.",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "Tên thành phố, ví dụ: Bắc Kinh" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}
]
}

Cấu trúc bên trong của input_schema giống hệt với parameters của OpenAI, cả hai đều là JSON Schema tiêu chuẩn. Sự khác biệt chỉ ở wrapper bên ngoài.

MụcOpenAI (/v1/chat/completions)Claude (/v1/messages)
Wrapper bên ngoài{ "type": "function", "function": {...} }Cấu trúc phẳng trực tiếp, không có wrapper
Trường tên công cụfunction.namename
Trường mô tảfunction.descriptiondescription
Trường tham sốfunction.parametersinput_schema
Schema tham sốJSON Schema tiêu chuẩnJSON Schema tiêu chuẩn (giống hệt)
Trả về từ mô hìnhMảng message.tool_callsKhối tool_use trong content
Vai trò truyền kết quảTin nhắn role: "tool" độc lậpKhối tool_result trong tin nhắn user
Trường liên kết kết quảtool_call_idtool_use_id

Để biết chi tiết đầy đủ về giao thức Claude (bao gồm is_error, streaming input_json_delta, v.v.), xem Giao thức Claude Messages. Các ví dụ tiếp theo trên trang này mặc định sử dụng định dạng OpenAI.

tool_choice kiểm soát cách mô hình chọn công cụ.

Giá trịHành vi
"auto"Mô hình tự quyết định có gọi công cụ hay không và gọi cái nào. Giá trị mặc định khi có tools
"none"Cấm gọi bất kỳ công cụ nào, mô hình chỉ xuất văn bản
"required"Phải gọi ít nhất một công cụ, nhưng mô hình chọn cái nào
{ "type": "function", "function": { "name": "get_weather" } }Buộc gọi công cụ được chỉ định
{ "tool_choice": "auto" }

Phổ biến nhất. Phù hợp cho các kịch bản “câu hỏi của người dùng đôi khi cần công cụ, đôi khi trả lời trực tiếp”.

{ "tool_choice": "none" }

Tạm thời vô hiệu hóa công cụ trong khi giữ định nghĩa. Thường được sử dụng ở giai đoạn kết thúc “để mô hình tóm tắt, không gọi lại công cụ”.

{ "tool_choice": "required" }

Buộc mô hình đi theo đường công cụ. Phù hợp cho các kịch bản như trích xuất có cấu trúc mà “phải tạo ra kết quả có cấu trúc”.

{
"tool_choice": {
"type": "function",
"function": { "name": "get_weather" }
}
}

Tương đương định dạng Claude là { "type": "tool", "name": "get_weather" }, required tương ứng với { "type": "any" }, none tương ứng với { "type": "none" }.

Một cuộc gọi công cụ hoàn chỉnh bao gồm ít nhất hai vòng yêu cầu.

Vòng đầu tiên: mô hình trả về tool_calls

Phần tiêu đề “Vòng đầu tiên: mô hình trả về tool_calls”

Gửi yêu cầu với tools:

Terminal window
curl https://api.routeapi.ai/v1/chat/completions \
-H "Authorization: Bearer $ROUTEAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [
{ "role": "user", "content": "Thời tiết ở Bắc Kinh bây giờ thế nào?" }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Truy vấn thời tiết hiện tại cho thành phố được chỉ định.",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}
]
}'

Khi mô hình xác định cần một công cụ, finish_reason là tool_calls, message.content là null, và tool_calls chứa yêu cầu gọi:

{
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"北京\"}"
}
}
]
},
"finish_reason": "tool_calls"
}
]
}

Lưu ý arguments là một chuỗi JSON, không phải đối tượng; bạn cần tự phân tích nó bằng JSON.parse / json.loads. Mô hình đôi khi có thể tạo JSON không hợp lệ, vì vậy phân tích phải nằm trong try/catch.

Phân phối theo function.name đến logic thực tế của bạn, thực thi với các tham số đã phân tích:

import json
args = json.loads(tool_call["function"]["arguments"])
result = get_weather(**args) # "Bắc Kinh, nắng, 23 độ C"

Thêm tin nhắn assistant của vòng đầu tiên (với tool_calls) trở lại messages nguyên trạng, sau đó thêm một tin nhắn role: "tool" mang kết quả. tool_call_id phải khớp chính xác với id từ vòng đầu tiên:

{
"model": "gpt-5.5",
"messages": [
{ "role": "user", "content": "Thời tiết ở Bắc Kinh bây giờ thế nào?" },
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\": \"北京\"}" }
}
]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "Bắc Kinh, nắng, nhiệt độ 23 độ C, độ ẩm 45%."
}
],
"tools": []
}

Yêu cầu vòng thứ hai trả về kết quả ngôn ngữ tự nhiên, finish_reason trở về stop:

{
"choices": [
{
"message": {
"role": "assistant",
"content": "Bắc Kinh hiện tại trời nắng, nhiệt độ 23 độ C, độ ẩm 45%, khá thoải mái."
},
"finish_reason": "stop"
}
]
}

Mô hình có thể cần nhiều vòng gọi công cụ để hoàn thành nhiệm vụ: trước tiên kiểm tra số đơn hàng, sau đó sử dụng số đơn hàng để kiểm tra vận chuyển. Mỗi vòng tuân theo vòng lặp “mô hình trả về tool_calls → thực thi → truyền trở lại kết quả” cho đến khi finish_reason trở về stop. Mã sản xuất nên được viết dưới dạng vòng lặp với giới hạn số vòng tối đa để ngăn vòng lặp vô hạn:

MAX_TURNS = 5
for _ in range(MAX_TURNS):
resp = call_model(messages)
msg = resp["choices"][0]["message"]
messages.append(msg)
if not msg.get("tool_calls"):
break # Mô hình đưa ra phản hồi cuối cùng
for tc in msg["tool_calls"]:
result = dispatch(tc) # Thực thi và trả về chuỗi
messages.append({
"role": "tool",
"tool_call_id": tc["id"],
"content": result,
})

Trong một vòng, mô hình có thể đồng thời yêu cầu nhiều công cụ độc lập (ví dụ: kiểm tra thời tiết ở cả Bắc Kinh và Thượng Hải), dẫn đến tool_calls là một mảng nhiều phần tử. Bạn cần thêm một tin nhắn role: "tool" tương ứng cho từng tool_call, với tool_call_id căn chỉnh một-một; thiếu một cái sẽ gây lỗi trong vòng tiếp theo.

Để vô hiệu hóa tính song song và buộc mô hình chỉ gọi một công cụ tại một thời điểm, thêm "parallel_tool_calls": false ở định dạng OpenAI, hoặc thêm "disable_parallel_tool_use": true trong tool_choice cho định dạng Claude.

Khi đặt stream: true, các tham số gọi công cụ được trả về dần dần theo từng phần.

delta.tool_calls của mỗi chunk SSE chứa index (xác định cuộc gọi công cụ nào), và function.arguments là một đoạn của JSON tham số:

data: {"choices":[{"delta":{"tool_calls":[{"index":0,"id":"call_abc123","type":"function","function":{"name":"get_weather","arguments":""}}]}}]}
data: {"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"city\":"}}]}}]}
data: {"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"北京\"}"}}]}}]}
data: {"choices":[{"finish_reason":"tool_calls"}]}
data: [DONE]

Tích lũy theo nhóm index: id và name thường chỉ xuất hiện trong đoạn đầu tiên, arguments cần nối tất cả các đoạn trước khi phân tích. Không cố phân tích các trạng thái trung gian trong quá trình nối (đó là JSON chưa hoàn chỉnh).

buffers = {} # index -> {"id", "name", "args"}
for chunk in stream:
for tc in chunk["choices"][0]["delta"].get("tool_calls", []):
i = tc["index"]
buf = buffers.setdefault(i, {"id": "", "name": "", "args": ""})
if tc.get("id"):
buf["id"] = tc["id"]
fn = tc.get("function", {})
if fn.get("name"):
buf["name"] = fn["name"]
if fn.get("arguments"):
buf["args"] += fn["arguments"]
# Phân tích sau khi stream kết thúc
for buf in buffers.values():
args = json.loads(buf["args"])

Tham số công cụ streaming định dạng Claude được trả về dần dần qua partial_json của sự kiện input_json_delta, tích lũy theo index rồi phân tích; chi tiết trong Giao thức Claude Messages.

Hầu hết các mô hình chính thống được tích hợp bởi RouteAPI đều hỗ trợ Tool Calling, bao gồm dòng OpenAI GPT, Claude, Gemini, v.v. Trước tiên hãy dùng endpoint danh sách mô hình để xác nhận mô hình có khả dụng trong tài khoản của bạn:

Terminal window
curl https://api.routeapi.ai/v1/models \
-H "Authorization: Bearer $ROUTEAPI_KEY"

Endpoint này chỉ trả về việc mô hình có khả dụng hay không và các endpoint dùng được, không trả về khả năng Tool Calling. Hãy căn cứ vào tài liệu của nhà cung cấp hoặc một request thực tế có kèm tools để xác định mô hình có hỗ trợ Tool Calling.

Các mô hình khác nhau có sự khác biệt trong các khía cạnh sau; xác minh trong môi trường thử nghiệm trước khi đưa vào sản xuất:

Khía cạnhMô tả
Số lượng công cụ tối đaGiới hạn trên của các công cụ có thể được khai báo trong một yêu cầu khác nhau tùy mô hình
Gọi song songMột số mô hình không hỗ trợ trả về nhiều tool_calls trong một vòng
Hỗ trợ tool_choiceKhông phải tất cả các mô hình đều hỗ trợ required / chỉ định công cụ cụ thể
Độ phức tạp tham sốJSON Schema lồng sâu hoặc rất lớn có thể bị cắt bớt hoặc bỏ qua bởi một số mô hình
Độ chi tiết gia tăng streamingCách phân đoạn arguments không nhất quán giữa các mô hình; phải tích lũy theo index

Ví dụ công cụ truy vấn thời tiết (từ đầu đến cuối)

Phần tiêu đề “Ví dụ công cụ truy vấn thời tiết (từ đầu đến cuối)”

Ba đoạn mã sau có chức năng tương đương: định nghĩa công cụ → vòng đầu tiên lấy tool_calls → thực thi → vòng thứ hai truyền trở lại kết quả → lấy phản hồi cuối cùng.

curl (hoàn thành thủ công hai vòng):

Terminal window
# Vòng đầu tiên: gửi yêu cầu với công cụ
curl https://api.routeapi.ai/v1/chat/completions \
-H "Authorization: Bearer $ROUTEAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [{ "role": "user", "content": "Thời tiết ở Bắc Kinh bây giờ thế nào?" }],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Truy vấn thời tiết hiện tại cho thành phố được chỉ định.",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}]
}'
# Vòng thứ hai: truyền trở lại kết quả công cụ (tool_call_id sử dụng giá trị từ vòng đầu tiên)
curl https://api.routeapi.ai/v1/chat/completions \
-H "Authorization: Bearer $ROUTEAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [
{ "role": "user", "content": "Thời tiết ở Bắc Kinh bây giờ thế nào?" },
{
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\": \"北京\"}" }
}]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "Bắc Kinh, nắng, nhiệt độ 23 độ C, độ ẩm 45%."
}
]
}'

Python (SDK OpenAI, tự động hoàn thành hai vòng):

import json
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["ROUTEAPI_KEY"],
base_url="https://api.routeapi.ai/v1",
)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Truy vấn thời tiết hiện tại cho thành phố được chỉ định. Sử dụng tên đầy đủ của thành phố bằng tiếng Trung.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "Tên thành phố, ví dụ: Bắc Kinh"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["city"],
},
},
}
]
def get_weather(city: str, unit: str = "celsius") -> str:
# Thay thế bằng cuộc gọi dịch vụ thời tiết thực tế
return f"{city}, nắng, nhiệt độ 23 độ C, độ ẩm 45%."
messages = [{"role": "user", "content": "Thời tiết ở Bắc Kinh bây giờ thế nào?"}]
# Vòng đầu tiên
resp = client.chat.completions.create(
model="gpt-5.5", messages=messages, tools=tools
)
msg = resp.choices[0].message
if msg.tool_calls:
# Tin nhắn assistant phải được thêm trở lại nguyên trạng, nếu không tool_call_id không thể căn chỉnh
messages.append(msg)
for tc in msg.tool_calls:
args = json.loads(tc.function.arguments)
result = get_weather(**args)
messages.append(
{"role": "tool", "tool_call_id": tc.id, "content": result}
)
# Vòng thứ hai
resp = client.chat.completions.create(
model="gpt-5.5", messages=messages, tools=tools
)
print(resp.choices[0].message.content)

Node.js (gói openai, tự động hoàn thành hai vòng):

import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.ROUTEAPI_KEY,
baseURL: 'https://api.routeapi.ai/v1',
});
const tools = [
{
type: 'function',
function: {
name: 'get_weather',
description: 'Truy vấn thời tiết hiện tại cho thành phố được chỉ định. Sử dụng tên đầy đủ của thành phố bằng tiếng Trung.',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: 'Tên thành phố, ví dụ: Bắc Kinh' },
unit: { type: 'string', enum: ['celsius', 'fahrenheit'] },
},
required: ['city'],
},
},
},
];
function getWeather(city, unit = 'celsius') {
// Thay thế bằng cuộc gọi dịch vụ thời tiết thực tế
return `${city}, nắng, nhiệt độ 23 độ C, độ ẩm 45%.`;
}
const messages = [{ role: 'user', content: 'Thời tiết ở Bắc Kinh bây giờ thế nào?' }];
// Vòng đầu tiên
let resp = await client.chat.completions.create({
model: 'gpt-5.5',
messages,
tools,
});
const msg = resp.choices[0].message;
if (msg.tool_calls) {
messages.push(msg); // Thêm tin nhắn assistant trở lại nguyên trạng
for (const tc of msg.tool_calls) {
const args = JSON.parse(tc.function.arguments);
const result = getWeather(args.city, args.unit);
messages.push({ role: 'tool', tool_call_id: tc.id, content: result });
}
// Vòng thứ hai
resp = await client.chat.completions.create({
model: 'gpt-5.5',
messages,
tools,
});
}
console.log(resp.choices[0].message.content);

Ví dụ công cụ truy vấn cơ sở dữ liệu

Phần tiêu đề “Ví dụ công cụ truy vấn cơ sở dữ liệu”

Sử dụng công cụ như wrapper bảo mật cho hệ thống nội bộ. Quan trọng: SQL không nên được tạo trực tiếp bởi mô hình; thay vào đó, mô hình chọn tham số, và mã của bạn tạo các truy vấn có tham số để tránh injection.

tools = [
{
"type": "function",
"function": {
"name": "query_order",
"description": "Truy vấn trạng thái đơn hàng và số tiền theo số đơn hàng.",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "Số đơn hàng, như ORD-20260917-001",
}
},
"required": ["order_id"],
},
},
}
]
def query_order(order_id: str) -> str:
# Sử dụng truy vấn có tham số, không bao giờ nối đầu ra mô hình trực tiếp vào SQL
row = db.execute(
"SELECT status, amount FROM orders WHERE order_id = %s",
(order_id,),
).fetchone()
if row is None:
return json.dumps({"found": False})
return json.dumps({"found": True, "status": row[0], "amount": row[1]})

Kết quả công cụ nên được truyền trở lại dưới dạng chuỗi JSON; mô hình có thể phân tích các trường có cấu trúc một cách đáng tin cậy hơn.

Khai báo nhiều công cụ cùng lúc; mô hình chọn theo nhu cầu hoặc thậm chí gọi song song. Ở đây sử dụng hai công cụ: thời tiết + tỷ giá hối đoái:

tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Truy vấn thời tiết hiện tại cho thành phố được chỉ định.",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
},
{
"type": "function",
"function": {
"name": "get_exchange_rate",
"description": "Truy vấn tỷ giá hối đoái giữa hai loại tiền tệ.",
"parameters": {
"type": "object",
"properties": {
"from_currency": {"type": "string", "description": "Như USD"},
"to_currency": {"type": "string", "description": "Như CNY"},
},
"required": ["from_currency", "to_currency"],
},
},
},
]
dispatch = {
"get_weather": lambda city: f"{city}, nắng, 23 độ C.",
"get_exchange_rate": lambda from_currency, to_currency: f"1 {from_currency} = 7.2 {to_currency}",
}
messages = [{"role": "user", "content": "Thời tiết ở Bắc Kinh thế nào? Cũng cho tôi biết tỷ giá USD sang CNY."}]
# Vòng lặp để xử lý gọi công cụ nhiều vòng / song song, đặt giới hạn để ngăn vòng lặp vô hạn
for _ in range(5):
resp = client.chat.completions.create(
model="gpt-5.5", messages=messages, tools=tools
)
msg = resp.choices[0].message
messages.append(msg)
if not msg.tool_calls:
break
# Trong cuộc gọi song song, tool_calls là mảng nhiều phần tử; mỗi cái phải được truyền trở lại
for tc in msg.tool_calls:
args = json.loads(tc.function.arguments)
result = dispatch[tc.function.name](**args)
messages.append(
{"role": "tool", "tool_call_id": tc.id, "content": result}
)
print(messages[-1]["content"])

Tool Calling giới thiệu ba điểm lỗi tiềm năng: phía mô hình, phía mã của bạn và phía dịch vụ upstream.

Mô hình có thể trả về JSON không hợp lệ, hoặc thiếu tham số bắt buộc, hoặc truyền giá trị ngoài phạm vi enum. Đảm bảo:

  • Bọc JSON.parse / json.loads trong try/catch.
  • Sau khi phân tích, xác thực các trường bắt buộc và phạm vi giá trị theo schema.
  • Khi xác thực thất bại, truyền thông báo lỗi trở lại như kết quả công cụ để cho phép mô hình tự sửa, thay vì ném trực tiếp ngoại lệ để kết thúc cuộc trò chuyện:
try:
args = json.loads(tc.function.arguments)
city = args["city"] # Xác thực trường bắt buộc
except (json.JSONDecodeError, KeyError) as exc:
result = f"Phân tích tham số thất bại: {exc}. Vui lòng cung cấp lại tham số city hợp lệ."
else:
result = get_weather(city)
messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})

Công cụ được hỗ trợ bởi các dịch vụ thực tế có thể hết thời gian hoặc không khả dụng. Đặt giới hạn thời gian chờ cho mỗi cuộc gọi công cụ; nếu hết thời gian, truyền kết quả trở lại dưới dạng mô tả lỗi rõ ràng để cho phép mô hình chuyển sang chiến lược khác:

try:
result = call_service(args, timeout=5)
except TimeoutError:
result = "Dịch vụ hết thời gian, không lấy được dữ liệu, vui lòng thử lại sau hoặc sử dụng phương pháp khác."

Không thực hiện thử lại tự động cho các công cụ có tác dụng phụ (đặt hàng, gửi email), vì chúng có thể thực thi lặp lại. Thiết kế idempotent hoặc kiểm tra trước khi ghi an toàn hơn.

Lỗi cấp độ nghiệp vụ (không tìm thấy đơn hàng, không có quyền) cũng nên được truyền trở lại cho mô hình, thay vì trả về giá trị rỗng một cách im lặng. Truyền trở lại thông tin lỗi có cấu trúc; mô hình có thể cung cấp giải thích hợp lý cho người dùng:

messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps({"error": "order_not_found", "order_id": order_id}),
})

Định dạng Claude tương ứng đặt "is_error": true trên khối tool_result, xem Giao thức Claude Messages.

  • Khả năng Tool Calling, số lượng công cụ tối đa, hỗ trợ gọi song song phụ thuộc vào mô hình được chọn; xác minh trong môi trường thử nghiệm trước khi đưa vào sản xuất.
  • arguments luôn là chuỗi JSON, không phải đối tượng; phải phân tích rõ ràng.
  • Trong các kịch bản streaming, phải tích lũy các đoạn arguments theo index, phân tích sau khi nối hoàn chỉnh.
  • Các tham số tùy chọn được truyền rõ ràng là 0 hoặc false được coi là do người dùng đặt, không được coi là giá trị mặc định và loại bỏ.
  • Ghi lại request ID, ID mô hình, mã trạng thái và sử dụng token cho mỗi yêu cầu để khắc phục sự cố dễ dàng hơn. Chi tiết cấu trúc lỗi trong Lỗi và gỡ lỗi.