Function Calling 的本质是:模型根据工具 Schema 生成结构化调用请求,而不是模型直接执行任意 API。本周先实现一个只读工具的完整闭环,再决定是否增加供应商适配层;如果项目一开始就需要同时接入 OpenAI、Google Gemini 和 Claude API,就不要把任何一家平台的 JSON 直接写死在业务代码里。
这篇文章适合三类人:
- 初次构建工具型 AI 的后端开发者,需要理解请求、执行、回传的通用流程。
- 多模型平台工程师,需要设计统一的调用事件和状态管理。
- 安全负责人,需要控制凭据、权限和高风险动作。
先看共同闭环:模型提议,应用执行
无论你使用哪一家平台,Function Calling JSON API 通常都可以拆成以下链路:
- 你的应用向模型发送用户消息和工具声明。
- 模型判断是否需要工具,并生成工具名称与参数。
- 你的适配层解析模型响应,转换成内部调用事件。
- 执行器校验参数、认证并真正请求外部 API。
- 执行器把结果封装后回传模型。
- 模型根据结果生成最终文本,或者继续提出下一次工具调用。
关键边界只有一句话:模型负责提出调用意图,应用负责执行调用动作。Google Gemini 官方文档明确说明,函数代码需要由你的应用提取名称和参数后执行;Claude API 的客户端工具也由你的应用运行,再把 tool_result 回传。(ai.google.dev)
这也回答了一个常见误解:Function Calling 并不是给模型一把可以直接访问数据库、文件系统或支付接口的钥匙。它更像一份“可调用能力目录”和一张结构化请求单。
模型层对比:声明方式相似,响应事件不同
模型接入开发者首先要声明工具。工具至少应包含:
- 唯一名称,例如
get_order_status; - 清晰描述,说明什么时候使用、什么时候不能使用;
- 输入 Schema,定义字段类型、必填项和允许值;
- 调用策略,例如自动选择、强制调用、禁止调用或限制并行调用。
JSON Schema 解决的是结构问题。它可以约束对象、数组、字符串、数字、布尔值等类型,也可以表达 required、enum 和嵌套属性。但 Schema 本身不是业务代码,无法独立判断“这个用户是否拥有该订单”。(json-schema.org)
OpenAI:关注调用项与调用结果的关联
在 OpenAI 的 Responses API 中,你通常会从响应输出中筛选 function_call 项,读取工具名称、参数和 call_id,执行完成后再提交 function_call_output。官方示例强调,即使 SDK 提供辅助函数,真正的工具仍由你的应用运行。(platform.openai.com)
如果使用严格工具 Schema,OpenAI 文档和帮助中心都要求你关注受支持的 JSON Schema 子集。strict: true 能提高参数符合 Schema 的确定性,但 Schema 不被支持时,请求可能直接被拒绝。(help.openai.com)
因此,内部事件可以保存:
{
"provider": "openai",
"event_type": "tool_call",
"tool_name": "get_order_status",
"call_id": "call_123",
"arguments": {
"order_id": "A1001"
},
"raw_response": {}
}
上面的格式只是内部事件示例,不是三家平台通用的请求格式。你不能把它直接发送给 Google Gemini 或 Claude API。
Google Gemini:函数声明与函数结果分开管理
Google Gemini 的函数声明通常包含 name、description 和 parameters。模型返回函数调用后,你的应用执行函数,再把结果作为函数结果发送回模型。官方文档还区分了并行调用和组合调用:互不依赖的工具可以同时调用,有依赖关系的工具则需要按顺序执行。(ai.google.dev)
这会影响你的状态管理。比如:
- 查询天气和查询汇率互不依赖,可以并行;
- 先查询用户身份,再查询该用户的订单,不能把第二步提前;
- 每个并行调用都必须保留自己的名称、参数和调用标识;
- 任意一个工具失败时,要明确记录是整体失败还是部分成功。
如果你使用 Gemini 的无状态模式,就要自行保存完整历史,包括用户输入、模型生成的函数调用步骤和函数结果。历史丢失后,模型可能看不到自己刚才提出的调用,导致重复请求或无法继续流程。(ai.google.dev)
Claude API:tool_use 与 tool_result 是核心边界
Claude API 的客户端工具流程更容易从响应块理解:模型返回 tool_use,你的应用执行工具,再发送带有 tool_use_id 的 tool_result。当 Claude 需要工具时,响应中的 stop_reason 会反映 tool_use 状态。(docs.anthropic.com)
Claude 的工具声明使用 input_schema。在支持严格工具使用的场景中,可以通过 strict: true 要求调用参数匹配 Schema。需要注意的是,严格匹配仍然只是结构保证;工具是否允许执行,依旧由你的权限系统和业务服务决定。(docs.anthropic.com)
⚠️ 不要为了“统一格式”而丢掉平台原始响应。原始响应中的停止原因、思考状态、并行调用信息或平台特有字段,往往是定位线上问题的唯一线索。
适配层对比:统一内部契约,不统一所有细节
多模型项目最容易踩的坑,是设计一个看似通用、实际无法覆盖三家的 JSON 请求格式。
更稳妥的做法是分成两层:
内部契约层只表达业务真正关心的内容:
{
"tool_name": "get_order_status",
"arguments": {
"order_id": "A1001"
},
"request_id": "req_789"
}
供应商适配层负责转换:
- OpenAI 的
function_call、call_id和function_call_output; - Google Gemini 的函数调用步骤、函数名称和函数结果;
- Claude API 的
tool_use、tool_use_id和tool_result。
适配层至少要保留三份记录:
- 原始供应商响应;
- 转换后的内部事件;
- 执行器返回的标准结果和错误原因。
不要只保留“成功或失败”。线上排错需要知道:是模型没有选工具、参数无法解析、Schema 不兼容、权限被拒绝,还是外部 API 超时。
如果你正在搭建多模型平台,可以先阅读 Hashvps 的帮助中心,把环境、账号和部署权限的说明与应用层工具权限分开管理。工具调用问题通常不是单一模型问题,而是模型、适配层和执行环境共同造成的。
执行层对比:工具调用不是 API 密钥代理
真正执行 API 的代码,应当由工具执行开发者维护。它不能把模型返回的参数直接拼接成任意网络请求,也不能从提示词中读取长期凭据。
执行器至少需要完成以下检查:
- 认证:从密钥管理系统读取短期凭据,不把长期密钥放入提示词、聊天历史或模型可读文件。
- 授权:根据用户身份、租户、资源所有权和动作等级判断是否允许执行。
- 参数校验:先做 JSON 解析,再做 Schema 校验,最后做业务语义校验。
- 网络控制:限制目标域名、请求方法、重定向和响应体大小。
- 超时与重试:区分连接超时、服务端错误、参数错误和业务拒绝。
- 幂等处理:写操作必须携带幂等键,避免模型重复调用导致重复创建或重复扣款。
- 结果封装:只把完成任务所需的信息回传模型,避免泄露内部堆栈、密钥和不必要的用户数据。
遇到模型生成的参数缺失、类型错误或字段值不合法时,执行器应先拒绝调用,再按错误类型决定后续动作。可以返回类似下面的结构:
{
"ok": false,
"error_code": "INVALID_ARGUMENT",
"message": "order_id 缺失或格式不符合要求",
"retryable": true
}
但“可重试”也要有限制。参数缺失可以让模型补充,权限失败不能让模型无限重试,破坏性动作更不能因为模型再次请求就自动放行。
安全层对比:Schema 合规不等于授权通过
安全与业务团队最好把工具分成四类:
- ✅ 只读工具:查询状态、读取公开信息、获取日志摘要。
- ⚠️ 低风险写入:创建草稿、更新非关键配置、生成待审核任务。
- ❌ 高风险写入:发送通知、修改生产配置、创建计费记录。
- ❌ 破坏性或开放网络工具:删除资源、执行任意命令、访问未限制的外部地址。
每一类工具都应有不同的审批策略。只读工具可以自动执行,但仍要检查租户边界;低风险写入可以要求用户确认;高风险动作应进入人工审批或二次验证;破坏性工具则应限制目标、参数和执行环境。
这与 JSON Schema 的官方说明是一致的:Schema 主要描述数据结构和验证规则,并不包含任意业务代码。换句话说,Schema 能告诉你参数“长什么样”,不能告诉你请求“该不该做”。
如果工具涉及 macOS 命令、Xcode 构建或苹果自动化,执行环境也应单独隔离。不要让模型直接连接开发者的日常电脑。可以先通过 远程 Mac 相关服务说明了解节点、权限和任务周期,再决定是否需要专用执行节点。
测试层对比:不要只测“成功调用”
测试团队应使用同一个只读业务样例,分别记录 OpenAI、Google Gemini 和 Claude API 的 SDK、模型、接口入口与调用日志。不要只比较最终答案,因为最终答案正确,并不代表中间调用安全。
建议建立以下检查清单:
- [ ] 工具名称不存在时,系统会拒绝执行,而不是猜测相近名称。
- [ ] 必填参数缺失时,系统会要求补充或返回可修正错误。
- [ ] 参数类型错误时,不会进入真实 API。
- [ ] 多个并行调用各自保留调用 ID 和执行结果。
- [ ] 工具超时时,模型不会自动重复执行不可幂等动作。
- [ ] 工具返回恶意或异常文本时,不会改变系统权限。
- [ ] 历史消息被截断后,系统仍能识别当前调用状态。
- [ ] 最终响应错误时,可以回看原始模型响应和执行日志。
- [ ] 同一业务样例在三家平台上都有独立的格式适配记录。
测试重点不是“模型会不会调用工具”,而是“调用失败时系统是否仍然可控”。
常见疑问:从格式误解回到责任边界
Function Calling 会直接执行 API 吗?
不会。它只生成结构化请求。API、数据库、命令行或自动化脚本的执行权属于你的应用。即使参数完全符合 Schema,也必须经过认证、授权、资源所有权和业务规则检查。
三家模型的工具调用 JSON 格式一样吗?
不一样。共同概念包括工具名称、参数、调用结果,但字段和消息结构不同。OpenAI 更强调响应输出项与 call_id,Google Gemini 依赖函数调用步骤和历史状态,Claude API 则围绕内容块中的 tool_use 与 tool_result 组织闭环。
Function Calling 为什么需要 JSON Schema?
因为自然语言描述不够稳定。Schema 可以把参数类型、必填项、枚举值和嵌套对象写成可检查的契约。它能降低解析错误,但不能代替权限系统,也不能完成复杂的业务语义判断。
参数校验失败后,系统应当如何恢复?
执行器先拒绝调用,随后根据错误类型决定是否回传模型修正。格式错误可以重试一次或要求用户补充;权限错误、资源不存在和高风险动作不应通过无限重试解决。
多模型场景怎样共享同一批工具能力?
复用工具的业务定义,不复用供应商请求 JSON。你可以维护一份内部 Schema 和工具目录,再由适配层生成各平台需要的声明与结果回传,同时保留原始响应。
架构选择:直接接 SDK,还是增加适配层?
你可以按下面的条件分支做决定:
- 若项目只接入一家模型、工具数量少、团队需要快速验证,直接使用官方 SDK。先把参数校验、凭据隔离和执行日志做好。
- 若项目需要同时接入 OpenAI、Google Gemini 和 Claude API,增加内部工具契约层。不要让业务代码依赖
tool_use或function_call等单一平台字段。 - 若多个产品共享同一批工具,把工具目录、版本、权限等级和审批策略独立成服务。
- 若项目需要连续状态、并行调用或长流程恢复,增加调用状态存储,并为每次调用设置唯一请求 ID。
- 若工具依赖 macOS、Xcode 或苹果自动化,再评估远程 Mac 执行节点;如果只是普通 HTTP 只读 API,通常不必为此增加 Mac 环境。
这里可以结合 Hashvps 的服务条款确认环境使用边界,但不要把基础设施权限等同于应用工具权限。远程节点只能提供执行环境,最终的 API 授权仍由你的系统负责。
三家平台的内部适配对照
| 对比维度 | OpenAI | Google Gemini | Claude API |
|---|---|---|---|
| 工具声明重点 | 函数名称、描述、参数 Schema、严格模式 | 函数声明、参数 Schema、调用策略 | 工具名称、描述、input_schema、工具选择 |
| 模型调用信号 | function_call 输出项 |
function_call 步骤或对应函数调用结构 |
tool_use 内容块,通常配合 stop_reason |
| 结果回传重点 | function_call_output 与 call_id |
函数结果与对应调用关联 | tool_result 与 tool_use_id |
| 执行责任 | 你的应用执行自定义函数 | 你的应用执行函数代码 | 客户端工具由你的应用执行,部分服务端工具由平台执行 |
| 适配层注意点 | 不要丢失原始输出项和调用 ID | 管理并行、组合调用与历史 | 保留内容块顺序、工具 ID 和错误结果 |
表中的字段来自各家官方工具调用文档,具体支持范围仍应以你选定的模型、API 入口和 SDK 版本为准。不要把不同入口的示例拼成一个“三家通用请求”。
如果你的当前方案是把工具代码直接跑在开发者本地电脑上,常见问题是权限边界混乱、环境依赖难以复现,以及多人共享时无法稳定审计;如果使用临时云环境,又可能遇到 macOS、Xcode 或苹果自动化能力不足。对于需要短期测试、持续自动化或隔离执行的任务,租赁 Hashvps 的远程 Mac 可以把执行节点与日常设备分开,按工具权限和任务周期选择环境会更稳妥。长期稳定重负载、必须拥有物理接口,或需要完全控制硬件的人,则应优先评估自购设备,而不是强行租赁。
为你的 AI 工具链配备稳定的远程 Mac 执行节点
通过 Hashvps 租用远程 Mac,将 Function Calling 生成的 JSON 指令安全接入真实应用与自动化任务。
无需自行采购和维护硬件,按需获得可远程使用的 Mac 环境,降低工具型 AI 的部署与测试成本。