Skip to content

长任务恢复策略专题

版本:v1.1

最后更新:2026-07-07

只要 Agent 开始处理长文档、多步骤审批、批量任务、跨系统编排或后台异步执行,就一定会遇到一个问题:

  • 任务做到一半断了,接下来怎么办

如果系统只能“失败后全量重跑”,那么一旦任务链路变长、依赖变多、外部副作用变重,成本、时延、稳定性和人工运维压力都会一起上来。长任务恢复策略的目标,不是把任务永远跑成功,而是让系统在中断、重试、重启、审批暂停、工具失败和人为接管时,仍然知道自己现在在哪里、下一步该做什么、哪些动作绝不能重复执行。

1. 什么叫长任务恢复

更实用的定义不是“任务失败后再试一次”,而是:

  • 任务有可识别的生命周期
  • 中间状态可持久化
  • 失败后能区分重试、续跑、补偿、取消和人工接管
  • 恢复后能证明状态没有被破坏

换句话说,恢复策略本质上是一份执行契约,而不是一段兜底代码。

2. 为什么 Agent 长任务比普通异步任务更难恢复

传统异步任务常见难点主要是:

  • 执行时间长
  • 依赖远端服务
  • 工作者重启后任务丢失

Agent 长任务还会叠加更多复杂性:

  • 模型推理和工具调用交替出现
  • 中间结论并不总是最终输出,但会影响后续决策
  • 人工审批可能把流程暂停数分钟到数天
  • 某些步骤有外部副作用,例如发消息、建工单、扣减额度、写 CRM
  • 任务可能按批次、游标、文件分片或对话回合渐进推进

所以恢复策略必须同时回答三件事:

  1. 当前进度在哪里。
  2. 这一步能不能安全重做。
  3. 如果不能重做,系统如何跳过、对账或补偿。

3. 哪些任务最应该优先做恢复设计

建议优先覆盖这些场景:

  • background mode 后台长任务
  • 批量文件理解、切片、结构化抽取
  • 多工具 Agent workflow
  • 跨审批节点的业务流程
  • 大规模评测、回放和重算任务
  • Realtime 会话里派生出来的后台补全任务

只要一条链路同时满足“耗时长、步骤多、有副作用、无法接受全量重跑”中的两个条件,就值得单独设计恢复策略。

4. 先建立统一的任务状态模型

恢复能否成立,首先取决于系统是不是知道自己当前在哪个状态。建议至少维护这些字段:

字段作用
task_id业务任务标识,跨重试保持稳定
run_id本次执行实例标识,用来区分不同尝试
workflow_version当前工作流定义版本
state任务当前状态,如 queuedrunningawaiting_approvalfailedcompleted
step_id当前或最近完成的步骤
cursor批量处理进度,如文件分片、页码、记录偏移量
checkpoint_ref最近一次持久化快照或状态引用
side_effect_log已执行外部动作的记录
retry_count当前步骤或任务的重试次数
owner当前责任人或责任服务
last_error最近一次错误摘要

如果没有这些最小字段,所谓恢复通常只能靠日志猜。

5. 长任务不要只分“成功”和“失败”

更可执行的状态模型一般至少包含:

  • queued
  • running
  • awaiting_tool_result
  • awaiting_approval
  • paused
  • retryable_failed
  • non_retryable_failed
  • compensating
  • cancelled
  • completed

这样设计的价值在于,系统可以明确表达:

  • 现在是在等人,还是在等工具
  • 是允许自动续跑,还是必须人工判断
  • 是已经开始补偿,还是仍可继续前进

6. 恢复点应该放在哪些位置

恢复点不是越多越好,而是应该放在“边界清晰、价值高、能稳定对账”的位置。

更适合设置 checkpoint 的位置通常是:

  • 工具调用前后
  • 模型输出已经结构化并完成校验后
  • 批处理某一分片完成后
  • 检索与 rerank 结果确定后
  • 审批发起后与审批完成后
  • 外部副作用执行前后

