测一家
首页/教程/中转站的工具调用为什么老失败:五个层面的原因
协议接入

中转站的工具调用为什么老失败:五个层面的原因

工具定义、schema、返回解析、多轮工具、流式工具,每一层都有各自的失败方式,附最小验证请求。

agentapiai.com 2026-11-13 约 8 分钟

Agent 框架跑不起来,八成是工具调用这一环。而中转站是这一环里最容易加戏的地方:它可能改写工具定义、丢字段、把结构化返回拍平成自然语言。

一、先跑一个最小的工具调用

{
  "model": "gpt-4o-mini",
  "messages": [{"role": "user", "content": "北京现在天气怎么样?"}],
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "查询城市天气",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    }
  }],
  "tool_choice": {"type": "function", "function": {"name": "get_weather"}}
}

tool_choice 强制指定,是为了排除"模型自己决定不调用"这个变量。如果这样都不返回 tool_calls,问题不在模型。

二、五层失败方式

第一层:工具定义被丢弃

有些站点只代理文本生成,tools 字段直接被忽略。表现是模型用自然语言回答"我无法查询天气",而不是返回工具调用。

验证:看响应里有没有 tool_calls 字段。没有就是被丢了。

第二层:schema 被简化

站点的转换层可能只保留 name 和 description,把 parameters 丢掉。模型不知道该传什么参数,就会瞎编。

验证:在参数里放一个必填的、不常见的字段(例如 city_code),看模型返回的参数里有没有它。没有就说明 schema 没传过去。

第三层:返回结构不标准

标准返回:

{
  "choices": [{
    "message": {
      "role": "assistant",
      "tool_calls": [{
        "id": "call_abc",
        "type": "function",
        "function": {"name": "get_weather", "arguments": "{\"city\":\"北京\"}"}
      }]
    },
    "finish_reason": "tool_calls"
  }]
}

注意 arguments 是字符串,不是对象。有些站点会直接给对象,你的 JSON.parse 就会炸。

验证:打印 typeof arguments。应该是 string。

第四层:多轮工具调用断了

完整流程是:模型返回 tool_calls → 你执行工具 → 把结果作为 role: tool 消息发回去 → 模型继续。

有些站点在第二轮不认 role: tool 消息,或者要求 tool_call_id 必须匹配但又不返回 id。表现是第二轮 400,或者模型无视工具结果。

验证:手动构造两轮请求,第二轮带一条 {"role":"tool","tool_call_id":"...","content":"..."}。

第五层:流式工具调用拼接错误

流式下 tool_calls 是分片到达的:第一个块有 id 和 name,后续块只有 arguments 的片段。客户端要按 index 拼接。

站点如果在这里改写,可能把片段顺序打乱或丢掉 index。

验证:流式请求,打印每个块的 tool_calls,看 index 是否连续、arguments 拼起来是否是合法 JSON。

三、Anthropic 和 Gemini 的差异

OpenAIAnthropicGemini
工具定义tools[].functiontools[](name/description/input_schema)tools[].functionDeclarations
调用返回tool_calls[].function.arguments(字符串)content[].type == "tool_use",input 是对象parts[].functionCall,args 是对象
强制调用tool_choicetool_choice: {"type":"tool"}toolConfig.functionCallingConfig.mode = "ANY"
结果回传role: toolrole: user,content 里放 tool_result blockrole: user,parts 里放 functionResponse

如果站点声称"兼容 Anthropic",但工具调用返回的是 OpenAI 的 tool_calls,你的 Anthropic SDK 会解析失败。

四、一个自测脚本的思路

  1. 发强制工具调用请求
  2. 断言 finish_reason 是 tool_calls(或对应值)
  3. 断言 arguments 是字符串且能解析
  4. 断言解析出的对象包含 schema 里的必填字段
  5. 第二轮带上工具结果,断言模型能继续
  6. 流式重复一遍,断言片段能拼成合法 JSON

六步都过,工具调用这一环才算通。

五、本站的覆盖情况

检测台的第六项检查做的是"强制函数调用必须返回标准结构",当前版本只覆盖 OpenAI 两种协议;Anthropic 和 Gemini 的工具检查记 SKIP,不假装通过。这个限制写在方法页里,是因为不同协议的工具体差异太大,用一个通用检查会给出误导性的结论。

六、如果站点不支持工具

有两个选择:

  1. 换站。如果工具调用是你的核心需求,不值得在兼容层上赌。
  2. 自己实现 ReAct。让模型输出 JSON 格式的动作,你解析后执行。这样不依赖站点的工具支持,但需要处理模型输出格式不稳定的问题。

第一种更省事,第二种更可控。取决于你的容错预算。