DeepSeek-Reasonix 工具中断后的持久恢复:执行证据、恢复卡与安全重试机制全解析
2026/9/12 16:37:46 网站建设 项目流程

DeepSeek-Reasonix 工具中断后的持久恢复:执行证据、恢复卡与安全重试机制全解析

【免费下载链接】DeepSeek-ReasonixDeepSeek-native AI coding agent for your terminal. Engineered around prefix-cache stability — leave it running.项目地址: https://gitcode.com/GitHub_Trending/de/DeepSeek-Reasonix

导读

工具调用是 Agent 与外部世界交互的边界,而进程崩溃、网络中断或工具超时会让"是否真的产生了外部效果"变成悬案。DeepSeek-Reasonix 通过将执行证据交由 Go runtime 管理,实现了一套不依赖新数据库的工具中断持久恢复机制:所有执行证据沿 session、turn ledger 与文件 checkpoint 持久化,Electron 本地 tab 与 Remote tab 共用同一套 Controller API,用户可在重启后通过恢复卡安全地检查、确认或受限重试未决的工具调用。读完本文,你将掌握其执行边界模型(调用回执、幂等键、写入屏障)、界面与传输链路(快照、修订号、前台写权限)、重试与幂等约束(EffectVerifierRecoveryScope)、协议兼容策略以及完整的验证与交付流程。

设计总览:为何执行证据由 Go runtime 管理

文档开宗明义:执行证据由 Go runtime 管理。这意味着恢复能力的核心不在前端界面,也不在 Electron 进程,而在负责会话与回合(turn)账本的主机侧。Electron 本地 tab 与 Remote tab 共用同一套 Controller API,沿用 session、turn ledger 和文件 checkpoint,不新增恢复数据库。从源码看,这一架构体现在两个层面:

  • 会话层:internal/agent/tool_recovery_records.go中的Session.toolRecoveryRecord/setToolRecoveryRecord直接读写会话消息里的ToolCall.Recovery字段,恢复记录作为消息的局部元数据随 session checkpoint 一起落盘;
  • 控制层:internal/control/tool_recovery.goController.ToolRecoverySnapshot()ResolveToolRecovery()负责生成快照和执行恢复操作,两者都复用与模型回合相同的会话写权限与准入互斥逻辑。

因此,整个恢复体系是既有持久化设施的"语义层扩展",而非旁路的新存储系统。

执行边界:回执先落盘,再执行工具

回执的生命周期

文档规定:参数检查、工具目标解析和权限判定完成后,才建立调用回执。回执包含:

  • session、turn、call、attempt 标识;
  • 规范化参数摘要(ArgumentDigest,对规范化参数做 SHA-256);
  • 幂等键(IdempotencyKey);
  • 写入状态(ToolRunStarted/ToolRunRunning/ToolRunUnknown/ToolRunNotStarted/ToolRunFailed/ToolRunCompleted/ToolRunUserConfirmed等)。

执行顺序严格固定:先通过现有 session checkpoint 持久化回执,再同步落盘tool_started,最后才调用工具。第一个写工具同时把身份和 transcript 摘要绑定到文件 checkpoint,形成"谁在何时、以何参数、对哪个资源发起了什么写入"的完整审计链。对应的实现位于 internal/agent/tool_recovery_records.go 的beginToolRecovery:在设置恢复记录后调用emitToolStarted,任何一步失败都会把状态回退为ToolRunNotStarted,保证"未持久化即视为未开始"。

"调用过"不等于"效果已确认"

文档明确区分了两个概念:"调用过""外部效果已确认"。重启后,已开始但未确认结果的调用仍为未知(unknown);新格式中只有 dispatch、没有 start 的调用可以认定未开始(not started);旧记录缺少证据时保守处理,不猜测成功或失败unresolvedToolRecord(见 internal/agent/tool_recovery_records.go)将以下状态视为未解决:ToolRunStartedToolRunRunningToolRunUnknown,以及"显式失败但效果未知且非只读"的ToolRunFailed + effect_unknown。值得注意的是,显式工具错误只能证明失败,不能证明外部部分效果不存在——这是本设计最关键的保守性原则之一。