一个简单原则是:

  • 计算密集但无副作用的步骤,可以少量粗粒度 checkpoint
  • 有副作用的步骤,必须有细粒度且可对账的 checkpoint

这里还要额外注意一个常见误区:

  • “框架支持持久化”不等于“中途崩溃一定能从最近一步恢复”

以 LangGraph 的 checkpointer 文档为例,exit 持久化模式只会在图执行退出时统一落盘,包括正常结束、报错退出或 human-in-the-loop interrupt;如果进程在中途崩溃,尚未落盘的中间状态并不会自动可恢复。所以恢复设计不仅要问“有没有 checkpoint”,还要问:

  • checkpoint 什么时候刷盘
  • 是步骤级刷盘,还是只在整段执行退出时刷盘
  • worker 宕机时最多会丢掉哪一段进度

7. 四种最常见的恢复方式

7.1 全量重试

适合:

  • 任务短
  • 幂等性强
  • 成本低
  • 中间状态价值不高

不适合:

  • 模型推理成本高
  • 任务已产生外部副作用
  • 某些步骤依赖人工审批或外部时窗

7.2 断点续跑

适合:

  • 任务可拆步骤
  • 每步输入输出可持久化
  • 上一步结果已稳定

这是 Agent 长任务中最常见、也最值得优先建设的方式。

7.3 补偿后继续

适合:

  • 某个副作用已执行,但后续失败
  • 不能简单重做
  • 有明确反向动作或对账动作

例如:

  • 已创建外部工单,需要先取消或标记失效
  • 已写入 CRM,需要回滚或生成对账任务

7.4 转人工接管

适合:

  • 自动恢复无法判断当前真实状态
  • 重复执行有明显业务风险
  • 安全、财务、合规动作需要人拍板

人工接管不是失败,它是恢复策略的一部分。

8. 先给步骤分类型,再决定恢复方式

不是所有步骤都该同一种恢复策略。建议至少区分四类:

8.1 纯计算步骤

例如:

  • 文本清洗
  • 嵌入计算
  • 内容摘要

特点:

  • 无外部副作用
  • 可重算
  • 更适合自动重试或续跑

8.2 外部读取步骤

例如:

  • 调第三方检索 API
  • 拉取用户资料
  • 查询审批状态

特点:

  • 没有写副作用,但结果可能随时间变化
  • 恢复时要注意时效性和缓存失效

8.3 外部写入步骤

例如:

  • 发消息
  • 建工单
  • 扣积分
  • 改状态

特点:

  • 幂等要求最高
  • 必须记录 request_id、外部对象 ID 和写入结果

8.4 人机协同步骤

例如:

  • 等待人工审批
  • 等待业务确认
  • 进入人工纠偏工作台

特点:

  • 恢复不是继续推理,而是恢复上下文和决策现场

9. 幂等性是恢复策略的底层前提

很多系统以为自己做了恢复,实际上只是反复重放副作用。

只要任务里存在外部写动作,就要尽量满足:

  • 每个动作有稳定的幂等键
  • 能记录动作是否已成功落地
  • 能根据外部返回结果判断是“已执行”还是“未执行”

建议至少对这些对象做幂等设计:

  • 外部消息发送
  • 审批单创建
  • 工单创建
  • 账务或额度相关动作
  • 知识库发布与索引切换

否则系统一旦在写成功但本地状态未落盘时崩溃,就会出现经典问题:

  • 不知道该不该重发
  • 重发又可能造成真实重复操作

10. 批处理任务要有游标和分片语义

长任务很常见的一类是:

  • 扫描大量记录
  • 处理大文件
  • 对数据集逐项评测

这类任务如果没有游标,恢复就只能从头开始。建议显式记录:

  • 当前批次号
  • 当前分片号
  • 已完成对象列表或范围
  • 失败对象列表
  • 最后一次成功提交点

更稳妥的做法是:

  • 对每个分片单独产出结果
  • 主任务只负责汇总
  • 单分片失败不拖垮整批

11. 人工审批会把恢复策略变成“暂停与恢复”

