前面的文章已经解决了 Multi-Agent 中的控制问题:任务怎样拆分、Agent 之间传什么、下一步由谁执行,以及运行过程中如何管理 Context、State 和 Memory。
然而,流程设计正确,不代表任务一定能够执行完成。
一个任务可能运行几十分钟,调用几十次模型和外部工具。期间任何一次网络超时、API 限流、进程重启等,都可能让执行中断。
本文我们主要讨论:怎样让已经确定的流程可靠地执行下去,并在发生故障后继续完成。
这也是 Multi-Agent 从原型进入生产环境后须补上的一层防护机制:Execution Runtime,执行运行时。
一、流程控制与可靠执行
我们首先区分两个容易混在一起的问题:流程控制与可靠执行。
假设一个研究任务需要三个 Agent:
Planner | ├── Agent A:研究市场 ├── Agent B:研究竞品 └── Agent C:研究技术 | ▼ SynthesizerPlanner 决定创建哪几个 Agent,以及完成之后进入哪个步骤,这属于控制流。
但真正运行时,还会遇到另一类问题:
Agent A ✓ Agent B ✓ Agent C ───── X timeout此时系统已经知道下一步应该做什么,问题在于 C 没有执行完成。
如果程序直接退出,再重新启动整个任务,那么 A、B 已经完成的搜索、模型调用和计算都会被重新执行。
对于几秒钟的流程,这可能还能接受。
对于运行十几分钟甚至更久的 Agent,这种方式则不能接受。
Anthropic 在其 Multi-Agent Research 系统的生产经验中提到,Agent 会长期运行并持续维护状态,错误也会随着执行步骤累积。因此,他们会结合 checkpoint、retry 等机制,让失败后的任务从现有进度继续,而不是每次从头开始。
所以流程控制与可靠执行的职责分别是:
Control Plane 决定:接下来做什么 Execution Runtime 保证:这件事最终能够执行完成接下来要介绍的 Checkpoint、Retry、Idempotency,都围绕执行运行时(Execution Runtime),确保 Agent 可靠地执行完成。
二、Checkpoint 保存执行进度
实现故障恢复的第一步,是保存执行进度。
这种保存点通常称为 Checkpoint。
可以把一次长任务理解为下面这条时间线:
t0 t1 t2 t3 start checkpoint checkpoint crash | └──────────→ resume如果系统只在t0保存输入,那么t3崩溃以后,只能重新开始。
如果t1、t2都保存了执行状态,那么系统可以找到最近一次有效状态,从那里继续。
例如:
Checkpoint #12 task_id: research-001 A: status: completed artifact: artifact-a.json B: status: completed artifact: artifact-b.json C: status: running恢复以后就不需要重新执行 A 和 B。
运行时只需要知道:
A completed → 跳过 B completed → 跳过 C unfinished → 继续执行这就是持久化执行(Durable Execution)的核心思想之一:执行过程中的关键状态不能只存在于当前进程内存中。
LangGraph 的 persistence 机制采用 checkpointer 保存 Graph State。执行过程中可以通过thread_id关联到同一条运行线程,之后继续读取已经保存的状态。它的 checkpoint 通常发生在 Graph 的节点边界,因此节点划分也会直接影响恢复粒度。
例如把任务设计成:
search ↓ analyze ↓ write如果analyze失败,通常只需要重新执行当前节点。
但如果把三项操作全部塞进一个节点:
search + analyze + write即使最后一步失败,也可能需要重新执行整个节点。
因此,节点粒度除了影响代码结构,也会影响故障恢复成本。
不过 checkpoint 也不能无限细。
每一个微小步骤都写一次数据库,会增加存储和协调开销。更实用的做法是优先保存那些:
成本较高的模型调用结果;
已经完成的子任务;
外部系统返回的重要 ID;
后续步骤无法轻易重新计算的数据;
人工确认后的决策。
Checkpoint 的目标不是记录所有变量,而是确保发生故障以后,已有成果不会轻易丢失。
三、Retry 处理临时故障
有了 checkpoint,还需要处理另一类更常见的问题:临时性的失败。
例如:
HTTP 429 HTTP 502 connection reset request timeout database connection unavailable这些错误有一个共同特点:
稍后再执行一次,有可能成功。
因此系统通常会进行 Retry,也就是重试。
最简单的策略是:
失败 ↓ 等待 ↓ 重试但生产环境里的 Retry 通常还需要三个限制。
1. 最大重试次数
例如:
max_attempts = 3如果已经连续失败三次,就不要无限尝试。
否则一个下游服务故障,可能让几百个 Agent 不断发送请求,进一步放大故障。
2. Backoff
Backoff 指每次失败后逐渐增加等待时间。
例如:
第 1 次失败 → 等 1 秒 第 2 次失败 → 等 2 秒 第 3 次失败 → 等 4 秒这就是常见的 Exponential Backoff,指数退避。
它可以避免大量失败任务同时立即重试。
3. Retry Budget
系统还可以给整个任务设置重试预算。
例如:
整个任务最多允许: 20 次 Tool Retry 3 次 Agent Retry 10 分钟恢复时间这样能够防止一个异常任务无限消耗 token、API 配额和计算资源。
LangGraph 的官方示例也把错误分成不同类型:网络错误、限流等临时问题适合自动 Retry;需要用户补充信息的问题可以暂停;未知错误则应该暴露出来供调试。
因此,一个重要原则是:Retry 只适合有较大概率自行恢复的错误。
相反,类似参数本身错误:
HTTP 400 invalid schema unsupported tool permission denied连续执行十次通常也不会改变结果,这一类错误不应使用 Retry 策略。
因此,可靠系统需要先判断失败类型,再决定是否重试。
四、幂等避免重复执行
Retry 看起来很简单,但执行时可能会隐含着问题。
假设 Agent C 的任务是:
创建一个客服 Ticket第一次调用外部系统:
Agent C | ├── create_ticket() | Ticket System | Ticket #1001 创建成功 | X response timeout这里出现了一个不正常的状态。
Agent 看到的是:
timeout于是 Runtime 判断任务失败,并再次执行:
create_ticket()结果可能变成:
Ticket #1001 Ticket #1002事实上第一次已经成功,只是响应没有成功返回。
因此:
能够 Retry,并不代表能够安全 Retry。
这里就要引入 Idempotency,幂等性。
幂等要求同一个逻辑操作执行多次,最终结果仍然与执行一次相同。
例如查询:
GET /customer/123重复执行多次结果都完全一样。
但下面这些操作多次执行可能存在幂等问题:
创建订单 发送邮件 写数据库 扣款 提交工单 发布内容一旦自动 Retry,就必须考虑重复执行。
常见做法是给一次逻辑操作生成唯一的 operation_id:
operation_id = "research-001-create-ticket-C"第一次执行:
create_ticket( operation_id="research-001-create-ticket-C" )第二次 Retry 仍然使用相同 ID。
外部系统如果发现这个 operation 已经成功处理,就直接返回第一次的结果:
operation already completed ticket_id = 1001这样可以避免重复创建。
如果目标系统本身不支持幂等键,还可以在自己的数据库中保存:
operation_id status external_resource_id然后在执行前进行去重检查。
对于无法做到严格幂等的操作,还可以使用 Compensation,补偿操作。
例如:
创建资源 ↓ 后续失败 ↓ 删除刚才创建的资源这种思路在分布式系统中非常常见。
因此真正可靠的 Retry 通常要和三件事一起设计:
Retry + Idempotency + Compensation五、同步执行与异步执行
Multi-Agent 系统还会遇到一个运行方式上的选择:同步还是异步。
假设三个 Agent 并行执行:
A ───────── 15s B ───────────────── 30s C ───────────────────────── 60s同步模式下,Coordinator 通常会等待:
await A await B await C只有全部完成之后才进入下一步。
这种方式的优势非常明显:
状态简单 结果容易聚合 错误传播容易理解因此很多系统最开始都会采用同步执行。
Anthropic 当前公开的 Multi-Agent Research 架构也提到,Lead Agent 会等待一批 Subagent 完成后再继续。这降低了协调复杂度,但一个执行很慢的 Subagent 也可能阻塞整个过程。
异步模式则允许结果独立到达:
A finished ↓ 立即处理 A B finished ↓ 立即处理 B C 继续运行甚至 Coordinator 可以在 A 返回以后,又创建新的 Agent D:
A result | └── spawn D B result C running D running吞吐量和并行能力会明显提高。
但 Runtime 也必须处理更多问题:
哪个结果已经到达? 哪个任务还在执行? 某个 Agent 失败是否影响其他 Agent? 结果到达顺序改变怎么办? Coordinator 什么时候可以继续?这些都属于状态协调问题。
在实践中,原则上:任务规模较小时优先同步;当等待时间和并行度已经成为明显瓶颈,再引入异步执行。
异步可以提高性能,但它同时会增加恢复、状态一致性和错误传播的设计成本。
六、一次完整的故障恢复
现在把前面的机制放进同一个例子。
一个任务同时启动三个 Agent:
A ───────────── completed B ─────────────────── completed C ───────────── X timeoutA 和 B 已经产生 Artifact。
系统此时存在一个 checkpoint:
Checkpoint #18 A: status: completed artifact: artifact-a B: status: completed artifact: artifact-b C: status: runningC 调用了外部 Ticket API:
operation_id: job-123-agent-c-ticket外部系统实际上已经创建:
Ticket #8421但响应返回过程中发生网络超时。
Runtime 捕获异常以后,首先判断:
Timeout → transient error → 可以 Retry随后读取 checkpoint:
A completed → 不执行 B completed → 不执行 C failed → retryC 再次调用:
create_ticket( operation_id="job-123-agent-c-ticket" )外部系统发现相同 operation 已经处理:
return Ticket #8421C 得到结果:
C completed artifact-c saved随后写入新的 checkpoint:
A ✓ B ✓ C ✓Coordinator 再进入后续汇总阶段。
整个过程可以表示为:
Checkpoint | ├── A artifact ✓ ├── B artifact ✓ └── C pending | retry | idempotency check | resume | C artifact ✓ | next stage从这个例子可以看到,真正的故障恢复并不是简单地“失败以后再调用一次”。
它至少涉及:
Checkpoint 知道已经完成了什么 Retry 重新执行可恢复的失败 Idempotency 避免重复执行而产生错误结果。 Resume 从已有状态继续流程缺少其中任何一个环节,都可能让恢复过程重新产生新的问题。
总结
很多 Multi-Agent Demo 的重点放在 Agent 怎么调用、Prompt 怎么写、Supervisor 怎样决定下一步。
这些内容解决的是“系统如何思考和调度”。
进入真实业务以后,必须要解决另一类更基础的问题:
任务跑到一半会不会丢? API 超时以后怎么办? 已经完成的步骤会不会重新执行? 重试会不会创建两张订单? 系统升级会不会破坏正在运行的任务?这些问题已属于典型的分布式系统工程范围。
Temporal 这类 durable workflow 系统的核心价值,也正是在进程崩溃、网络故障或基础设施中断以后,仍然能够利用持久化的执行历史恢复工作流。
而在 Agent Runtime 中,同样可以借鉴这些成熟思想。
最终,一个可靠的 Multi-Agent 执行层可以概括为:
State 持久化 + Checkpoint + Retry Policy + Idempotency + Timeout + Observability流程设计告诉系统下一步应该运行谁。
可靠执行则保证:在模型失败、工具超时、进程重启和系统升级都可能发生的环境里,已经确定的工作仍然能够继续向前推进,并最终得到一个可确认的结果。
这也是 Multi-Agent 从“能够运行”走向“能够长期运行”必须补上的工程基础。