写入屏障:未知写操作阻止后续写操作

未解决的写操作构成写入屏障:即使模型换了 call ID 发起新的写工具调用,也会被beginToolRecovery中的检查拒绝(返回recovery_required: inspect and resolve the previous uncertain tool effect before another write),而只读诊断可以继续。从源码看,屏障在 internal/agent/tool_recovery_records.go 实现:遍历所有未解决记录,只要存在非只读的未决效果(且不是同一重试链),就阻止本次写调用。同一 session 的压缩(compaction)或历史改写(rewind)也不会抹除未解决回执——retainUnresolvedToolRecords(同文件 L175-L194)会把未解决的非只读记录以LocalOnly消息形式保留在新历史中。

用户确认语义

用户确认记录为user_confirmed,系统不会伪造工具成功输出,也不会自动再次执行已确认的相同工具和参数beginToolRecovery中专门检查confirmedRecoveryEffect:如果该精确效果(幂等键匹配)已被用户确认发生,新的写调用会被拒绝,理由是"用户已确认此效果发生过,不再重复写入"(internal/agent/tool_recovery_records.go)。

界面与传输:恢复卡与快照协议

桌面统一入口与 Remote 转发

桌面统一入口是GetToolRecoveryForTabResolveToolRecoveryForTab,实现在 desktop/tool_recovery.go:

  • 本地 tab:通过tabAndCtrlByID拿到 Controller,若实现toolRecoveryController接口(ToolRecoverySnapshot()ResolveToolRecovery(...)),直接调用;
  • Remote tab:通过remoteToolRecovery转发到 Serve 的GET/POST /tool-recovery(desktop/tool_recovery.go),沿用认证、会话路径与前台写权限保护;响应中的SessionPath与会话路径不一致时会报 "remote recovery session changed"。

服务端路由注册在 internal/serve/tool_recovery.go:GET /tool-recovery返回快照,POST /tool-recoveryforegroundMutation包装以施加前台写权限保护,且请求体限制 16 KiB 并DisallowUnknownFields

快照字段与操作约束

ToolRecoverySnapshot携带以下字段(internal/control/tool_recovery.go):

字段含义
sessionPath快照所属会话路径
runtimeEpoch运行时纪元,重启后变化
revision内容修订号,对快照 JSON 做 SHA-256 得到
calls未解决的工具调用列表
retryEnabled是否开放重试(受REASONIX_TOOL_RECOVERY_RETRY控制)
statistics未知、已确认、重试、拒绝、阻断等计数
silent是否静默恢复

关键设计:普通快照隐藏原始参数Calls[i].Arguments = nil),前端拿到的是不可变身份与检查事实,而不是可执行载荷。任何恢复操作必须提交快照及attemptIdinspectionId;若请求中的sessionPathruntimeEpochrevision与当前快照不一致,会返回 "recovery snapshot changed; refresh before resolving"。此外,运行中、结束处理中、会话切换中、已关闭的 Controller 状态(running/finishing/rotating/closed)都会拒绝操作(返回ErrTurnRunning,见 internal/control/tool_recovery.go)。

恢复卡操作类型

恢复卡支持四类操作(Action字段,见 internal/control/tool_recovery.go):

  • 检查(inspect):执行效果检查,只有显式检查才返回本地参数回执;检查结果区分四种状态——效果存在(present)、文件后置条件满足(postcondition_satisfied)、效果不存在且旧尝试已被阻止提交(absent_fenced)、无法确认(unknown)。
  • 确认已生效(confirm):将记录置为user_confirmed,必须在已检查的同一 attempt上操作(inspectionId必须匹配)。
  • 不重试(reject):保留未知事实与写入屏障。注意文档强调:拒绝重试不能证明外部效果不存在
  • 受限重试(retry):见下一节。

