函数调用深入 — OpenAI、Anthropic、Gemini
对比三大前沿提供商的函数调用格式差异,构建统一翻译器实现跨提供商移植
函数调用深入 — OpenAI、Anthropic、Gemini
三大前沿提供商在2024年趋同于相同的工具调用循环,然后在其他一切上分道扬镳。OpenAI使用
tools和tool_calls。Anthropic使用tool_use和tool_result块。Gemini使用functionDeclarations和唯一id关联。本课并排对比三者,使在一个提供商上发布的代码在移植时不会中断。
类型: 构建 语言: Python(标准库,模式翻译器) 前置条件: Phase 13 · 01(工具接口) 时间: ~75分钟
学习目标
- 陈述OpenAI、Anthropic和Gemini函数调用负载之间的三个形状差异(声明、调用、结果)。
- 将一个工具声明翻译为所有三个提供商格式,并预测严格模式约束的不同之处。
- 在每个提供商中使用
tool_choice来强制、禁止或自动选择工具调用。 - 了解每个提供商的硬限制(工具数量、模式深度、参数长度)以及违反限制时的错误签名。
问题所在
函数调用请求的形状因提供商而异。2026年生产栈的三个具体示例:
OpenAI Chat Completions / Responses API。 你传递 tools: [{type: "function", function: {name, description, parameters, strict}}]。模型的响应包含 choices[0].message.tool_calls: [{id, type: "function", function: {name, arguments}}],其中 arguments 是你必须解析的JSON字符串。严格模式(strict: true)通过约束解码强制模式合规。
Anthropic Messages API。 你传递 tools: [{name, description, input_schema}]。响应以 content: [{type: "text"}, {type: "tool_use", id, name, input}] 形式返回。input 已经解析(一个对象,不是字符串)。你用包含 {type: "tool_result", tool_use_id, content} 块的新 user 消息回复。
Google Gemini API。 你传递 tools: [{functionDeclarations: [{name, description, parameters}]}](嵌套在 functionDeclarations 下)。响应以 candidates[0].content.parts: [{functionCall: {name, args, id}}] 形式到达,其中 id 在Gemini 3及以上版本中用于并行调用关联。你用 {functionResponse: {name, id, response}} 回复。
相同的循环。不同的字段名称、不同的嵌套、不同的字符串与对象约定、不同的关联机制。在OpenAI上编写天气Agent的团队需要两天移植到Anthropic,又需要一天移植到Gemini,仅用于管道工作。
本课构建一个翻译器,将三种格式统一为一个规范工具声明,并在边缘路由。Phase 13 · 17将相同模式泛化为LLM网关。
核心概念
共同结构
每个提供商需要五样东西:
- 工具列表。 每个工具的名称、描述和输入模式。
- 工具选择。 强制特定工具、禁止工具或让模型决定。
- 调用发出。 命名工具和参数的结构化输出。
- 调用id。 将响应与正确的调用关联(并行时重要)。
- 结果注入。 将结果与调用关联的消息或块。
逐字段形状差异
| 方面 | OpenAI | Anthropic | Gemini |
|---|---|---|---|
| 声明信封 | {type: "function", function: {...}} | {name, description, input_schema} | {functionDeclarations: [{...}]} |
| 模式字段 | parameters | input_schema | parameters |
| 响应容器 | assistant消息上的 tool_calls[] | 类型为 tool_use 的 content[] | 类型为 functionCall 的 parts[] |
| 参数类型 | 字符串化JSON | 解析后的对象 | 解析后的对象 |
| Id格式 | call_...(OpenAI生成) | toolu_...(Anthropic) | UUID(Gemini 3+) |
| 结果块 | 角色 tool,tool_call_id | user 带 tool_result,tool_use_id | functionResponse 带匹配 id |
| 强制工具 | tool_choice: {type: "function", function: {name}} | tool_choice: {type: "tool", name} | tool_config: {function_calling_config: {mode: "ANY"}} |
| 禁止工具 | tool_choice: "none" | tool_choice: {type: "none"} | mode: "NONE" |
| 严格模式 | strict: true | 模式即模式(始终强制) | 请求级 responseSchema |
你会实际碰到的限制
- OpenAI。 每个请求128个工具。模式深度5。参数字符串 <= 8192字节。严格模式要求无
$ref,无重叠的oneOf/anyOf/allOf,每个属性列在required中。 - Anthropic。 每个请求64个工具。模式深度实际上无限制但实际限制为10。无严格模式标志;模式是契约,模型倾向于遵守。
- Gemini。 每个请求64个函数。模式类型是OpenAPI 3.0子集(与JSON Schema 2020-12略有分歧)。并行调用自Gemini 3起有唯一id。
tool_choice 行为
每个人都支持的三种模式,命名不同。
- Auto。 模型选择工具或文本。默认。
- Required / Any。 模型必须调用至少一个工具。
- None。 模型不得调用工具。
加上每个提供商独有的一种模式:
- OpenAI。 按名称强制特定工具。
- Anthropic。 按名称强制特定工具;
disable_parallel_tool_use标志区分单次与多次。 - Gemini。
mode: "VALIDATED"将每个响应通过模式验证器路由,无论模型意图。
并行调用
OpenAI的 parallel_tool_calls: true(默认)在一个assistant消息中发出多个调用。你运行所有调用并用包含每个 tool_call_id 一个条目的批量工具角色消息回复。Anthropic历史上做单次调用;disable_parallel_tool_use: false(Claude 3.5起默认)启用多次。Gemini 2允许并行调用但不给稳定id;Gemini 3添加UUID,使乱序响应干净地关联。
流式传输
三个提供商都支持流式工具调用。线路格式不同:
- OpenAI。
tool_calls[i].function.arguments的增量块逐步到达。你累积直到finish_reason: "tool_calls"。 - Anthropic。 block-start / block-delta / block-stop 事件。
input_json_delta块携带部分参数。 - Gemini。
streamFunctionCallArguments(Gemini 3新增)发出带有functionCallId的块,使多个并行调用可以交错。
Phase 13 · 03深入并行 + 流式重组。本课侧重于声明和单次调用形状。
错误与修复
无效参数错误看起来也不同。
- OpenAI(非严格)。 模型返回
arguments: "{bad json}",你的JSON解析失败,你注入错误消息并重新调用。 - OpenAI(严格)。 验证在解码期间发生;无效JSON不可能出现,但
refusal可以出现。 - Anthropic。
input可能包含意外字段;模式是建议性的。服务器端验证。 - Gemini。 OpenAPI 3.0怪癖:对象字段上的
enum被静默忽略;自行验证。
翻译器模式
你代码中的规范工具声明看起来像这样(你选择形状):
Tool(
name="get_weather",
description="Use when ...",
input_schema={"type": "object", "properties": {...}, "required": [...]},
strict=True,
)
三个小函数将其翻译为三个提供商形状。code/main.py 中的线束正是这样做的,然后通过每个提供商的响应形状往返一个假工具调用。不需要网络 — 本课教授形状,不是HTTP。
生产团队将此翻译器包装在 AbstractToolset(Pydantic AI)、UniversalToolNode(LangGraph)或 BaseTool(LlamaIndex)中。Phase 13 · 17发布一个网关,在任何三个提供商前面暴露OpenAI形状的API。
实践
code/main.py 定义一个规范的 Tool 数据类和三个翻译器,它们发出OpenAI、Anthropic和Gemini声明JSON。然后它将每个形状的手工提供商响应解析为相同的规范调用对象,演示语义在表面下是相同的。运行它并并排对比三个声明。
关注点:
- 三个声明块仅在信封和字段名称上不同。
- 三个响应块在调用所在位置不同(顶级
tool_calls、content[]块、parts[]条目)。 - 一个
canonical_call()函数从所有三个响应形状中提取{id, name, args}。
交付
本课产生 outputs/skill-provider-portability-audit.md。给定一个针对某个提供商的函数调用集成,该技能生成可移植性审计:它依赖哪些提供商限制,哪些字段需要重命名,以及移植到每个其他提供商时什么会中断。
练习
-
运行
code/main.py并验证三个提供商声明JSON都序列化相同的底层Tool对象。修改规范工具以添加枚举参数,并确认只有Gemini翻译器需要处理OpenAPI怪癖。 -
为每个提供商添加一个
ListToolsResponse解析器,提取模型在list_tools或发现调用后返回的工具列表。OpenAI原生没有这个;注意这种不对称。 -
实现
tool_choice转换:将规范ToolChoice(mode="force", tool_name="x")映射为所有三个提供商形状。然后映射mode="any"和mode="none"。查看本课的差异表。 -
选择三个提供商之一,从头到尾阅读其函数调用指南。找到其模式规范中其他两个不支持的一个字段。候选:OpenAI
strict、Anthropicdisable_parallel_tool_use、Geminifunction_calling_config.allowed_function_names。 -
编写一个测试向量:一个参数违反声明模式的工具调用。通过每个提供商的验证器运行它(第01课中的标准库版本可作为代理)并记录哪些错误触发。记录你会在生产中使用哪个提供商来获得严格性。
关键术语
| 术语 | 人们怎么说 | 实际含义 |
|---|---|---|
| Function calling | ”工具使用” | 用于结构化工具调用发出的提供商级API |
| Tool declaration | ”工具规范” | 名称 + 描述 + JSON Schema输入负载 |
tool_choice | ”强制/禁止” | Auto / required / none / 特定名称模式 |
| Strict mode | ”模式强制” | OpenAI标志,通过约束解码强制模式合规 |
tool_use block | ”Anthropic的调用形状” | 带有id、name、input的内联内容块 |
functionCall part | ”Gemini的调用形状” | 包含name、args和id的 parts[] 条目 |
| Arguments-as-string | ”字符串化JSON” | OpenAI将参数作为JSON字符串返回,不是对象 |
| Parallel tool calls | ”一轮扇出” | 一个assistant消息中的多个工具调用 |
| Refusal | ”模型拒绝” | 严格模式专用的拒绝块,替代调用 |
| OpenAPI 3.0 subset | ”Gemini模式怪癖” | Gemini使用与JSON Schema略有不同的方言 |
延伸阅读
- OpenAI — Function calling guide — 包括严格模式和并行调用的权威参考
- Anthropic — Tool use overview —
tool_use和tool_result块语义 - Google — Gemini function calling — 并行调用、唯一id和OpenAPI子集
- Vertex AI — Function calling reference — Gemini的企业表面
- OpenAI — Structured outputs — 严格模式模式强制详情