多 Agent 协作更像社会,而不是一个大脑长出了几只手。
一个 Agent 被启动之后,只能活在自己的上下文里。它知道系统告诉它的规则,看到分配给它的任务,使用被允许的工具,然后根据工具返回的结果继续行动。至于另一个 Agent 此刻看到了什么、做到了哪一步、遇到了什么问题,它不会天然知道。Agent 之间没有心灵感应,也不存在一个自动同步的共同意识。
这意味着,把一个 Agent 复制成多个实例,并不会自然得到一支团队。数量增加带来的首先不是能力叠加,而是关系问题:它们如何确认彼此的身份,如何理解上下级与依赖关系,如何知道一项任务已经被领取,如何把局部结论交给下一个执行者,又如何避免所有人拿着不同版本的事实继续工作。
很多所谓的 Multi-Agent 协作,表面上是在划分 Planner、Executor、Reviewer,实际上只是让几个模型轮流说话。角色名称描述了“谁应该做什么”,却没有解决“它们实际上如何一起工作”。真正的协作必须发生在模型之外:系统需要规定合法的协作方式,保存不断变化的共同进度,控制信息在不同主体之间流动,限制每个执行者能够影响的范围,并在分歧、失败和循环出现时,把整个过程重新拉回到一个确定的终点。
因此,Multi-Agent 的第一性问题不是如何创造更多角色,而是如何让一群彼此隔离、各自只掌握局部信息的执行者,形成一个可以协作、可以恢复,也可以停止的系统。
协议:先让每个 Agent 知道自己身处什么关系
人进入一个团队以后,通常不需要别人从头解释什么叫汇报、交接、权限和责任。我们过去的经验会自动补全这些规则。但 Agent 没有这种默认的社会常识。系统如果只告诉它“你是一名开发者”,它知道的往往只是应该完成哪类工作,并不知道自己在这次协作中处于什么位置。
所以,多 Agent 协作的第一层不是分工,而是协议。
这里的协议,不只是 Agent 之间发送消息时使用的 JSON 格式,也不只是写在 System Prompt 里的一段角色设定。它是一组在协作开始前就必须确定的静态规则:系统里有哪些参与者,每个参与者拥有什么身份,能够执行哪些动作,需要对谁负责,又应该通过什么方式改变外部世界。
一个 Agent 被拉起时,它至少应该能回答六个问题:
- 我是谁? 我是否拥有稳定且唯一的身份,系统能否把我的动作、消息和产物重新关联到我身上。
- 我的职责是什么? 我需要完成什么,同时有哪些事情明确不属于我的职责。
- 我处在什么关系中? 我是独立执行者、团队成员,还是某个主控 Agent 派生出来的 Sub-agent;谁可以给我分配任务,我又可以把任务委派给谁。
- 我从哪里获得任务? 任务来自一次直接调用、一个共享任务板,还是上游 Agent 发来的结构化请求。
- 我如何与其他参与者通信? 我应该向谁报告完成、阻塞和失败,消息发出以后是否需要确认,多久没有回应才应该升级处理。
- 我能够影响什么范围? 我能使用哪些工具,读取哪些上下文,修改哪个工作空间,以及哪些动作需要审批。
这六个问题共同定义了 Agent 的协作坐标。缺少身份,系统无法追责和恢复;缺少职责,多个 Agent 会重复劳动;缺少关系,委派会变成无边界递归;缺少任务入口,Agent 不知道下一步应该做什么;缺少通信规则,局部完成无法变成团队进度;缺少作用范围,一个执行者的错误就可能扩散到整个系统。
协议真正约束的也不是 Agent 应该说什么,而是它可以如何参与系统。模型可以提出动作,但动作是否有效,不能由模型自己决定。领取任务、修改文件、提交产物、声明阻塞和结束任务,都应该通过系统能够识别和校验的入口发生。否则一句自然语言里的“我完成了”,可能既没有更新任务,也没有留下产物,更没有触发下游工作。
这也是协议与提示词最重要的区别。提示词可以让 Agent 理解规则,协议则必须让系统执行规则。前者影响模型的行为倾向,后者决定哪些行为能够真正改变系统状态。可靠的协作不能建立在“希望每个 Agent 都记得照做”上,而应该建立在“即使某个 Agent 忘了,系统也不会允许它绕过去”上。
协议因此更像团队的静态宪法。它在协作开始前规定身份、关系和边界,但它并不负责描述任务此刻进行到了哪里。任务被谁领取、哪个产物已经生成、谁正在等待谁,这些会随着执行不断变化的信息,属于协议之后的另一层:状态。
状态:不要让 Agent 猜测系统正在发生什么
协议是静态的,状态则是动态的。
协议告诉 Agent 可以怎样参与协作,状态告诉整个系统协作已经进行到了哪里。一个任务是否已经被领取,一名执行者是否仍在工作,一份产物是否通过验证,一个下游任务是否已经满足启动条件,这些都不是角色描述能够回答的问题。它们会随着每一次执行不断变化,也会直接决定系统下一步允许发生什么。
最容易出现的误区,是把对话历史当成状态。
某个 Agent 在上下文里读到“任务 A 正在执行”,只能说明这句话曾经被写进它的上下文,不能证明任务 A 此刻仍在执行。它可能已经完成,可能因为超时被回收,也可能被另一个执行者重新领取。模型看到的是一次经过筛选的历史切片,而系统状态描述的是当前仍然有效的事实。两者有时一致,但不能被当成同一种东西。
Context同样不能承担这个职责。Context适合保存当前目标、近期结论、相关文件和少量后续还会使用的信息,它解决的是“下一轮推理应该知道什么”。状态解决的则是“系统现在究竟是什么样”。前者服务认知连续性,后者服务控制连续性。如果把二者混在一起,系统就会开始依赖模型自己回忆任务进度、判断产物是否存在,甚至猜测另一个 Agent 是否已经退出。
因此,多 Agent 系统中的关键状态必须离开任何一个 Agent 的上下文,成为外部可以读取、校验和持久化的事实。Agent 可以申请改变状态,却不应该仅凭一句自然语言直接定义状态。比如它可以提交“任务已经完成”的请求,但系统仍然需要确认产物是否存在、验证是否通过、任务是否确实属于它,然后再把任务从执行中迁移到已完成。
这就是状态机在 Agent 系统里的意义。它并不是为了把流程图画得更复杂,而是为了把原本需要模型猜测的隐含规则,变成系统能够执行的显式转换:
- 等待中的任务可以被符合条件的 Agent 领取;
- 执行中的任务在同一时刻只能属于一个执行者;
- 遇到依赖缺失或外部阻碍时,任务可以进入阻塞;
- 只有产物经过验证,任务才可以进入完成;
- 执行者超时或退出以后,系统可以释放占用,让任务重新回到可处理状态。
这些状态名称本身并不重要。不同系统完全可以使用不同的阶段划分。真正重要的是,每个状态都要回答三个问题:谁拥有它,什么事件可以改变它,改变以后会触发什么后果。只要其中一个问题含糊,状态就只是展示在看板上的标签,无法真正约束执行。
但只有一份“当前状态”还不够。当前快照可以告诉我们任务停在哪里,却不能解释它为什么停在这里。一个可恢复、可诊断的系统通常还需要保存状态变化的过程:哪个 Agent 在什么时间领取了任务,调用了什么工具,留下了什么产物,又因为什么原因完成、失败或被回收。
于是,状态至少表现为三种不同性质的信息:
- 当前快照:回答系统现在停在哪里,供调度器和 Agent 决定下一步。
- 过程事件:回答状态是怎样一步步变成现在这样,供恢复、审计和问题定位。
- 运行工件:保存计划、代码、报告、验证结果等真正能够被后续参与者消费的产物。
把这三类信息分开非常重要。快照需要便于读取和覆盖,事件需要尽量保持追加和不可变,工件则需要稳定的地址、版本和归属关系。如果把它们全部塞进一段不断增长的聊天记录,系统既难以找到当前事实,也难以判断哪些中间结论已经失效。
状态外置之后,Agent 就不必扮演系统数据库。它只需要读取与当前任务有关的状态,根据协议提出下一步动作,再由运行时负责检查和落地。这样做看起来限制了 Agent 的自由,实际上减少了它需要猜测的事情:模型负责判断,系统负责记账;模型提出变化,状态机保证变化合法。
不过,外置状态并不会自动出现在每个 Agent 的上下文中。一个参与者完成了任务,另一个参与者仍然需要被告知;某个任务进入阻塞,上游需要知道缺少什么,下游也需要停止等待。推动状态在不同参与者之间传播的,是多 Agent 协作的第三层:消息。
消息:通信的本质是上下文传递的取舍
Agent 之间没有心灵感应,但这并不意味着它们应该共享同一个大脑。
一个看起来最省事的方案,是把所有 Agent 的对话、工具输出和执行记录放进一份公共上下文。这样每个参与者似乎都能知道全局发生了什么,也不用专门设计通信机制。但当任务变长、参与者变多,这份“共同记忆”很快就会变成一间堆满杂物的会议室:每个人都听见了所有人的发言,却越来越难找到真正和自己有关的信息。
全量共享首先会浪费上下文预算。一个执行者为定位问题读取了几十个文件,另一个负责验收的 Agent 通常不需要重新看到全部搜索过程。它需要的可能只是修改了哪些文件、为什么这样改、验证结果是什么。把前者的完整轨迹交给后者,不但没有增加有效信息,反而会挤压验收标准和当前产物的位置。
更麻烦的是认知污染。Agent 的中间过程并不总是事实,其中混杂着假设、失败尝试、过期结论和未经验证的判断。如果这些内容不加区分地进入其他 Agent 的上下文,一个局部误判就会沿着通信链传播。原本只是某个执行者临时怀疑“问题可能出在配置层”,到了下一个 Agent 眼里,可能已经变成了默认前提。
所以,多 Agent 通信真正需要解决的不是“如何把信息都传过去”,而是“什么信息值得穿过上下文边界”。
一条有效的协作消息,至少应该让接收者知道四件事:
- 这是谁发来的,和哪项任务有关。 没有发送者和任务归属,消息就无法被追踪,也无法判断发送者是否有权提出这个请求。
- 为什么要发这条消息。 它是在分配任务、询问信息、报告进度、声明阻塞,还是提交产物。明确的意图决定接收者应该采取什么动作。
- 发生了什么变化。 消息应该说明新增的事实或需要处理的问题,而不是重新复述全部背景。
- 证据和产物在哪里。 重要结论需要指向文件、测试结果、状态记录或其他可检查的工件,而不是只留下一个无法验证的自然语言判断。
这四类信息构成的不是一段聊天,而是一份交接。它应该足够完整,让接收者能够继续工作;也应该足够克制,不把发送者的整个思考过程复制过去。
因此,结论、证据和过程最好使用不同的载体。消息负责告诉接收者“发生了什么以及接下来需要什么”,产物负责承载可以继续加工或验证的结果,过程日志则保留完整执行轨迹,供出现问题时回看。正常协作消费结论和产物,诊断失败时才下钻到过程。这样既没有丢掉可观察性,也不会让每个 Agent 都背负整支团队的历史。
消息与状态也不能混为一谈。Agent 发出“任务完成”是一条消息,系统确认产物存在、验证通过并把任务迁移到完成,才是状态变化。消息表达某个参与者的声明或请求,状态记录系统已经接受的事实。如果收到消息就直接覆盖状态,一个重复投递、过期消息或无权操作的 Agent 都可能让整个任务板失真。
至于消息通过邮箱、任务看板、事件队列还是直接调用传递,属于传输层选择。对协作可靠性更重要的是,消息能否被关联、确认、去重和追踪。接收者暂时离线时,消息不能凭空消失;同一条消息被读取两次时,也不应该让任务完成两次。异步通信的价值就在这里:发送者不必一直等待接收者,但系统必须替双方保存这段尚未完成的交接关系。
并不是所有消息都应该发送给所有人。任务分配和修改请求通常只需要到达具体执行者;关键目标变化可能需要通知整个团队;运行事件可以进入全局记录,但不必进入所有 Agent 的上下文。通信范围越大,信息失真和注意力争夺的成本越高。广播应该是少数,全量共享更应该是例外。
好的消息设计,最终是在两个风险之间取平衡:传得太少,Agent 会因为缺少背景而重复调查或做出错误判断;传得太多,Agent 又会被无关过程淹没。系统需要传递的不是最多的信息,而是接收者为了完成下一步所需的最小充分上下文。
而要做到这一点,系统必须先承认一件事:不同 Agent 本来就不应该看到相同的世界。它们需要各自的上下文、权限和执行空间,只在必要时通过消息交换经过筛选的信息。这就进入了第四层——隔离。
隔离:给每个 Agent 一间属于自己的房间
人需要自己的精神空间,Agent 也一样。
隔离经常被理解成安全措施,例如不允许 Agent 访问某些文件、不允许执行危险命令。这当然重要,但对于 Multi-Agent 来说,隔离首先是一种协作结构。它让每个参与者只处理自己应该处理的问题,不必同时承担整个团队的认知负担。
最先需要隔离的是上下文。
负责拆解任务的 Agent 需要看到全局目标、约束和已有进度,却不一定需要阅读执行者的每一条调试输出。负责实现的 Agent 需要具体任务、相关文件和上游产物,却不应该被其他并行任务的探索过程持续打断。负责验证的 Agent 需要验收标准和最终产物,同时还应该与执行者的自我评价保持距离,否则它很容易沿用对方的假设,而不是独立检查结果。
这不是刻意制造信息差,而是在维护角色的判断条件。不同职责需要不同的世界视图。上下文如果完全相同,角色分工就只剩下名字;一个 Agent 的错误假设也会迅速变成所有参与者的共同假设。认知隔离让每个 Agent 保留独立判断,消息机制则负责在必要时搭桥。
上下文隔离并不意味着每个 Agent 都要从零开始。系统仍然可以提供一份稳定的共同基线,例如总体目标、不可违反的规则、共享术语和已经确认的关键事实。在这份基线之上,再根据身份、任务和当前阶段组装各自的局部上下文。公共部分保证团队不会失去共同方向,局部部分保证每个参与者看到的是当前最该看到的信息。
第二层是能力隔离。
Agent 能够看见什么,和它能够改变什么,是两种不同的权限。一个调查型 Agent 可能需要读取大量资料,却没有理由修改文件;一个执行者可以修改自己的任务范围,却不应该更新其他任务的状态;一个验证者可以运行检查并提交结论,却不应该为了让测试通过而顺手修改被验证的产物。
因此,工具不应该因为系统已经接入,就自动暴露给所有 Agent。每个 Agent 能看到的工具集合,本身就是协议与职责的一部分。工具调用在真正执行前,还需要经过参数校验、路径检查、权限判断和风险审批。模型负责提出“我想做这个动作”,运行时负责判断“你是否可以在这里这样做”。
能力隔离的价值不只在防止恶意行为。多数失控并不是因为 Agent 有意越权,而是因为它根据不完整信息做出了局部上看似合理的决定。限制工具和修改范围,相当于承认模型可能犯错,并提前规定错误最多能够走多远。
第三层是执行空间隔离。
当多个 Agent 并行工作时,如果它们直接操作同一份可变环境,一个人的中间修改会立刻改变另一个人的输入。后者可能基于旧状态完成推理,却把动作落在已经变化的文件上;也可能把尚未验证的半成品当成稳定事实。此时即使两个 Agent 各自的行为都合理,组合结果也可能不可复现。
独立工作目录、独立分支、临时沙箱或其他形式的执行环境,解决的都是同一个问题:让每个 Agent 的动作先在自己的边界内发生。它可以读取明确版本的输入,产生属于自己的修改和运行记录,再把经过验证的产物提交给系统,而不是直接把中间过程暴露给所有参与者。
但隔离不是把 Agent 永远关起来。独立空间里的结果最终仍然需要进入共同系统,这就需要一个对齐阶段:检查产物基于什么版本生成,是否与其他修改冲突,是否满足共同契约,以及合并后是否仍然通过验证。执行 Agent 可以负责生产变化,却不应该仅凭自己的判断把变化直接升级成团队事实。
从这个角度看,隔离与消息是一对互补关系。隔离决定默认情况下什么不能穿过边界,消息决定什么信息可以以何种形式穿过边界。没有隔离,消息机制会退化成全量共享;只有隔离而没有消息,团队又会变成一群互不相干的单 Agent。
隔离最终控制的是错误的爆炸半径:
- 上下文隔离限制错误假设污染多少参与者;
- 权限隔离限制错误决定能够执行哪些动作;
- 工作空间隔离限制错误修改影响哪些产物;
- 状态与工件隔离限制一次失败破坏多少可恢复信息。
我们无法要求一个具有随机性的模型永远不犯错,但可以要求系统不要让一个局部错误立刻变成全局事故。这也是 Harness 最重要的职责之一:不是消灭不确定性,而是把不确定性关在可以观察、可以验证、也可以回滚的范围里。
到这里,协议规定了协作规则,状态保存了共同进度,消息连接了彼此隔离的参与者,隔离又限制了每次行动的影响范围。但一个系统即使边界清楚,也仍然可能无限重试、相互推诿、不断派生新任务,或者在所有 Agent 都停止以后留下无人处理的半成品。
所以,Multi-Agent 最后一层不是让它们继续工作,而是确保它们最终能够停下来:收敛。
收敛:把分叉的执行线拉回“神圣时间线”
单 Agent 会陷入循环,多 Agent 则会以更多方式发散。
它们可能反复调用相同工具,可能把任务在彼此之间来回退回,可能为了处理一个局部问题不断派生新的 Sub-agent,也可能每个参与者都完成了自己的部分,却没有人负责确认整体目标是否已经实现。协作主体越多,局部动作就越容易掩盖全局停滞。
所以,收敛不是最后加上的一个停止按钮,而是一套贯穿运行过程的控制机制。它需要同时解决两类问题:系统还能不能继续运行,以及继续运行是否还有意义。前者是物理收敛,后者是语义收敛。
物理收敛:让系统在故障以后还能回到主线
物理收敛关心的是运行现场。
一个 Agent 领取任务后可能因为异常退出、网络中断、资源耗尽或超过执行时间而消失。如果任务状态只留在它自己的上下文中,系统甚至不知道这项工作已经无人处理。任务依旧显示为执行中,下游继续等待,其他 Agent 又因为占用关系无法接手,整个团队会被一个已经不存在的参与者锁住。
这时,系统需要能够检测执行者是否仍然存活,判断占用是否已经过期,并根据最后一次可信状态恢复现场。未完成的任务要释放锁并重新进入可领取状态,已经产生的中间产物要标记版本和可信度,重复执行可能造成副作用的动作则需要谨慎回放,而不是简单地从头再来一次。
恢复的目标不是让原来的 Agent 复活,而是让任务重新变得可继续。只要状态、事件和工件保存在 Agent 之外,新的执行者就可以从最近的可信位置接管工作。原本因故障产生的分叉,会被重新拉回系统承认的那条“神圣时间线”。
但恢复不能无限发生。同一任务连续失败,可能说明问题并不在某个 Agent,而在任务本身、依赖环境或验收条件。系统需要设置重试次数、执行步数、运行时间和资源预算。超过边界以后,与其继续消耗,不如把任务明确标记为阻塞或失败,保存当前恢复点,并把决定权交还给上层调度或人类。
物理收敛保证的是:无论某个参与者是否还在,系统中都不会长期留下无人负责、无法判断、也无法重新分配的悬挂工作。
语义收敛:模型说完成,不代表系统真的完成
系统能够继续运行,并不意味着它应该继续运行;所有 Agent 都停止,也不意味着目标已经完成。
语义收敛关心的是结果是否成立。一个执行者可以认为代码已经修改完成,验证者却发现验收条件没有满足;两个 Agent 也可能围绕同一个方案不断提出修改意见,双方的局部理由都成立,但讨论始终无法结束。此时问题不再是进程是否存活,而是谁有权定义正确、什么证据足以结束争论。
因此,完成条件必须在任务开始前尽量外置为可检查的契约。最终产物应该是什么,必须满足哪些结构和约束,需要通过哪些验证,哪些失败可以接受,哪些失败必须阻止进入下一阶段。Agent 可以参与解释模糊目标,却不应该在执行结束时临时降低完成标准。
一次可靠的成功收敛,至少需要同时满足几件事:
- 目标已经满足。 最初要求解决的问题得到了回应,而不只是所有子任务都被标记为完成。
- 产物真实存在。 结果已经落到系统可以读取、保存和继续使用的工件中,而不只是存在于某个 Agent 的回答里。
- 证据通过验证。 测试、规则检查、人工确认或其他验收机制能够支持“已经完成”这一结论。
- 状态彼此一致。 任务、消息、产物和执行者状态之间没有互相矛盾的记录。
- 没有悬挂工作。 不再存在无人处理的执行中任务、未解决的关键阻塞或尚未消费的必要交接。
如果这些条件不能满足,系统也需要以一种明确的方式失败收敛。达到最大步数、预算用尽、同一错误连续出现、关键依赖长期缺失,或者多个 Agent 的争议超过允许轮次,都应该触发终止。失败收敛不是假装任务完成,而是在保留当前状态和证据的前提下承认:系统已经无法在现有条件中可靠地继续推进。
多 Agent 的分歧同样需要终点。执行者和验证者可以进行有限轮次的返工,但不能无限互相否定。超过阈值以后,需要由更高层级的仲裁者、确定性规则或人类作出决定。仲裁的目的不是保证每次选择都绝对正确,而是避免系统把全部资源耗在无法自行结束的争论中。
从运行视角看,收敛是一系列停止条件;从系统视角看,它其实是一种责任归属:谁判断执行者已经死亡,谁决定任务可以重试,谁验证产物已经合格,谁在争议中作出最终裁决。只要这些责任没有被明确分配,所谓的自动协作就可能只是把“不知道怎么办”在多个 Agent 之间传递。
因此,收敛层真正拉回的并不只是失败任务,而是整个系统对共同事实的认定。它让运行中的分叉最终归并为一份可信状态、一组可验证产物和一个清楚的停止原因。成功、失败与阻塞都可以成为终点,唯独无限悬而未决不应该成为系统的常态。
结语:智能来自模型,可靠性来自模型之外
回过头看,协议、状态、消息、隔离和收敛并不是五个彼此独立的功能模块,而是一条连续的控制链。
协议先规定谁能够以什么方式参与协作;状态把协作中的共同事实保存在 Agent 之外;消息让局部变化能够穿过上下文边界,推动其他参与者继续行动;隔离限制每次行动和错误能够影响的范围;收敛则负责验证结果、处理故障,并决定整个系统何时停止。
它们环环相扣。没有协议,状态变化就缺少合法边界;没有状态,消息只能传递彼此矛盾的主观描述;没有消息,隔离的 Agent 无法形成协作;没有隔离,任何错误都会沿着共享环境扩散;没有收敛,前面建立的一切最终都可能变成一个运行得很有秩序的无限循环。
这也是为什么 Multi-Agent 的核心从来不是多几个角色、多几段提示词,或者让模型之间多进行几轮讨论。Agent 数量增加以后,真正增长的是关系、状态、通信和冲突的复杂度。模型能力决定了每个参与者能把局部任务做得多好,Harness 则决定了这些局部行为能否组合成一个可靠的整体。
一个好的 Harness 并不试图把 Agent 变成确定性程序。它承认模型会猜错、会遗忘、会重复、会在不完整信息下作出判断,然后通过外部协议和运行机制,把这些不确定性限制在系统能够承担的范围内。它不要求每个 Agent 永远正确,而是要求错误可以被发现、过程可以被解释、现场可以被恢复、结果可以被验证。
所以,多 Agent 协作更像社会,而不是一个大脑长出了几只手。
社会中的个体没有共享意识,却可以依靠身份、规则、记录、通信、边界和仲裁形成组织。Agent 也是如此。真正让一群独立模型成为团队的,不是它们拥有同一个大脑,而是它们即使看见不同的世界,仍然能够遵守同一套规则,围绕同一份事实协作,并最终抵达同一个终点。