OpenAI 的长任务和 Agent 官方资料都强调后台执行、异步完成和人工审批节点的重要性。放到工程实现里,这意味着:

  • 任务不只是执行和失败两种命运
  • 很多时候它会停在 awaiting_approval
  • 真正需要恢复的是上下文、证据和下一步决策点

所以审批恢复应至少保留:

  • 当前候选动作
  • 模型给出的理由与证据
  • 审批对象、时效和超时策略
  • 审批前已经完成的步骤
  • 审批被拒绝后的分支路径

这样审批人回来时看到的是“可继续执行的现场”,而不是一段已经失去上下文的日志。

12. 后台执行不等于天然可恢复

OpenAI 官方 Background mode 的核心价值是把长耗时工作从同步请求里移出去,但这并不自动等于恢复能力已经具备。

你仍然需要自己定义:

  • 后台任务状态怎么持久化
  • 成功、失败、取消和超时如何回写业务层
  • 任务结果由轮询、Webhook 还是事件总线通知
  • 中途失败后是重试整任务还是续跑某一步

如果只把同步请求改成异步任务,却没有状态持久化和恢复契约,那只是把问题从前台移到了后台。

13. 建议把“恢复决策树”写进 runbook

每个长任务都建议有一套统一判断顺序:

  1. 当前失败发生在哪个步骤。
  2. 这个步骤是否有外部副作用。
  3. 该副作用是否确认已落地。
  4. 是否具备幂等键或外部对象 ID。
  5. 是否可以直接从最近 checkpoint 续跑。
  6. 是否必须先补偿。
  7. 是否应转人工接管。

当团队把这套判断写成 runbook 后,值班处理速度会明显快于“边看代码边猜”。

14. 任务所有权、租约和心跳要单独设计

很多恢复失败,并不是因为没有 checkpoint,而是因为系统根本说不清“现在到底谁在执行这个任务”。

只要存在这些场景,就要考虑任务所有权:

  • 多个 worker 可能抢同一任务
  • worker 重启后旧实例可能还在误判自己活着
  • 审批通过后恢复执行的人和最初执行的人不是同一个进程
  • 后台任务跨班次、跨实例迁移

建议至少单独维护:

  • lease_owner
  • lease_expires_at
  • heartbeat_at
  • resume_requested_at
  • resume_token 或恢复授权标识

这样才能解决几个关键问题:

  1. 某个实例失联多久后可以被接管。
  2. 恢复时谁有资格继续跑。
  3. 同一个恢复动作如何避免被重复领取。

恢复策略如果没有所有权设计,最终很容易变成:

  • 旧实例继续写
  • 新实例也开始写
  • 两边都觉得自己在做正确的事

15. 会话状态不要只靠日志回放,最好保留可续接上下文

长任务里的模型状态,不只是最终文本输出,还包括:

  • 之前的 tool calls
  • tool results
  • 当前决策点之前的关键上下文
  • 继续执行所需的响应链

根据 OpenAI Conversation state 文档在 2026-07-07 可访问的说明,可以通过 previous_response_id 在 Responses API 里延续上下文链;如果无法解析该 ID,则需要显式回传完整上下文。

这对恢复策略有很直接的影响:

  1. 不能默认平台一定替你永久保存可恢复上下文。
  2. 应用层要知道哪些内容必须自持久化。
  3. 恢复时要区分“继续原链路”和“基于摘要重建现场”。

根据 OpenAI Background mode 文档在 2026-07-07 可访问的说明,后台模式会为了轮询保存响应数据大约 10 分钟,因此它适合承接长耗时任务,但不能把“短期可轮询”误解成“长期可恢复历史”。

更稳妥的做法通常是:

  • 短时恢复依赖平台返回对象
  • 中长期恢复依赖自己保存的 checkpoint、摘要和外部对象引用
  • 如果上下文链已经不可续接,就显式走“重建上下文后续跑”的路径

16. 超长工作流要考虑历史裁剪与执行链续接

很多团队会把“长任务恢复”理解成失败后的重试,却忽略另一类问题:

  • 任务没失败,但执行历史越来越长,最终把运行时拖垮

