工具声明格式族混用被拒 (Mixed Tool Schema Rejection)
触发条件
在同一个请求的 tools 数组里混用不同格式族的工具声明:
- OpenAI / function-calling 风格:
{"type": "function", "function": {"name": "...", "parameters": {...}}} - Gemini 风格:
{"googleSearch": {}}、{"codeExecution": {}}等裸键对象
症状
- 只发
{"tools": [{"googleSearch": {}}]}:可以通过,接口正常识别。 - 只发纯 function-calling 数组:可以通过。
- 把两者放进同一个
tools数组:被拒,报 schema 校验错误,接口不认。
尝试过的做法
- 确认单个工具定义本身格式正确(
{"googleSearch": {}}单独发没问题)。 - 尝试直接拼接两种格式的数组 → 失败。
根因(推断)
接口/网关在解析 tools 时按元素形态探测格式族:
- 数组里出现
{"function": {...}}→ 走 OpenAI function-calling 解析器 → 不认googleSearch; - 数组里出现
{"googleSearch": {}}→ 走 Gemini 风格解析器 → 不认function; - 混合时探测分支或校验器要求数组内元素同构,直接拒绝。
核心规则:tools 数组格式族必须自洽,不能混用。 单一形态时无歧义,所以能过。
最终解决
- 全部用 OpenAI 风格:
[{"type": "function", "function": {...}}, ...] - 或全部用 Gemini 风格:
[{"googleSearch": {}}, ...] - 需要两类能力时,走代理层做格式转换(把 Gemini 声明映射成 function-calling 或反之),而不是在同一数组里拼接。
变体:格式错层 → 静默丢弃(而非报错)
与"混用被拒"不同的第二种表现:数组内部自洽,但与接收方约定的格式族不一致 → 不报错,整个 tool 被静默丢弃。
触发条件
- 原请求(如 req.json 抓包)中
tools是 OpenAI 风格{"type": "function", "function": {...}},说明这条链路(网关/代理)只认该格式; - 用户用 Gemini 原生格式
{"functionDeclarations": [...]}覆盖 tools(自己做了格式转换),再追加{"googleSearch": {}}。
症状
- 请求成功,不报错;
- 模型回复"只看到 Google search"——25 个函数全部不可见。
根因
- 网关解析
tools时按type+function形态找函数:functionDeclarations条目匹配不上任何函数 → 整项丢弃; googleSearch是 Gemini 原生特殊工具,网关不做转换直接透传 → 存活。- 判定信号:请求成功 + 部分工具消失 + 消失的是被改写格式的那部分,说明中间有网关在做格式解析/转换,而非模型或直连 API 的问题。
修复
- 与产生原请求的格式族保持一致:OpenAI 风格函数数组 + 追加
{"googleSearch": {}}独立元素(已验证可透传)。 - 若直连 Gemini API(无网关):
functionDeclarations格式本身正确,但 Claude Code 原生 schema 带的$schema和additionalProperties字段会被 Gemini 严格校验,需先剥掉;且直连时非法 schema 通常整请求 400,不会出现"只看到部分工具"。
下次如何识别
- 报错指向
tools/tool/ schema 校验,且你的数组里同时存在function键条目和googleSearch/codeExecution等裸键条目 → 就是这个问题。 - 单独发某种格式没问题、合在一起被拒,是典型的"格式族探测/同构校验"信号。
- 请求成功但模型声称只看到部分工具 → 不是模型问题,是覆盖的 tools 格式与链路约定不一致,被网关静默丢弃。用抓包里的原始工具格式对照即可定位。
相关概念
- OpenAI 兼容反代的协议兼容与语义保真 (Protocol Compatibility and Semantic Fidelity) — 传输层兼容与语义层保真的区分,同一思路
- MCP (Model Context Protocol)
本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/AI apps