中断、重试与恢复:先确认哪一步已经生效
返回机制目录 · Pi 固定版本代码导读 · LangGraph checkpoint 导读
单独打开 SVG · 可编辑图源 · PNG 预览 · 图的文字版
假设 Agent 请求工具向外部系统创建一张工单,工具已发出请求,但等待响应时连接断开。读者现在需要判断:能否再次调用工具? 关键事实是“Agent 没收到成功响应”只说明结果不可见,不说明外部动作没发生。恢复要先分清运行控制、保存的状态和真实世界的副作用。
五个动作分别解决什么
| 动作 | 改变什么 | 不能直接推出什么 |
|---|---|---|
| 取消/中断 | 停止当前运行或发取消信号;停止后的确认程度取决于被调用方 | 已发出的外部请求被撤销,或此前的动作被回滚 |
| 重试 | 再做一次模型请求或工具动作,通常受错误类别、次数和退避约束 | 上一次没有生效,或再次执行一定安全 |
| 幂等 | 由外部系统识别同一个业务动作标识,重复提交时给出同一效果或既有结果;需核对接口合同 | 只凭 Agent 的调用 ID 就自动具备幂等性 |
| checkpoint/恢复 | 读取可定位的执行状态,从某处继续或分叉 | 外部系统也回到该快照的时间点 |
| 人介入 | 在证据不足、结果冲突或不可逆动作前决定继续、补偿或停止 | 人的点击天然能补齐未知的执行结果 |
“重试”还须问重试哪一层:重发模型请求可能只重新求一段推理;重放工具调用则可能再次写入外部系统。进程崩溃、用户主动取消、服务端超时和工具返回明确失败,产生的证据也不同。设计时为每个有副作用的动作记录业务键、请求参数摘要、调用/响应标识与最终确认结果;在再次发送前按该键查询外部状态。若外部接口支持幂等键,重试沿用同一业务键;更换键可能被视为新动作。这是推荐的应用设计,具体系统是否提供这些能力必须逐一核对。
一条可判断的失败路径
- 执行前:把“要创建工单 X”的意图与业务键 K 记下来。若动作需要审批,在发送前完成审批;审批只决定能否启动,不替代结果确认。
- 请求发出后收到成功响应:记录外部工单 ID 和状态,再推进 Agent 状态。若响应明确失败,按错误类别判断是否可以重试。
- 请求发出后超时或取消:标记结果未知,先用 K 或外部请求 ID 查询。查到已创建,就记录既有结果并继续,避免第二次创建。
- 确认未创建,且接口合同允许安全重试:在预设上限
N和截止时间内,用原 K 重发;每次再出现未知结果仍先对账。达到上限、查询不可用、结果相互矛盾或动作不可逆时,暂停给人对账。 - 恢复 checkpoint 时,再核对外部结果与新运行计划。checkpoint 可能让图重新调度某个节点,不能代替第 3 步的外部确认。
第 1–5 步是工程建议,并非下面两个框架共同提供的内置流程。反例是“创建请求超时 → 从旧 checkpoint 重跑 → 新建第二张工单”:保存的图状态可能合法,两张外部工单仍然是重复副作用。若接口既不支持按业务键查询,也不支持幂等提交,自动重试不能证明安全;应保留未知状态并交由人核对,必要时制定补偿动作。
固定版本中的实际边界
Pi 的取消与重试。 静态阅读 earendil-works/pi@898ab804050730e9dcefb4443875d5a932aa6a32:核心 Agent.abort() 发当前运行的取消信号;coding-agent 的 AgentSession.abort() 还会取消重试等待等操作并等待空闲。核心取消 · 应用层取消 对可重试的模型错误,AgentSession 在一次运行结束后检查开关和次数、做可取消退避,再调用 Agent.continue();失败尝试留在原始记录,但下一次模型上下文投影可省略它。后运行重试 · 退避 这是模型请求层的路径,不证明已发出的第三方工具动作可撤销,也不意味着每个工具错误都会自动重试。
LangGraph 的 checkpoint。 静态阅读 langchain-ai/langgraph@bdb85b5aa87a21de68371d2e534b81aeed398f57 的 Python 同步 Pregel 与示例 InMemorySaver:循环可以按 checkpoint_id 加载旧状态;update_state(old_config, values) 从旧状态写出一个新版本(无法唯一判定写入节点时还须指定 as_node),再以返回 config 继续。加载指定版本 · 读取旧版 · 歧义检查与保存新版 · 分叉测试 新状态与已持久化也有区别:该版本 durability 默认为 async,sync 在下一步前保存,exit 只在退出时保存。源码中的参数说明 官方持久化文档说明 checkpoint 用于图状态和恢复;它不构成外部工单已回滚的证据。InMemorySaver 本身被源码注释限定为调试/测试用途。示例 saver 注释
两条固定版本路径各说明一个局部边界:Pi 展示运行/模型重试的层次,LangGraph 展示图状态版本的选择。图里的工单场景是教学抽象,不是声称这两个项目内置工单查询、幂等键或人工对账流程。
证据与待验证
- 源码事实与官方文档声明:上面的 Pi、LangGraph 行为限定在链接的固定 commit 与官方文档所述范围;LangGraph 仓库里的测试是上游断言,不是本文的运行记录。
- 工程推断:结果未知时先对账、重复请求沿用业务键、恢复后复核外部动作,是从“运行状态与外部副作用分离”推得的应用设计建议。
- 未验证:本文未运行两个固定版本、未做故障注入,也未检验具体外部 API 的幂等合同、生产 saver 的跨进程恢复、取消信号在网络与工具中的实际传播。上线前应记录故障点、checkpoint ID、调用 ID、业务键和外部结果,分别做超时、取消、崩溃和重复投递试验。
图的文字说明 · interruption-recovery
这是一张教学决策图,不是某一产品的内部架构或保证。它用创建工单说明:Agent 先记录动作意图和业务键 K,再经工具发送创建请求。请求一旦越过本地进程边界,即使本地随后超时、取消或断线,外部系统也可能已经创建工单。
蓝色“发送创建请求”经紫色箭头跨过竖向虚线标出的副作用边界,进入外部系统的紫色节点。外部动作可能已生效,但响应未抵达;橙色“结果未知”因此进入“按 K / 请求 ID 对账”。查询后分三路:确认已创建,复用已有工单 ID;确认未创建且接口合同允许,进入有次数上限的重试;仍未知,暂停交给人对账。安全重试的蓝色回路从重试节点返回原创建请求,只在 n < N 时沿用同一 K;达到上限的橙色箭头转向人工对账。左侧无边框的 checkpoint 文字是没有流程箭头的旁注:它只恢复图状态,不撤销外部动作。
箭头表示推荐的判断顺序,不表示查询一定可用、人工能立即判明真相,或业务键自动使接口幂等。蓝色是 Agent 侧的动作与有条件回路,紫色是外部副作用边界,橙色是未知/人工确认,绿色是已确认的既有结果;文字标签给出同样的信息。N 是预设的有限重试上限,并非某框架固定参数。
图的依据分两层:Pi 的固定提交 898ab804050730e9dcefb4443875d5a932aa6a32 展示取消信号及 coding-agent 的模型错误重试;LangGraph 的固定提交 bdb85b5aa87a21de68371d2e534b81aeed398f57 展示 checkpoint 选择/分叉。查询工单、K 的设计和人工对账是章节中的工程建议,不是对任一项目现成功能的声明。源码定位、适用范围和未验证项见章节。
运行 python3 figures/interruption-recovery/build.py 可重新生成原生 .excalidraw。用 excalidraw-agent 的固定渲染器从该图源导出 SVG 和 PNG;重新生成时须重查文本布局与裁切。
在线预览稿:书稿仍在校稿,系统篇以文内固定源码版本为准;静态阅读不等于运行验收。