跳转到主要内容

持久执行还不够:Restate 式检查点及其丢失的意图

检查点保存的是状态,而非意图。恢复后的 agent 运行可以干净利落地完成,却在原始授权之外行事。持久执行需要记录决策上下文。

A checklist with a red checkmark symbolizing a completed task on a pastel blue background.

持久执行究竟保证了什么

持久执行引擎——Restate、Temporal 及类似系统——解决的是一个真实而困难的问题:它们让工作流在进程死亡、网络分区和部署中存活下来。这些保证是精确的,值得明确陈述。

  • 步骤恰好执行一次。 已完成的步骤在重放时不会重新执行。其结果从持久日志中读取。
  • 崩溃后可恢复。 worker 可以在运行中途死亡;另一个 worker 会接管日志并从最后提交的边界继续。
  • 已完成工作的确定性重放。 给定相同的日志,运行时重建相同的副作用序列。

这些是运行时的属性。它们对 agent 的推理只字未提。一个干净恢复的工作流只证明了运行时能够重建调用图——而不是下一个动作仍然被需要、仍然被允许,或仍然与启动该运行的目标一致。

这种混淆很容易产生,因为词汇重叠了。“持久”听起来像“可信”。“恰好一次”听起来像“正确”。在传统工作流中——扣款、发送邮件、更新一行数据——意图在编写时就已固定并编码在图本身之中。图就是意图。在 agent 运行中,图是在运行时根据模型对目标的推理生成的。日志记录的是 agent 做了什么,而不是它为什么被允许这样做。

恢复时的意图缺口

想一想一次运行在启动时实际持有什么。有一个目标(“解决这位客户的退款请求”)、一个计划(agent 提出的工具调用序列)、一组已接受的约束(退款上限、允许的工具、agent 可读取的数据)以及一个预算(token、挂钟时间、外部调用次数)。

检查点通常序列化的是变量:当前步骤索引、工具输出、草稿本内容,也许还有一个计划对象。它并不序列化产生这些变量所依据的授权。退款上限是由一项可能已经改变的政策授予的。工具范围是为一个可能已经过期的会话批准的。用户的指令——“最多退款到发票上的金额”——可能已被恢复后的运行永远看不到的后续消息所取代。

在崩溃与恢复之间,世界在变化。价格变了。库存被重新分配。权限被撤销。监管机构发布了指引。用户改变了主意。这些事件都不在日志中,因为日志只知道 agent 在停止之前观察到的东西。

这产生了一种特定而棘手的失败模式:一次恢复后的运行成功完成,却做了原始轨迹从未授权的事情。 完成是看似合理的。日志是干净的。步骤序列确定性地重放。而这个动作是错误的,因为为其辩护的意图已经过时。

Dynamic stock market trading chart showcasing live financial data and indicators for analysis.

这是一个正确性缺陷,而不是可用性缺陷。它不会出现在崩溃恢复测试中。它会在数周后出现在生产环境中,当有人问为什么一个 agent 发出了政策早已禁止的退款时。

记录决策上下文

修复方法是将决策上下文与状态一起持久化。具体来说,检查点应携带:

  • 运行开始时陈述的目标,以及源指令的哈希;
  • agent 正在执行的计划版本,而不仅仅是下一步;
  • 工具范围授予——哪些工具、依据哪项政策、何时到期;
  • 以真正重要的单位表示的剩余预算(调用次数、token、以政策边界而非金额表示的货币等价限制);
  • 下一步的原因,以验证器可以检查的形式。

在恢复时,运行时会在继续之前根据当前政策重新验证此上下文。重新验证不是形式主义。它要问:这个目标仍然被允许吗?这个工具范围仍然被授予吗?剩余预算仍然与计划一致吗?下一步的原因仍然得到目标的支持吗?

重新验证失败是一个终止状态,而不是重试。重试政策失败只会消耗预算并产生同样的拒绝。运行应当停止,呈现原始意图与当前意图之间的差异,并交还给人类或更高层的编排器。

这是一个真实的权衡。重新验证会在每次恢复时增加延迟,并要求政策可作为服务查询,而不是嵌入在代码中。它还意味着一些本来会正确完成的运行会被停止以接受审查。替代方案——在过期的授权下静默完成——更糟,但代价并非为零,应当诚实说明。

幂等不是同意

幂等键是一种被充分理解的机制:它们确保重试的外部调用不会重复扣款、重复发送或重复写入。它们是必要的。它们并不充分。

幂等键防止同一个调用生效两次。它对于在过期授权下进行的单次调用毫无作用。如果 agent 发出退款的授

Historic US Army checkpoint at Berlin's Mauermuseum near former Berlin Wall.

  • 幂等键标识操作,因此重试是安全的;
  • 授权令牌标识授权,因此执行是被允许的。

两者在执行时都必须有效。长时间运行的运行需要会到期并被显式续期的授权,而不是在启动时铸造并贯穿工作流整个生命周期的令牌。续期是一个决策点:它可以被授予、被拒绝,或以更窄的范围被授予。那个决策点正是意图重新进入系统的地方。

长周期运行的设计规则

从上述内容可以得出几条规则。它们不是免费的,但比一次静默的正确性失败更便宜。

以授权生命周期而非基础设施的耐心来限定运行时长。 如果授权有效一小时,运行就有一小时。不要因为队列容忍就让工作流运行三天。当授权到期时,运行暂停并请求续期。

在恢复时重新推导计划,而不是重放过时的计划。 计划是提案,不是脚本。在恢复时,应把目标、当前世界状态和剩余预算交给 agent,并要求它再次提出下一步。日志约束已经发生的事情;它不应规定接下来发生什么。

记录每一次恢复事件,并附上原始意图与重新验证后意图之间的差异。 差异才是有价值的产物。它记录了发生了什么变化——政策、预算、范围、世界状态——以及运行为何继续或停止。没有它,事后复盘就无从下手。

一个行之有效的具体机制:在日志旁边存储一份决策上下文记录,进行版本化,并在每个恢复边界由一个小型验证器服务检查。验证器接收该记录和当前政策快照,返回继续、暂停或终止。日志仍然是运行时的关注点;决策上下文是 agent 的关注点。将它们分开使意图缺口可见,而不是将其隐藏在序列化的栈中。

更广泛的一点是,持久执行引擎对它们所声称的东西是正确的。它们让工作流在失败中存活。它们并不让 agent 基于正确的理由行事。第二个属性必须在之上进行工程化,而它始于承认:没有意图的检查点只是一种非常可靠的出错方式。

封面照片:panumas nikhomkhai / Pexels。

分享XLinkedIn永久链接6 分钟阅读

这能回答的问题

What is the intent gap in durable agent execution?

The intent gap is the difference between the state a checkpoint serialises (variables, step index, tool outputs) and the decision context that justified the run (goal, plan version, tool scope, budget, reason for the next step). A resumed run can replay state correctly while acting under intent that is no longer valid.

Why is idempotency not the same as consent?

Idempotency ensures a retried call does not take effect twice. It does not ensure the call was authorised. A single call made under an expired mandate will execute exactly once and still be wrong. Idempotency keys and authority tokens must both be valid at execution time.

How should a runtime handle a failed re-validation on resume?

Treat it as a terminal state, not a retry. Retrying a policy failure produces the same refusal and burns budget. The run should stop, log the delta between original and re-validated intent, and hand back to a human or orchestrator.

Should an agent replay its plan on resume?

No. The plan should be treated as a proposal, not a script. On resume, re-derive the next step from the goal, current world state, and remaining budget. The journal constrains what has already happened; it should not dictate what happens next.