检查逻辑实现在 internal/agent/tool_recovery_actions.go 的InspectToolRecovery:若工具实现了tool.EffectVerifier,先校验RecoveryScope()与原记录的资源身份一致,再调用InspectEffect判定 present / absent+fenced;否则检查checkRecordedWrite是否满足文件后置条件。文件后置条件只证明当前状态,不证明原调用结果——这正是postcondition_satisfiedpresent被区分的根本原因。

切换 tab/session 后,旧异步响应不得覆盖新界面——ResolveToolRecovery中的SessionPath/RuntimeEpoch/Revision三重校验正是为此服务。

重试与幂等:默认关闭,条件严苛

重试开关

重试默认关闭。只有在拥有该 session 的 Go 主机设置环境变量REASONIX_TOOL_RECOVERY_RETRY=1时才开放重试操作;远程 tab 的重试能力由远端主机决定。从 internal/control/tool_recovery.go 可见RetryEnabled直接读取该环境变量;ResolveToolRecoveryretry动作会先检查view.RetryEnabled,否则返回 "tool recovery retry is disabled"。

重试的管线约束

重试使用新 call ID 和 attempt ID,保留原幂等键和原始参数,并经过正常的参数、权限、hook、租约和工具执行管线——它不是一个特权捷径。实现见 internal/agent/tool_recovery_actions.go 的RetryToolRecovery

  1. 校验 attempt 未解决、inspectionId匹配;
  2. 校验旧执行者已退出(stragglers.live == 0,否则 "the previous executor is still running");
  3. 对写工具强制校验tool.EffectVerifier:重试前必须重新证明"效果不存在,且旧尝试已无法再提交"(InspectEffect返回absentFenced == true)。仅观察到不存在不够;
  4. 校验重试期间目标未改变(CanonicalToolArgumentDigestResourceScope三者一致,否则 "retry target changed during policy resolution");
  5. retry_<hex>新 call ID 创建新工具调用,原记录标记SupersededBy,旧请求不能再提交同一重试。

幂等键与验证器接口

幂等键的构成(见 internal/agent/tool_recovery_records.go):对"身份 + 规范化参数摘要 + 资源作用域"(不含 attempt)做 SHA-256。同一动作的所有显式重试共享同一幂等键。工具可通过tool.RecoveryIdempotencyKey(ctx)读取该键(internal/tool/recovery.go),在实际接收端实现去重。

验证器接口定义在 internal/tool/recovery.go:

type EffectVerifier interface { RecoveryScope() string // 稳定的接收端/账户/资源身份,不随重试变化 InspectEffect(context.Context, string, json.RawMessage) (EffectInspection, error) }

EffectInspectionState取值present | absent | unknownFenced表示旧尝试已无法再提交——Absent 只有在 Fenced 时才对重试安全。验证器的RecoveryScope必须与原记录的接收端、账户和资源身份一致(在检查与重试两处都会校验)。对于没有实现验证器的工具,资源作用域保守地覆盖整个 session("session:" + SessionID),绝不猜测路径或采用模型提供的 scope 来削弱屏障。

能力边界:不做无法兑现的承诺

文件工具复用已有WriteVerifier做只读检查——internal/tool/write_recovery.go 定义WriteVerifier.VerifyWrite(ctx, FileWriteIntent),配合RecordWriteIntent钩子记录文件的 before/after 意图,可判定satisfied | unchanged | conflict | unknown。而通用 shell 和任意 MCP 服务无法自动证明外部效果不存在,因此默认保持未知。本实现不对没有权威回执或去重能力的第三方服务承诺 exactly-once——这是诚实的能力边界声明,也是"受限重试"而非"自动重试"的根本原因。

协议与兼容:可选元数据,向前向后兼容

transcript gate 与执行恢复相互独立

transcript gate(转录门控)与执行恢复是两条独立机制:前者在 provider-request interceptor 之后、请求发送之前,验证 adapter 配对规范化后的视图;后者管理执行证据。已被 host 确认未执行的坏参数调用,可以在请求修复视图中使用空对象并保留错误说明,但本地原参数不改写。正常请求字节不变,恢复回执不进入系统提示、工具 schema 或模型消息——它纯粹是宿主侧的本地元数据。

兼容性保证

  • 新字段(ToolCall.Recovery等)为可选本地元数据:旧 JSON 可读、旧事件编号不变;
  • 旧远程客户端仍受新服务端执行屏障的保护(屏障在服务端强制);
  • 旧版本可执行程序没有新恢复保证,因此应先解决未确认效果,再降级 Go runtime;
  • 本功能不自动改写存储格式,也不执行降级迁移,避免静默破坏历史数据。

统计信息语义

快照还提供从保留的 session 证据计算的未知、已确认、重试、拒绝和阻断计数ToolRecoveryStatistics),但这些数据是当前恢复状态的视图,不当作历史全量遥测——它们只反映保留中的未决记录及其处置情况。

