多智能体编排模式:队列、状态与交接
可靠的多智能体系统靠的不是巧妙的提示词,而是枯燥的分布式系统纪律:智能体之间用持久队列、状态保存在模型之外、交接做到幂等以扛住重试。模型是工人,队列才是主干。
每周三。28,400+ 读者。纯干货。
✓ 请查收邮箱 — 点击确认链接以完成订阅。
✓ 订阅成功!
✓ 您已在订阅列表中。
目录
2026 年 6 月更新。
TL;DR: 可靠的多智能体系统不是靠巧妙的提示词赢来的,而是靠枯燥的分布式系统纪律赢来的。在智能体之间放一个持久队列,把状态保存在模型之外,并让每一次交接都幂等,这样重试就不会重复执行。模型是工人,队列才是主干。把这三点做对,编排就不再可怕。
操作者视角: 我那 100 多个智能体里,大多数都是单步的。那些不是单步的——先分类、再充实、再行动的流水线——只有当我不再把它想成”提示词链”、而开始把它想成”带 LLM 工人的任务队列”时,才变得可靠。这是架构,不是提示词工程。
“多智能体”听起来像是智能体彼此对话。实际上,可靠的版本恰恰相反:智能体根本不直接通信。它们把消息丢到队列上,再从队列里取活,编排就藏在它们之间的管道里。下面是那些能在生产环境中扛住的模式。
模式 1:在每个智能体之间放一个持久队列
第一反应是从智能体 A 内部直接调用智能体 B。别这么做。直接调用把两者耦合在一起:B 慢,A 就阻塞;B 失败,A 的工作就丢了;想扩展 B,不动 A 就办不到。
正确的做法是,A 完成自己的工作,然后为 B 入队一条消息。B 是一个独立的工人,按自己的节奏排空队列。
// 智能体 A 完成后通过队列交接——不直接调用 B
await env.ENRICH_QUEUE.send({
traceId,
type: "enrich",
payload: classifierResult,
});
// A 的任务已完成。B 会独立地取走它。在 Cloudflare 上,我正是用 Workers Queues 来做这件事——和我使用的智能体技术栈背后是同样的原语。队列免费给你四样东西:缓冲(B 宕机也不丢工作)、重试(失败的消息会重新投递)、背压(峰值会排队而不是崩溃)和解耦(扩展或重新部署 B 都不必动 A)。这其中每一项,否则你都得自己手搓并搞砸。
模式 2:始终把状态保存在模型之外
最常见的多智能体 bug,是假设模型在各步骤之间记得什么。它不记得。每次模型调用都是无状态的,唯一的记忆就是你放进提示词里的东西。所以”这个任务在流水线里走到哪儿了”的事实来源,必须存在数据库里,而不是对话里。
我维护一条单一的任务记录,每个智能体都读取并更新它:
interface JobState {
traceId: string;
stage: "classified" | "enriched" | "acted" | "done" | "failed";
data: Record<string, unknown>;
attempts: number;
updatedAt: number;
}每个智能体都跑同一个循环:读取任务状态、干自己的活、写入新状态、把下一阶段入队。模型从不持有状态——它把相关切片作为输入接收,并返回一个结果。这正是让系统可重启的关键:如果某个工人在任务中途挂掉,状态记录仍然准确地记着事情走到了哪儿,重新投递的队列消息会从那里接手。它也让调试变得可控,因为状态表是每个任务旅程的可查询记录——这和我如何衡量一个智能体是否真的在工作里同样的度量心态。
模式 3:让每一次交接都幂等
队列保证的是至少一次投递,而非恰好一次。这意味着一条消息可能被投递两次——网络抖动、重试、重新部署。如果你智能体的动作不是幂等的,双重投递就会重复执行:两封确认邮件、两份预订、两笔扣款。这是最棘手的一类编排 bug,也是团队在生产环境才发现的那一类。
修复办法是用一个键让动作幂等:
async function handleEnrich(msg: QueueMessage, env: Env) {
const job = await getJob(env, msg.traceId);
if (job.stage !== "classified") {
// 已经处理过这一阶段——这是重复投递。跳过。
return;
}
const result = await enrich(job.data);
await advanceJob(env, msg.traceId, "enriched", result);
await env.ACT_QUEUE.send({ traceId: msg.traceId, type: "act" });
}阶段检查让操作可以安全地运行两次:第二次投递发现任务已经推进,就什么都不做。对于外部副作用(发邮件、扣信用卡),把一个幂等键传给下游 API,让它也去重。假设每条消息都会被投递两次,并把设计做到这无害——因为它迟早会发生。
模式 4:编排者 vs 编舞——刻意做选择
接线流程有两种方式,正确的选择取决于复杂度。
编舞(我默认采用的):每个智能体只知道下一步并把它入队。流程从链条中涌现。简单、去中心化、易于扩展——插入一个队列就能加一个阶段。缺点是没有一个单独的地方描述整个流程,所以复杂的流水线可能变得难以推理。
编排(一个中央协调者):一个编排者掌握流程,依次调用每个智能体,并根据结果决定下一步。整个流程都住在一个可读的地方,分支逻辑是显式的。代价是一个本身必须持久的中央组件——如果编排者自己的状态没有外置(模式 2),它就成了单点故障。
我的规则:在分支变复杂之前用编舞,之后用持久的编排者。 一条线性的三阶段流水线是编舞。一个带条件路由、并行扇出和汇合的流程,需要一个状态存在数据库里、以便崩溃后能恢复的编排者。
模式 5:扇出、扇入而不丢失任何一块
当一个任务衍生出 N 个并行子任务(充实 50 条记录、总结 20 份文档),而你需要等它们全部完成才能继续时,你就需要一次汇合(join)。诀窍是任务状态里的一个计数器:
- 父任务入队 N 条子消息,并在任务记录里写入
expected: N, completed: 0。 - 每个子任务干完自己的活,并原子地递增
completed。 - 把
completed推到等于expected的那个子任务,把下一阶段入队。
这个原子递增是承重的——没有它,两个同时完成的子任务可能都以为自己不是最后一个,于是汇合永远不触发。用一个数据存储能原子递增的计数器,或者用一个事务。这个模式让你能把流水线中昂贵的中段并行化(往往是 Haiku 能廉价处理的活——见 Haiku 与 Sonnet 的成本算账),同时在末尾保持一次干净的汇合。
我会略过的东西
做这一切,你都不需要一个重量级的智能体框架。队列、一张状态表和幂等键,都是每个平台早已具备的原语。我见过有团队为了拿到队列免费就给的功能,去搬来精心设计的多智能体框架,结果继承了一个比它所替换的管道更难调试的黑盒。从枯燥的原语开始。只有当你真切感受到某个框架能解决的具体痛点时,才去伸手拿它。
总结:智能体是无状态的工人,队列是持久的主干,状态住在数据库里,每一次交接都能安全地运行两次。这就是全部的游戏。
常见问题
智能体应该彼此直接调用,还是走队列?
走队列。直接调用会耦合智能体——一方的失败或缓慢会传播到另一方,而且你无法独立扩展或重新部署。持久队列免费给你缓冲、重试、背压和解耦。
多智能体状态应该存在哪里?
存在模型之外、数据库里,作为一条每个智能体都读取并更新的任务记录。模型调用是无状态的,所以流水线进度的事实来源必须是外部的——这正是让系统在崩溃后可重启的关键。
我如何防止一个智能体在同一个任务上执行两次?
让交接幂等。行动前先检查任务的阶段,若已推进就什么都不做,并把幂等键传给外部 API。队列至少投递一次,所以假设每条消息都可能到达两次,并把设计做到重复无害。
我需要一个多智能体框架吗?
通常不需要。持久队列、一张状态表和幂等键,用你平台早已提供的原语就能覆盖大多数生产需求。只有当你撞上某个框架能独到解决的具体问题时才采用它,而不是默认就用。
每周三。28,400+ 读者。纯干货。
✓ 请查收邮箱 — 点击确认链接以完成订阅。
✓ 订阅成功!
✓ 您已在订阅列表中。
将AI实战手册发送到您的邮箱
每周三。28,400+ 读者。纯干货。
请查收邮箱。
我们已向您发送确认邮件 — 点击其中的链接以完成订阅。如果一分钟内没收到,请检查垃圾邮件。
订阅成功。
欢迎 — 下一期很快就会送达您的邮箱。
您已在订阅列表中 — 每周三留意查收。