DSH 动态工具更新与会话恢复
DeepSeek Harness 动态工具更新:Adapter 与会话恢复
- 作者
- DeepSeekAgent.io 编辑部
- 发布
- 更新
在 DeepSeek Harness 中安装、升级或移除插件后,当前 Session 的可用工具会发生变化。DSH 需要让模型知道哪些工具刚加入、哪些已经不可用;如果 Provider 路由不能正确表达这种“会话中途的工具更新”,一次正常的插件操作就可能变成持续性的 HTTP 400。
2026 年 10 月 21 日的社区报告在 v0.2.1-rc.2 上记录了这条链路:Agent Loop 把工具增删写成持久的 developer/message,DeepSeek Adapter 再把其中的 tool-addition 或 tool-removal 序列化进请求。上游接口拒绝该结构后,失败记录仍留在 Session 历史中;重试、重启甚至 /compact 都可能再次投影同一条记录。
这与 REQUEST_EXTENSION 不是同一种故障。后者发生在 HTTP 请求发出之前,常见原因是插件清单或 manifest 无法解析;动态工具故障则是请求已经构造并发往 Provider,但消息结构不被当前路由接受。
DSH 为什么要记录工具变化
Agent 开始一轮任务时,系统提示和工具定义构成了一个基线。如果运行过程中插件注册了新工具,直接在下一次请求中替换完整工具列表,会让模型无法区分“此前不可用”和“现在新增”。动态工具协议用显式变更描述差异:
上一轮工具集合
→ 插件安装、更新或卸载
→ 计算 additions / removals
→ 追加 developer/message
→ Adapter 投影为 Provider 请求
这套设计对长 Session 很有价值:模型可以保留此前上下文,同时理解工具注册表已经变化,也有机会保持稳定的 Prompt 前缀与 KV Cache。但它依赖 Adapter 与 Provider 对同一种中途变更协议达成一致。
故障发生在哪一层
社区复现中的 Agent Loop 会比较工具基线与当前名称集合。有差异时,它把 tool-addition 和 tool-removal 内容写入 Session。DeepSeek Adapter 随后将这些内容投影到消息列表,并在存在工具变更时加入对应的 Beta Header。
报告捕获到的请求片段类似:
{
"role": "system",
"content": [
{
"type": "tool_addition",
"tool": { "type": "tool_reference", "name": "ops_bash" }
}
]
}
Provider 返回的错误指向 tool_addition 所在位置。关键问题不是插件工具本身执行失败,而是工具更新消息在当前 API 路由上的表达不兼容。
从 Harness 设计看,这里至少有三道能力判断:
- Agent Loop 是否应该为当前 Prepared Call 生成动态工具记录;
- Adapter 是否声明支持 addition、removal 或两者;
- Provider 路由是否真正接受该消息结构与 Header。
只要声明能力与实际接口不一致,Session 就可能写入一条以后无法投影的持久记录。
为什么重启和换模型不一定有效
工具变化不是只存在于内存中的临时状态,而是写入 Session V4 日志。重启 DSH 后,历史仍会被重新投影。若模型切换仍走同一 Adapter,或者投影阶段在选择模型前就保留了那条 Developer Message,换模型也不会自动清除它。
社区的独立复现还显示,/compact 可能同样失败:压缩也要先构造包含历史的模型请求,因此会再次遇到不兼容记录。由此产生的用户体验是:
- 插件更新前 Session 正常;
- 更新后第一条消息立即返回 400;
- 之后每次发送都得到相同错误;
- 新建 Session 可能正常,因为新会话从当前工具基线开始,不需要重放中途变更。
最后一点是重要诊断信号,但不是绝对证明。仍应保存完整错误和 Provider 请求 ID,排除凭证、额度和普通上下文溢出。
哪些操作最容易触发
- 在仍然打开的 Session 中安装带工具的插件;
- 把插件升级到新增、重命名或删除工具的版本;
- 停用插件或切换 Profile,使一批工具同时移除;
- 插件热重载后注册的工具集合与 Header 基线不同;
- 从旧版本恢复 Session,而当前版本的工具命名已经变化。
单纯打开插件市场不会产生问题;真正的触发条件是当前 Session 观察到工具集合发生变化,并把差异写入历史。
先判断是不是这一类 400
| 现象 | 更可能的层级 |
|---|---|
报错包含 REQUEST_EXTENSION,无 Provider 请求 ID | 本地请求前置扩展 |
报错指向 tool_addition 或 tool_removal | 动态工具消息投影 |
| 插件更新后只有原 Session 失败 | 持久工具变更记录 |
| 新旧 Session 都返回 401、402、429 | 凭证、余额或限流 |
错误是 context_length_exceeded | 上下文窗口或压缩 |
| 新 Session 正常、旧 Session 重启后仍失败 | 旧历史中存在不可重放结构 |
不要看到“插件更新后失败”就删除整个 Profile。REQUEST_EXTENSION、插件运行时崩溃、工具协议不兼容和 Provider 认证失败需要完全不同的处理。
安全恢复顺序
1. 冻结现场
停止自动续轮,不要继续安装或卸载更多插件。记录 DSH 版本、插件及版本、当前 Profile、模型路由、完整 HTTP 状态和 Provider 请求 ID。
2. 用新 Session 做最小验证
保持当前插件集合,新建一个 Session,只发送不调用工具的短消息,再执行一个只读工具调用:
- 新 Session 正常:Provider 与当前工具基线基本可用,旧 Session 历史是重点;
- 新 Session 也失败:检查插件注册、Adapter 路由和全局配置,不要急着迁移历史。
3. 导出旧 Session 的任务状态
能打开时先复制关键结论、未完成清单、修改过的文件与已经执行的副作用。不能继续请求模型时,使用 DSH 提供的导出或只读查看方式保留 Transcript。
4. 从摘要恢复工作
在新 Session 中写入人工检查过的短摘要,明确:目标、已完成工作、待办、关键路径、测试状态和禁止重复的外部操作。先做只读核验,再继续执行。
5. 保留原始数据
不要把直接修改压缩 Session 日志当作常规修复。社区报告中,删除或改写 Developer Message 后,即使日志本身可读,也可能因为 Projection Cache 与原字节状态不一致而从 UI 消失。原 Session 更适合作为审计记录。
如何避免再次发生
- 重要长任务期间不要更新插件。 在任务完成或创建检查点后再改变工具集合。
- 用隔离 Session 验证插件升级。 先确认工具注册、只读调用和模型路由都正常,再回到生产任务。
- 更新前导出工具清单。 对比新增、删除和重命名项,尤其关注一次变化几十个工具的插件。
- 为长任务保存外部状态摘要。 不让恢复能力完全依赖单个 Session 日志。
- 固定 DSH 与插件版本。 可复现任务不要在运行中追随浮动版本。
对 DSH 实现而言,更稳健的策略是在写入动态工具记录前检查 Prepared Call 能力;不支持时重新发送完整工具定义或丢弃增删块。Provider 拒绝动态工具消息后,系统也应允许后续投影忽略该记录,而不是让一次失败永久阻断 Session。
与 KV Cache 的关系
动态工具的另一个目标是减少每轮完整重写工具 Schema,稳定 Prompt 前缀并提高缓存复用。但缓存优化不能越过协议兼容性:
- 支持中途工具更新的路由,可以发送差异并保持较稳定前缀;
- 不支持的路由,应回退到完整工具列表或重新建立 Session 基线;
- Adapter 不能仅凭模型名称假设网关实现了同一 Beta 协议。
因此排查时要记录完整路由,而不只是模型 ID。deepseek-flash 经过官方账号适配、Anthropic 兼容端点或第三方网关,实际接受的消息结构可能不同。
相关阅读
- DeepSeek Harness REQUEST_EXTENSION 故障排查
- DeepSeek Harness 动态工具与 KV Cache
- DeepSeek Harness 插件管理机制
- DeepSeek Harness v0.2.1-rc.2:会话不可见排查与恢复