多智能体这东西,不少团队都在折腾,但很多人做出来的其实是"链式调用"而不是"多智能体协作"。什么意思呢?就是一个 Agent 输出完,丢给下一个 Agent,全程只有一个 Agent 在工作,其他都在干等。这个模式不是不对,但当任务拆解得越来越细、子任务越来越多之后,排队就成了瓶颈——明明配了八个 Agent,七个在等活儿,这还叫并行吗?
OpenClaw 的做法是把"调度"和"执行"彻底拆开,用一个中心协调器负责任务分解、状态跟踪和结果汇总,让一批 Worker 真正同时开跑。这篇文章不绕弯子,直接讲架构和实操:从并行协作的核心设计思路,到四层架构拆解,再到最小可用环境的配置、常见问题的排查,全程带配置和踩坑记录。适合已经跑过 Agent 基础应用、想往多 Agent 协作方向走的开发者,也适合被"并行"这个词吸引进来、结果发现自己的 Agent 根本没有真正并行起来的同学。
1. 多智能体并行协作,解决的是"排队"而不是"对话"
1.1 串行链和并行协:一个是单线程,一个是流水线
用一个厨房的比喻。传统链路式 Multi-Agent 像是一个厨师同时管三口锅:切菜、炒菜、装盘都是他一个人,只不过他每次换一把刀、换一个锅。你确实定义了三个 Agent,A 负责切,B 负责炒,C 负责装盘,但调用方式是 A 跑完 B 才开始,C 只能站在旁边等。这个模式不叫流水线,这叫一个人多线程。
OpenClaw 的思路更像把后厨真正拆开:切菜组、炒菜组、装盘组同时开工,每个组只等自己需要的食材和工序。这就是并行协作和串行链最本质的区别——不是单个任务变快了,而是整个流程的等待时间被压缩了。系统里的 Agent 不再共享一个"当前执行步骤",而是各自从任务队列里消费独立的子任务,干完上报,再领下一个。
这个区别落到实际业务里非常直观。比如做一份竞品分析报告:串行模式下,先让调研 Agent 搜集资料,然后分析 Agent 读资料,再让写作 Agent 动笔,最后排版 Agent 调整格式。整体耗时就是各个环节的累加,最短也得十几分钟。并行协作模式下,调研这个环节就能拆成三个独立 Worker:一个查官网产品页,一个查社区用户反馈,一个查招聘信息推断业务方向,三个同时跑,再汇总结果。整体耗时可能压到原来的三分之一,而且每个环节的质量还可以独立校验。
1.2 要并行,先回答三个问题
并行不是你在 prompt 里写一句"你们同时跑吧"就能跑的。真的落地时,必须面对三个现实问题。
第一个,任务怎么拆。一个总目标要拆成多大粒度的子任务?拆得太粗,几个 Agent 干的活重叠,并行度上不去;拆得太细,协调通信的开销反而超过收益。合理的粒度是:每个子任务有明确的输入边界、独立的执行链路、可验证的输出结果。
第二个,结果怎么合。每个子任务的输出格式、更新时机、冲突处理方式都得提前定义好。否则所有 Agent 跑完了,汇总阶段比执行阶段还折腾,各种格式不统一、字段对不上、内容互相覆盖。并行架构里,聚合逻辑必须前置设计,不能等结果出来了再去适配。
第三个,资源怎么分。大模型 API 有并发限制,单个跑还好,一旦并行,限流、超时、上下文窗口溢出全都会冒出来。这也是 OpenClaw 这类面向并行协作的框架和普通 Agent 框架最大的不同:它把任务分解、结果聚合、并发控制作为一等公民内置在设计里,而不是靠一句"你要记得等别人完成再继续哦"来约束 Agent。
1.3 不是所有场景都适合并行协作
这里先给一个反直觉的结论:如果你的总目标严重依赖前置结果,比如写代码必须先完整读完需求文档,那并行协作不会让它变快,反而会引入多余的同步成本。
适合并行的场景通常有两个特征:一是子任务之间依赖弱,或者可以分阶段汇总;二是每个子任务需要独立的工具链或知识源。常见例子:
- 市场调研:多个渠道同时抓取,互不影响,最后汇总。
- 代码审查:多个模块分别读代码、提意见,汇总成审查报告。
- 长文创作:把文章拆成不同章节并行写作,预先指定风格规范和衔接要求。
- 数据处理:多个数据源同时清洗、提取特征,最后合并。
不适合的场景也有:强依赖链路、需要逐步逼近单一答案的推理、对全局上下文要求极高的写作。这类任务强行并行,最后拼出来的内容往往"散"成一盘,还不如老老实实串行,或者只做 2-3 个环节的少量并行。
2. OpenClaw 架构拆解:一个协调器插在 Agent 之间
2.1 四层架构到底长什么样
OpenClaw 的整体架构可以抽象成四层:调度层、执行层、通信层、记忆层。第一次接触会觉得层级多,其实核心就一句话——Agent 之间不直接说话,所有消息都通过协调器和消息总线流转。
这是刻意为之的设计。直接点对点通信,三个 Agent 还好,十个 Agent 就会退化成全连通网络,谁的消息发给谁、谁在等谁,没人说得清。而且点对点会导致 Agent 之间耦合极高,改一个 Agent 的接口,其他所有 Agent 都得跟着改。集中式调度就是把这个混乱收拢到一个点,让协作变得可预测、可观测、可控制。
下面的表格概括了四层各管什么:
| 层级 | 核心组件 | 职责 |
|---|---|---|
| 调度层 | 协调器 + 任务队列 | 分解总目标、分配子任务、跟踪任务状态 |
| 执行层 | Worker 池 + 角色定义 | 真正的 Agent 实例,消费任务、执行工具调用 |
| 通信层 | 消息总线 + 事件机制 | 异步传递消息、发布任务事件、解耦上下游 |
| 记忆层 | 共享状态存储 | 保存全局上下文、验证结论、任务产物 |
2.2 调度层:任务状态机是全部核心
调度层是大脑。它维护一个任务队列,接收上层传来的总目标,把目标拆成若干子任务,再根据当前 Worker 的空闲状态进行分配。调度层还负责记录每个子任务的全生命周期状态:pending(待执行)、running(执行中)、done(已完成)、failed(失败)。一旦某个任务失败,调度器根据重试策略决定是重新入队还是直接标记失败。
这里的关键设计是"集中式调度 + 分布式执行"。集中式调度保证了全局状态可控,不会出现两个 Worker 抢同一个任务的情况;分布式执行则保证了实际干活时可以水平扩展。类似操作系统里的进程调度器——它本身不执行运算,只决定谁在什么时候用多少资源。
任务状态机是排查问题的第一抓手。我每次排查卡死问题,第一件事就是拉出所有任务的状态分布:如果大量任务停留在 running 且长时间不推进,要么是 Worker 崩了,要么是某个任务触发了死锁等待,要么是外部 API 挂起。没有状态机,这些问题全都得靠猜。
2.3 执行层:Worker 池和角色定义
执行层是真正干活的 Worker 池。每个 Worker 对应一个 Agent 实例,有自己的 system prompt、工具集、角色设定。OpenClaw 在 Worker 设计上比较灵活,支持一个 Agent 绑定一个独立的模型实例,也支持多个 Worker 共享同一个模型后端,只靠不同的 prompt 和工具绑定来区分身份。
所有 Worker 只和调度器通信,通过事件上报状态,通过调度器领取任务。这样职责边界非常干净:我手上有一个任务,我做完了,上报结果,然后领下一个。你不需要知道谁消费了你的输出,调度器会在合适的时机通知你需要等待什么。
我整理了一个最小角色配置示例,可以直接参考:
workers: - name: analyst role: 情报分析 model: fast-model tools: - web_search - fetch_url channels: - intelligence_queue concurrency: 2 max_retries: 3 - name: summarizer role: 摘要聚合 model: strong-model tools: - file_write channels: - report_queue concurrency: 1注意这里analyst的并发度是 2,summarizer只有 1。为什么这么配?因为分析任务耗时长、需要频繁调用外部接口,并发 2 个实例可以掩盖网络等待;而摘要聚合逻辑复杂、模型成本高,串行执行更稳、更可控。这个配置背后是一条重要经验:并发度不是越高越好,要根据单个任务的耗时和资源成本来设置。
2.4 通信层:事件驱动的消息总线
通信层是一条消息总线,所有消息以事件驱动的方式发布和订阅。比如 analyst 完成任务后,向总线发布一个task.completed事件,事件内容包含任务 ID、Agent ID、结构化输出、时间戳等。调度器订阅这个事件,把对应任务标记为完成,再检查是否有后续任务因为这个结果而解锁。
事件驱动带来三个非常实际的好处。异步是第一位:生产者发布消息后不用等消费者处理完,吞吐量提升非常明显。解耦是第二位:新 Worker 上线不需要修改现有 Worker 的代码,只要它订阅对应的事件就行。可观测是第三位:所有事件都有记录,可以完整回放整个协作过程,排查问题效率高出几个量级。
通信消息的格式需要规范。我一般建议配合框架内置的消息结构使用,一个典型的任务完成事件长这样:
{ "message_id": "msg_8f2a91", "task_id": "task_03", "from": "analyst-1", "to": "scheduler", "type": "task.completed", "payload": { "output": "欧洲市场的主要竞品是...", "elapsed_ms": 48213, "confidence": 0.87 }, "timestamp": 1730000000000 }type字段尤其重要,它决定消息进入哪条处理逻辑。我见过因为手误把task.completed写成task_complete,导致调度器一直匹配不上,整个协作流程卡在最后一步,排查了大半天。这类细节在并行系统里会被放大很多倍。
2.5 记忆层:多 Agent 怎么共享上下文
记忆层解决的是"多个 Agent 怎么共享上下文"这个经典难题。最粗暴的方案是把所有上下文塞进每个 Agent 的 prompt,但并行场景下很快就会把上下文窗口撑爆——三个 Agent 各带两万字,汇总的时候还要全部再读一遍,成本直接翻几倍。
更合理的设计是分层记忆。全局记忆保存总目标、约束条件、已验证结论,放在共享存储里,所有 Agent 按需只读;局部记忆是单个 Worker 的执行历史和中间推理,留在自己的上下文里;汇总记忆是任务完成的产物,在任务结束后写入全局记忆,供下游 Agent 消费。
OpenClaw 采用共享状态存储,用键值方式保存全局记忆,Agent 按需拉取,而不是全量注入。实际项目里,记忆层最容易出的问题不是"不够用",而是"太脏"——多个 Worker 同时写同一个 key,后写的把先写的覆盖了,数据直接丢失。这个并发写冲突的问题,在第四部分我会专门讲怎么处理。
3. 实操过程:搭一个能跑的并行协作环境
3.1 初始化项目骨架
以常见的部署方式为例,启动步骤一般是:
# 安装框架依赖 pip install openclaw # 初始化项目骨架 openclaw init demo-project cd demo-project # 检查环境配置 openclaw doctor初始化完成后,目录里会生成三个核心区域:clawfile.yaml是全局配置,roles/存放角色定义,workflows/存放任务流程定义。第一次运行之前,强烈建议先执行openclaw doctor。它会自动检查模型 API 密钥是否配置、网络连通性是否正常、并行 Worker 数是否超过了账号限流上限。这一步能提前暴露大多数环境问题,省得后面调试业务逻辑时还要分心处理这些基础项。
3.2 定义工作流:拆解、聚合、调用
用一个实际的调研报告场景来演示。目标是生成一份覆盖欧洲、亚洲、美洲三个市场的竞品分析简报。并行思路是:先把三个市场拆成三个独立子任务,分别投递给 analyst Worker 消费,等全部完成后,再触发 summarizer 进行汇总。
对应的 workflow 配置大致如下:
workflows: research_report: type: parallel_split source: target_brief split_rule: key: markets by: europe, asia, america gather: type: wait_all timeout_sec: 300 on_timeout: partial then: invoke: summarizersplit_rule的意思是按markets字段拆成三个子任务;gather配置了wait_all,等待所有子任务完成,超时 300 秒后接受部分结果;然后触发 summarizer 汇总。这样配置之后,三个市场的调研 Agent 就能真正并行跑,汇总 Agent 会在它们全部结束后被调用。
值得留意的是on_timeout: partial这个参数。它表示即使某些子任务超时未完成,系统也接受已完成的部分结果继续推进。这种配置在真实场景里很实用——总不能让一个市场的数据源挂掉,就把整个报告卡死两小时。
3.3 验证并行是否真的发生
搭建完成后的第一件事,是验证并行是否真的生效。很多配置看起来并行,实际因为模型客户端是单例的,请求被底层锁串行化了。这不是框架的问题,是集成时的常见错误。
验证方法很简单:看时间线。执行openclaw logs --follow观察任务开始时间,如果三个子任务的start_time几乎相同,说明并行生效;如果第二个任务的开始时间比第一个晚了十几秒甚至几十秒,说明执行层没有真正并发。
我踩过这类坑:模型 API 的 SDK 客户端默认带线程安全的锁,导致多线程调用变成串行。解决办法是给每个 Worker 创建独立的模型客户端连接池,而不是共享同一个 client 对象。另一个细节是记得配置重试和超时——并行最怕某个子任务挂起后,整个 gather 阶段一直空等。timeout_sec和max_retries这两个参数看着不起眼,在长任务里就是救命稻草。
3.4 结果聚合:先校验再合并
子任务结果回来后,聚合不是简单地把字符串拼在一起。比较稳妥的做法是定义一个输出 Schema,要求每个 Worker 按统一结构返回结果:
output_schema: market: string key_findings: list[string] risk_flags: list[string] data_confidence: float这样聚合逻辑就可以写得很机械:读结果、校验字段、按规则合并。如果某个子任务的data_confidence低于阈值,在汇总阶段就能打回重跑,而不是等最终报告写完了才发现数据质量不行。
这里有一个特别容易忽略的细节:并行任务的结果返回顺序不保证和输入顺序一致。如果下游逻辑强依赖顺序,聚合时一定要按子任务的 key 重新排序,不要依赖返回顺序。我见过有人用数组索引直接拼接结果,三个市场的报告章节互相串位,交出去才发现,返工成本很高。
4. 核心机制细节:并发控制、消息可靠性与状态一致
4.1 并发控制:令牌桶与流量整形
在并行架构里,并发控制不是为了制造排队,而是为了保护下游资源和维持系统稳定。OpenClaw 使用令牌桶机制对模型调用限流:系统维护一个令牌池,每个 API 请求需要申请一个令牌,用完后归还。令牌补充速率可以根据账号并发限制来配置。
这样做的逻辑很直接:多 Agent 并行时如果没有限流,所有 Worker 同时打模型 API,很容易触发限流错误。一旦触发,重试风暴又会加剧问题;而令牌桶相当于在应用层加了一个缓冲,把突发的请求整形成平稳的流量。
并发度设置的经验值:刚开始别贪多。先把并发度调到 2-3,观察 API 延迟和错误率,再逐步调大。如果发现某个 Agent 频繁超时,往往不是模型的问题,而是令牌补充速率太低,队列里积压了大量请求。判断瓶颈在哪个环节,要看任务耗时曲线:如果等待时间占比越来越高,先检查限流配置。
4.2 消息可靠性:ACK、重试与幂等
分布式系统里有一条定律:网络不可靠,消息可能丢,进程可能崩溃。多智能体协作也不例外。OpenClaw 的消息总线采用 ACK 机制:Worker 消费任务后必须回执确认,调度器如果一段时间没收到回执,会把任务重新投递给其他空闲 Worker。
这里有一个关键原则:重试必须幂等。如果任务的核心操作是"写文件",重试两次就会写两次,结果不可控。解决方法是在消息里带上唯一的task_id,执行端先检查是否已经处理过。OpenClaw 内置了基于任务 ID 的去重逻辑,但如果你在自研方案里实现类似机制,一定要守住这个原则:宁可重复上报结果,也不可重复执行有副作用的操作。
4.3 状态一致性:版本号与冲突合并
记忆层并发写的问题在前面提到过。比如两个 Worker 同时更新同一个 key:A 读到了旧版本,B 也读到了旧版本,A 写回后再被 B 覆盖,A 的更新就丢了。解决方案是版本号机制:每次写操作带上当前版本号,写入时校验版本号是否还是原来的,不是就放弃,或者进入合并流程。
我在一个实际项目里维护过一份共享的知识库,多个 Worker 都会往里补充内容。最初的实现是直接 Set,结果经常丢条目。后来改成带version和updated_at时间戳的写操作,写入前先比对,冲突时把两个版本的内容都保留,交给后续流程去合并或人工确认。数据的完整性优先于呈现的整洁度,这条原则在多 Agent 协作场景里非常值得记住。
状态一致性还有一个容易被忽视的点:全局记忆的读取时机。一个 Worker 在任务开始时读了一次全局状态,执行到一半,别的 Worker 改了状态,这个 Worker 用的就是过期数据。OpenClaw 允许在任务完成前重新拉取状态,但对性能有一定影响。折中方案是给状态打上"不可变快照"的标签:任务开始时获取一次快照,整个执行期间基于快照,汇总阶段再把新数据合并进来。
5. 常见问题排查与避坑记录
5.1 流程卡死:Agent 互相等待
现象:日志显示多个任务进入 running,但没有任何一个进入 done,系统长时间不推进。
排查思路:先判断是不是死锁,也就是 Worker 之间形成了循环等待。集中式调度能避免大部分死锁,但如果 workflow 里存在循环依赖——比如 A 的产物要喂给 B,B 的产物又要喂给 A——调度器的状态机就会陷入等待。解决办法是检查 workflow 的依赖图,确保它是一个有向无环图;如果业务上必须循环,就通过全局记忆作为中转,而不是任务之间直接传递。
5.2 聚合顺序错乱
现象:汇总报告的章节顺序不对,或者内容被错误归到其他分类下。
排查思路:并行任务的完成顺序不等于提交顺序。聚合逻辑如果依赖数组索引或者返回顺序,非常容易串位。解决方法是按子任务的 key 显式排序,再拼装最终结果。同时,输出 Schema 里最好带上分类标识字段,聚合时用它做分组锚点。
5.3 API 限流导致重试风暴
现象:某段时间的模型调用全部被限流,任务失败率陡增,重试又进一步加剧负载。
排查思路:先检查令牌补充速率是否设置得太激进,再看重试策略有没有退避。指数退避加少量抖动,比固定间隔重试的效果好很多。抖动是为了避免多个 Worker 同步重试造成"惊群效应"——所有失败任务同时醒来重试,又把限流打满,形成恶性循环。
5.4 上下文窗口被撑爆
现象:任务开始后不久,某个 Agent 开始报 token 超限,或者内存持续增长。
排查思路:看全局记忆是不是被无条件注入到每个 Worker 的 prompt 里。改进方向是按需读取,或者用摘要代替全文。全局记忆写入要做索引,Agent 先读取索引,再决定拉取哪些详细内容,而不是一股脑全部塞进去。
5.5 日志交织严重,定位困难
现象:多个 Worker 并行输出日志,交织在一起,想追踪某个任务的全过程要翻半天。
排查思路:日志系统必须打上结构化的关联 ID——包括task_id、worker_id、run_id,并贯穿整个生命周期。这样即使日志交织,也能按 ID 过滤出单任务的完整轨迹。实操时,在子任务的开始和结束各打一条带耗时的日志,能快速对比出哪些环节最慢,优化方向一目了然。
下面是用表格整理的排查速查:
| 问题现象 | 常见原因 | 优先排查项 |
|---|---|---|
| 任务卡在 running | 循环依赖、外部 API 挂起 | workflow 依赖图是否 DAG |
| 结果顺序错乱 | 聚合依赖返回顺序 | 是否按 key 显式排序 |
| 限流重试风暴 | 令牌速率过低、无退避 | 令牌池配置、重试策略 |
| token 超限 | 全局记忆全量注入 | 是否按需读取 |
| 日志难追踪 | 缺少关联 ID | 是否贯穿 task_id / worker_id |
6. 架构之外,三个实操提醒
再说几个从实操里沉淀下来的提醒。
第一,别让 Agent 数量超过你的管理能力。并行度 10 和并行度 100 的调试成本不是一个量级。10 个 Agent 出问题还能靠日志逐个看,100 个 Agent 出问题,必须先依赖监控面板和告警策略。如果你的平台还停留在"看日志排错"的阶段,并行度控制在 5 以内会舒服得多,先把机制跑稳再扩容。
第二,重试不一定总是好事。对 LLM 调用来说,重试通常是值得的;对已经产生副作用的写操作,无脑重试可能让数据污染扩大。务必区分幂等任务和非幂等任务,分别配置重试策略。幂等的可以放心重试,非幂等的必须有去重机制兜底。
第三,架构是骨架,调用是血肉。多智能体并行协作的价值不只在"跑得更快",更在于它让系统"能承接更大的任务"。当单个 Agent 的上下文、工具、记忆都到了瓶颈时,并行协作不是用更多 token 去硬拼,而是把一个大问题切成多个可以独立验证的小问题,各自解决再合流。这个思维方式的转变,比任何框架配置都重要。
我在实际调试过程中,最深的体会是:并行协作的瓶颈往往不在模型,而在编排。模型的单点能力再强,如果任务边界不清晰、状态流转不可靠,最后产出的质量一定飘。先把 workflow 的依赖图画清楚,把消息格式定义死,再谈加大并发度。很多团队一上来就想堆 Agent 数量,结果活没少干,协作的锅也没少背。这篇指南的核心价值也在于此——把调度、通信、并发、状态这些骨架问题先立住,Agent 再多,系统也不容易乱。