工具调用:一套请求格式,调所有模型
Tool calling(函数调用)让模型返回一个结构化的函数调用请求,而不是一段文字。各大模型家族都支持它,但每家官方 API 的格式都不一样——这正是网关能省掉的集成成本。Router One 遵循 OpenAI Chat Completions 规范:同一个 tools 数组、同一套响应处理循环,在 GPT、Claude、Gemini、DeepSeek 上通用;具体支持程度因模型而异,动手前先查目标模型的目录页。
在请求里声明工具
工具就是附在普通 chat completion 上的 JSON Schema 函数声明。从 /models 目录选一个支持工具调用的模型 ID:
curl https://api.router.one/v1/chat/completions \
-H "Authorization: Bearer sk-your-router-one-key" \
-H "Content-Type: application/json" \
-d '{
"model": "<model-id-from-/models>",
"messages": [{"role": "user", "content": "上海今天天气?"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}
}]
}'四步调用循环
处理一次工具调用是一段四步对话,无论请求最终由哪个模型家族服务,结构完全一致:
1. 模型请求调用工具
响应里没有 content,而是 finish_reason 为 "tool_calls" 和一个 tool_calls 数组——每个元素带 id、函数名,以及 JSON 字符串形式的参数。
2. 你执行函数
解析参数字符串、按你的 schema 校验,然后执行真正的函数。模型自己不会执行任何东西——它只是提出调用。
3. 你回传结果
先把收到的 assistant 消息原样追加进消息列表,再追加一条 role 为 "tool" 的消息,带上对应的 tool_call_id 和函数结果。
4. 模型完成回答
把扩展后的消息列表发回同一个端点。模型把工具结果织进最终回复——或者继续请求下一个工具,循环重复。
from openai import OpenAI
import json
client = OpenAI(
base_url="https://api.router.one/v1",
api_key="sk-your-router-one-key",
)
resp = client.chat.completions.create(
model="<model-id-from-/models>", messages=messages, tools=tools
)
msg = resp.choices[0].message
if resp.choices[0].finish_reason == "tool_calls":
messages.append(msg)
for call in msg.tool_calls:
result = run_tool(call.function.name,
json.loads(call.function.arguments))
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
})
final = client.chat.completions.create(
model="<model-id-from-/models>", messages=messages, tools=tools
)各模型的差异在哪
- 是否支持工具调用——先看模型目录页上的能力标记,再决定在它上面构建。
- 并行调用:有的模型一次响应返回多个 tool_calls,有的一次一个。永远遍历整个数组,不要只读第 0 个。
- Schema 严格度:不同模型对参数遵循 JSON Schema 的程度不同。执行前一律先校验。
- 调用意愿:有的模型逢机会就调工具,有的更倾向直接文字回答。更锋利的函数描述和 tool_choice 参数对两类都有效。
在网关上排查工具调用
两类故障占绝大多数。模型用文字回答而不调工具:收紧函数描述、写明触发条件,或用 tool_choice 强制。参数解析失败或校验不过:绝不要盲目执行——先校验,失败时把错误信息作为工具结果回传,让模型自己纠正。无论哪种,每次尝试都是 Dashboard → Logs 里的一行请求记录,带 Token、花费、延迟和状态——循环每转一圈花了多少,看得一清二楚。
常见问题
- 哪些模型支持工具调用?
- 支持程度因模型而异。Router One 遵循 OpenAI Chat Completions 规范,/models 目录标注了每个模型的能力——按你要用的具体模型查目录页,不要按家族想当然。
- 工具声明算 Token 吗?
- 算——工具声明随 prompt 一起发送,按输入 Token 计费,而且循环每一轮都会计入。Dashboard → Logs 的每请求 Trace 会显示 Token 和费用影响,过大的 schema 一眼就能看出来。
- 该用工具调用还是结构化输出?
- 工具调用适合「要执行动作」:模型决定调你的函数,你把结果喂回去。如果只是想让回复固定成某个 JSON 结构、没有真函数要跑,用 schema 约束输出是更简单的模式。很多应用两者并用。
- 带工具调用的响应可以流式吗?
- 可以。开启流式后,工具调用的片段会在 delta 分块中逐步到达——累积 id、函数名和参数片段,直到流以 finish_reason 为 tool_calls 结束,然后走正常循环。分块格式见 LLM 流式输出指南。
- 同一套循环真的跨模型家族通用吗?
- 这正是 OpenAI 兼容规范的意义:tools 数组、tool_calls 响应、tool 角色的结果消息保持同一个形状,Router One 把请求路由到你指定的模型。各模型的行为差异(并行调用、严格度)依然存在,但循环结构不用变。