AI Agent 跑了半小时突然崩了,怎样才能不从头再来?
长时间Agent崩溃后不必整条重跑。本文从检查点、结果未知、幂等副作用、版本隔离和故障注入出发,拆解一套可验证的断点续跑机制。
不一定从头再来。真正可靠的恢复,不是让大模型接着上一个 Token 继续写,而是让系统从最近一个“结果已经被确认”的步骤继续执行:已完成的工作直接复用,未执行的工作正常运行,结果不明的外部操作先核对、再决定是否重试。
一个 AI 编程 Agent 已经运行了半小时。
它读完仓库,定位到问题,修改了 6 个文件,下载了依赖,跑过两轮测试,还在第三轮修复里创建了一个临时分支。然后进程被系统杀掉,容器重启,网络连接断开。
重新打开任务时,最令人沮丧的提示出现了:请从头开始。
这不是大模型能力不够,而是执行系统把一条长任务当成了一次普通函数调用。所有进度都只存在内存和对话窗口里,进程一消失,系统就失去了回答三个问题所需的证据:哪些步骤已经完成,哪些外部操作已经生效,下一步从哪里开始才安全。
所以答案是:并非只能从头再来,但“恢复”必须在崩溃之前就被设计进去。
本文不讨论某个产品界面里的“继续”按钮,而是从工程角度拆解长时间 Agent 如何实现可验证的断点续跑,以及为什么仅仅保存聊天记录远远不够。
一、先纠正一个误解:恢复的不是那一瞬间,而是工作流状态
很多人把断点续跑理解为:模型已经输出到第 8,327 个 Token,恢复后从第 8,328 个 Token 接着生成。
现实中,单次模型请求如果在服务端完成前中断,通常仍要重新发起。模型采样具有非确定性,即使输入相同,重试结果也可能不同。工具进程执行到一半,也未必能从某条 CPU 指令继续。
工程上更有价值的恢复粒度是“步骤”或“活动”:
- 已确认完成的代码扫描,不再重复扫描;
- 已生成并校验过的计划,直接读取持久化结果;
- 已成功安装的依赖,通过环境快照确认后复用;
- 尚未完成的模型调用,可以重新发起;
- 已调用但结果未知的发布、付款或建单操作,先查询外部系统。
这意味着,断点续跑不是保存一段半成品文字,而是保存一套能够证明执行进度的状态。

聊天上下文告诉模型“之前聊了什么”,执行状态则要回答:
- 当前任务和运行实例是谁;
- 走到了哪个步骤;
- 每个工具收到什么请求、返回什么结果;
- 哪些文件或制品已经产生,它们的校验值是什么;
- 哪些操作正在等待人工审批;
- 当时使用的模型、提示词、工具协议和 Agent 代码是什么版本;
- 还剩多少时间、Token、重试次数和费用预算。
记忆解决的是“知道什么”,检查点解决的是“做到了哪里”。 两者都重要,但不能互相替代。
二、真正危险的不是崩溃,而是“结果未知”
一次步骤执行,至少会经过这样的链路:
记录执行意图
↓
调用工具或外部服务
↓
获得结果
↓
持久化结果
如果 Agent 在调用前崩溃,恢复相对简单:这一步还没执行,可以重新执行。
如果结果已经持久化,恢复也简单:读取结果,跳过这一步。
最麻烦的是第三种情况:外部系统已经成功,Agent 却在收到响应之前,或者在写入本地状态之前崩溃。
比如 Agent 调用接口创建云服务器。云平台已经创建成功,但返回响应时连接断了。Agent 只看到超时。如果它把“没有收到成功响应”解释成“没有成功”,恢复后再次创建,就会多出一台服务器。
发邮件、提交订单、写数据库、创建工单、发布文章、合并代码,都可能遇到同样的问题。
因此,一个可恢复步骤不能只有“成功”和“失败”,至少要区分:
NOT_STARTED 尚未执行,可以开始
COMPLETED 已确认完成,复用结果
UNKNOWN 可能已生效,必须核对

