一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Qwen3.7-Max 在 OpenCode 中为什么会调用不存在的 unknown 工具?

时间:2026-09-13 08:36:01 编辑:袖梨 来源:一聚教程网

Qwen3.7-Max 在 OpenCode 中调用名为 unknown 的工具,通常不表示系统里存在一个隐藏工具。它表示模型或中间适配层返回的 function.name 没有匹配当前请求的 tools[]。应先保存 OpenRouter 原始响应与 OpenCode 实际发送的工具定义,确认 unknown 是上游直接生成、流式解析后产生,还是客户端名称映射失败后的占位值。

错误的准确含义

来源帖子显示,OpenCode 提示模型尝试调用不可用的 unknown 工具,并同时列出当前可用工具。这类错误发生在执行之前:调度器找不到对应工具,因此不应创建一个同名空工具来“看看会传什么”。这样做会掩盖根因,还可能把本应拒绝的参数交给意外代码执行。

OpenRouter 官方将工具调用错误分为无效 JSON、未知名称和参数不符合 schema 三类。其中 UnknownName 的定义就是返回的 function.name 不存在于请求的工具数组。这个分类能说明校验失败的位置,但不能单独判断错误来自模型、provider 模板还是 OpenCode。

工具名称可能在哪一层丢失

  1. OpenCode 组装请求时没有发送预期工具,或权限配置把它移除。
  2. OpenRouter 或实际 provider 转换协议时改变了工具定义。
  3. 模型生成了字面量 unknown,而不是已提供的名称。
  4. 流式工具名分片没有正确拼接,客户端用占位值代替空名称。
  5. MCP 或自定义工具注册名与执行器中的映射名不同。

只看终端中的最终提示无法区分这些情况。至少需要比较请求、原始响应和执行器错误三份证据。

保存实际发送的 tools 数组

打开脱敏调试日志,保存模型请求中的完整 tools[]。每个工具都应有唯一、稳定且清晰的函数名、用途描述和合法参数 schema。名称不能在一次会话中动态改变,也不要让多个插件注册相同名称。

{
  "type": "function",
  "function": {
    "name": "read_file",
    "description": "Read one file from the workspace",
    "parameters": {
      "type": "object",
      "properties": {
        "path": {"type": "string"}
      },
      "required": ["path"],
      "additionalProperties": false
    }
  }
}

如果错误日志列出的可用工具与请求体不一致,说明工具列表在客户端内部又被过滤或覆盖。OpenCode 的全局、项目和 agent 级权限都可能影响工具可见性;agent 级设置还可能覆盖全局设置。

检查 OpenCode 权限与工具注册

OpenCode 使用 permission 控制工具是允许、询问还是禁止。被禁止的工具不应继续暴露给模型。自定义工具和 MCP 工具常带有前缀,模型看到的正式名称可能与服务器内部函数名不同。

诊断时先关闭非必要插件和 MCP 服务,只保留一个内置只读工具。如果最小工具集正常,再逐个恢复扩展。每恢复一个扩展,都记录最终工具名列表;最先引入名称冲突或 unknown 的扩展就是主要调查对象。

核对原始 OpenRouter 响应

绕过 OpenCode 的界面格式化,保存同一请求的原始响应。关注 choices[].message.tool_calls[].function.name、工具调用 ID 和 arguments。根据结果分流:

  • 原始响应就是 unknown:模型或实际 provider 返回未知名称。
  • 原始响应名称正确,OpenCode 日志变成 unknown:客户端转换或注册映射有问题。
  • 原始名称为空或被截断:检查流式增量拼接。
  • 名称存在但请求 tools 中没有:检查请求构造和权限过滤。

不要只打印最终文本,因为工具调用通常位于结构化字段中,正文内容可能为空。

流式响应要按索引累计

流式工具调用中,ID、名称和参数可能出现在不同数据块。客户端应按 choice 索引和工具调用索引维护缓冲区,只追加非空片段,不能用后续空字符串覆盖已经收到的名称。

for event in stream:
    for call in event.tool_call_deltas:
        key = (event.choice_index, call.index)
        if call.name_fragment:
            buffers[key].name += call.name_fragment
        if call.arguments_fragment:
            buffers[key].arguments += call.arguments_fragment

validate_after_stream_end(buffers, advertised_tools)

若非流式请求正常、流式请求出现 unknown,证据会明显指向增量解析或协议转换。反之,两种模式都返回相同未知名称,则应继续检查模型与 provider。

固定 provider 做对照

OpenRouter 可以为同一模型路由到不同 provider,并允许失败时回退。不同 provider 的模型模板和工具兼容性可能不同。复现时固定一个 provider、关闭 fallback,并在模型页面查看该 endpoint 的 Tool Call Error Rate。

随后在同一提示、同一工具 schema 下更换 provider。若只有一个 provider 生成未知名称,应把请求 ID 和 provider 信息提交给 OpenRouter;若所有 provider 都一致失败,则更可能是模型行为或共享请求结构问题。

不要自动执行未匹配工具

执行器必须采用白名单:只有名称完全匹配、参数 JSON 可解析且通过 schema 校验的调用才能进入权限判断。未知名称应被拒绝,并将简洁错误作为工具结果反馈给模型,允许它选择现有工具重新尝试。

对 shell、写文件、提交、部署等有副作用的工具,应默认询问或拒绝,不能把 unknown 映射到通用 shell。OpenCode 官方权限规则支持对工具设置 allowaskdeny;排查期间可将编辑和 shell 保持为询问。

一个可复现的测试顺序

  1. 固定 OpenCode、模型 ID、OpenRouter provider 和版本。
  2. 仅暴露一个名字清楚的只读工具,执行非流式请求。
  3. 保存 tools 数组、原始 tool_calls 和 OpenCode 执行日志。
  4. 开启流式输出,比较工具名是否发生变化。
  5. 逐个恢复 MCP、自定义工具和 agent 权限。
  6. 更换 provider 重复相同请求,统计 UnknownName 比例。

成功标准不是“偶尔不报错”,而是所有返回工具名都属于当前请求的白名单,参数均能通过 schema,且未知调用不会进入执行阶段。

所以,unknown 是工具名称校验失败的表现,不是需要补装的工具。先比较请求中的工具清单、OpenRouter 原始响应和 OpenCode 映射结果,再通过非流式对照与固定 provider 缩小范围;同时保持严格白名单和权限确认,才能在定位兼容问题时避免意外执行。

热门栏目