一个 Agent 正在发布一篇博客。资料已经查完,正文已经写好,构建也通过了。它调用发布接口后,文章其实已经上线,但确认响应还没写进本地状态,进程就崩了。重启后,checkpoint 仍显示“待发布”,于是 Agent 又发了一次——线上出现两篇相同文章。
从运行日志看,它“成功恢复”了:找到了原任务,接上了原步骤,继续执行了下一条动作。
从任务效果看,它恢复失败了:恢复位置是对的,恢复出来的世界却是错的。
这正是 Agent 长任务最容易被低估的问题。很多系统把“保存 messages”“记住计划”或“从上一个节点继续”叫作断点恢复。它们解决的只是模型下一轮看见什么,没有回答更难的三个问题:中断前哪些动作已经真实发生?哪些证据还值得相信?恢复后怎样证明任务效果没有变差?
先给结论:
可恢复的不是对话,而是任务状态;恢复的不是 token,而是一个经过核对、可以继续验收的世界。
真正可靠的 Agent 断点续传,需要同时做到三件事:
- 能继续:知道任务身份、完成了什么、下一步是什么。
- 不重复破坏:知道哪些副作用已经提交,结果未知时先对账而不是盲目重放。
- 效果仍可信:重新获取过期事实,并用外部 verifier 验收最终世界,而不是相信 Agent 说“完成了”。
这是《从能跑到可托付:顶级 Agent 应用工程师的六层系统与三条闭环》里“运行时与信任层”的一次下钻。那篇给全景,这篇只钉住一个问题:任务怎样穿过进程、会话、模型和人工暂停的边界,仍然保持同一个目标和可验证的效果。
一、先建立心智模型:Agent 任务不是聊天,而是一项可恢复事务
文件断点续传之所以可靠,是因为它不靠客户端“记得自己传到哪了”。它通常有稳定的文件标识、版本或大小、分片边界、已确认偏移量和校验和。连接断了,客户端先确认服务端已经收到哪些分片,再从缺口继续;传完后,再对整份文件做校验。
把这些部件映射到 Agent 任务,轮廓会立刻清楚:
| 文件断点续传 | Agent 可靠执行 | 解决什么问题 |
|---|---|---|
| 文件 ID 与版本 | task_id、目标契约、输入与计划版本 | 恢复的是不是同一任务 |
| 分片 | 可独立提交、独立验证的步骤 | 从哪里安全重做 |
| 已确认偏移量 | 已提交步骤集合与执行游标 | 已经完成了什么 |
| 分片校验和 | 工具结果证据、步骤 verifier | 这一步是否真的有效 |
| 重传 | 有界重试、换策略或人工接管 | 局部失败怎样处理 |
| 服务端确认 | 外部世界查询与副作用账本 | 动作到底有没有发生 |
| 文件总校验 | 端到端 verifier | 整个任务是否成功 |
但这个类比只能带我们走一半。
文件通常是一串不变字节;Agent 操作的是一个持续变化的世界。网页会更新、权限会过期、分支会前进、库存会变化、邮件无法“取消发送”。因此,Agent 恢复比文件续传至少多两步:
- 对账:本地记录与外部世界冲突时,以谁为准?
- 重新验证:昨天正确的计划和证据,今天是否仍然成立?
1985 年的经典论文 Distributed Snapshots 讨论的正是为什么“分别保存每个局部状态”不等于得到一致的全局状态:进程状态与传输中的消息必须共同构成一个有意义的截面。这个思想对 Agent 很有用,但不能机械照搬——Agent 往往无法冻结它调用的所有网页、数据库、邮件系统和第三方 API。
所以 Agent checkpoint 的目标不是“把全世界快照下来”,而是:
保存足够的控制状态和证据引用,使系统能在恢复时重新观察世界、消解不确定状态,并找到下一条安全的状态转移。
二、一次任务其实有四种状态,别全塞进 messages
很多恢复方案的根问题,是把所有东西都装进一段对话:用户目标、计划、工具结果、文件变化、重试次数、完成声明,全都变成消息。只要消息还在,Agent 看起来就“有状态”;消息被压缩或进程消失,状态也跟着蒸发。
工程上至少要拆开四类状态:
| 状态类型 | 例子 | 能否作为真相源 |
|---|---|---|
| 对话与推理痕迹 | 消息、草稿、模型解释、临时计划 | 不能;可压缩、可重建 |
| 控制状态 | 当前步骤、依赖、尝试次数、预算、租约 | 可以,但必须持久化和版本化 |
| 世界状态 | 文件、Git 分支、数据库、发布平台、消息系统 | 通常是副作用事实的最终真相源 |
| 完成证据 | 测试结果、查询结果、部署状态、业务事件、人工批准 | 是 verifier 的输入,不等于一句完成声明 |
这个拆分有两条直接后果。
第一,对话不是数据库。模型输出可以解释为什么做某件事,却不应该独自决定这件事是否已经发生。
第二,checkpoint 不是任意序列化。把进程内所有对象 dump 下来,只能说明这些对象可以重新加载,不能说明它们与外部世界一致。
2026 年的预印本 Plans Don’t Persist 给了一个很有意思的实验证据。作者在 ALFWorld 的 ReAct Agent 上做上下文压缩:保留完整上下文时成功率为 56.7%;只保留系统提示与最近四条消息,成功率降到 22.0%。更关键的是,只把原计划固定在上下文里也没有救回来,成功率为 20.7%。作者的解释很克制:丢掉的不只是计划,还有最近动作与观察组成的工作状态。
这篇论文是预印本,实验范围也有限,不能推出“所有模型都不会保持计划”。但它足以击穿一个常见幻想:
只保存计划不等于保存任务,只恢复聊天也不等于恢复执行。
三、可靠恢复的五个部件
一个框架无关的最小可靠执行系统,可以收束为五个部件:任务契约、事件日志与 checkpoint、副作用账本、恢复对账与上下文重建、分层 verifier。
1. 任务契约:先固定“同一个任务”到底是什么
恢复前先回答一个看似多余的问题:我们恢复的还是原任务吗?
一个任务契约至少包含:
- 稳定的
task_id; - 目标结果与禁止条件;
- 输入、规格和 schema 版本;
- 权限、时间、token 与重试预算;
- 步骤级不变量;
- 端到端完成证据;
- 停止、补偿和转人工条件。
如果用户修改了目标、代码仓库已经前进、工具 schema 发生变化、发布权限已经过期,旧 checkpoint 就不能静默续跑。系统应该迁移状态、重新规划或暂停确认,而不是假装“还是原来的下一步”。
任务契约相当于文件续传里的文件 ID、版本和总长度。没有它,所谓恢复只是在相似上下文里继续生成。
2. 事件日志与 checkpoint:一个记录事实,一个加速恢复
这两个概念经常被混在一起:
- 事件日志是只增的事实序列:收到了什么目标、提出了什么动作、动作是否被允许、开始执行、返回了什么、验证结果如何。
- checkpoint是从事件与当前控制状态物化出的恢复截面:现在在哪、下一步是什么、哪些结果可以复用。
事件日志适合审计、重放和解决争议;checkpoint 适合快速启动。只有 checkpoint 没有日志,恢复出错时很难知道状态怎样走到这里;只有日志没有 checkpoint,长任务每次都要从头折叠历史。
LogAct 把这个思路直接用于 Agent:它把 Agent 拆成一个在共享日志上运行的状态机,动作意图先持久写入日志,再经过 voter 决定是否允许执行,执行结果也写回同一日志。这样,动作在发生前是可见、可拦截的,发生后有审计和恢复依据。
这不意味着“加一条日志就得到 exactly-once”。LogAct 论文自己明确指出:执行器操作外部环境时,普通状态机复制并不安全,因为命令未必幂等;恢复时仍需要隔离旧执行器,或依赖幂等、事务化 API。它提供的是一个让动作可见、可停止、可恢复的控制面,不是替外部世界自动发明回滚键。
还有一个很重要的工程反直觉:checkpoint 不是越密越好。
Crab 研究 Agent 沙箱的 checkpoint/restore。它发现只恢复聊天会漏掉文件系统、进程和运行时副作用;但每轮都做完整 OS 快照又太贵。在其 shell 与代码修复工作负载中,聊天恢复的正确率只有 8%–13%(不同 Agent/任务有所差异),语义感知的完整恢复达到 100%;同时,最多 87% 的轮次没有产生需要恢复的 OS 状态,可以跳过 checkpoint。
这个实验不能直接代表所有业务 Agent,但它留下一个耐用结论:
检查点应该对齐“恢复相关状态发生变化”的边界,而不是机械地对齐每一次模型调用。
3. 副作用账本:把“想做”与“做成了”分开
发布、付款、发信、建单这类写操作有一个经典危险窗口:
外部系统已经执行成功
↓
确认响应尚未持久化
↓
Agent 或网络发生故障
恢复后,本地只能看到“没有成功记录”。但这不是失败,而是 UNKNOWN:结果未知。
可靠系统需要为每个副作用保存一份账本,至少包括:
- 动作意图与参数摘要;
- 稳定的幂等键;
PREPARED / STARTED / COMMITTED / UNKNOWN / COMPENSATED状态;- 外部系统返回的稳定结果 ID;
- 查询真实结果的方法;
- 能否重试、如何补偿、谁有权批准。
恢复时遇到 UNKNOWN,第一动作必须是按幂等键或外部 ID 查询,而不是再次调用写接口。只有确认未发生,才能重试;无法确认,就应该阻塞、补偿或转人工。
这也是为什么“至少一次重试”与“幂等工具”总是一起出现。LangGraph 当前的中断文档直接提醒:恢复会重新运行包含 interrupt() 的节点,所以中断前发生的副作用应设计为幂等,或拆到独立节点。它的时间旅行文档也明确说明,checkpoint 之后的 LLM 调用和 API 请求会重新触发,结果可能不同。
框架可以替你保存游标,却无法替一个不支持幂等键的第三方接口消除重复副作用。
4. 恢复对账与上下文重建:先看世界,再决定相信什么
恢复不是 load(checkpoint),而是一次受控的状态重建:
- 读取任务契约、事件日志和最近 checkpoint。
- 校验任务、schema、工具、输入与关键依赖版本。
- 查询所有
STARTED和UNKNOWN副作用的外部终态。 - 将步骤重新分类为已提交、未提交、结果未知、证据过期或需要补偿。
- 重新获取会变化的事实,如网页、分支头、库存、权限和部署状态。
- 从真相源、已验证产物和最近必要观察中组装最小工作上下文。
- 从第一个安全的未提交状态转移继续。
注意第 6 步叫重建,不是把旧对话原样塞回去。恢复上下文应该优先包含:任务根、当前状态、最近已验证证据、未决问题、下一步允许动作。历史解释、重复日志和已经失效的观察可以留在外部,需要时再检索。
5. 分层 verifier:恢复位置正确,不代表任务效果正确
验证器至少分两层:
- 步骤级 verifier:这一小步的输出是否满足不变量?例如 Markdown 能解析、测试通过、写操作返回的对象能查到。
- 任务级 verifier:最终世界是否满足目标?例如公开 URL 可访问、标题唯一、内容是目标版本、站点没有因发布而构建失败。
Agent 自己的总结可以帮助诊断,但不能做唯一 verifier。原因很简单:生成动作和评价动作的是同一个概率系统,它可能带着同一份误解一起通过“自检”。
τ-bench 的评测思路值得迁移:它主要比较对话结束后的数据库状态与标注目标状态,而不是欣赏 Agent 最后的说法。这是对“验收世界,而不是验收回答”的一次漂亮实现。
四、完整恢复算法:先对账,再继续,最后重新验收
把五个部件串起来,任务生命周期应该更像下面这台状态机:
flowchart TD
A[RUNNING<br/>执行下一安全步骤] --> B[CHECKPOINTED<br/>持久化控制状态与证据引用]
B --> A
A -->|进程故障、人工暂停、预算耗尽| C[INTERRUPTED]
C --> D[RECONCILING<br/>校验版本并对账外部世界]
D -->|副作用结果未知| E[BLOCKED<br/>查询、人工确认或等待]
E --> D
D -->|发现部分提交且任务不可继续| F[COMPENSATING<br/>执行补偿并验证]
F --> D
D -->|状态一致且证据新鲜| G[RESUMING<br/>重建最小上下文]
G --> A
A -->|候选任务完成| H[VERIFYING<br/>验收最终世界状态]
H -->|未通过| D
H -->|通过| I[DONE]
一个够用的 checkpoint 长什么样
下面不是标准 schema,而是一份最小设计草图:
{
"task_id": "publish-agent-resume-post",
"task_spec_version": 3,
"checkpoint_schema_version": 2,
"status": "INTERRUPTED",
"cursor": "publish",
"completed_steps": [
{
"step_id": "build",
"evidence_ref": "build-run:8421",
"verified_at": "2026-08-03T14:20:00Z"
}
],
"effects": [
{
"operation": "create_post",
"idempotency_key": "task:publish-agent-resume-post:publish:v3",
"status": "UNKNOWN",
"remote_ref": null
}
],
"volatile_evidence": ["target-site-state", "publish-permission"],
"retry_budget": { "publish": 1 },
"next_safe_action": "reconcile_publish_effect"
}
它刻意没有保存完整思维链,也没有把“模型认为已经完成”作为状态。可靠恢复需要的是决策所依赖的事实、版本、动作承诺和完成证据,不是把每一个内部推理 token 永久化。
恢复时的七步协议
可以把恢复代码写成下面的伪流程:
1. LOAD 载入契约、事件日志、checkpoint
2. COMPATIBLE 检查任务、schema、工具、输入是否仍兼容
3. RECONCILE 查询所有未确认副作用的外部结果
4. REFRESH 重读易变事实,废弃过期证据
5. HYDRATE 组装当前决策需要的最小上下文
6. EXECUTE 从第一个安全的未提交步骤继续
7. VERIFY 步骤级验收;终点再验收完整世界
如果只能记住一条顺序,就记住:
不要 checkpoint → load → continue;要 checkpoint → load → reconcile → refresh → continue → verify。
五、恢复不了的动作怎么办:重试、替代、补偿、阻塞
不是所有副作用都能做成真正幂等,也不是所有操作都能撤销。退款可以冲正,邮件发出后不能从收件箱里“回滚”,一次公开披露更无法恢复到无人看过的状态。
1987 年的 Sagas 为长事务提供了一个经典答案:把长事务拆成一系列可提交的小事务;如果后续无法完成,就执行补偿事务来修正已发生的部分执行。注意,补偿不是时间倒流,而是把业务带到一个可接受的新状态。
2026 年的同行评议论文 Robust Agent Compensation(RAC) 把这条线直接接到 Agent:工具拦截器把开始、完成和错误写入持久事务日志;失败后先判断能否重试,再尝试替代动作,仍然失败时从日志重建执行关系并运行补偿工具。
这个方向很实用,但也暴露了补偿的真实成本:系统必须知道 book_flight 对应 cancel_flight,还要知道补偿参数怎样从原输入与输出得到。没有这份语义,模型只能临场猜;有些动作甚至根本不存在补偿。
因此,工具设计时应显式声明:
| 工具类型 | 恢复策略 |
|---|---|
| 纯读取 | 可重试,但要处理证据过期与返回变化 |
| 天然幂等写入 | 使用稳定业务键重试,并查询终态 |
| 可去重写入 | 调用方提供幂等键,保存外部结果 ID |
| 可补偿写入 | 记录补偿工具、参数映射和补偿 verifier |
| 不可逆写入 | 提交前审批;结果未知时阻塞,禁止盲重试 |
可靠性不是让 Agent “更聪明地处理异常”,而是让每种工具在进入 Agent 动作空间前,就带上恢复语义。
六、怎样保障恢复后的任务效果:五道质量门
到这里,我们解决了“任务怎样接上”。还差题目里的后半句:怎样保障接上以后,任务效果没有掉?
质量门 1:任务契约不能静默漂移
checkpoint 保存 task_spec_version、关键输入摘要、工具/schema 版本和依赖指纹。版本不兼容时,恢复流程进入迁移、重新规划或人工确认,不允许直接执行旧的 next_action。
质量门 2:证据有来源,也有保质期
每条重要事实带上来源、获取时间和适用对象。恢复时按类型决定:
- 不变证据,如某次构建产物的内容哈希,可以复用。
- 可变证据,如网页内容、分支头、权限、库存,必须重读。
- 无来源摘要只能做导航,不能做提交依据。
质量门 3:每个步骤都缩短“不可验证区间”
不要让 Agent 连续做十个互相依赖的动作,最后才检查。把任务拆成可提交、可观察、可验证的小段:写稿后检查结构,构建后保存构建证据,发布后查询公开终态。
这不是为了多打勾,而是为了把恢复点放在已经获得证据的边界。
质量门 4:终点必须重新验收
即使中断前所有步骤都通过,恢复后仍要运行端到端 verifier。原因可能是环境漂移,也可能是恢复本身引入了重复、遗漏或旧版本覆盖。
“所有步骤都有 completed: true”不是终态证据。线上只有一篇目标文章、公开 URL 返回成功、内容哈希对应目标版本、站点构建健康,才是。
质量门 5:恢复路径必须像正常路径一样接受评测
很多测试只覆盖从头顺利执行,恢复代码第一次真正运行就是生产事故。更有效的做法是在每个状态边界注入故障:
- 动作执行前崩溃;
- 外部成功但确认丢失;
- checkpoint 写到一半;
- 恢复期间任务规格升级;
- 外部证据过期;
- 补偿动作本身失败;
- 两个恢复 worker 同时接管。
每次注入后检查三个性质:最终效果是否正确、有没有重复副作用、是否只重做必要工作。
七、一个最小概率直觉:偶尔续上,不等于稳定续上
成功率可以先理解成:同一类任务重复运行很多次,有多少次真的达到目标终态。若简化地假设每次运行彼此独立,连续成功就要把每次成功概率相乘。
比如一个 Agent 单次恢复成功率看起来有 90%,连续 8 次恢复都成功的概率只有:
这正是 τ-bench 提出 pass^k 的动机:pass@k 关心跑 k 次能否撞中一次,pass^k 关心 k 次是否每次都成功。原论文里,GPT-4o function calling 在当时零售任务上的平均成功率超过 60%,但 pass^8 低于 25%。这些旧模型数字不该拿来做今天的排行榜,它们留下的是一把更适合生产的尺子:能力看能不能做到,可靠性看能不能反复做到。
Measuring AI Ability to Complete Long Tasks 也给出相同警告的另一种表达。在其特定的软件与机器学习任务分布上,模型达到 80% 成功率时的任务时间跨度,比 50% 成功率口径短 4–6 倍。换句话说,“有一半机会做完两小时任务”与“可以托付两小时任务”之间,还隔着一整套可靠性工程。
所以恢复评测不应只统计:
resume() 有没有返回成功?
而要统计:
在不同步骤、不同故障窗口和环境漂移下:
最终世界是否正确?
副作用是否至多发生一次业务效果?
连续多次是否仍然通过?
八、框架分别解决哪一层,别让一个 checkpoint 承担所有期待
工程落地时,可以把能力分成四层:
| 层 | 典型能力 | 它没有自动解决什么 |
|---|---|---|
| Agent/图框架 | thread、graph state、interrupt、节点级 checkpoint | 外部 API 是否已经生效 |
| Durable workflow | 事件历史、重放、定时等待、worker 故障恢复 | 非幂等 Activity 的重复业务效果 |
| 沙箱/运行时 | 文件系统、进程、容器状态的快照与恢复 | 沙箱外数据库、邮件和网页状态 |
| 业务可靠性层 | 幂等键、副作用账本、补偿、终态 verifier | 开放问题中的语义判断 |
Temporal 官方文档代表 durable execution 这一层:让工作流跨进程和基础设施故障继续存在。LangGraph 代表图状态 checkpoint 一层。Crab 证明沙箱的 OS 状态也可能是恢复必需品。LogAct 和 RAC 则把日志、动作意图、执行结果、安全检查与补偿推到更靠近 Agent 行为的位置。
它们不是互相替代的答案,而是不同恢复边界。选型前先问:
你要恢复的是图状态、程序执行、沙箱,还是已经被 Agent 改变的外部世界?
如果答案是最后一个,就必须有业务 ID、对账接口、幂等或补偿语义和外部 verifier。任何框架的 checkpoint=true 都不会让这些凭空出现。
九、四种看似可恢复、其实很危险的方案
1. 保存完整聊天记录
它保留了叙事,不保证控制状态、OS 状态与外部副作用一致。Crab 的实验正面展示了聊天恢复的缺口。
2. 保存一份 TODO 列表
TODO 表示 Agent 的计划,不表示动作已经提交,也不表示完成项仍然有效。《Plans Don’t Persist》进一步提醒:即便固定计划,丢掉最近工作状态仍会严重伤害表现。
3. 失败后直接重跑当前步骤
在“执行成功、确认丢失”的窗口里,直接重跑是制造重复付款、重复发信和重复发布的最快方法。先标记 UNKNOWN,再从外部对账。
4. 让模型决定是否补偿
模型可以帮助识别异常、提出补偿候选,但不可承受的约束要由确定性机制执行:工具声明补偿对、权限控制补偿范围、verifier 检查补偿终态。LogAct 也明确区分规则型 verifier 的硬保证与 LLM voter 的软覆盖。
十、最后用十个问题体检你的 Agent
- 每个任务是否有稳定
task_id和版本化目标契约? - checkpoint 是否区分对话、控制状态、世界状态和完成证据?
- 事件日志是否记录了动作意图、许可、开始、结果和验证?
- 副作用是否有稳定幂等键与外部结果 ID?
- “调用超时”是否被当作
UNKNOWN,而不是自动当作失败? - 恢复前是否查询外部世界,而不是只信本地 checkpoint?
- 易变证据是否带时间,并在恢复时重新获取?
- 不可逆动作是否有提交前审批和禁止盲重试的边界?
- 是否在每个步骤边界做过故障注入与并发接管测试?
- 最终 verifier 是否检查真实世界状态,并用重复运行衡量一致性?
如果这十个问题只能回答“我们把 messages 存数据库了”,系统拥有的是会话续接,不是任务恢复。
真正的 Agent 断点续传,不是让模型醒来后想起自己说过什么,而是让整个系统能回答:
我正在完成哪个契约?哪些动作已经对世界生效?哪些事实仍然可信?下一步怎样做才不会重复伤害?最后由什么证据证明任务真的完成?
文件续传用校验和守住字节;Agent 续传要用日志、对账、幂等、补偿和 verifier 守住世界。
论文与资料清单
- Distributed Snapshots: Determining Global States of Distributed Systems(Chandy & Lamport, 1985)——一致全局状态与 checkpoint 的经典基础。
- Sagas(Garcia-Molina & Salem, 1987)——把长事务拆成小事务,并用补偿修正部分执行。
- LogAct: Enabling Agentic Reliability via Shared Logs(Balakrishnan et al., 2026)——共享日志上的 Agent 状态机、动作投票与语义恢复。
- Crab: A Semantics-Aware Checkpoint/Restore Runtime for Agent Sandboxes(Wu et al., 2026)——Agent 沙箱的恢复状态缺口与选择性 checkpoint。
- Robust Agent Compensation (RAC): Teaching AI Agents to Compensate(Perera et al., ACM CAIS 2026)——工具日志、重试、替代与确定性补偿。
- Plans Don’t Persist: Why Context Management Is Load Bearing for LLM Agents(Mehta & Datta, 2026,预印本)——计划与工作状态在上下文压缩中的脆弱性。
- τ-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains(Yao et al., 2024)——用数据库终态和
pass^k衡量 Agent 的一致可靠性。 - Measuring AI Ability to Complete Long Tasks(Kwa et al., 2025–2026)——用任务时间跨度刻画长任务能力与可靠性差距。
- LangGraph Persistence / Interrupts——图状态 checkpoint、重放与中断恢复的实现参考。
- Temporal Documentation——durable execution、事件历史与故障恢复的实现参考。