DeepSeek Harness 第二轮才 400:redacted_thinking 与 Gemini schema

故障排查发布于 2026-10-30作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSH400redacted_thinkingGeminiUNSUPPORTED_CONTENTschema
DeepSeek Harness 里第一步工具调用成功、第二步才报 400:一个 redacted_thinking 块被回放时无法翻译。同族还有 Gemini 每轮重发工具 schema 触发的 INVALID_ARGUMENT,按时间线拆开看两处成因。

第一步成功了,第二步才炸——这个顺序本身就是线索:块不是在「收到」时出问题,而是在「回放」时出问题。 第一步的响应里带了 Anthropic 的 redacted_thinking 块,它被写进历史;第二步要把历史重新翻译成请求,适配器不认这个块,直接抛 UNSUPPORTED_CONTENT(#11139)。同族的另一条时间线在 Gemini 上:工具 schema 每一轮都被原样重发,所以一坏就是整场坏(#10904)。

DeepSeek Harness 第一轮:块第一次返回,还没进入下一份请求

第一轮能成功,是因为这个块只是被接收;此时代码走的是正常的分块读取路径,没有任何地方要求「把它翻译出去」。

  1. 看环境:@deepseek-ai/dsh-llm-deepseek 0.2.1-rc.3,端点是通过 Bifrost 的 Anthropic 兼容网关。
  2. 第一步:agent 调一个 MCP 工具,成功返回。
  3. 响应里出现 redacted_thinking 块——这是 Anthropic 协议定义的块类型,data 是签名载荷。
  4. 此时会话还正常,用户看不出任何异常。
  5. 但这一步的响应已经落盘,成为下一轮要回放的历史。

DSH插件 第二轮:历史回放时它被重新翻译

回放要把每个历史块翻译成 DeepSeek Messages 能表示的形态;startBlock() 只处理 text、thinking、tool_use,其余走 default 分支抛错。

  1. 看报错:DeepSeek Messages does not support response block redacted_thinking,code=UNSUPPORTED_CONTENT。
  2. 安装包里抛错位置在 lib/index.js:1878,源码对应 packages/llm/llm-deepseek 的翻译层。
  3. 事件流显示 assistant/attempt 以 error 结束,turn/end 带同一个错误。
  4. 后果:第二步之后的所有工具调用都跑不了,多步 agent 循环直接中断。
  5. 换模型(deepseek-v4-pro 与 deepseek-flash)无效,设 reasoningEffort: none 也无效——触发的是块类型,不是模型或推理档位。

DeepSeek Harness 为什么「丢掉这个块」不是修法

它的 data 是签名载荷,后续步骤依赖它原样回传;接受但不记录,会让推理连续性静默变化,失败从大声变成无声。

  1. 仓库里的协议往返测试已经把要求写清楚:输入 { type: 'redacted_thinking', data: '...' }。
  2. 记录形式是 { type: 'reasoning', thinkingSignature: '...', redacted: true }。
  3. 断言要求下一次请求仍然带着这份载荷。
  4. 而 dsh-llm-deepseek 这条链路有三处要一起改:翻译层对未知块抛错、ReplayBlock 类型没有 redacted 标记与 data 字段、序列化层只会把 reasoning 写成 thinking + signature。
  5. 所以真正的修复是「接受并记录,再在原索引重新发出」,属于持久化结构的变更,需要版本号配合。

另一个边界也要说清:抛错发生在 translate() 的生成器里,早于任何 llm/stream 监听器。外层包装只能看到「一个错误」,看不到那个块;而且请求侧也需要载荷已经被持久化,否则外部没有东西可回放。

DeepSeek插件 同族时间线:Gemini 的 schema 每轮都在重发

另一条「第二轮才炸」的变体更极端:它不是第二轮,而是每一轮;因为完整工具清单每轮都发,一个坏 schema 就让整场失败。

  1. 现象:会话切到 Gemini 后,每条消息都返回 400 INVALID_ARGUMENT,与内容无关。
  2. 对照实验:无工具 200、扁平 schema 200、oneOf 200;含 $ref、$defs、additionalProperties 全部 400。
  3. 报错细节:Invalid JSON payload received. Unknown name "$defs" at 'tools[0].function_declarations[0].parameters',而 UI 只显示泛化的 Request contains an invalid argument.。
  4. 原因:DSH 把工具 schema 原样转发,Gemini 只接受 OpenAPI 3.0 子集。
schema 形态结果
无工具 / 扁平 schema / oneOf200
含 $ref / $defs400
含 additionalProperties400
  1. 时间线差异:redacted_thinking 是「上一轮留下的东西在下一轮炸」,schema 是「每一轮都带同一份清单,所以每轮都炸」。

社区核对后还指出两个结构性事实:additionalProperties 在核心 schema 语言里是 type: 'object' 的必填项,所以大量内置工具天然带着它;而 MCP 桥把服务器的 inputSchema 原样挂上去,$schema、examples 一个不改。

DeepSeek Harness 两条临时路线,按代价排序

redacted_thinking 换走 pi-ai 的 anthropic-messages 路由;Gemini schema 先挂规范化插件做观察,再等核心在适配器边界做降级。

  1. 换路由(redacted 场景):用 pi-ai 适配器声明 anthropic 路由,api: anthropic-messages,指向同一网关。仓库内的往返测试表明这条路径能处理 redacted 块。
  2. 这条的证据是「对 mock Anthropic 服务器的树内测试」,不是对具体网关的实测,按「能处理该块的适配器」来理解,而不是「已验证的修复」。
  3. 挂规范化插件(Gemini 场景):@argszero/dsh-mcp-schema-normalizer 0.2.0,提供 inline-ref、prune-defs、drop-additional-properties 三条规则。
  4. 先开 mode: observe,它只报告会改什么,不改请求;确认命中范围后再切 normalize。
  5. 顺序上要挂在连接 MCP 服务器的那一行之前。
  6. 认清边界:它只作用于 mcp__<server>__<tool>,内置工具与 $schema、examples 仍会 400。

社区实测的对比数据很直观:挂载前后,$ref 从 745 降到 0、$defs 从 169 降到 0、additionalProperties 从 1422 降到 12;剩下 12 个来自 ask_user_question、workflow、present、todo_write 等 8 个非 MCP 工具。

DSH插件 修复方向:决定要放在序列化那一层

两类问题的正确修法都在适配器边界:为每个协议各做一步规范化,而不是让工具作者写多份 schema。

  1. redacted 场景:在 durable replay 信封里加入 redacted 标记与 data,序列化时按原索引重新发出,并同步更新固定当前行为的测试。
  2. Gemini 场景:在为某个路由序列化请求之前做一次降级——内联 $ref/$defs(带深度上限防递归)、删除不支持的关键字、必要时替换成宽松 fallback。
  3. 更彻底的做法是改用允许清单姿态:只输出 Gemini 接受的关键字集合,否则逐条删键永远会碰到下一个键。
  4. 内置声明那一半只能由核心改,插件够不到;function_response 那一半(另一条相关报告)也在核心。
  5. 顺带一个便宜的改进:把响应体里已有的字段级原因(Unknown name "$defs")透出来,而不是只显示泛化文案。

注意事项

  1. 别把「第一轮成功」当成没问题。块已经落盘,问题只是延后一轮。
  2. 别靠调低推理档位绕开。reasoningEffort: none 实测无效。
  3. 别在请求监听器里改 schema。那个请求是深冻结的,监听器只能读、不能替换。
  4. 别把插件当核心修复。它重写的是「告诉模型什么」,不是「提供商接受什么」。
  5. 报问题时带上字段级错误。泛化文案会把「协议不兼容」误导成「密钥或网络问题」。

还有一条通用经验:两条时间线的共同点是「持久化的东西在下一轮被重放」。只要报错位置每轮都一样、内容却与你这轮输入无关,就先去翻历史里最近新增了什么块或什么声明。

如果你的 400 里既没有块类型名、也没有 Unknown name,那属于第三类问题,先去查请求体格式与协议端点。

两条时间线的处置顺序也不同:redacted 只能换路由或等核心改持久化结构,Gemini schema 至少可以先用插件把 MCP 那一半压下去。相关排查见 DeepSeek Harness Gemini 工具调用 400:thought_signature 缺失怎么修。

常见问题

DeepSeek Harness 第一步工具调用成功,第二步报 UNSUPPORTED_CONTENT,为什么?

因为第一步的响应里带了 redacted_thinking 块,它被写进 DeepSeek Harness 的会话历史;第二步要回放这份历史时,适配器只认 text/thinking/tool_use 三种块,其余走 default 分支直接抛错,报「DeepSeek Messages does not support response block redacted_thinking」。

把 redacted_thinking 块丢掉,DeepSeek Harness 不就能继续了吗?

不行,这块不能丢:它的 data 是签名过的载荷,后续步骤需要原样回传,丢掉会让 DeepSeek Harness 的推理连续性在服务端静默变化——失败从「大声报错」变成「悄悄出错」,更糟。

Gemini 报 400 INVALID_ARGUMENT、切到 DeepSeek 就正常,是 DeepSeek Harness 的工具 schema 问题吗?

是,DeepSeek Harness 在 Gemini 上遇到的 400 就是工具 schema 引起的:Gemini 的 functionDeclarations 只接受 OpenAPI 3.0 子集,遇到 $ref、$defs、additionalProperties 直接 400。社区实测无工具、扁平 schema、oneOf 都返回 200,这三类关键字全部 400。

为什么一个工具 schema 有问题,整个 DeepSeek Harness 会话都不能用?

因为 DeepSeek Harness 每轮都重发完整工具清单,只要其中一个工具带不支持的关键字,请求在推理前就被拒。所以失败与你的消息内容无关,表现为「每一条都 400」。

dsh-mcp-schema-normalizer 能修好 DeepSeek Harness 在 Gemini 上的 400 吗?

只能修一半,dsh-mcp-schema-normalizer 修不动 DeepSeek Harness 里的内置工具。它按 mcp__<server>__<tool> 选择器重写 MCP 工具 schema,社区实测 $ref/$defs/additionalProperties 计数从 745/169/1422 降到 0/0/12;剩下 12 个来自内置工具,插件够不到,而且 $schema、examples 仍会被拒。

相关术语

redacted_thinking
redacted_thinking 是 Anthropic Messages 的一种内容块,携带经过加密或脱敏的推理内容,客户端应原样回传而不解释。丢弃它会让后续请求的推理连续性发生变化。— https://github.com/deepseek-ai/deepseek-harness/discussions/11139
UNSUPPORTED_CONTENT
UNSUPPORTED_CONTENT 是 DeepSeek 适配器遇到无法表示的响应块时返回的错误码。它在翻译阶段抛出,早于任何流式监听器,因此外部无法通过过滤响应来修复。— https://github.com/deepseek-ai/deepseek-harness/discussions/11139
functionDeclarations
functionDeclarations 是 Gemini generateContent 请求里声明可调用函数的结构,其 parameters 只接受 OpenAPI 3.0 子集。出现 $ref、$defs、additionalProperties 等键会返回 Cannot find field。— https://github.com/deepseek-ai/deepseek-harness/discussions/10904
inline-ref
inline-ref 是把本地 $ref 指向的子 schema 直接内联展开的处理方式,用于让 Gemini 这类只认扁平结构的端点接受原本带引用的 schema。— https://github.com/deepseek-ai/deepseek-harness/discussions/10904

来源