DeepSeek Harness redacted_thinking:多步工具调用兼容

作者
DeepSeekAgent.io 编辑部
发布
更新

在 Anthropic 兼容协议下接入 DeepSeek 模型时,多步工具调用可能在第二轮突然中断。社区复现中,第一步 Tool Call 正常完成,模型继续响应时返回了 redacted_thinking 内容块,DSH 适配器随后抛出 UNSUPPORTED_CONTENT,提示 DeepSeek Messages 不支持该响应块。

问题的关键不在工具本身,也不在是否打开可见推理。redacted_thinking 是 Anthropic 协议为不可展示的推理内容准备的块类型,客户端需要把它作为不透明数据保存并在后续消息中按协议回传。

为什么第一步成功、第二步失败

第一次请求尚未携带上一轮的完整 Assistant 内容,模型可以正常产生 tool_use。工具结果写回后,第二次请求进入多步链路,需要带上前一轮的内容块。网关或模型返回 redacted_thinking 时,适配器的块分派只认识 text、thinking 与 tool_use,默认分支便将它当作未知内容抛错。

因此,表面症状是“工具调用到第二步停了”,实际故障点位于响应内容块的协议映射。切换 reasoningEffort 为 none 或 high 都可能复现,因为这不是界面是否展示思考文本的开关问题。

redacted_thinking 应该怎样处理

这种块不能被转换成普通文本,也不应在日志或 UI 中展开。兼容层需要满足三件事:

  1. 接受:解析器识别该块,不因未知分支中止整个响应;
  2. 保留:原样保存协议要求回传的字段,避免改写签名或块顺序;
  3. 隔离展示:用户界面不显示受保护内容,调试日志也只记录类型和位置。

如果下游 DeepSeek Messages 数据结构无法表达该块,适配器可以将它保存在专用的 opaque 元数据中,并在 Anthropic 协议下一轮请求时恢复。简单丢弃是否安全,取决于网关对签名和上下文连续性的要求;默认做法应优先保持往返一致性。

如何复现和定位

建立一个至少需要两次工具调用的最小任务,并保存以下材料:

  • 使用的 DSH、Provider 插件与网关版本;
  • 模型 ID、协议类型和推理档位;
  • 第一轮响应块的类型顺序;
  • Tool Result 写回后的第二轮原始块类型;
  • 适配器错误栈中处理块的函数与索引。

如果第一步稳定成功、第二步在相同块类型处失败,就不必继续更换工具或扩大超时。应直接审查协议适配器的联合类型与默认分支。网关经过 Bifrost 等转换层时,还要确认是上游原本返回该块,还是中间层将其他推理内容映射成了 redacted_thinking。

修复后的回归测试

兼容层更新后,至少需要覆盖:

  • text → tool_use → tool_result → text;
  • thinking → tool_use;
  • redacted_thinking → tool_use;
  • 多个内容块交错及多个 Tool Call;
  • 响应保存、Session 重放和下一轮回传;
  • 日志、导出与 UI 不泄露受保护内容。

只验证“不再抛错”还不够。第二轮必须能继续调用工具,Session 重开后也要维持相同的块顺序与上下文。

如果遇到整轮固定等待而非明确的 UNSUPPORTED_CONTENT,可转到每轮固定 30 秒的事件时间线分析。两类问题应依据事件与错误码分开处理。

参考资料

常见问题

关闭 reasoningEffort 就能避开吗?

不一定。社区案例在不同推理档位都能复现,决定因素是协议响应中是否出现适配器未处理的块类型。

能把 redacted_thinking 转成普通文本吗?

不应这样做。它是不可展示的不透明协议内容,转换会破坏隐私边界和可能的签名要求。

为什么错误常在第二次 Tool Call 出现?

多步链路需要保存并回传上一轮 Assistant 内容,协议中此前未覆盖的块类型会在这时进入适配器的完整往返路径。