Checkpoint 与回滚

Learn~60 minutesPython (stdlib, checkpoint and rollback state machine)先修要求: Phase 15 · 12 (Durable execution), Phase 15 · 15 (Propose-then-commit)
每一次图状态转换都会持久化。工作进程崩溃时,它的租约到期,另一个工作进程从最新 checkpoint 继续。Cloudflare Durable Objects 可以跨数小时或数周持有状态。先提议后提交(第 15 课)为每个操作定义回滚计划。行动后验证闭环。EU AI Act 第 14 条使有效的人类监督对高风险系统成为强制要求——在实践中,这意味着 checkpoint 必须可查询、回滚必须演练、审计轨迹必须经受住一次部署。最尖锐的失效模式:没有幂等键和前置条件检查,瞬时故障后的重试可能重复执行一个已经批准的操作。行动后验证正是捕捉它的手段。

问题

持久执行(第 12 课)让崩溃的 Agent 可恢复。先提议后提交(第 15 课)让已批准的操作可审计。本课把两者结合起来:一个已批准的操作部分执行、崩溃、然后恢复时会发生什么?回滚何时运行,针对什么状态?

真实系统的接法各不相同:

  • LangGraph 把每一次图状态转换 checkpoint 到 PostgreSQL。工作进程崩溃时,租约释放,另一个工作进程从最新 checkpoint 恢复。工作流在 interrupt() 处暂停,interrupt() 本身也会持久化。
  • Cloudflare Durable Objects 按 key 持有状态,跨越数小时或数周。把计算与被批准操作的存储同置。
  • Microsoft Agent Framework 在工作流 API 中暴露 Checkpoint 原语;重放加幂等覆盖重试。

在每种情况下,真正有效的组合是:幂等键(防止重复执行)+ 前置条件检查(状态仍然是我们批准时的状态)+ 行动后验证(副作用确实发生)+ 验证失败时回滚。

概念

每一次转换都持久化

图状态转换是指把工作流从一个命名状态移到另一个命名状态的任何步骤。朴素实现只在特定提交点持久化;生产实现持久化每一次转换。成本(多几次写入)相对于可靠性收益(重放可以落在任何位置,租约恢复精确)很小。

租约恢复

当工作进程崩溃时,工作流不会丢失;租约(一个短命的声明,表示该工作进程正在执行本次运行)只是到期。另一个工作进程拿起最新 checkpoint 并恢复。租约机制是生产系统能在滚动部署中存活而不丢失进行中工作的原因。

幂等加前置条件

仅有幂等还不够。考虑:一个工作流被批准「当余额 > 1000 时从 A 向 B 转账 100 美元」。工作流被提交,执行中途崩溃,然后恢复。如果只检查幂等键,执行恢复,转账运行一次(正确)。但考虑崩溃与恢复之间,A 的余额经由另一个工作流降到 500。幂等检查仍然通过;前置条件不通过。没有前置条件检查,我们就发出了透支。

每个有后果的操作都需要两者:

  • 幂等键:防止重复执行。
  • 前置条件检查:确认状态仍然与批准时一致。

行动后验证

「工具返回 200」不是验证。真正的验证重新读取目标状态,确认副作用确实发生。模式:

  • 数据库更新:UPDATE ... RETURNING *,然后断言返回的行与预期状态匹配。
  • 邮件发送:提交后检查已发送文件夹中的消息 ID。
  • 文件写入:读回文件并计算哈希。
  • API 调用:对目标资源做后续 GET。

如果验证失败,工作流处于已知的坏状态。回滚启动。

回滚计划

先提议后提交(第 15 课)中的每个有后果操作都携带回滚计划。类型:

  • 带内回滚:直接反转副作用(INSERT 之后 DELETE,发送之后发更正邮件)。
  • 补偿事务:一个新操作中和原始操作(标准 SAGA 模式)。
  • 带外回滚:告警给人类,暂停工作流,保留坏状态供调查。

空操作回滚(「我们无法撤销这个」)必须在提议中写明。没有回滚的操作在提交时需要更强的 HITL(第 15 课的挑战-应答)。

EU AI Act 第 14 条的可操作解读

第 14 条要求高风险系统具备「有效的人类监督」。在操作层面,实施者将其解读为:

  • Checkpoint 可被审计员查询。
  • 回滚经过演练(至少端到端测试一次)。
  • 审计轨迹经受住一次部署(checkpoint 后端不是短暂的)。
  • 失败的验证会触发告警,而不是被静默记录。

一个在提交中途崩溃、恢复、并完成副作用却没有验证 + 回滚路径的工作流,无法通过第 14 条的检验。

最尖锐的失效模式:重复执行

这个领域最常见的生产事故:

  1. 操作被批准,幂等键 k。
  2. 提交开始,执行,返回 200。
  3. 工作流在持久化「已提交」状态之前崩溃。
  4. 工作流恢复;看到「已批准但未提交」;重新执行。
  5. 副作用触发两次。

缓解措施:在执行前持久化一个「进行中」意图,用幂等键执行,然后只在行动后验证成功后才标记「已提交」。如果操作触发而状态写入失败,你知道需要验证并(如有必要)重新触发。如果状态写入成功而操作失败,你通过恢复路径验证并恰好触发一次。

checkpoint-replay静态版本未包含此交互图

使用它

code/main.py 实现一个带幂等、前置条件、验证和回滚的 checkpoint 工作流。驱动程序模拟四种场景:干净运行、崩溃后重试(幂等捕捉)、前置条件失败(工作流中止而不触发)、验证失败(回滚触发)。

交付它

outputs/skill-rollback-rehearsal.md 为拟议工作流设计一个回滚演练测试,并审计 checkpoint 后端的审计轨迹持久性。

练习

  1. 运行 code/main.py。验证四种场景。对于提交期间崩溃的案例,确认操作在多次重试中恰好触发一次。
  1. 修改「先标记完成,再执行」模式,让状态写入在操作之后触发。重跑崩溃场景。测量触发了多少重复操作。
  1. 为一个具体生产操作(例如「发布到 Slack 频道」)设计回滚计划。归类为带内、补偿或带外。论证选择。
  1. 拿一个你了解的工作流。找出每一个状态转换。为每个标注持久性要求(持久化 / 不持久化)。数一数你当前没有持久化的那些。
  1. 演练回滚测试:设计一个端到端测试,运行真实工作流,使其崩溃,并确认回滚路径触发。测试断言什么?

关键术语

术语人们怎么说实际含义
Checkpoint「保存点」每一次图状态转换都持久化到持久存储
租约「工作进程声明」工作进程正在执行一次运行的短命声明;崩溃时到期
前置条件「状态门」断言状态仍与已批准操作一致
行动后验证「重读检查」确认副作用确实发生在目标系统中
带内回滚「直接撤销」用逆操作反转副作用
补偿事务「SAGA 撤销」中和原始操作的新操作
先标记完成「状态写入顺序」在从提交返回之前持久化已提交状态
第 14 条「EU AI Act 人类监督」操作层面:可查询 checkpoint、演练回滚、可审计轨迹

延伸阅读

示意图共 1 张

checkpoint-lifecycle diagram
checkpoint-lifecycle

“Stay hungry, stay foolish.”

— Steve Jobs

esc
↑↓切换↵打开esc关闭