Temporal 官方文档明确给出了这类风险:单个 Workflow Execution 的 Event History 有大小与事件数量上限,官方推荐在历史接近阈值时使用 Continue-As-New,把当前状态显式传给新的执行链,让同一个 workflow_id 在新的 run_id 上继续跑。

对 Agent 长任务来说,这个思路非常实用,因为以下对象都会持续膨胀:

  • tool call 历史
  • 审批往返记录
  • 批量分片完成事件
  • 重试、补偿和人工接管事件
  • 恢复校验与观测事件

更稳妥的做法不是把所有回合都塞进一个永不结束的 run,而是:

  • 保持稳定的 task_idworkflow_id
  • 允许 run_id 分段轮换
  • 在切换前产出明确的 carry-over state
  • 让新 run 只接住“继续执行所需的最小状态”

建议额外持久化这些字段:

字段作用
continued_from_run_id标记本次 run 由哪个 run 续接而来
continue_reason为什么触发执行链切换,例如历史过长、审批跨天、上下文重建
carry_state_ref续接所需的最小状态快照
history_compacted_at何时完成历史裁剪

如果系统天然支持长历史续接,可以直接复用框架能力;如果不支持,也建议在应用层自己做“逻辑上的 Continue-As-New”,否则超长任务后期的恢复复杂度只会越来越高。

17. 部分结果要能提前暴露,但要和最终结果分层

长任务不是只有“开始”和“结束”两个时刻。对用户和运维同学来说,更有价值的是:

  • 现在任务卡在哪
  • 已经产出了哪些阶段性结果
  • 这些结果是预览、草稿,还是最终可提交版本

OpenAI 的 Background modeDeep research 等官方资料都强调了长任务的异步执行、轮询和 webhook 回调能力。这意味着工程上通常应该把“任务状态”和“阶段性产物”拆开管理,而不是等全部结束后一次性吐出最终结果。

建议把部分结果设计成正式对象,而不是临时日志:

字段作用
artifact_id阶段性产物标识
artifact_version产物版本,便于替换和回放
provisional是否仍属暂存结果
visible_to_user是否允许前台可见
derived_from_checkpoint该产物对应哪个恢复点
finalized_at何时转为最终版本

这样做有三个直接收益:

  1. 恢复时能先展示已有产物,不必假装“一切从零开始”。
  2. 人工接管时能看到已完成部分,而不是只看到失败提示。
  3. 系统可以把“部分完成”与“最终完成”区分清楚,避免误把草稿当成正式结果。

18. 恢复演练要常态化,不要等事故时第一次走通

恢复策略最怕的不是文档没写,而是:

  • 文档写了,但真实故障来临时没人确认它真的可用

所以长任务系统应该像灾备演练一样,定期做恢复演练。建议覆盖这些最容易暴露问题的场景:

  • worker 在工具调用后、落盘前崩溃
  • 外部接口超时但实际已经落地
  • 审批节点跨天恢复
  • 相同恢复请求被重复投递
  • 历史 checkpoint 可读,但关联产物缺失
  • 续跑时发现权限、租约或上下文版本已经变化

演练的目标不是证明“系统永远没问题”,而是验证这些关键问题:

  • runbook 是否真的可执行
  • 值班同学是否知道该查哪些对象
  • 恢复指标是否能被正确观测
  • 人工接管入口是否真的能接住异常链路

建议把演练本身也记成结构化记录,例如 drill_idscenarioexpected_recovery_pathactual_outcomefollowup_action,否则每次演练都会变成一次性经验。

19. 毒丸任务要隔离,不要无限重试

有些任务不是“再试一次也许能过”,而是:

  • 输入本身损坏
  • 外部契约已经漂移
  • 权限长期不满足
  • 业务前置条件根本没成立

这类任务如果继续自动重试,只会造成三种后果:

  • 不断占用 worker 和配额
  • 放大重复副作用风险
  • 把真正需要人工处理的问题埋在重试噪音里

更成熟的做法是引入 quarantine 语义。达到阈值后,不再进入普通重试队列,而是转入隔离区并等待明确处置。

