Function calling trông giống hệt giữa các nhà cung cấp —cho đến khi nó không. OpenAI gửi tool_calls như một delta bạn tích lũy qua các chunk streaming. Anthropic trả tool_use như một khối nội dung ngang hàng với khối text. Google bọc mọi thứ trong candidates với đối tượng functionCall. DeepSeek bám sát OpenAI —cho đến khi không, ở lời gọi song song.
Code agent của bạn vỡ mỗi lần bạn đổi model. Hướng dẫn này sửa điều đó. Mã chạy được cho cả bốn nhà cung cấp. Bảng khác biệt cho biết thứ gì vỡ ở đâu. Và một mẫu wrapper thống nhất cho phép bạn viết định nghĩa công cụ một lần và dùng mọi nơi. Function calling là nền tảng để xây dựng các agent AI —nắm vững vòng lặp công cụ, kiến trúc agent trở nên đơn giản.
Cách function calling thực sự hoạt động
Mẫu giống nhau ở mọi nhà cung cấp. Hiểu nó một lần quan trọng hơn ghi nhớ cú pháp từng nhà cung cấp.
Vòng lặp công cụ:
- Bạn định nghĩa công cụ —tên, mô tả, JSON Schema cho tham số
- Bạn gửi tin nhắn người dùng + định nghĩa công cụ đến model
- Model quyết định trả lời bằng văn bản hay yêu cầu lời gọi công cụ
- Nếu lời gọi công cụ: code của bạn phân tích tên hàm và đối số —thực thi hàm —gửi kết quả về
- Model xử lý kết quả —quyết định: trả lời bằng văn bản, hay gọi công cụ khác
- Lặp lại cho đến khi model trả lời bằng văn bản hoặc bạn chạm giới hạn lặp tối đa
Function calling so với đầu ra có cấu trúc. Function calling: model quyết định khi nào dùng công cụ —dùng khi model cần quyền tự chủ (“tự xác định thông tin cần và lấy nó”). Đầu ra có cấu trúc: model luôn trả schema của bạn —dùng khi cần định dạng đảm bảo (“luôn trả đối tượng JSON với các trường này”).
Nhà cung cấp 1: OpenAI Function Calling
Triển khai function calling của OpenAI trưởng thành nhất và là chuẩn tham chiếu mà người khác theo.
from openai import OpenAI
import json
client = OpenAI(
base_url="https://api.tokspan.com/v1",
api_key="ts-your-key-here"
)
tools = [{
"type": "function",
"function": {
"name": "get_stock_price",
"description": "Get the current stock price for a ticker symbol. Returns price in USD.",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker, e.g. AAPL"}
},
"required": ["symbol"]
}
}
}]
response = client.chat.completions.create(
model="gpt-5.5",
messages=[{"role": "user", "content": "What's Apple's stock price?"}],
tools=tools,
tool_choice="auto"
)
msg = response.choices[0].message
if msg.tool_calls:
for tool_call in msg.tool_calls:
args = json.loads(tool_call.function.arguments)
result = execute_stock_lookup(args["symbol"])
# Send result back
messages = [
{"role": "user", "content": "What's Apple's stock price?"},
msg,
{"role": "tool", "tool_call_id": tool_call.id, "content": str(result)}
]
final = client.chat.completions.create(model="gpt-5.5", messages=messages)
print(final.choices[0].message.content)
Chi tiết OpenAI. Lời gọi công cụ song song: GPT-5.5 có thể yêu cầu nhiều công cụ trong một phản hồi —kiểm tra nhiều mục trong msg.tool_calls. Streaming: tool_calls đến như delta; tích lũy index —function.name —function.arguments qua các chunk. Đầu ra có cấu trúc + function calling: định nghĩa tham số công cụ với strict: true cho tuân thủ schema đảm bảo.
Nhà cung cấp 2: Sử dụng công cụ Anthropic
Sử dụng công cụ của Claude khác biệt về cấu trúc —công cụ xuất hiện như khối nội dung trong tin nhắn, không phải trường riêng.
import anthropic
client = anthropic.Anthropic(
base_url="https://api.tokspan.com/anthropic",
api_key="ts-your-key-here"
)
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
tools=[{
"name": "get_stock_price",
"description": "Get the current stock price for a ticker symbol.",
"input_schema": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker, e.g. AAPL"}
},
"required": ["symbol"]
}
}],
messages=[{"role": "user", "content": "What's Apple's stock price?"}]
)
for block in response.content:
if block.type == "tool_use":
# Execute the tool Claude requested
result = execute_stock_lookup(block.input["symbol"])
# Build the conversation continuation —the full cycle:
# 1. The assistant message contains ALL content blocks from Claude's response
# 2. The user message contains tool_result blocks matching each tool_use
assistant_msg = {"role": "assistant", "content": response.content}
tool_result_msg = {
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": block.id,
"content": str(result)
}]
}
# Send the result back and get Claude's final response
follow_up = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
messages=[
{"role": "user", "content": "What's Apple's stock price?"},
assistant_msg,
tool_result_msg
]
)
# Claude will return a text block with the final answer
for follow_block in follow_up.content:
if follow_block.type == "text":
print(follow_block.text)
Khác biệt chính với OpenAI. Định nghĩa công cụ dùng input_schema thay vì parameters. Lời gọi công cụ là khối nội dung tool_use trong response.content —ngang hàng khối text, không phải trường riêng. Kết quả công cụ gửi như khối nội dung tool_result trong tin nhắn người dùng. Streaming bao gồm khối tool_use một phần —bạn nhận tên và đối số công cụ dần dần.
Nhà cung cấp 3: Google Gemini Function Calling
# Gemini uses a different structure —function declarations with OpenAPI-like schema
tools = [{
"function_declarations": [{
"name": "get_stock_price",
"description": "Get the current stock price for a ticker symbol.",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker, e.g. AAPL"}
},
"required": ["symbol"]
}
}]
}]
# Response structure:
# response.candidates[0].content.parts[0].function_call.name
# response.candidates[0].content.parts[0].function_call.args
Chi tiết Gemini. Function calling tự động: Gemini có thể gọi và thực thi hàm trong một request API —đặt automatic_function_calling trong cấu hình công cụ. Grounding tìm kiếm: “công cụ” tích hợp phản hồi grounded trong kết quả Google Search mà không cần bạn triển khai API tìm kiếm.
Nhà cung cấp 4: DeepSeek Function Calling
DeepSeek theo định dạng OpenAI. Cùng định nghĩa công cụ, cùng cấu trúc phản hồi. Khác biệt thực: lời gọi công cụ song song kém tin cậy hơn GPT-5.5 —công cụ nên gọi song song đôi khi bị gọi tuần tự. Kiểm thử các kịch bản đa công cụ cụ thể nếu bạn chuyển từ GPT-5.5 sang DeepSeek.
# Identical to OpenAI code —just change base_url and model
client = OpenAI(
base_url="https://api.tokspan.com/v1",
api_key="ts-your-key-here"
)
# Same tool definitions, same response handling as OpenAI example above
Bảng khác biệt giữa các nhà cung cấp
| Tính năng | OpenAI | Anthropic | DeepSeek | |
|---|---|---|---|---|
| Định dạng định nghĩa công cụ | function.parameters (JSON Schema) | input_schema (JSON Schema) | function_declarations.parameters | Giống như OpenAI |
| Vị trí phản hồi | message.tool_calls[] | khối content[] | candidates[].content.parts[] | Giống như OpenAI |
| Lời gọi công cụ song song | Có, đáng tin cậy | Có, đáng tin cậy | Có | Một phần, kém tin cậy hơn |
| Công cụ streaming | Delta, tích lũy | Khối một phần | Candidates một phần | Giống như OpenAI |
| Kiểm soát chọn công cụ | tool_choice: "auto"/"required"/"none" | tool_choice với tùy chọn tương tự | function_calling_config | Giống như OpenAI |
| Số công cụ tối đa mỗi request | 128 | Không ghi chép (lớn) | Không ghi chép | Theo OpenAI |
| Thay đổi code để chuyển đổi | — | 100% (SDK khác) | ~80% | 0% (từ OpenAI) |
Cạm bẫy phổ biến
Đây là những bug đi vào sản xuất. Mỗi cái có sửa lỗi bạn triển khai trong một buổi chiều —nhưng chỉ nếu bạn biết tìm trước khi người dùng tìm.
1. Bug tích lũy tool_calls streaming. Trong chế độ streaming, tool_calls đến qua nhiều chunk —mỗi cái mang index, function.name một phần và chuỗi function.arguments một phần. Sai lầm: gọi json.loads() trên chuỗi đối số trước khi chunk delta cuối với finish_reason: "tool_calls" hạ cánh. Bạn nhận JSONDecodeError mỗi lần, và logic retry làm tệ hơn vì trạng thái một phần bị hỏng. Tích lũy đối số theo index qua các chunk. Chỉ phân tích khi luồng báo hoàn tất. Đây là một trong các chế độ lỗi gọi công cụ sản xuất phổ biến nhất.
2. Khác biệt JSON Schema giữa nhà cung cấp. OpenAI hỗ trợ $ref, anyOf và oneOf lồng nhau trong schema tham số công cụ. Gemini âm thầm bỏ qua định nghĩa $ref —công cụ của bạn vẫn chạy, nhưng model không bao giờ thấy schema được tham chiếu. Anthropic chạy xác thực phía máy chủ nghiêm ngặt hơn OpenAI; schema qua được GPT-5.5 trả 400 trên Claude với lỗi xác thực mù mịt. Kiểm thử schema của bạn với mỗi nhà cung cấp trong CI, không phải thủ công ngày trước ra mắt. Một bước xác thực schema CI với API mỗi nhà cung cấp bắt được điều này trong vài phút.
3. Lệch ID lời gọi công cụ song song. Model trả get_price("AAPL") và get_price("GOOGL") trong một phản hồi. Bạn thực thi cả hai đồng thời. Kết quả đến sai thứ tự. Bạn gán lại tool_call_id sai vì giả định vị trí khớp thứ tự thực thi. Model nhận giá GOOGL dưới ID AAPL và tạo câu trả lời tự tin, hợp lý và hoàn toàn sai. Luôn lập chỉ mục kết quả theo tool_call_id trước khi xây tin nhắn kết quả. Không bao giờ dựa vào vị trí mảng.
4. Lỗi công cụ trôi qua như dữ liệu hợp pháp. Lời gọi HTTP của hàm get_stock_price của bạn quá hạn. Bạn bắt ngoại lệ và trả chuỗi "Error: connection timeout". Model đọc chuỗi đó như dữ liệu và trả lời: “Giá hiện tại là Error: connection timeout.” Định dạng lỗi công cụ với tiền tố nhận diện như TOOL_ERROR: <type> —<message>. Mô tả xử lý lỗi trong trường description của công cụ để model biết retry hoặc báo bạn công cụ thất bại. Model không thể phân biệt bug với dữ liệu bất thường trừ khi bạn cho nó tín hiệu.
Wrapper function calling thống nhất
Mẫu wrapper: định nghĩa công cụ một lần trong định dạng không phụ thuộc nhà cung cấp. Dịch sang định dạng gốc mỗi nhà cung cấp lúc gọi. Chuẩn hóa phản hồi về định dạng thống nhất.
class UnifiedToolClient:
"""One tool definition. Any provider. Automatic translation."""
def __init__(self, base_url: str, api_key: str):
self.openai_client = OpenAI(base_url=base_url, api_key=api_key)
def call_with_tools(self, model: str, messages: list, tools: list):
"""Provider-agnostic tool calling. Handles translation internally."""
# Tools defined in OpenAI format —works for OpenAI, DeepSeek, and
# platforms that translate to Anthropic/Google natively
response = self.openai_client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
tool_choice="auto"
)
return self._normalize_response(response)
def _normalize_response(self, response):
"""Return a unified format regardless of which provider served the request."""
msg = response.choices[0].message
return {
"text": msg.content,
"tool_calls": [
{"name": tc.function.name, "arguments": json.loads(tc.function.arguments)}
for tc in (msg.tool_calls or [])
] if msg.tool_calls else []
}
Lối tắt nền tảng tổng hợp. Wrapper này 30 dòng code. Nhưng nó chỉ xử lý dịch cho nhà cung cấp nói định dạng tương thích OpenAI. Cho tính năng gốc Anthropic (suy nghĩ + sử dụng công cụ cùng nhau, kết quả công cụ một phần trong streaming) và tính năng gốc Google (function calling tự động), bạn cần nền tảng có hỗ trợ giao thức gốc cho mỗi nhà cung cấp —nếu không bạn đang duy trì ba đường code riêng. Nền tảng hỗ trợ đa giao thức xử lý điều này ở cấp hạ tầng. Code của bạn giữ không phụ thuộc nhà cung cấp trong khi tính năng riêng mỗi nhà cung cấp vẫn khả dụng. Nếu bạn mới bắt đầu với gọi công cụ thống nhất, hướng dẫn bắt đầu nhanh TokSpan đưa bạn thiết lập request công cụ đa nhà cung cấp đầu tiên trong dưới năm phút.
Câu hỏi thường gặp
Nhà cung cấp nào có function calling tốt nhất?
GPT-5.5: đáng tin cậy nhất, gọi song song tốt nhất, hệ sinh thái mạnh nhất. Claude Opus: tốt nhất cho chuỗi công cụ đa bước phức tạp nơi chiều sâu suy luận quan trọng. Gemini: function calling tự động là chiến thắng tiện lợi cho công cụ đơn giản. DeepSeek: đủ tốt cho công cụ đơn giản, đôi khi không đáng tin cậy cho gọi song song. Dùng GPT-5.5 khi độ tin cậy công cụ quan trọng. Dùng Claude khi chiều sâu suy luận công cụ quan trọng hơn độ tin cậy thô.
Tôi có thể dùng cùng định nghĩa công cụ cho mọi nhà cung cấp không?
Không gốc. JSON Schema dùng chung, nhưng định dạng wrapper khác. Dùng lớp dịch (30 dòng Python) hoặc nền tảng tổng hợp dịch tự động. Định nghĩa công cụ của bạn —tên, mô tả, schema tham số— có thể chuyển được ngay cả khi định dạng wrapper không.
Tôi có thể định nghĩa bao nhiêu công cụ mỗi request?
OpenAI: 128. Anthropic: không ghi chép nhưng lớn. Google: không giới hạn cứng. Trong thực tế, hơn 10 công cụ làm giảm độ chính xác chọn lựa —model bắt đầu nhầm công cụ tên giống. Giữ tập công cụ hoạt động tập trung.
Tôi nên tự xây wrapper hay dùng nền tảng?
Xây nếu bạn dùng 1–2 nhà cung cấp và cần kiểm soát cụ thể vòng lặp gọi công cụ. Dùng nền tảng nếu bạn muốn tự do đổi nhà cung cấp và tránh duy trì bốn đường code. Mẫu wrapper trong hướng dẫn này mất 30 phút triển khai và duy trì. Nền tảng biến nó thành không —xem tài liệu TokSpan cho API cấp nền tảng xử lý dịch và chuẩn hóa qua cả bốn nhà cung cấp.
Function calling là nền tảng của mọi agent AI. Nhưng đây là câu hỏi khó chịu ngành chưa trả lời: tại sao, năm 2026, mỗi nhà cung cấp LLM vẫn có định dạng hơi khác cho định nghĩa công cụ? JSON Schema dùng chung. Khái niệm “tool_call” dùng chung. Vậy mà định dạng wrapper —input_schema so với parameters, khối tool_use so với mảng tool_calls— vẫn bướng bỉnh riêng theo nhà cung cấp. Một cơ quan tiêu chuẩn có thể sửa điều này trong nhóm làm việc sáu tháng. Cho đến nay, chưa ai triệu tập. Câu hỏi: thị trường sẽ ép chuẩn hóa qua mặc định tương thích OpenAI, hay tính năng sử dụng công cụ gốc sẽ trở nên khác biệt đến mức tương thích đa nhà cung cấp bị bỏ rơi vĩnh viễn?
Thử function calling thống nhất —một định nghĩa công cụ. Bốn nhà cung cấp. Không code wrapper —trong khi ngành đang tìm hiểu liệu chuẩn hóa có thực sự đến không.