跳到正文
Elaine Blog
返回

崩溃后怎样继续:持久化边界、Checkpoint 与 Replay

更新于:
Agent Runtime

Worker 正在核验退款申请时退出。重新启动后,是从 Python 最后一行继续,还是从整个任务开头重做?答案取决于运行系统保存了什么,以及它把哪一段工作视为可恢复边界。

Durable Execution 不是保存任意进程的全部内存。它通常保存任务状态、步骤结果或历史事件,再用这些记录重建控制过程。模型上下文、数据库事务和网络连接不会因为有一个 Checkpoint 就自动拥有相同生命周期。

先观察三个崩溃时刻

第一种:模型请求尚未返回,Worker 就退出。系统没有拿到完整结果,恢复后可能需要重新调用。即使远端实际上已经生成,调用方也不一定能够查询到;费用与重复生成需要由调用策略处理。

第二种:模型结果已返回,但尚未持久保存。恢复器看不到结果,仍可能重做。把变量赋值成功当作步骤完成,会低估这个窗口。

第三种:结果已持久记录,下一阶段尚未开始。恢复器可以读取已有结果并继续,而不再请求模型。区别来自保存边界,而不是模型这一次是否“记得自己做过”。

调用模型 → 收到结果 → 保存步骤结果 → 调度下一步
   ①           ②               ③

① 和 ② 都可能重新调用,③ 才具备复用已保存结果的条件。具体保存时机还受框架配置和存储成功语义影响,不能只看到 compile(checkpointer=...) 就认为所有窗口消失。

Checkpoint 里不应该塞什么

可以保存可序列化的输入、节点位置、已完成结果和外部引用。活动数据库连接、打开的文件句柄、线程锁和网络 socket 不适合直接作为持久任务状态。

大文件最好存储为带版本和权限的引用,而不是无限塞进消息与 Checkpoint。恢复时还要确认引用存在、版本相同且当前主体有权读取。一个路径字符串只能定位对象,不能保证对象仍可用。

敏感凭证也不应被长期复制进历史。恢复时重新取得有效凭证,并核对权限变化;从旧快照读取到过去的 API Key,不代表它仍然有效或允许使用。

不同引擎恢复的是什么

图系统可能保存执行步的状态与待执行节点。历史重放系统则重新运行确定性编排代码,利用事件中记录的结果重建状态。两者都能持久执行,但“重放”一词不能掩盖具体差异。

Temporal 的 Workflow 重放会读取已有 Activity 结果来重建编排;Activity 自身失败、超时或结果未确认时的重新尝试,是另一回事。LangGraph 从保存的图状态继续时,也要按具体节点和中断语义判断哪些代码会再次执行。

因此阅读框架时可以问:执行结果在哪里记录,什么标识定位这个结果,恢复时怎样判断复用还是重做?源码篇会分别沿这条路径展开,不把所有引擎简化成“从最后一行继续”。

模型不确定性怎样被封装

同样的输入两次请求可能得到不同模型输出。若已经保存第一次结果,恢复时复用它,就能让后续编排继续依据同一事实。若没有保存,只能重新调用,则应记录这是新的尝试,并考虑结果差异。

温度为零也不能被当作跨版本、跨请求的确定性保证。把模型调用放进受记录的步骤或 Activity,是为了将外部不确定性与编排重建分开,不是强迫模型永远生成相同字节。

普通系统时间、随机数和外部读取也有类似问题。持久工作流通常提供适合重放的时间、等待和随机机制。直接在编排重建中读取当前业务数据库,可能让历史分支改变。

暂停恢复为什么可能从节点开头重跑

LangGraph 的 interrupt() 把所需输入暴露给调用方,恢复时中断所在节点会重新进入。中断前若发送通知、写业务数据,就需要考虑重复。

把准备动作与批准后的外部执行拆开,可以减少误操作窗口。但仅仅拆节点还不够:执行节点自身也可能在远端成功后、本地结果保存前崩溃。这个窗口需要下一篇的幂等与对账。

多次中断和并行中断还涉及返回值与具体中断的匹配。不要在更新代码时随意改变旧节点里中断的顺序,否则恢复数据可能对应错误的问题。

恢复前先核对执行环境

Checkpoint 说候选文件已生成,文件可能已经删除;状态说审批通过,批准可能过期;任务仍显示 running,原 Worker 可能已经失去所有权。恢复前应检查这些外部条件,再决定是否重新执行。

任务版本也需要匹配。旧代码保存的字段若被新代码重新解释,即使 JSON 能反序列化,也可能发生业务含义变化。版本迁移应说明哪些状态仍有效、哪些步骤需要重新校验。

从旧状态分叉不是回滚业务

开发时可以从旧 Checkpoint 尝试另一条路径,但先前的退款或邮件不会撤销。调试应使用模拟工具或隔离数据;生产分叉必须明确操作标识与授权,不能复制历史后无条件重做写操作。

本系列的SQLite 任务示例下载保存任务与操作意图,另用独立数据库模拟支付。它能展示进程更换后的查询和继续,但不声称把两个数据库变成一个事务。下一篇详细分析副作用一致性


分享这篇文章:

上一篇
工作流怎样执行:节点、分支、循环、并行与 Reducer
下一篇
怎样避免重复副作用:幂等、事务、Outbox 与补偿