建议至少维护:

字段作用
quarantine_reason进入隔离区的主因
quarantined_at隔离时间
last_safe_checkpoint最近确认安全的恢复点
review_owner下一位负责判断的人或队列
release_condition什么条件满足后允许重新放回执行队列

这样系统才能把“暂时失败、可以自动恢复”和“已经变成毒丸、必须人工决策”分开治理。

20. 恢复后的第一件事不是继续跑,而是先验真

很多系统恢复失败,不是因为没恢复起来,而是因为恢复后没校验状态一致性。

建议至少做三类校验:

20.1 进度一致性

  • 当前 step_id 是否和持久化记录一致
  • 批处理游标是否跳步或回退

20.2 副作用一致性

  • 外部系统对象是否已创建
  • 本地记录是否和外部状态对得上

20.3 质量一致性

  • 模型输出是否仍满足结构化校验
  • 回滚或续跑后,关键基线样例是否恢复

21. 可观测性必须覆盖恢复过程,而不只是最终状态

对长任务而言,只有“任务成功/失败”远远不够。建议最少记录这些事件:

  • 任务创建
  • 任务开始
  • checkpoint 写入
  • 工具调用开始/完成/失败
  • 审批发起/通过/拒绝/超时
  • 自动重试
  • 人工接管
  • 补偿开始/完成/失败
  • 恢复成功
  • 恢复放弃

如果没有这些事件,后续做事故复盘、补偿对账和恢复优化都会非常痛苦。

22. 关键指标建议单独监控

建议至少补齐这些指标:

  • resume_success_rate
  • checkpoint_coverage
  • avg_recovery_time
  • human_handoff_rate
  • replay_duplicate_side_effect_rate
  • compensation_success_rate
  • approval_timeout_rate
  • per_step_retry_rate
  • batch_partial_failure_rate
  • quarantine_rate
  • continue_as_new_rate

这些指标能帮助团队分清楚:到底是系统太容易中断,还是恢复机制本身不可靠。

23. 常见反模式

23.1 只靠超时后全量重试

任务越长,这种策略越贵,也越危险。

23.2 没有 side effect log

一旦进程在外部写成功后本地崩溃,就无法判断该不该重放。

23.3 把审批当成普通等待

审批暂停往往会跨人、跨班次、跨天,不保留上下文就谈不上恢复。

23.4 checkpoint 粒度过粗

失败后虽然能恢复,但恢复点离当前太远,成本依然高。

23.5 恢复后不校验

任务“继续跑了”不代表状态就真的正确了。

23.6 把隔离任务继续塞回普通重试队列

这会让毒丸任务长期污染吞吐、告警和人工排障视野。

24. 推荐搭配阅读

25. 重点官方资源

以下资源是本次补写时重点参考的官方资料,适合继续补强后台执行、状态持久化、人工中断恢复和长任务编排设计:

26. 落地检查清单

  • 是否为每类长任务定义了稳定的 task_idrun_id 和状态模型
  • 是否在关键边界设置了 checkpoint,而不是只在任务开始和结束记状态
  • 是否确认 checkpoint 真的是“中途崩溃可恢复”的刷盘语义,而不是只在整段执行退出时持久化
  • 是否对所有外部写动作设计了幂等键和 side effect log
  • 是否设计了任务所有权、租约和心跳,而不是默认单实例永远在线
  • 是否显式区分可重试失败、不可重试失败、审批暂停和人工接管
  • 是否为批处理任务记录了游标、分片和部分失败结果
  • 是否为超长执行链设计了历史裁剪、分段续接或 Continue-As-New 机制
  • 是否为审批与人工纠偏保留了完整上下文,而不是只留一条失败日志
  • 是否能把阶段性产物作为正式对象对外暴露,并明确区分暂存结果与最终结果
  • 是否对重复失败任务做隔离治理,而不是无限重试
  • 是否定期做恢复演练,而不是只在事故发生后第一次验证 runbook
  • 是否记录了恢复相关事件和指标,能支持复盘与持续优化