全部文章

LLM Function Calling 跨服务商对比:Tool 定义格式、执行差异与多服务商路由踩坑指南(2026)

跨服务商对比 function calling(tool use)格式差异:OpenAI、Anthropic、DashScope、DeepSeek、SiliconFlow。覆盖 tool 定义 schema、响应格式、并行调用、流式 tool chunk、strict mode,以及 gateway 层需要做哪些格式归一化。

· TheRouter

Function calling——或者按 Anthropic 的叫法叫 "tool use"——是让 LLM 从纯文本生成器变成能查数据库、调 API、触发 workflow 的 agent 的核心机制。所有主流服务商都支持,但格式各不相同。

我们每天都在 OpenAIAnthropicDashScopeDeepSeekSiliconFlow 之间路由 function calling 流量。这篇文章是我们在归一化所有服务商 tool call 格式时希望早点读到的参考:schema 差异、响应格式的不一致、流式传输的坑、以及实际路由中会出问题的地方。

OpenAI 兼容指供应商提供一个 chat-completions 接口,其请求与响应结构与 OpenAI API 契约足够接近——只需替换三个值(API key、base URL、模型名),原来的 OpenAI SDK 调用即可直接工作。最小实践面是POST /v1/chat/completionsmessagesmodel, 并返回 OpenAI 形式的流式响应。

TL;DR 对比表

维度OpenAIAnthropic ClaudeDashScope (Qwen)DeepSeekSiliconFlow
术语Function callingTool useFunction callingTool callsFunction calling
请求字段tools 数组tools 数组tools 数组tools 数组tools 数组
Schema 键名parametersinput_schemaparametersparametersparameters
响应格式assistant 消息上的 tool_callstool_use content blockassistant 消息上的 tool_callsassistant 消息上的 tool_callsassistant 消息上的 tool_calls
参数类型JSON 字符串已解析的对象JSON 字符串JSON 字符串JSON 字符串
并行调用支持支持支持支持(非思考模式)支持
Strict mode支持(strict: true不支持不支持支持(Beta,/beta 端点)不支持
流式 tool chunktool_calls delta chunkcontent_block_delta + input_json_deltatool_calls delta chunktool_calls delta chunktool_calls delta chunk
Tool choiceauto / required / none / 指定auto / any / tool(指定)auto / required / none / 指定auto / required / none / 指定auto / required / none / 指定
最大 tool 数无硬性限制(实测 ~128)64+取决于模型取决于模型取决于模型

Tool 定义 Schema:第一个分歧点

核心思路所有服务商一样:用 name、description 和 JSON Schema 描述参数。结构差异从 schema 的键名开始。

OpenAItools 数组中使用 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\"}"
      }
    }
  ]
}

关键细节:argumentsJSON 字符串,不是已解析的对象。你需要 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 在 content block 里,不是独立的 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 调用。但实现语义有差别。

OpenAItool_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 使用完全不同的流式事件模型:

  1. content_block_start:包含 type: "tool_use"、tool idname
  2. content_block_deltatype: "input_json_delta" 携带参数片段
  3. 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 支持。所有属性必须为 requiredadditionalProperties 必须为 false

DeepSeek 提供 Beta 版 strict mode。需要使用 /beta 的 base URL(https://api.deepseek.com/beta)并在每个 function 上设置 "strict": true。支持 patternformatminimum/maximumenumanyOf 约束。

AnthropicDashScopeSiliconFlow 目前不提供 strict mode。模型通常能生成合规参数,但没有服务端保证。应该在应用代码中做参数校验。

Tool Choice 控制

服务商强制调用禁止调用指定 tool自动(默认)
OpenAItool_choice: "required"tool_choice: "none"tool_choice: {"type": "function", "function": {"name": "X"}}tool_choice: "auto"
Anthropictool_choice: {"type": "any"}tool_choice: {"type": "none"}tool_choice: {"type": "tool", "name": "X"}tool_choice: {"type": "auto"}
DashScopetool_choice: "required"tool_choice: "none"tool_choice: {"type": "function", "function": {"name": "X"}}tool_choice: "auto"
DeepSeektool_choice: "required"tool_choice: "none"tool_choice: {"type": "function", "function": {"name": "X"}}tool_choice: "auto"
SiliconFlowtool_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 → 服务商):

  1. Tool 定义格式parametersinput_schema 映射,为 Anthropic 展平/包装 function 对象
  2. Tool choice 格式"required"{"type": "any"} 转换
  3. 请求结构:OpenAI 用 messages + tools;Anthropic 用不同的消息 role 提交 tool result

出站(服务商 → gateway → 客户端):

  1. 响应格式:Anthropic 的 tool_use content block → OpenAI 风格 tool_calls 数组
  2. 参数解析:Anthropic 返回已解析的对象;OpenAI 兼容服务商返回 JSON 字符串
  3. 停止原因stop_reason: "tool_use"finish_reason: "tool_calls"
  4. 流式事件:Anthropic 的 content_block_start/content_block_delta → OpenAI 风格 delta.tool_calls chunk

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 交错AnthropicContent block 架构原生支持
OpenAI SDK 兼容DashScope、DeepSeek、SiliconFlowTool 定义和响应零代码修改
最大 tool 数量OpenAI实测 128+;更大注册表用 tool_search
低成本 function callingDeepSeek 或 SiliconFlow最便宜的 token 定价且完整支持 tool call
思考模式 + tool callingDeepSeek V3.2+ 或 Qwen3都支持推理模式下的 tool call
多服务商 fallbackTheRouter跨所有服务商归一化 tool call 格式

踩坑清单

  1. 双重 JSON 编码:部分 OpenAI 兼容客户端通过代理转发时会双重编码 tool call arguments。DashScope 会拒绝——这是一个已知问题

  2. Tool call ID 格式:OpenAI 用 call_ 前缀,Anthropic 用 toolu_ 前缀,DeepSeek 用 call_ 前缀。如果你的系统存储或引用这些 ID,不要假设格式统一。

  3. Tool call 消息的空 content:OpenAI 兼容模型返回 tool call 时 content 通常是 null。部分客户端库无法处理 null content。

  4. DashScope GLM 模型:通过 DashScope 使用 GLM 模型时,请求中必须包含 extra_body={"tool_stream": True},否则模型不会返回 tool_calls

  5. DeepSeek 思考模式限制:V3.2+ 支持思考模式下的 tool call,但并行调用行为可能与非思考模式不同。

  6. Anthropic tool_result role:Tool result 必须用 role: "user" 消息发送,不是 role: "tool"。每个从 OpenAI 迁来的开发者都会在这里踩坑。

  7. 流式 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 检索)。

本文涉及的模型

客服支持