验证与交付:测试覆盖与浏览器验收

自动化测试覆盖

文档列出的测试覆盖点,在仓库中均有对应实现:

  • 权限/参数拒绝开始前持久化失败状态分类:见 internal/agent/tool_recovery_actions_test.go 与 internal/control/tool_recovery_test.go;
  • 快照隔离旧 attempt 拒绝:见 internal/control/tool_recovery_crash_test.go;
  • 幂等接收端重复重试存储失败后的确认回滚:见 internal/agent/live_tool_recovery_confirm_test.go 与 internal/agent/unknown_recovery_test.go;
  • 真实子进程场景:真实子进程在副作用 fsync 后、结果返回前退出,重启与并发确认验证结果持久化,并确保副作用只发生一次——覆盖"崩溃窗口内的效果确认"这一核心竞态。

浏览器验收命令

cd desktop/frontend && node bench/tool-recovery.mjs

该验收脚本(desktop/frontend/bench/tool-recovery.mjs 及其 fixture desktop/frontend/bench/tool-recovery-fixture.tsx)覆盖:检查、确认、继续任务、禁止不安全重试、切换 session 后拒绝迟到响应。前端恢复卡组件实现位于 desktop/frontend/src/components/ToolRecoveryPanel.tsx,类型契约见 desktop/frontend/src/lib/toolRecovery.ts。

部署注意点

部署时必须配套更新 Go runtime 与 Electron 生成契约desktopContract.generated系列由契约生成流程产出),先保持重试关闭;核实具体工具的接收端语义后再开启。关闭重试不会删除证据或人工恢复能力。远端发布和生产灰度是独立交付步骤,不等同于本地实现与测试完成——恢复功能的可靠性门槛以本地全链路验证通过为前提。

总结

DeepSeek-Reasonix 的工具中断恢复机制围绕一条核心原则展开:对不确定的外部效果保持保守,绝不猜测、绝不重复、绝不伪造。它通过"回执先落盘再执行"建立执行边界,通过写入屏障阻止不确定状态下的级联写入,通过EffectVerifier+RecoveryScope+ 幂等键实现受限重试的安全前提,并通过快照修订号与 epoch 校验保证恢复操作的会话一致性。对于无法提供权威回执的通用 shell 与任意 MCP 服务,系统诚实地将效果保持为"未知",交由用户在恢复卡上基于检查事实做出确认或拒绝的决定。这套机制既可用作运行时崩溃后的自愈底座,也可作为 Agent 工具链路审计与人工交接的标准实践参考。

【免费下载链接】DeepSeek-ReasonixDeepSeek-native AI coding agent for your terminal. Engineered around prefix-cache stability — leave it running.项目地址: https://gitcode.com/GitHub_Trending/de/DeepSeek-Reasonix

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询