第 28 章 多智能体协调:让一群 Agent 一起干活不掉链子
一个 Agent 是主角,一群 Agent 是剧组
前面讲的多是单个 Agent。第七部分进入规模化到多智能体:让多个 Agent 一起工作。
单个 Agent 最难的是能力,多智能体最难的是协调。一群 Agent 各干各的,会遇到:会话之间的 Agent 没法通信(共享上下文窗口不跨会话)、消息通道静默失败没人发现、多个领域 Agent 对同一份资源给出互相打架的建议、任务并发执行互相干扰。
这一章讲五个模式,覆盖多智能体协调的五个侧面:
- Board-Mediated Coordination(黑板协调):用看板卡片当 Agent 间的消息通道。
- Versioned Constitution Governance(文件系统宪法):团队的"宪法"放进版本控制的仓库。
- Dual-Rail Message Delivery(双轨消息):每条消息走两条独立通道。
- Lane-Based Execution Queueing(车道队列):按"车道"隔离并发,互不干扰。
- Cross-Domain Conflict Resolution(冲突消解):多个领域 Agent 的建议打架时,用策略裁决。
模式一:黑板协调(Board-Mediated Inter-Agent Coordination)
问题
在不同会话里跑的 Agent,没有人在中间转发消息就互相通信不了。共享上下文窗口不跨会话存活。 常见的变通办法(人在终端之间复制粘贴、共享文件、专用消息队列)各有各的失败:人成了瓶颈、没有投递保证、或者协调系统和被协调的工作脱节。
协调问题在"你希望这次交流以后还有用"时会加剧。发在 Slack 线程里的评审意见从工作记录里消失了;单独文件里做的决定和需要它的那张卡片毫无关联。工作和塑造它的上下文被拆开了。
方案
用看板(Kanban)的卡片评论线程当 Agent 间的主协调通道。 协调发生在工作所在的那张卡片上,而不是在另一个系统里。
机制分两层:
路由约定(信号层):Agent A 需要 Agent B 的输入时,在相关卡片上发一条带 B 前缀(方括号里)的评论:[quality-guardian]。这是轻量约定,不是协议。写它不需要基础设施,只需要一个扫描器检测它。
收件箱卡片(通知层):一个处理器检测新评论里的 [prefix] 标记,在专门的收件箱列里创建一张瞬态收件箱卡片,发给目标 Agent。收件箱卡片含源卡片 ID 和前 80 个字符的评论,够知道去哪看,不是完整消息。目标 Agent 按计划轮询收件箱列,看到发给自己的卡片就导航到源卡片读完整线程。
真正的交流发生在源卡片上。Agent B 读完整评论线程,在同一张卡片上发回复(用 Agent A 的前缀当确认信号),处理器闭环。收件箱卡片处理完就关闭。
Agent A → 看板: 在卡片 #42 上发 [agent-b] 评论
看板 → 处理器: 评论事件触发
处理器 → 看板: 建收件箱卡片 "[agent-b] #42 — ..."
Agent B → 看板: 轮询收件箱列
Agent B → 看板: 读卡片 #42 完整评论线程
Agent B → 看板: 在卡片 #42 发 [agent-a] 回复
处理器 → 看板: 给 Agent A 建返回收件箱卡片
Agent A → 看板: 在卡片 #42 读回复
看板 → 看板: 收件箱卡片关闭,卡片 #42 保留完整线程为什么协调要放在工作卡片上:评论线程变成审计轨迹。评审、澄清问题、改变方法的决定,这些是工作事件,不是边角聊天。把它们和卡片放在一起,意味着未来读卡片历史的 Agent 看到的不仅是做了什么,还有为什么做、决策前经过了什么争论。单一事实来源随着每次交流越来越丰富。
对话模式(/watch-card):需要同步式交流时(Agent A 要回复才能继续),发帖后进入主动等待。按固定间隔(通常 10 分钟)查收件箱,最多 6 次。回复到了就继续;一小时内没回,卡片自动阻塞并给人留评论。等待期间 Agent 不闲着,继续干其它卡片的活。
防级联:Agent 回复用带斜杠的修改前缀([notification/Agent-X])。检测正则(/\[([A-Za-z][A-Za-z0-9-]*)\]/)不匹配带斜杠的标记。回复不触发新收件箱卡片,只有新请求触发。
怎么用
前置条件:
- 一个有 REST API 和专门收件箱列的看板。收件箱列在正常工作列之外,不计入 WIP 限制。
- 一个扫描新评论找
[prefix]标记、创建收件箱卡片的事件处理器。可以推送式(看板业务规则在评论创建时触发)或轮询式(定时进程每 60-90 秒扫活跃卡片)。强烈倾向推送式,轮询当回退。 - 每个命名 Agent 一个唯一路由前缀(
quality-guardian、lead-agent、mosaic)。每个前缀只能属于一个 Agent。两个 Agent 共享前缀会都去动同一张收件箱卡片。
前缀所有权:
命名投递 Agent: 每领域一个(各一个前缀)
协调枢纽: 每范围一个(各一个前缀)
沙箱/临时 Agent: 无前缀、无收件箱、无命名协调沙箱 Agent 读写看板但不收收件箱通知。它们便宜,不需要协调开销。
发协调请求:
# Agent A 需要 Agent B 在卡片 #42 上的输入
board.comment(card_id=42, text="[agent-b] 能审一下这里的限流方案吗?")
# 如果 Agent A 需要回复才能继续(/watch-card 模式):
for attempt in range(6):
wait(10 分钟)
inbox = board.get_inbox_cards(prefix="agent-a")
if inbox:
response = board.get_card(inbox[0].source_card_id)
process(response.latest_comment)
board.close(inbox[0])
break
else:
board.block(card_id=42, comment="[human] 等 agent-b 回复;1 小时后自动阻塞")按角色的轮询间隔:
| 角色 | 间隔 | 为什么 |
|---|---|---|
| 项目 Agent | 60 分钟 | 计划内交付工作,异步延迟可接受 |
| 协调枢纽 | 15 分钟 | 横切关注点,快回复有全局价值 |
| 主动等待(/watch-card) | 10 分钟 | 对话模式,Agent 在等特定回复 |
| 分布式同步会话 | 5 分钟以内 | 围绕单卡片的类实时协作 |
主动性唤醒:规划计划进入活跃列时,处理器从计划标题里提取前缀(如 [mosaic] v2 重构),自动建收件箱卡片,不需要评论。给计划命名时带上负责 Agent 的前缀,策略唤醒就免费了。
取舍
- 好处:协调上下文和工作上下文放在一起,决策、评审、阻塞都记录在它们相关的卡片上;不需要共享上下文窗口,Agent 跨会话通过看板协调,无交接文档、无人工转达;模式能扛基础设施变化,路由约定和收件箱列稳定,只有投递机制(推 vs 轮询)变;审计轨迹内置,每次交流都记录在工作卡片上;扩展到异步协作,不同会话调度的 Agent 自然协调。
- 代价:延迟,推送式几秒出收件箱卡片,轮询式处理器最多 90 秒、Agent 收件箱轮询最多 60 分钟;多重性要求,每个前缀必须映射到恰好一个活跃 Agent,跑两个同名实例会弄坏路由(这是流程纪律,不是技术);启动缺口,评论恰好在卡片进扫描列那一刻发出可能漏掉(缓解:首次扫描也处理 5 分钟内的评论);看板工具依赖,评论事件 webhook 不是所有工具都支持(Businessmap 支持业务规则,Trello 和 GitHub Projects 不支持);shell 转义边角情况,带括号或特殊字符的双引号字符串经 shell 参数进 JSON 会失败。
模式二:文件系统宪法(Versioned Constitution Governance)
问题
当 Agent 能修改策略/宪法文本时,安全回退可能被逐步引入而不被注意。没有版本控制、签名和策略审查门,团队无法证明"谁改了什么、为什么改、关键防护是不是被削弱了"。
方案
把宪法存进版本控制、签名过的仓库:
- YAML/TOML 规则放 Git 里做自动规则强制;自然语言原则指导基于 LLM 的评估。
- 每个提交都签名(比如 Sigstore);CI 跑自动化策略检查。
- 只有被批准的审查者或自动化测试签名的提交才合并。
- Agent 可以提议改动,但把关者(gatekeeper)合并它们。
- 用语义化版本:MAJOR 改核心安全原则,MINOR 加内容,PATCH 澄清。
把策略即代码和发布纪律结合:每个宪法改动在激活前都可 diff、可审查、有测试门禁。这给了治理历史、回滚能力和对对齐策略演进的审计控制。
怎么用
- 要求
git commit -S或类似签名。 - 跑基于 diff 的 lint,标记关键规则的删除。
- 把宪法
HEAD作为只读上下文暴露在每次 Agent 回合里。
取舍
- 好处:审计性强、策略演进更安全、坏宪法改动回滚快。
- 代价:策略迭代变慢,签名、审查、CI 检查有额外运维负担。
模式三:双轨消息(Dual-Rail Message Delivery)
问题
在不同机器上跑的 Agent 通过单一通道协调:同步文件夹、队列、或聊天。那个通道迟早会坏,而且静默地坏。
损失不是停机本身,是它造成的歧义。死掉的通道和空闲的通道产生完全相同的观察结果:什么也没来。发送方的日志写着"已发送"就停了。接收方没什么可报告的,因为它从不知道有消息存在过。没有异常、没有重试触发,整个舰队还表现得像"沉默等于同意"。停机通常几小时后才被发现,由人注意到某个下游事情从没发生。
两个性质让这个失败模式代价高昂:
- "已发送"不等于"已投递","已投递"不等于"已完成",但单一通道把三者塌缩成一个不可观察的事件。
- 机器到机器的流量对人不可见,所以能干预的人总是最后一个知道。
重试解决不了。在死掉的通道上重试产生更多沉默。
这是经典结果的现实面:在异步系统里,光靠观察消息无法区分崩溃的参与者和慢的参与者(Chandra & Toueg,1996)。第二条独立失败的通道提供了另一个观察。 消息出现在一条轨上没出现在另一条,这个分歧把故障定位到某条轨。两条轨都不投递或确认时,沉默仍然歧义:对端可能慢、可能不可用、可能两条轨都挂了。超时和升级对这种情况仍然必要。
方案
每条消息同时走两条独立通道,并且让"只走一条通道"在结构上不可表达,而不是仅仅被劝阻。
两条轨:
- 轨道 A,文件邮箱:同步文件夹上的文件邮箱(Syncthing、Dropbox、git)。离线可用、扛大载荷、能挺过聊天提供商的停机。
- 轨道 B,群聊:机器和人都读的群聊(Telegram、Slack、Discord)。能挺过同步链路停机,让流量零成本对人可见。
四个让它成立的机制:
- 单一入口点。所有发送都走一个函数,它没有选择轨道的参数。不存在能表达"单轨发送"的 API。这比把规则写进文档更重要:在单一入口点出现前,临时单轨发送反复出现在自己的代码里。
- 死轨是信号,不是静默降级。一条轨失败,消息仍经另一条到达,同时触发降级告警。投递和健康解耦。
- 分歧是诊断。消息出现在轨道 B 不在轨道 A,一个轮询间隔内就把故障定位到轨道 A,而不是留给你一个舰队级的"哪里不对劲"。
- 之上加 ACK 纪律。接收方回
ACK <msg-id>。发送方拥有的是结果,不是交接:超过 SLA 的沉默触发追赶(chase),然后升级给人。这是区分"已投递"和"已完成"的东西。
两条轨必须独立失败。 同一条网络路径后面的两个云 API 是一条轨穿了两件外套。本地文件同步 + 托管聊天 API 是好的搭配,因为它们的失败模式很少重叠。
载荷策略是公共信封的一部分。两条轨携带相同的行内安全载荷,或相同的透明引用加完整性摘要。敏感载荷用加密信封或两条轨都安全的 capability/引用。没有安全的公共表示时,这个模式不适用于那条消息。
send(msg):
envelope = common_envelope(msg) # 行内安全、引用、或加密
id = stable_id(envelope) # 两条轨用同一个 id,支持去重
a = try(file_rail.append(id, envelope))
b = try(chat_rail.post(id, envelope))
if not (a or b): raise HardFailure # 两条轨都死 → 大声失败,绝不静默
if not a: alert("轨道 A 降级") # 部分投递仍成功,
if not b: alert("轨道 B 降级") # 但健康单独报告
ledger.record(id, sent_on=[a, b], acked=False)
receive():
for id, envelope in dedupe_by_id(file_rail.poll() + chat_rail.poll()):
msg = resolve_and_verify(envelope)
handle_once(id, msg) # 处理器必须幂等:两份副本都可能到
send_ack(id) # 经同一个双轨入口点
chase(): # "已发送"不是"已完成"
for id in ledger.unacked_past_sla():
reping(id) or escalate_to_human(id)证据
- 证据等级:中(
medium)。 - 有价值发现:一个 5 机舰队约 2 个月的日常运营里,每次观察到的协调停机都是静默单通道失败,不是消息内容失败。第二条轨把这些停机从"几小时后被人发现"变成"一个轮询间隔内的告警"。在 API 层强制不变量(无法命名某条轨)比靠约定强制更持久,约定会退化,缺失的参数不会。因为轨道 B 是人已经读的聊天,人在任何看门狗之前两次注意到一台卡住的机器,人可见性这个副作用不是设计目标,但成了真检测器。
- 未验证:单一生产部署(n=1 运营),没有对单轨基线的受控对比,改进大小没量化只有方向;高流量未测,每天数千条时无条件重复投递的成本未知;没有三轨以上是否值得的数据。
怎么用
什么时候用:
- 两个以上 Agent 跑在不同机器、网络、或操作者的笔记本上。
- 系统里沉默有歧义,Agent 保持安静可能意味着"没事做"或"从没收到"。
- 想让人类能看舰队,又不想专门建个仪表盘。
前置条件:
- 稳定消息 id。接收方会收到两份副本,没有确定性 id 就无法去重。
- 幂等处理器。假设每条消息至少投递两次。按至少一次设计,不是恰好一次。
- 一个账本,两个独立列:
delivered和acked。把它们合并会在新地方重建原问题。
别用它:Agent 共享进程或单台机器时。那里通道不是弱点,普通队列就是更简单的正确答案。
取舍
- 好处:单轨失败在轨道分歧时变得可观察,同时沉默仍需超时和升级;降级运行仍投递,一条轨挂是警告不是停机;轨道分歧在监控间隔内定位失败路径;人可见性免费,因为一条轨是人已经读的聊天。
- 代价:每条消息都重复。不幂等的接收方会双重执行,这个模式要求幂等,不授予它。两条集成要建、要保活,每条轨的健康要单独监控。大载荷要外化到访问控制的存储,两条轨带相同的透明引用和完整性摘要。机密要加密信封或双轨都安全的 capability/引用,绝不静默降级到单轨,没有安全公共信封就用别的投递设计。
模式四:车道队列(Lane-Based Execution Queueing)
问题
传统 Agent 系统把所有操作串行通过单一执行队列,造成限制吞吐的瓶颈。并发执行可取但有风险:
- 交错危害:多条命令同时写 stdin/stdout 会弄坏面向用户的输出。
- 竞态条件:没有正确同步的共享状态访问造成数据损坏。
- 死锁风险:幼稚的并发排队会在操作间制造循环依赖。
Agent 系统需要并行性保持响应(比如后台任务不该阻塞用户交互),但必须保住隔离保证。
方案
带独立队列和每车道可配置并发的隔离执行车道。 每条车道是一个命名队列,有自己的并发限制,独立排空,互不干扰。
核心概念:
- 会话车道:按对话队列防止消息交错。每个用户会话一条隔离车道(
session:telegram:user123)。 - 全局车道:后台任务(cron 作业、健康检查)在专用车道(
cron、subagent)跑,不阻塞会话车道。 - 层级组合:操作可以嵌套车道(会话 → 全局),通过队列链。外层车道等内层车道,用结构化排队防死锁。
- 可配置并发:每条车道支持
maxConcurrentworker(默认 1)。高吞吐车道并行跑任务,串行车道保顺序。 - 等待时间警告:排队太久触发警告和回调,暴露性能问题。
function drainLane(lane: string) {
const state = getLaneState(lane);
// 泵任务直到并发上限
while (state.active < state.maxConcurrent && state.queue.length > 0) {
const entry = state.queue.shift();
state.active += 1;
entry.task().finally(() => {
state.active -= 1;
pump(); // 排空下一条
});
}
}用层级组合防死锁:
// 会话车道排一个任务,任务本身又排到全局车道
await enqueueCommandInLane(sessionLane, () =>
enqueueCommandInLane(globalLane, () =>
doBackgroundWork()
)
);
// 外层 promise 在内层完成时解析;无循环等待Clawdbot 的车道示例:
main:CLI 命令的默认串行车道。cron:定时任务,和用户交互隔离。subagent:派生的 Agent 工作,和父级并行。session:<id>:每用户自动回复队列。
怎么用
- 识别隔离边界:把不能交错的操作用户/通道分组。
- 定义车道名:用层级(
session:telegram:user123)做作用域。 - 设并发限制:串行车道
maxConcurrent=1,并行车道更高。 - 排任务:
enqueueCommandInLane(lane, task)调度工作。 - 层级组合:排队的任务要派发到另一条车道时,从外层任务 await 内层入队。
可观测性:跟踪每车道指标(队列深度、活跃数、等待时间)检测背压和饥饿。关键信号:queue_size_per_lane、active_tasks_per_lane、wait_time_p95。
要避开的坑:
- 过度并行:并发 worker 太多会耗尽资源(文件句柄、内存)。盯
active数。 - 饥饿:高优先车道总是满时,低优先车道可能无限等待。用等待时间警告检测。
- 缺层级:不嵌套排队的跨车道直接依赖有死锁风险。总是用
enqueueCommandInLane(() => enqueueCommandInLane(...))组合。 - 动态车道增殖:用不稳定标识(时间戳)建车道导致内存无界增长。用稳定名,会话级车道做垃圾回收。
取舍
- 好处:隔离保证,车道间无交错,每条车道保顺序;灵活并行,每车道并发让混合负载(串行 UI、并行后台)跑起来;心智模型简单,层级组合对应结构化编程模式;可观测性,车道级指标帮调试。
- 代价:内存开销,每条车道维护一个队列,上千条空闲车道浪费内存;要调优,并发限制要按工作负载特征校准;不是通用调度器,没有优先队列、截止时间、工作窃取,复杂需求用正规调度器。
模式五:冲突消解(Cross-Domain Agent Conflict Resolution)
问题
团队越来越多地对同一环境或数据集跑几个独立的领域 Agent:成本优化 Agent、可靠性 Agent、合规 Agent、性能 Agent 都在评估同一套云基础设施。每个 Agent 在自己的领域内都很能干,但谁都不知道别人存在。
这产生静默、未解决的冲突:一个 Agent 建议释放空闲资源省钱,另一个建议给同一资源加冗余保可靠,第三个建议为性能扩容。如果建议被独立执行(比如自动修复),结果就是抖动、白做的工作、甚至有害的变更,*而且没有记录为什么冲突发生、哪条建议该赢。*
方案
在领域 Agent 之上插一个协调/治理层,它自己绝不做领域评估,而是:
- 收集每个领域 Agent 的输出,作为按共享资源标识符索引的结构化发现/建议。
- 交叉引用按资源 ID 的建议,检测冲突("网格"评估步骤),给每条建议算一个复合优先级分。
- 用策略即代码消解冲突(OPA/Rego 或内嵌规则引擎),而不是硬编码 if/else。这样组织治理规则(哪个领域赢、什么条件下)显式、版本化、可审计,只在没有策略匹配时回退到领域优先级层级或 LLM 辅助仲裁。
- 监控漂移:把每次新评估和之前的基线对比,风险级别、KPI 或建议结果随时间实质变化时标记。
领域 Agent 1 → 冲突检测器
领域 Agent 2 → 冲突检测器
领域 Agent N → 冲突检测器
冲突检测器 → 策略引擎
策略引擎 →|策略匹配| 已消解结果
策略引擎 →|无匹配| 优先级阶梯 / LLM 仲裁
优先级阶梯 / LLM 仲裁 → 已消解结果
已消解结果 → 漂移监控 vs 基线怎么用
适用场景:
- 不止一个 Agent(或团队)独立评估/作用于同一批资源、数据集或环境。
- 冲突建议被盲目执行有真实代价(财务、运维或合规风险)。
- 你需要"为什么选这条不选那条"的可审计记录,而不只是哪条"赢了"。
实施考虑:
- 建议要跨所有领域 Agent 有公共资源标识符方案,没有它冲突检测根本没法匹配记录。
- 从覆盖最高风险冲突的少量显式策略开始(比如"生产资源上可靠性保护优先于成本优化"),再尝试覆盖每种情况。
- 保留回退消解路径(优先级阶梯、人工升级、或 LLM 辅助仲裁)处理没有策略预见的冲突,别让系统静默丢掉不匹配的冲突。
- 协调层保持领域无关:加新领域 Agent 不该改代码,只需新策略和映射进共享 schema。
取舍
- 好处:冲突变得可见可审计,而不是静默互相覆盖或按未定义顺序执行;治理逻辑在版本化、可审查的策略文件里,不散在应用代码里;加新领域不用改仲裁逻辑,只加策略/映射。
- 代价:比让各 Agent 独立行动多一跳协调和延迟;要在原本独立的 Agent 团队间投入共享资源标识符方案;策略覆盖要主动维护,过时或缺失的策略回退到更弱的启发式(优先级阶梯/LLM),不如显式规则可审计。
五个模式怎么选
| 场景 | 推荐模式 |
|---|---|
| 跨会话的 Agent 要通信 | 黑板协调 |
| 团队的规则/宪法要可演进可审计 | 文件系统宪法 |
| 消息通道会静默失败 | 双轨消息 |
| 并发任务要隔离防干扰 | 车道队列 |
| 多个领域 Agent 建议打架 | 冲突消解 |
五个模式覆盖多智能体协调的完整拼图:黑板解决"怎么通信",宪法解决"共同规则怎么管",双轨解决"消息靠不靠谱",车道解决"并发乱不乱",冲突消解解决"建议打架怎么办"。通信、规则、可靠性、并发、裁决,五个都配上,一群 Agent 才算是真正的"剧组"而不是"各演各的"。
实践清单
- 跨会话协调:用看板卡片评论线程,
[prefix]约定 + 收件箱列,协调上下文和工作放一起 - 每个前缀只属于一个 Agent;回复用带斜杠前缀防级联
- 团队宪法进版本控制 + 签名仓库,Agent 只提议、把关者合并,语义化版本
- 关键消息走双轨:文件邮箱 + 群聊,稳定消息 id、幂等处理器、delivered/acked 双列账本
- 单轨失败触发降级告警,超 SLA 沉默升级到人
- 并发任务按车道隔离:每车道独立队列 + maxConcurrent,层级嵌套防死锁
- 盯每车道队列深度/等待时间,防饥饿和资源耗尽
- 多领域 Agent 共享资源:公共资源 ID + 协调层 + 策略即代码消解冲突
- 冲突消解保留回退路径(优先级阶梯/人工/LLM),别静默丢冲突;监控漂移 vs 基线
本章小结
- 黑板协调:看板卡片评论当消息通道,协调上下文和工作共置,审计轨迹内置。
- 文件系统宪法:规则进签名版本仓库,Agent 提议、把关者合并,演进可审计可回滚。
- 双轨消息:每条消息走两条独立通道,静默失败变成可见告警,"已发送"不等于"已完成"。
- 车道队列:按车道隔离并发,互不干扰,层级组合防死锁。
- 冲突消解:策略即代码裁决领域 Agent 的打架建议,冲突从静默变可审计。
下一章讲模型路由:一群 Agent 里,谁用哪个模型?Oracle 与 Worker、多模型编排、预算感知路由、按模型性格分模式。