Agent 的断点续传:任务如何中断、恢复,并且不把效果丢在路上

结合分布式快照、Saga、LogAct、Crab、RAC、Plans Don’t Persist 与 τ-bench,解释 Agent 长任务如何用任务契约、事件日志、副作用账本、恢复对账和外部验证器实现可中断、可恢复且效果可验收。

一个 Agent 正在发布一篇博客。资料已经查完,正文已经写好,构建也通过了。它调用发布接口后,文章其实已经上线,但确认响应还没写进本地状态,进程就崩了。重启后,checkpoint 仍显示“待发布”,于是 Agent 又发了一次——线上出现两篇相同文章。

从运行日志看,它“成功恢复”了:找到了原任务,接上了原步骤,继续执行了下一条动作。

从任务效果看,它恢复失败了:恢复位置是对的,恢复出来的世界却是错的。

这正是 Agent 长任务最容易被低估的问题。很多系统把“保存 messages”“记住计划”或“从上一个节点继续”叫作断点恢复。它们解决的只是模型下一轮看见什么,没有回答更难的三个问题:中断前哪些动作已经真实发生?哪些证据还值得相信?恢复后怎样证明任务效果没有变差?

先给结论:

可恢复的不是对话,而是任务状态;恢复的不是 token,而是一个经过核对、可以继续验收的世界。

真正可靠的 Agent 断点续传,需要同时做到三件事:

  1. 能继续:知道任务身份、完成了什么、下一步是什么。
  2. 不重复破坏:知道哪些副作用已经提交,结果未知时先对账而不是盲目重放。
  3. 效果仍可信:重新获取过期事实,并用外部 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),而是一次受控的状态重建:

  1. 读取任务契约、事件日志和最近 checkpoint。
  2. 校验任务、schema、工具、输入与关键依赖版本。
  3. 查询所有 STARTEDUNKNOWN 副作用的外部终态。
  4. 将步骤重新分类为已提交、未提交、结果未知、证据过期或需要补偿。
  5. 重新获取会变化的事实,如网页、分支头、库存、权限和部署状态。
  6. 从真相源、已验证产物和最近必要观察中组装最小工作上下文。
  7. 从第一个安全的未提交状态转移继续。

注意第 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 次恢复都成功的概率只有:

0.9843%0.9^8 \approx 43\%

这正是 τ-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

  1. 每个任务是否有稳定 task_id 和版本化目标契约?
  2. checkpoint 是否区分对话、控制状态、世界状态和完成证据?
  3. 事件日志是否记录了动作意图、许可、开始、结果和验证?
  4. 副作用是否有稳定幂等键与外部结果 ID?
  5. “调用超时”是否被当作 UNKNOWN,而不是自动当作失败?
  6. 恢复前是否查询外部世界,而不是只信本地 checkpoint?
  7. 易变证据是否带时间,并在恢复时重新获取?
  8. 不可逆动作是否有提交前审批和禁止盲重试的边界?
  9. 是否在每个步骤边界做过故障注入与并发接管测试?
  10. 最终 verifier 是否检查真实世界状态,并用重复运行衡量一致性?

如果这十个问题只能回答“我们把 messages 存数据库了”,系统拥有的是会话续接,不是任务恢复。

真正的 Agent 断点续传,不是让模型醒来后想起自己说过什么,而是让整个系统能回答:

我正在完成哪个契约?哪些动作已经对世界生效?哪些事实仍然可信?下一步怎样做才不会重复伤害?最后由什么证据证明任务真的完成?

文件续传用校验和守住字节;Agent 续传要用日志、对账、幂等、补偿和 verifier 守住世界。


论文与资料清单