跳转到内容

写入途中崩溃,怎样安全恢复

Agent 调用 write 更新 result.txt。文件系统已经返回,进程却在 tool_finished 写入 Session 之前退出。

重启后只剩一条 tool_started。如果恢复器直接重跑,文件会再写一次;把工具换成发消息、创建工单或提交订单,重复副作用会更加麻烦。

这类故障不能靠聊天摘要解决。系统缺的是一份落在副作用两侧的操作记录。

构件 它回答的问题
Trace 这一轮按什么顺序发生了什么
Session 哪些消息与状态已经持久化
Checkpoint 恢复时从哪个已确认位置继续
Operation Journal 某个外部动作是 started、completed 还是 interrupted

Trace 便于诊断,却不会阻止重复。Checkpoint 能保存待执行 tool call,但如果工具上次已经启动,恢复器仍需要判断能否安全重试。

只读查询通常可以重试,写入和外部发布通常不能。教学项目给 Tool 增加 retrySafe 元数据,并按以下顺序执行:

检查 journal 中的旧记录
completed:复用已保存结果
started / interrupted:
retrySafe=true 才允许重试
retrySafe=false 返回 blocked
journal.start()
tool.execute()
journal.complete(result)

如果同一个 call ID 被拿来执行另一组参数,恢复器也会失败。调用身份与参数摘要必须一起核对。

后面会反复用到三个词:

  • 幂等:同一动作执行一次或多次,最终效果相同;
  • 幂等键:让外部服务识别“这仍是同一次动作”的稳定标识;
  • reconciliation:查询现实状态,再把本地记录与现实对齐。

真实业务更适合使用稳定的 idempotency key,而不只依赖模型生成的 tool call ID。例如:

task-42:update-file:result.txt:content-sha256

同一次恢复和重试复用这个 key,目标或内容变化时才生成新 key。外部 API 如果支持幂等键和结果查询,应直接使用它们。

Pi 的 durable harness 文档列出了未完成模型流、队列持久化、非幂等工具与 operation 记录等故障模型。查看固定版本的故障分析

同一份文档提出 operation 与 tool 生命周期的记录方案,查看设计草案。这是仍在发展的设计方向,当前 Coding Agent 不能据此被描述为任意位置可恢复。

新的 AgentHarness 已有 Session、turn snapshot 与保存点,但完整恢复、自动 compaction 与通用 hook 仍有待办。查看 AgentHarness 进度

第八个 Lab 预先放入一条 started 的 write 记录,随后带着原 Checkpoint 恢复。恢复器能够找到同一个 tool call,但写工具被标记为不可自动重试。

Terminal window
cd agent-harness-lab
npm run lab -- 08

预期输出:

{
"terminal": "blocked",
"reason": "write 上次执行没有完成记录,不能自动重跑",
"writes": 0,
"recoveryEvent": "write 的副作用状态不确定"
}

工作区已经含有目标内容,用来模拟“副作用可能发生”。关键观察是本次恢复的 writes 仍为 0。系统没有根据“journal 没写 completed”推断“外部动作一定没有发生”。

接下来应由用户或专用 reconciliation 工具检查真实环境,再决定记录为 completed、回滚,或创建一项人工修复任务。

当前 MemoryOperationJournal 只服务离线实验。生产版本至少还要:

  • 在数据库事务中追加 operation 与 tool 状态;
  • 为外部动作保存业务 ID、参数哈希和结果摘要;
  • 明确 crash 后谁负责 reconciliation;
  • 对 completed 结果做缓存与重放;
  • 记录每次人工决策与操作者;
  • 让 Trace、Session 与 Journal 共享稳定的 run ID。

到这里,my-pi-agent 已经覆盖模型接缝、Tool Loop、权限、上下文变换、Session、停止、Verifier 和恢复防线。下一章回到 Pi 的 Extension、Skill 和 SDK,组装一个能在真实仓库里工作的领域 Agent。