UNKNOWN 不是一个可以被重试按钮消灭的异常,而是分布式系统里的事实:调用方和执行方对结果的认知发生了分歧。
正确的恢复顺序应该是:先用业务标识查询外部系统,确认操作是否已经产生;能找到就补记结果,找不到且确认未执行才重试,无法判断则进入人工处理。
三、检查点应该保存什么,才能真的恢复
只保存一句“正在修改代码”几乎没有价值。一个可以投入生产的检查点,通常需要覆盖五类信息。
1. 运行身份与步骤状态
每次任务都应有稳定的 run_id,每个可恢复步骤有稳定的 step_id。不要在重启后生成一套新标识,否则外部系统无法判断这是原请求的重试,还是一次新的业务意图。
2. 输入、输出与制品引用
大块文件、测试日志和补丁不必全部塞进数据库,可以保存到对象存储或制品仓库,再在检查点中记录引用、大小和哈希值。恢复时先验哈希,避免把被覆盖的文件误当成旧结果。
3. 工具调用凭据
这里的凭据不是密码,而是恢复所需的业务证据:请求摘要、幂等键、外部资源 ID、开始时间、结束时间、错误类型和结果位置。密钥仍应由密钥系统管理,不能跟着检查点明文落盘。
4. 环境快照
编程 Agent 尤其依赖环境。Git 提交、工作区 Diff、依赖锁文件、容器镜像、运行命令和测试数据版本都可能改变结果。仅恢复对话,却把 Agent 放进一个已经变化的仓库,本质上是在执行另一项任务。
5. 版本与预算
模型版本、提示词版本、工具 Schema、Agent 编排代码以及策略配置要与运行实例关联。时间、Token 和费用预算也应继承,而不是每次恢复都重新获得一整份预算,否则故障循环可能无限消耗资源。
一个简化的步骤记录可以长这样:
{
"run_id": "run-0187",
"step_id": "publish-preview",
"status": "UNKNOWN",
"idem_key": "run-0187:preview",
"request_hash": "sha256:...",
"resource_id": null,
"artifact_ref": null,
"agent_ver": "2026.08.24",
"tool_ver": "3",
"attempt": 1
}
这些字段的目的不是把状态表设计得漂亮,而是让恢复程序在没有原进程、没有原内存的情况下,仍然能做出安全决定。
四、先记账还是先执行?两边都有一道缝
一种常见设计是先调用工具,再写检查点。问题是工具成功后、检查点写入前可能崩溃。
另一种设计是先把步骤标记为完成,再调用工具。问题更严重:工具尚未执行,系统却已经认为完成。
更稳妥的方式是“意图日志 + 执行结果”:
- 原子地记录这一步即将执行,并生成稳定幂等键;
- 携带幂等键调用外部工具;
- 收到可验证结果后,原子地记录完成状态和结果引用;
- 恢复时,任何停留在执行中的记录都先进入核对,而不是盲目重试。
即便如此,本地状态库和外部服务之间通常也不存在一个横跨两边的数据库事务。这就是为什么幂等接口、外部查询和补偿动作不可缺少。
五、幂等不是“多执行几次没关系”
幂等的准确含义是:同一个业务意图被重复提交时,系统只产生一次逻辑效果,并返回与第一次语义一致的结果。
最常见的做法,是由调用方生成稳定的幂等键。外部服务第一次收到请求时保存键与结果;再次收到同一个键时,不重复创建资源,而是返回原结果。
幂等键还必须绑定请求摘要。否则同一个键第一次用于“创建 1 台服务器”,第二次却用于“创建 10 台服务器”,服务端不能悄悄把它们当成同一个意图。
AWS Builders’ Library 在“让重试对幂等 API 更安全”一文中讨论的核心就是:重试能降低调用方复杂度,但前提是服务端能识别同一个客户端请求,并处理“同一请求 ID、不同意图”和迟到请求等边界。
对于不能天然幂等的工具,可以采用三种策略:
- 查重:以业务唯一键查询已有结果,再决定是否创建;
- 去重账本:在工具网关持久化幂等键、请求摘要和结果;
- 补偿动作:无法撤销时定义反向业务操作,例如关闭误创建的资源,而不是假装分布式事务能够回滚一切。

