主题
函数调用(Tool Use / Function Calling)
让模型自己决定调什么工具 + 生成参数。GPT 叫 "function calling",Claude 叫 "tool use",本质同一回事。
协议差异速览
| Provider | 协议 | 字段名 |
|---|---|---|
| OpenAI 系列 | /v1/chat/completions | tools + tool_choice |
| Claude 系列 | /v1/messages | tools + tool_choice |
OpenAI 协议
POST https://shuro.vip/v1/chat/completions请求示例
json
{
"model": "gpt-5.5",
"messages": [
{
"role": "user",
"content": "北京今天天气怎么样?"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "获取指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,如:北京、上海"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
}
],
"tool_choice": "auto"
}响应示例
json
{
"id": "chatcmpl-DZwB90CSso7968zxe9srYK4PGt80N",
"object": "chat.completion",
"created": 1777457491,
"model": "gpt-5.5",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_er37krPTyuvOffIpsFRGrqji",
"type": "function",
"function": {
"name": "get_current_weather",
"arguments": "{\"location\":\"北京\",\"unit\":\"celsius\"}"
}
}
],
"refusal": null
},
"logprobs": null,
"finish_reason": "tool_calls"
}
],
"usage": {
"prompt_tokens": 72,
"completion_tokens": 21,
"total_tokens": 93
}
}注意:
content是null(模型选择调用 tool 而非直接回答)tool_calls[].function.arguments是字符串化的 JSON(要JSON.parse)finish_reason是tool_calls
tool_choice 取值
| 值 | 含义 |
|---|---|
"auto" | 模型自己决定调不调(默认) |
"none" | 强制不调 tool |
"required" | 强制至少调一个 |
{"type": "function", "function": {"name": "xxx"}} | 强制调指定 tool |
gpt-5.5 多了 reasoning_content
json
{
"message": {
"role": "assistant",
"content": null,
"reasoning_content": null, ← 新字段
"tool_calls": [...]
}
}GPT-5 系列在 tool call 前可能输出思考链放在 reasoning_content。
Claude 原生 Messages 协议
POST https://shuro.vip/v1/messages请求示例
json
{
"model": "claude-opus-4-7",
"max_tokens": 2048,
"tools": [
{
"name": "get_current_weather",
"description": "获取指定城市的当前天气",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
],
"messages": [
{"role": "user", "content": "北京今天天气怎么样?"}
]
}Claude vs OpenAI 字段差异
| OpenAI | Claude |
|---|---|
tools[].function.name | tools[].name |
tools[].function.parameters | tools[].input_schema |
tool_calls[].function.arguments(字符串) | content[].input(对象,不是字符串) |
Claude 响应示例
json
{
"id": "msg_xxx",
"model": "claude-opus-4-7",
"stop_reason": "tool_use",
"content": [
{"type": "text", "text": "我帮你查天气。"},
{
"type": "tool_use",
"id": "toolu_xxx",
"name": "get_current_weather",
"input": {
"location": "北京",
"unit": "celsius"
}
}
]
}Claude 的 input 是已解析的对象,不用再 JSON.parse。
多轮调用(Tool 结果反馈)
模型调用 tool → 你执行 → 把结果发回给模型 → 模型继续推理。
OpenAI 多轮
json
{
"model": "gpt-5.5",
"messages": [
{"role": "user", "content": "北京天气?"},
{
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_xxx",
"type": "function",
"function": {"name": "get_current_weather", "arguments": "{\"location\":\"北京\"}"}
}]
},
{
"role": "tool",
"tool_call_id": "call_xxx",
"content": "{\"temp\":18,\"description\":\"晴\"}"
}
],
"tools": [...]
}模型基于 tool 结果继续生成自然语言回答。
Claude 多轮
json
{
"model": "claude-opus-4-7",
"max_tokens": 2048,
"messages": [
{"role": "user", "content": "北京天气?"},
{
"role": "assistant",
"content": [
{"type": "text", "text": "我帮你查"},
{"type": "tool_use", "id": "toolu_xxx", "name": "get_current_weather", "input": {"location": "北京"}}
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_xxx",
"content": "{\"temp\":18,\"description\":\"晴\"}"
}
]
}
],
"tools": [...]
}注意 Claude 用 role: user 包裹 tool_result(OpenAI 用 role: tool)。
完整 Python 示例
python
from openai import OpenAI
import json
client = OpenAI(api_key="<key>", base_url="https://shuro.vip/v1")
def get_weather(location, unit="celsius"):
"""模拟查天气"""
return {"temp": 18, "description": "晴", "unit": unit}
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取天气",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["location"]
}
}
}]
messages = [{"role": "user", "content": "北京天气?"}]
# 第一轮:模型调用 tool
r1 = client.chat.completions.create(model="gpt-5.5", messages=messages, tools=tools)
tool_call = r1.choices[0].message.tool_calls[0]
# 你执行 tool
args = json.loads(tool_call.function.arguments)
result = get_weather(**args)
# 把结果发回
messages.append(r1.choices[0].message.model_dump())
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
})
# 第二轮:模型基于结果回答
r2 = client.chat.completions.create(model="gpt-5.5", messages=messages, tools=tools)
print(r2.choices[0].message.content)
# "北京今天 18 度,天气晴朗。"并行 tool 调用
GPT-5 系 / Claude 支持一次返回多个 tool_calls 并行执行:
json
{
"tool_calls": [
{"id": "1", "function": {"name": "get_weather", "arguments": "..."}},
{"id": "2", "function": {"name": "get_news", "arguments": "..."}},
{"id": "3", "function": {"name": "get_traffic", "arguments": "..."}}
]
}你并行执行三个,再一次性把结果都发回。比串行快 N 倍。
强制 JSON 输出(tool_use 模式)
利用 tool_use 强制 Claude 输出严格 JSON:
json
{
"model": "claude-opus-4-7",
"max_tokens": 1024,
"tools": [{
"name": "extract_data",
"description": "Extract structured data",
"input_schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"]
}
}],
"tool_choice": {"type": "tool", "name": "extract_data"},
"messages": [{"role": "user", "content": "我叫张三,30 岁"}]
}强制调用 extract_data,input 字段就是严格 JSON。详见 结构化输出。
支持的模型
| 模型 | Function/Tool 支持 |
|---|---|
gpt-5.5 / gpt-5.4 / gpt-5.4-mini | ✅ 完整 + 并行 |
claude-opus-4-8 / claude-opus-4-7 / claude-opus-4-6 / claude-fable-5 | ✅ 完整 + 并行 |
claude-sonnet-4-6 / claude-haiku-4-5-20251001 | ✅ 完整 + 并行 |
常见错误
arguments 不是合法 JSON
模型偶尔输出格式错的 JSON。用 try/except 处理:
python
try:
args = json.loads(tool_call.function.arguments)
except json.JSONDecodeError:
# 给模型反馈 + 重试
pass死循环调用同一 tool
模型一直调 get_weather 不返回最终答案。加 max_iterations:
python
for i in range(5): # 最多 5 轮
resp = client.chat.completions.create(...)
if resp.choices[0].message.tool_calls:
# 执行 + 继续
else:
break # 模型给出最终回答了tool_choice required 但模型不调
少数模型不严格遵守 required。用 Claude 的 tool_choice: {type: "tool", name: "xxx"} 更可靠。
跟 MCP 的区别
| Function Calling | MCP | |
|---|---|---|
| 调用方 | 模型 | 模型(透过 MCP server) |
| 工具定义 | 写在 request body | MCP server 自己声明 |
| 执行 | 你的应用代码 | MCP server 进程 |
| 共享 | 单个 request | 全局所有 chat |
| 适合 | 业务逻辑 | 跨应用复用(DB、GitHub 等) |
详见 MCP 数据连接器。
