DeepSeek Harness 报 unknown tool 空转?DSH plugin 工具调用 id 被覆盖排查
DeepSeek Harness 报 unknown tool ""、每个工具调用都失败、模型空转几步后退出,根因是流式翻译把工具调用的身份覆盖成了空:provider 在首帧给出 id 与 name 后,又发送带显式 null 的续帧,SSE 翻译用 !== undefined 判定,把已捕获的身份写成了 null。 这个问题不丢数据、不坏配置,换一个不发显式 null 续帧的 provider 就能立刻恢复。
DeepSeek Harness 报 unknown tool 空转是什么样
现象非常固定:日志里出现 [tool/call] {"callId":"","name":""},紧接着报 unknown tool ""。 有用户实跑遇到(讨论原文),表现如下:
- 模型正常发起工具调用,但打印出来的调用请求里
callId与name都是空字符串; - 运行时找不到名为空的工具,直接以
unknown tool ""拒绝执行; - 模型收不到有效结果,继续尝试若干步后退出,整轮对话零产出;
- 同一 provider 下所有工具调用一起失败,不是某一个插件的问题。
这也是它容易被误判的原因:报错文本指向「工具不存在」,看起来像插件没装上或名字写错,实际插件完全正常——被丢掉的是调用本身携带的身份信息,而不是工具(讨论原文)。
DSH plugin 工具调用身份为什么会被 null 覆盖
根因在 SSE 翻译层的判定写法:把「字段存在」与「字段有值」当成了一回事。 流式响应按帧下发,首帧带完整的 id 与 name,后续增量帧只带变化字段。社区核对源码后定位到 packages/llm/llm-deepseek/src/translate.ts,问题链条是:
- 翻译层累积工具调用身份时,用
!== undefined判断这一帧是否要更新字段; - provider 发的续帧里字段是
null而不是省略,null !== undefined成立,于是执行覆盖; - 首帧捕获的 id 与 name 被覆盖为空,日志里就出现
{"callId":"","name":""}; - 其中 空 name 才是致命项:name 为空直接导致工具无法解析,空 id 只是让调用难以对上号(来源)。
正确写法是只在字段「有实际值」时更新,把 undefined 与 null 都视为无效值。社区据此给出了修改判定条件的补丁分支 fix/tool-call-null-delta-identity,另有参考实现分支 Jstn-1g/reference/discussion-4671-sse-null-identity(提交 138691e0),两者都尚未合入。这个问题与 DSH插件、DeepSeek插件 各自的实现无关,因为覆盖发生在运行时共用的流式翻译层。
DSH plugin 场景下 unknown tool 空转怎么规避,修复到哪一步
在官方合入前,规避手段是绕开触发条件:换 provider / 模型,或回退版本。 按可达性从高到低排:
- 切换到不发显式
null续帧的 provider 或模型:这是最快见效的做法,同一会话换完即恢复; - 回退到出问题前使用的 DeepSeek Harness 版本,避开引入该翻译路径的改动;
- 排查时保留完整日志:
[tool/call]那一行的callId/name是否为空,是区分「工具真的不存在」与「身份被覆盖」的唯一判据; - 需要装插件或回退插件版本来对照时,可在「设置 → 插件市场」里管理已装插件,本项目的社区插件市场入口是 DSH Plugin Hub;
- 修复进度:补丁分支已经给出明确的改点(判定条件),但仍在等待官方合入,升级到含修复的版本前不要指望它自动消失。
DSH plugin 排查注意事项
先分清「工具不存在」与「调用身份被覆盖」再动手:前者要改插件,后者只要换 provider 或回退版本。 三条别踩的坑:
- 不要把
unknown tool ""当成插件安装问题去重装:插件卸载重装不解决任何问题,被覆盖的是运行时捕获的身份,与插件包本身无关。 - 该问题与模型能力无关,不要靠换提示词规避:无论提示词怎么写,续帧里的
null都会照常覆盖。 - 模型报错类问题还汇总在《DeepSeek Harness 模型报错合集:连接失败、模型清单缺失与配置排查》,可对照查看。

常见问题
DSH plugin 出现的这种空转,根因是流式翻译把工具调用的身份覆盖成了空。provider 在首帧给出 id 与 name 之后,又发了带显式 null 的续帧,SSE 翻译用 !== undefined 判定,把首帧已捕获的 id/name 覆盖成 null,于是每个工具调用都变成空名字,模型只能空转若干步后退出。
DeepSeek Harness 里,只有会发送显式 null 续帧的 provider 才触发这个覆盖。多数 OpenAI 兼容实现会直接省略空字段,字段缺席时不会被覆盖;一旦实现选择发 "id": null 这样的显式空值,就会命中这个判定缺陷。
在 DeepSeek Harness 的日志里,空 name 才是致命项。id 为空只会让调用难以对上号,name 为空则直接变成 unknown tool "",工具没有可执行目标,调用必然失败;排查日志时优先确认 name 这一项是否被覆盖。
DSH plugin 遇到 unknown tool 空转时,先换用不发显式 null 续帧的 provider 或模型,或回退到出问题前的版本,工具调用即可恢复。社区已给出修改 translate.ts 判定条件的补丁分支,等待官方合入后升级到含修复的版本即可。
相关术语
- DeepSeek Harness
- DeepSeek Harness(简称 DSH)是 DeepSeek 官方开源的智能体运行时,负责把模型、工具与插件组织成可执行的会话流程。— DeepSeek Harness 官方文档
- tool call(工具调用)
- tool call 是模型在会话中要求运行时执行某个工具的请求,由 id、name、arguments 三部分组成。— DeepSeek Harness 官方架构文档
- SSE delta(流式增量帧)
- SSE delta 是流式响应中在首帧之后追加的分片数据,只携带发生变化的字段,未变化的字段本应省略而不是填 null。— DeepSeek Harness 官方架构文档
来源
- deepseek-harness Discussion #161:工具调用被丢成 UNKNOWN_TOOL(id/name 为空)· deepseek-ai(GitHub Discussions)
- deepseek-harness Discussion #4671:provider 流式发显式 null 时丢 tool call id/name· deepseek-ai(GitHub Discussions)