这里要避免一个营销化表述:现实系统往往提供的是“至少执行一次的交付 + 幂等业务效果”,而不是凭空获得端到端的绝对“只执行一次”。Microsoft Durable Task 的官方编程模型也明确指出,活动在“已完成但结果尚未记录”时可能再次运行,因此活动应按至少一次执行来设计。
六、大模型调用如何恢复:重算可以,重复决策要受控
模型调用本身通常可以被当作一种非确定性活动。它失败时重新调用并不罕见,但系统必须决定重算的结果是否还能安全使用。
如果模型只在生成一段候选摘要,重新生成一次通常风险不高。如果模型的输出决定了接下来调用什么生产工具,就不能让重试后的新输出悄悄改变已经执行过的路径。
实用规则是:
- 尚未被下游使用的模型输出,可以重算;
- 已经被校验并驱动后续步骤的输出,应作为已接受事件持久化;
- 恢复时复用已经接受的输出,而不是重新问模型;
- 结构化输出先做 Schema 与业务约束校验,再写入检查点;
- 模型版本变化后,不要默认新输出与旧流程兼容。
这样做的本质,是把“模型建议”和“系统已接受的决定”分开。前者可以变化,后者一旦产生外部后果,就必须进入可审计的执行历史。
七、可重放不等于重新执行所有动作
Temporal、Durable Task 等持久化执行框架常用事件历史重建编排器状态。编排代码从头运行时,框架会读取历史:遇到已完成活动就回放旧结果,只有走到历史末尾时才调度新的工作。
所以“从头回放代码”与“从头重做业务”不是一回事。前者是在本地重建确定性状态,后者才会重复消耗 Token、修改文件或创建外部资源。
LangGraph 的官方文档也把持久化、检查点和恢复作为长时 Agent 的基础能力,并提醒把 API 调用放入可检查点的任务中,同时把任务设计为幂等,因为一个已经开始但没有成功完成记录的任务仍可能被重新执行。
一个框架无关的恢复器可以抽象成:
def resume(run_id):
state = load(run_id)
if not compatible(state):
return migrate(state)
for step in plan(state):
if step.done:
reuse(step.result)
elif step.new:
run_once(step)
else:
reconcile(step)
verify(state)
真正困难的不是这几行分支,而是每个 COMPLETED 是否有证据、每个 UNKNOWN 是否有核对接口、每个 execute 是否能安全重试。
八、人工审批也应该是一种持久状态
Agent 遇到发布、删除、付款和生产变更时,经常需要暂停等待人工批准。很多系统用一个阻塞中的 HTTP 请求等待用户点击,连接一断,审批就丢了。
更可靠的设计是把它建模为 WAITING_APPROVAL:
- 保存待审批动作、影响范围、请求摘要和过期时间;
- 给审批请求稳定 ID,拒绝重复消费;
- 审批结果作为事件写入执行历史;
- 超时后进入明确状态,不自动把沉默当成同意;
- 恢复时继续等待同一个审批,而不是重新发起一项看似相同的新操作。
这使得 Agent 可以等待几分钟、几天,甚至跨越部署和机器重启,而不必一直占用一个进程。
九、版本升级会让旧断点变成“危险存档”
假设一个任务在星期一暂停,星期三发布了新版本:工具参数变了,步骤顺序变了,提示词也变了。星期四直接拿旧状态交给新代码恢复,历史中的下一步可能已经没有相同含义。
Microsoft 的 Durable Task 文档强调,编排器依赖确定性重放,代码变更可能破坏正在运行的实例,因此需要版本隔离、兼容执行或显式迁移。
Agent 系统同样需要:
- 为运行实例固定 Agent、提示词、工具协议和策略版本;
- 保留能继续处理旧实例的兼容 Worker;
- 变更状态结构时提供显式迁移;
- 遇到不兼容时停止并交给人工,而不是“尽量猜”;
- 对模型升级进行恢复回归测试,而不仅测试新任务。

