LLM Function Calling 跨服务商对比:Tool 定义格式、执行差异与多服务商路由踩坑指南(2026)
跨服务商对比 function calling(tool use)格式差异:OpenAI、Anthropic、DashScope、DeepSeek、SiliconFlow。覆盖 tool 定义 schema、响应格式、并行调用、流式 tool chunk、strict mode,以及 gateway 层需要做哪些格式归一化。
Function calling——或者按 Anthropic 的叫法叫 "tool use"——是让 LLM 从纯文本生成器变成能查数据库、调 API、触发 workflow 的 agent 的核心机制。所有主流服务商都支持,但格式各不相同。
我们每天都在 OpenAI、Anthropic、DashScope、DeepSeek 和 SiliconFlow 之间路由 function calling 流量。这篇文章是我们在归一化所有服务商 tool call 格式时希望早点读到的参考:schema 差异、响应格式的不一致、流式传输的坑、以及实际路由中会出问题的地方。
OpenAI 兼容指供应商提供一个 chat-completions 接口,其请求与响应结构与 OpenAI API 契约足够接近——只需替换三个值(API key、base URL、模型名),原来的 OpenAI SDK 调用即可直接工作。最小实践面是POST /v1/chat/completions 带 messages、model, 并返回 OpenAI 形式的流式响应。
TL;DR 对比表
| 维度 | OpenAI | Anthropic Claude | DashScope (Qwen) | DeepSeek | SiliconFlow |
|---|---|---|---|---|---|
| 术语 | Function calling | Tool use | Function calling | Tool calls | Function calling |
| 请求字段 | tools 数组 | tools 数组 | tools 数组 | tools 数组 | tools 数组 |
| Schema 键名 | parameters | input_schema | parameters | parameters | parameters |
| 响应格式 | assistant 消息上的 tool_calls | tool_use content block | assistant 消息上的 tool_calls | assistant 消息上的 tool_calls | assistant 消息上的 tool_calls |
| 参数类型 | JSON 字符串 | 已解析的对象 | JSON 字符串 | JSON 字符串 | JSON 字符串 |
| 并行调用 | 支持 | 支持 | 支持 | 支持(非思考模式) | 支持 |
| Strict mode | 支持(strict: true) | 不支持 | 不支持 | 支持(Beta,/beta 端点) | 不支持 |
| 流式 tool chunk | tool_calls delta chunk | content_block_delta + input_json_delta | tool_calls delta chunk | tool_calls delta chunk | tool_calls delta chunk |
| Tool choice | auto / required / none / 指定 | auto / any / tool(指定) | auto / required / none / 指定 | auto / required / none / 指定 | auto / required / none / 指定 |
| 最大 tool 数 | 无硬性限制(实测 ~128) | 64+ | 取决于模型 | 取决于模型 | 取决于模型 |
Tool 定义 Schema:第一个分歧点
核心思路所有服务商一样:用 name、description 和 JSON Schema 描述参数。结构差异从 schema 的键名开始。
OpenAI 在 tools 数组中使用 type: "function" 的条目:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {
"location": { "type": "string" }
},
"required": ["location"],
"additionalProperties": false
}
}
}
Anthropic 用同样的顶层结构,但把 parameters 换成了 input_schema,并且去掉了 function 包装层:
{
"name": "get_weather",
"description": "获取指定城市的当前天气",
"input_schema": {
"type": "object",
"properties": {
"location": { "type": "string" }
},
"required": ["location"]
}
}
DashScope(Qwen) 和 DeepSeek 完全遵循 OpenAI 的格式——type: "function"、嵌套的 function 对象、parameters 键。这就是 OpenAI 兼容 API 的好处:你给 OpenAI 写的代码,接 DashScope 和 DeepSeek 时 tool 定义零修改。
SiliconFlow 同样遵循 OpenAI 兼容格式。
实际结论:如果你在做 gateway 层的 tool 定义归一化,需要处理两种格式——OpenAI 风格(OpenAI、DashScope、DeepSeek、SiliconFlow 通用)和 Anthropic 风格(仅 Anthropic)。映射很直接:parameters 改名 input_schema,展平 function 包装层即可。
响应格式:真正开始分化的地方
Tool 定义好处理。响应格式才是多服务商路由的难点。
OpenAI / DashScope / DeepSeek / SiliconFlow(OpenAI 兼容)
模型决定调用 tool 时,响应是一个带 tool_calls 数组的 assistant 消息:
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\": \"Tokyo\"}"
}
}
]
}
关键细节:arguments 是 JSON 字符串,不是已解析的对象。你需要 JSON.parse() 之后才能使用。从 Anthropic 迁过来的开发者经常在这里踩坑。
返回结果时用 tool role 的消息,引用 tool_call_id:
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"temperature\": 24, \"unit\": \"celsius\"}"
}
Anthropic Claude
Anthropic 使用 content block 架构。Tool call 和文本作为独立的 block 出现在同一个 assistant 响应中:
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "让我帮你查一下天气。"
},
{
"type": "tool_use",
"id": "toolu_01abc",
"name": "get_weather",
"input": { "location": "Tokyo" }
}
],
"stop_reason": "tool_use"
}
与 OpenAI 格式的关键区别:
- 参数是已解析的对象(
input),不是 JSON 字符串(arguments) - Tool call 在
contentblock 里,不是独立的tool_calls数组 stop_reason是"tool_use",不是"tool_calls"(OpenAI 用finish_reason: "tool_calls")- 文本和 tool call 可以交错出现——模型可能先解释推理过程再调用 tool
返回结果用 user 消息中的 tool_result content block:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01abc",
"content": "{\"temperature\": 24, \"unit\": \"celsius\"}"
}
]
}
这和 OpenAI 的 role: "tool" 方式完全不同。在 Anthropic 的模型里,tool result 是作为 user turn 的一部分发送的。
并行 vs 顺序 Tool 调用
所有主流服务商都支持并行 tool call——模型可以在单次响应中请求多个 tool 调用。但实现语义有差别。
OpenAI 在 tool_calls 数组中返回多个条目。你可以用 parallel_tool_calls: false 强制顺序调用。
Anthropic 在 content 数组中返回多个 tool_use block。也支持通过 tool_choice 中的 disable_parallel_tool_use: true 禁用并行。
DeepSeek 在非思考模式下支持并行 tool call。思考模式(V3.2+)支持 tool calling,但并行行为可能不同——推理活跃时模型倾向于顺序调用。
DashScope 遵循 OpenAI 的并行 tool call 格式。
流式 Tool Call Chunk
流式 function calling 引入了另一层格式差异。
OpenAI / DashScope / DeepSeek / SiliconFlow 以 delta 对象的形式流式发送 tool call:
{
"delta": {
"tool_calls": [
{
"index": 0,
"id": "call_abc",
"type": "function",
"function": { "name": "get_weather", "arguments": "" }
}
]
}
}
然后是参数片段:
{
"delta": {
"tool_calls": [
{
"index": 0,
"function": { "arguments": "{\"loc" }
}
]
}
}
你需要跨 chunk 累积 arguments 字符串,流结束时解析完整的 JSON。
Anthropic 使用完全不同的流式事件模型:
content_block_start:包含type: "tool_use"、toolid和namecontent_block_delta:type: "input_json_delta"携带参数片段content_block_stop:标记该 tool call 结束
对 gateway 实现来说,这是最难归一化的部分。OpenAI 兼容的服务商都用相同的 delta.tool_calls[index].function.arguments chunk 格式,但 Anthropic 的事件驱动模型需要完全不同的解析器。
Strict Mode 与 JSON Schema 校验
Strict mode 保证模型的 tool call 参数严格符合声明的 JSON Schema。并非所有服务商都支持。
OpenAI 通过在 function 定义中添加 "strict": true 支持。所有属性必须为 required,additionalProperties 必须为 false。
DeepSeek 提供 Beta 版 strict mode。需要使用 /beta 的 base URL(https://api.deepseek.com/beta)并在每个 function 上设置 "strict": true。支持 pattern、format、minimum/maximum、enum 和 anyOf 约束。
Anthropic、DashScope 和 SiliconFlow 目前不提供 strict mode。模型通常能生成合规参数,但没有服务端保证。应该在应用代码中做参数校验。
Tool Choice 控制
| 服务商 | 强制调用 | 禁止调用 | 指定 tool | 自动(默认) |
|---|---|---|---|---|
| OpenAI | tool_choice: "required" | tool_choice: "none" | tool_choice: {"type": "function", "function": {"name": "X"}} | tool_choice: "auto" |
| Anthropic | tool_choice: {"type": "any"} | tool_choice: {"type": "none"} | tool_choice: {"type": "tool", "name": "X"} | tool_choice: {"type": "auto"} |
| DashScope | tool_choice: "required" | tool_choice: "none" | tool_choice: {"type": "function", "function": {"name": "X"}} | tool_choice: "auto" |
| DeepSeek | tool_choice: "required" | tool_choice: "none" | tool_choice: {"type": "function", "function": {"name": "X"}} | tool_choice: "auto" |
| SiliconFlow | tool_choice: "required" | tool_choice: "none" | tool_choice: {"type": "function", "function": {"name": "X"}} | tool_choice: "auto" |
Anthropic 的 tool_choice 格式在结构上不同——用 {"type": "any"} 代替 "required",指定 tool 用 {"type": "tool", "name": "X"} 代替嵌套的 function 对象。
GPT-5.6 Sol Programmatic Tool Calling
OpenAI 的 GPT-5.6 Sol 引入了重大演进:programmatic tool calling。模型不再选择何时调用 tool 并生成 JSON 参数,而是可以在沙盒 V8 环境中生成并执行 JavaScript 代码来编排 tool 调用。
这代表了与传统 function calling 不同的范式。其他服务商目前没有发布等效功能。在多服务商路由中,传统 function calling 仍然是通用接口。
Gateway 归一化:路由层需要处理什么
如果你在多个服务商之间路由 function calling 请求——这正是我们在 TheRouter 做的事——归一化层需要处理以下内容:
入站(客户端 → gateway → 服务商):
- Tool 定义格式:
parameters↔input_schema映射,为 Anthropic 展平/包装function对象 - Tool choice 格式:
"required"↔{"type": "any"}转换 - 请求结构:OpenAI 用
messages+tools;Anthropic 用不同的消息 role 提交 tool result
出站(服务商 → gateway → 客户端):
- 响应格式:Anthropic 的
tool_usecontent block → OpenAI 风格tool_calls数组 - 参数解析:Anthropic 返回已解析的对象;OpenAI 兼容服务商返回 JSON 字符串
- 停止原因:
stop_reason: "tool_use"→finish_reason: "tool_calls" - 流式事件:Anthropic 的
content_block_start/content_block_delta→ OpenAI 风格delta.tool_callschunk
OpenAI 兼容的服务商(DashScope、DeepSeek、SiliconFlow)是简单场景——它们格式完全一致,在它们之间路由不需要任何 function calling 层面的归一化。
代码示例:最简多服务商 Tool Calling 客户端
import OpenAI from "openai";
import Anthropic from "@anthropic-ai/sdk";
// Tool 定义——同样的逻辑,两种格式
const openaiTool: OpenAI.ChatCompletionTool = {
type: "function",
function: {
name: "get_weather",
description: "获取城市天气",
parameters: {
type: "object",
properties: {
location: { type: "string", description: "城市名" }
},
required: ["location"],
additionalProperties: false
}
}
};
const anthropicTool: Anthropic.Tool = {
name: "get_weather",
description: "获取城市天气",
input_schema: {
type: "object" as const,
properties: {
location: { type: "string", description: "城市名" }
},
required: ["location"]
}
};
// 解析 OpenAI 兼容的 tool call
function parseOpenAIToolCalls(message: OpenAI.ChatCompletionMessage) {
return (message.tool_calls ?? []).map(tc => ({
id: tc.id,
name: tc.function.name,
args: JSON.parse(tc.function.arguments) // JSON 字符串 → 对象
}));
}
// 解析 Anthropic 的 tool call
function parseAnthropicToolCalls(response: Anthropic.Message) {
return response.content
.filter((b): b is Anthropic.ToolUseBlock => b.type === "tool_use")
.map(b => ({
id: b.id,
name: b.name,
args: b.input as Record<string, unknown> // 已经是对象
}));
}
解析层的关键区别:OpenAI 给你 JSON.parse(tc.function.arguments),Anthropic 直接给你 b.input 对象。忘了 parse OpenAI 的字符串会拿到原始字符串;试图 parse Anthropic 的对象会运行时报错。
选型矩阵:你需要 X,就选 Y
| 你需要... | 最佳选择 | 原因 |
|---|---|---|
| 严格 schema 校验 | OpenAI 或 DeepSeek(Beta) | 唯二支持服务端 strict: true 的服务商 |
| 文本与 tool call 交错 | Anthropic | Content block 架构原生支持 |
| OpenAI SDK 兼容 | DashScope、DeepSeek、SiliconFlow | Tool 定义和响应零代码修改 |
| 最大 tool 数量 | OpenAI | 实测 128+;更大注册表用 tool_search |
| 低成本 function calling | DeepSeek 或 SiliconFlow | 最便宜的 token 定价且完整支持 tool call |
| 思考模式 + tool calling | DeepSeek V3.2+ 或 Qwen3 | 都支持推理模式下的 tool call |
| 多服务商 fallback | TheRouter | 跨所有服务商归一化 tool call 格式 |
踩坑清单
-
双重 JSON 编码:部分 OpenAI 兼容客户端通过代理转发时会双重编码 tool call arguments。DashScope 会拒绝——这是一个已知问题。
-
Tool call ID 格式:OpenAI 用
call_前缀,Anthropic 用toolu_前缀,DeepSeek 用call_前缀。如果你的系统存储或引用这些 ID,不要假设格式统一。 -
Tool call 消息的空
content:OpenAI 兼容模型返回 tool call 时content通常是null。部分客户端库无法处理nullcontent。 -
DashScope GLM 模型:通过 DashScope 使用 GLM 模型时,请求中必须包含
extra_body={"tool_stream": True},否则模型不会返回tool_calls。 -
DeepSeek 思考模式限制:V3.2+ 支持思考模式下的 tool call,但并行调用行为可能与非思考模式不同。
-
Anthropic
tool_resultrole:Tool result 必须用role: "user"消息发送,不是role: "tool"。每个从 OpenAI 迁来的开发者都会在这里踩坑。 -
流式 tool call 累积:流式传输时必须跨 chunk 累积
arguments字符串后再解析。尝试逐个 chunk 解析会因 JSON 语法错误而失败。
FAQ
Q: 能在所有服务商之间共用同一份 tool 定义吗?
A: 在 OpenAI、DashScope、DeepSeek 和 SiliconFlow 之间可以完全相同。对 Anthropic,你需要把 parameters 改名为 input_schema 并去掉 function 包装层。
Q: 哪些服务商支持推理/思考模式下的 function calling?
A: DeepSeek(V3.2+)和 DashScope(Qwen3 系列 enable_thinking: true)都支持推理中的 tool call。OpenAI 的 o3/o4-mini 支持 tool use。Anthropic 的 extended thinking 也支持 tool use。
Q: 每个请求最多能定义多少个 tool?
A: OpenAI 没有硬性限制但超过 ~128 性能会下降。Anthropic 建议保持在 64 以内。DashScope 和 DeepSeek 的限制取决于具体模型。对于大型 tool 注册表,OpenAI 的 tool_search(GPT-5.4+)可以按需延迟加载。
Q: 所有服务商都支持 tool schema 中的 additionalProperties: false 吗?
A: OpenAI strict mode 必须。DeepSeek /beta strict mode 必须。Anthropic、DashScope、SiliconFlow 接受但不强制。
Q: 模型生成了无效的 tool call 参数怎么办? A: 没有 strict mode 时,模型可能生成不符合 schema 的参数。应用层应该在执行 tool 之前始终校验参数。有 strict mode(OpenAI、DeepSeek Beta)时,API 保证参数有效。
来源:OpenAI Function Calling 文档(2026-08-01 检索)、Anthropic Tool Use 文档(2026-08-01 检索)、DeepSeek Tool Calls 文档(2026-08-01 检索)、DashScope Function Calling 文档(2026-08-01 检索)、Qveris Function Calling Guide(2026-08-01 检索)、Digital Applied AI Function Calling Guide(2026-08-01 检索)。