断点不是一个孤立的 JSON 文件,它是某个执行语义下的状态。没有版本,恢复就可能从“省时间”变成“用新规则继续一项旧操作”。
十、怎样证明断点续跑真的有效
正常路径跑通,只能证明任务能运行,不能证明它能恢复。恢复能力必须通过故障注入验证。
至少覆盖以下场景:
- 工具调用前杀掉 Worker,恢复后应执行一次;
- 外部操作成功但响应被丢弃,恢复后不得产生重复效果;
- 检查点写入后立即崩溃,恢复后应复用旧结果;
- 模型输出完成但尚未被接受,验证重算不会越过安全边界;
- 等待审批时重启服务,审批记录仍然有效;
- 更新 Agent 版本后恢复旧任务,兼容则继续,不兼容则明确停止;
- 工作区被外部修改后恢复,系统能发现环境哈希不一致;
- 连续多次崩溃后,重试预算与费用预算不会被重置。
线上还应监控这些指标:恢复成功率、检查点距当前进度的时间差、UNKNOWN 步骤数量、重复副作用拦截次数、补偿动作次数、卡住的运行数量、审批等待时长、版本不兼容数量,以及恢复额外消耗的 Token 和时间。
如果一个系统只展示“任务失败率”,却不知道失败后重复创建了多少资源,它的可恢复性仍然不可验证。
十一、不是所有 Agent 都需要上持久化工作流
持久化执行会增加状态存储、序列化、版本治理、幂等设计和运维成本。一个只读、几十秒完成、失败后重跑很便宜的任务,直接重试往往更简单。
出现以下任一条件时,断点续跑的价值会迅速上升:
- 单次任务运行时间长、模型成本高;
- 会修改文件、数据库或外部系统;
- 需要等待人工审批或外部事件;
- 包含并行步骤、长轮询或多 Agent 协作;
- 失败后无法安全判断是否已经执行;
- 业务要求审计、追责和人工接管。
可以用一句话做取舍:重跑成本低且没有副作用,就接受重跑;重跑昂贵或可能重复产生业务效果,就投资持久化执行。
结语:恢复能力不是“记住更多”,而是“留下足够证据”
AI Agent 跑了半小时后崩溃,是否只能从头再来,取决于系统之前留下了什么。
只有聊天记录,它最多能让模型回忆上下文;有检查点、事件历史、制品快照、幂等键、版本信息和核对接口,它才可能安全地从上一个已确认步骤继续。
最终要建立的不是一个永不失败的 Agent,而是一条失败后仍然可判断、可恢复、可审计的执行链路:
已完成的,不重复;
未开始的,正常做;
结果未知的,先核对;
版本不兼容的,停下来;
产生副作用的,必须可幂等或可补偿。
这才是“断点续跑”的工程含义。
参考资料
- Temporal 官方文档:Durable Execution 与 Workflow Replay
https://docs.temporal.io/ - Temporal 官方 Retry Policies 文档
https://docs.temporal.io/encyclopedia/retry-policies - LangGraph 官方 Functional API 文档:Persistence、Determinism 与 Idempotency
https://docs.langchain.com/oss/python/langgraph/functional-api - AWS Builders’ Library:Making retries safe with idempotent APIs
https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/ - Microsoft Learn:Durable Task Programming Model
https://learn.microsoft.com/en-us/azure/durable-task/common/programming-model-overview - Microsoft Learn:Durable orchestrator code constraints
https://learn.microsoft.com/en-us/azure/durable-task/common/durable-task-code-constraints - Microsoft Learn:Orchestration Versioning
https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-orchestration-versioning