多智能体项目失控?三角色架构与通信契约的设计实践
上个月我复盘了一个做了六周的多智能体项目发现它从第三周开始就在同一个问题上原地打转三个负责不同环节的执行代理互相踢皮球都觉得自己已经做完了分内事但最终交付物的质量却惨不忍睹。这类问题反复出现之后我开始重新审视“agency-agents”这个词——它从来不是指“把任务丢给一群代理就撒手不管”而是指“如何在一套清晰的委托关系里让一群代理各自带着明确权限、边界和验收标准去协同工作”。这篇文章就把我这几轮探索里沉淀下来的三角色协作结构、通信契约、调度机制和踩坑记录完整地拆出来适合正在做多代理系统、或者准备把单代理改造成多代理流水线的开发者参考。1. 为什么多智能体项目会失控从过度授能到欠授能1.1 agency-agents 的本质不是数量而是委托关系很多团队拿到多智能体这个方向第一反应是我们要用五个代理重写整个业务流程。实际上多代理系统的复杂度并不来自代理的数量而来自代理之间的委托关系设计。我在一个模拟项目X里试验过完全相反的两种设计效果差异极大。第一种方案叫全权授出模式。主控代理把一整条从需求分析到代码生成再到测试执行的任务链连同全部上下文一股脑丢给一个执行代理指望这个执行代理自己再往下拆。结果这个代理确实会拆但它拆出来的子任务没有准入准出标准下游代理拿到的输入经常缺字段整个链路跑完十次有七次是坏结果。第二种方案叫过度收敛模式。主控代理把每个微小步骤都限定死执行代理没有任何决策空间所有判断都挤在主控代理一个上下文窗口里结果主控代理自己成了瓶颈单次任务处理时间被拉长到原来的四倍。这两种失败模式背后其实是同一个问题委托关系没有定义清楚。agency-agents 真正要解决的不是谁来干活而是谁对什么负责、什么算干完、干完交给谁、对方能不能无歧义地接住。只有当这些委托条件都成立多代理协作才是有效的否则代理越多系统熵增越快。1.2 三角色模型主控、执行者与协调层各司其职我在反复踩坑之后把系统收敛成一个三角色模型。主控代理只做任务拆解、进度跟踪和最终验收执行代理池里每个代理只负责一个专业领域比如文本生成、代码审查、数据抽取协调层则负责消息路由、状态存储和上下文传递它不产生任何业务判断只保证信息流不断裂。举个例子一个执行代理在处理提取三份PDF里的关键字段并汇总成表格这种任务时主控代理负责任务拆解和验收标准定义执行代理负责具体的抽取逻辑协调层负责把三份PDF的读取结果按顺序缓存好并在执行代理处理完一份后自动把下一份上下文推过去。这个结构里没有任何一个环节需要猜另一个环节在想什么因为所有跨代理的沟通都通过协调层落地成结构化消息。三角色模型的价值在于它把决策和执行彻底分离。主控代理可以做高层次的规划执行代理可以专注于领域内的产出协调层保证两者之间的信息同步。这个分离对上下文窗口的压力也友好很多主控代理不需要知道执行代理内部每一步的中间结果执行代理也不需要把主控代理的完整规划塞进上下文两边都只需要关心自己职责范围内的那部分数据。2. 一套可落地的三角色架构编排系统、执行代理池与能力总线2.1 架构选型为什么我放弃了“只用一个编排器”在设计初期我考虑过三种主流路径。第一种是把所有逻辑写在一个编排器内部通过函数调用来模拟多代理这种方式实现最快但代理之间没有真正的消息边界业务复杂度一上来编排器内部的分支逻辑就会膨胀到不可维护。第二种是线性链式调用也就是A代理处理完传给B、B再传给C这种模式对流水线型任务很直观但一旦某个环节需要回退或者并行处理链路就僵住了。第三种是事件驱动的代理网络代理之间直接互相通信这种模式灵活但最难驾驭很容易出现消息风暴和死循环。我最终选的是“编排系统 可插拔执行代理”的组合本质上是三角色模型的工程化落地。编排系统保留对流程的控制权但不再承担具体业务计算执行代理池负责专业处理每个代理独立部署、独立维护上下文能力总线作为中间层负责把执行代理的产出标准化成统一格式再路由给下游。方案优点缺点适合场景单编排器函数调用实现快、易调试上下文耦合重、扩展受限演示Demo、流程极其固定线性链式调用流程直观、依赖清晰无法并行、回退困难固定的流水线处理完全事件驱动网络扩展性最强消息风暴、状态难追踪复杂网状协作、需强治理编排器可插拔执行代理控制与执行分离、可观察性好需要额外设计通信协议多领域协作、长期演进系统我选第四种核心原因是它保留了“一处下发、多点执行”的治理能力同时又不限制代理内部的自由发挥。编排器只关心“任务有没有完成”“验收是否通过”执行代理只关心“自己的专业部分怎么做”两部分各自演进时互相影响最小。2.2 代理池的注册机制与调度策略代理池不是简单的代理列表还需要一套注册与发现机制。每个执行代理在启动时向编排系统注册自己包括代理名称、能力描述、输入模式、输出模式、并发上限这些元信息。编排系统在拆解任务时会根据任务类型和代理注册信息匹配候选代理再根据负载情况选择具体执行者。调度策略上我试过两种方式轮询调度与能力匹配调度。轮询实现简单但会出现文本处理任务被分给擅长数据抽取的代理这种错配。能力匹配调度则要求主控代理在拆解任务时把任务描述规范化到可以和代理注册信息做语义匹配的程度。实际操作里我采取的是两阶段策略先按任务类型粗筛再按实时负载精排。粗筛解决“找谁干活”的问题精排解决“谁现在有空干活”的问题。调度还需要考虑一个常被忽视的细节代理的幂等性。同一个任务被重新投递时执行代理能不能识别出这是重复请求并直接返回上次的结果而不是再跑一遍全流程。我在能力总线里为每个任务分配了唯一标识符执行代理收到任务后先查状态存储如果发现同样的任务ID已经在处理中或已完成就直接复用已有结果避免重复计算和状态污染。2.3 上下文传递的关键实现上下文传递是多代理架构里最容易出错的地方。我见过太多系统把主控代理的完整对话历史直接塞给执行代理这既浪费token又会让执行代理被无关信息干扰。我采用的方案是构建“轻量上下文包”。主控代理在拆解任务时只把执行该任务必要的最小字段集打包包括任务ID、目标描述、输入数据引用、验收标准、返回值格式。执行代理处理完任务后同样只返回结构化结果不带着中间推理过程。中间推理过程可以单独存进协调层的日志系统但不会自动流入下一个代理的上下文。上下文传递的另一个要点是“数据引用”而不是“数据拷贝”。当任务涉及大文件或长文本时主控代理不会把完整内容塞进消息体而是传递一个可访问的存储引用执行代理按需读取。这样既减轻了消息传递的压力也让同一份数据可以在多个代理间安全共享不需要反复复制。我举个具体例子。在一次信息抽取任务里主控代理把三份PDF文件的存储路径、需要抽取的字段名列表、输出表格的列名顺序放进上下文包执行代理按路径读取文件处理完只返回最终的表格数据。整个过程消息体很小传递速度很快而且任意一个环节需要重试都不会因为上下文丢失而前功尽弃。3. 通信契约与工作流流转让每个代理都能够“无歧义地交接”3.1 消息类型与状态码设计多代理系统里代理之间传递的消息需要遵循一套清晰可验证的契约。我在实际项目里定义了一套精简的消息类型所有代理之间的交互都基于这几种类型任何不在这套体系内的自定义消息都不允许跨代理传递。TASK_ISSUED编排系统向执行代理下发任务携带任务ID、输入引用、验收标准ASK_NEED_INFO执行代理发现自己缺信息向协调层请求补充字段REPORT_PROGRESS执行代理汇报阶段性状态携带进度百分比和当前结果摘要TASK_DONE执行代理完成任务携带输出引用和格式声明TASK_FAILED执行代理无法继续任务携带失败原因与可重试标记ESCALATE执行代理判断任务超出自身能力范围上传给主控代理重新规划与消息类型对应的是一套状态码。每个任务实例都维护一个状态pending、in_progress、wait_input、done、failed。wait_input 是一个我后来才加上的状态它表示“执行代理正在进行但需要等待外部补充信息才能继续”。这个状态非常关键没有它的话协调层会把等待中的任务误判为超时触发不必要的重试。3.2 一次完整的工作流流转我拿一个具体流程演示这套契约如何运转。假设系统接到一个任务“生成一份竞品功能对比报告”。主控代理首先进行任务拆解将任务分成三步第一步是数据采集第二步是功能点标注第三步是报告生成。主控代理为每一步生成任务ID并通过能力总线分别匹配一个采集代理、一个标注代理、一个报告生成代理。采集代理收到TASK_ISSUED后开始工作如果发现需要指定产品名单才能继续就会发出ASK_NEED_INFO协调层把这个问题路由回主控代理主控代理补充名单后再次下发。采集代理完成后返回TASK_DONE附带采集数据的存储引用。此时协调层把输出引用更新到任务依赖表标注代理收到TASK_ISSUED后从存储引用中读取采集数据开始功能点标注。标注代理处理过程中如果发现某个功能点的定义不明确会发REPORT_PROGRESS并附带问题描述。协调层可以配置两种处理方式一种是把问题转给主控代理决策另一种是让标注代理基于默认规则自行判断并标注。我一般在规则明确时选择第二种在规则模糊时选择第一种避免代理长时间卡在等待状态。最终报告生成代理把所有中间结果拉取回来生成最终报告返回TASK_DONE。主控代理根据验收标准校验报告完整性和格式合规性校验通过后整个工作流结束。这整个过程里没有任何一个代理需要知道其他代理内部的实现细节所有衔接都通过协调层的任务依赖和状态管理完成。3.3 工作流的并发拆分与合并单一流水线的流转只是基础实际业务场景更常见的需求是并发拆分与结果合并。主控代理在拆解任务时可以明确标注哪些子任务互不依赖、可以并行执行。比如在竞品报告这个任务中数据采集和问卷分析就不存在依赖关系可以作为两个并行分支同时下发。协调层需要维护一个简单的数据依赖图记录每个任务的前置任务和后续任务。当一个前置任务完成时协调层检查关联任务是否全部就绪再决定是否向后置任务发送TASK_ISSUED。这相当于在协调层内部做了一个轻量的DAG调度不依赖外部流程引擎实现起来并不复杂却能把整体处理时间压缩到串行模式的约三分之一。合并阶段则需要定义一个聚合任务。聚合任务不会分发给任何执行代理而是由协调层把并行分支的输出按照预定义的合并规则组合起来生成一份组合结果再作为整体结果返回给主控代理。我在实现时使用了一套“合并描述符”指定哪些字段需要拼接、哪些字段需要取并集、哪些字段需要按优先级覆盖这让合并逻辑变得可配置且可复用。4. 端到端场景推演一个内容运营流水线的实现记录4.1 需求拆解从模糊指令到结构化任务内容运营类需求是多智能体系统最好的试验场之一因为它的环节多、每个环节的产出边界清晰非常适合演示三角色架构的完整能力。我接到的一个典型需求是“为一款新产品生成一套完整的社媒发布物料包”。主控代理接到这个指令后第一件事不是直接调内容生成代理而是把需求拆解成结构化任务。它需要先明确产品的基本信息、目标人群、发布平台和内容风格这些信息从输入文档中抽取。接着任务被拆成几个并行分支核心卖点提炼、关键词库构建、文案草稿生成、视觉描述生成、合规风险检查。每个分支对应一个执行代理各自有独立的验收标准。拆解过程中主控代理还会为每个子任务生成初始上下文包。比如给视觉描述生成的上下文包里包含产品名、风格偏好、目标平台的图片尺寸规范给合规风险检查的上下文包里则包含发布平台的内容安全规范要点。这样每个执行代理拿到的都是“刚刚好够用”的信息不会因为信息过载而抓不住重点。4.2 执行代理协作数据怎么在代理之间流动我以一个子链路为例展示执行代理之间的协作关系。核心卖点提炼代理完成输出后它的结果不会自动流向文案草稿生成代理而是要经过协调层的格式标准化。协调层检查输出是否符合预定义的结构比如是否包含卖点标题、支持论据、目标人群标签再将其转换成下游代理可识别的格式写入共享存储。关键词库构建代理与文案草稿生成代理之间同样存在协作。文案生成代理在生成草稿时会从关键词库中读取高频词汇表并要求生成的文案里包含这些关键词。这里有一个细节关键词库构建代理输出的关键词列表如果为空文案生成代理不应该直接放弃而是应该发出ASK_NEED_INFO请求让上游重新检查。协调层在处理这种跨代理依赖时会记录请求-响应的完整链路方便定位问题出在哪个环节。视觉描述生成代理在整个协作链路中比较独立它只需要读取产品信息和风格偏好不需要依赖文案结果。但它生成的内容会被合规检查代理作为输入合规检查代理会检查视觉描述中是否有不符合平台规范的表述。这一步在内容运营场景里尤其重要因为视觉描述一旦生成后修改成本比较高前置检查可以避免后续返工。整个协作链路跑通一次之后我把每次执行的全过程日志包括每个代理的输入输出摘要、耗时、任务状态流转汇总到一张运行记录表里。这张表既是调试工具也是衡量各个代理表现的依据后续做优化时全靠它定位瓶颈。4.3 交付与验收主控代理如何判断“可以交付”最后一个环节是验收。主控代理在任务拆解阶段就定义了每条交付物的验收标准文字描述里称为“完成定义”。内容运营场景里完成定义包括文案长度在指定字数区间、必须包含核心关键词、卖点覆盖度不小于三个维度、没有合规风险提示、格式符合发布平台要求。主控代理对执行代理的产出逐项校验。遇到超出验收标准的内容比如文案里出现了未授权的高频词误用主控代理会生成一条返工指令附上问题描述和修改建议重新投递给对应执行代理。返工并不等同于完全重跑一遍上下文包会携带上一次的输出引用和修改点说明执行代理可以在原有基础上做增量修改而不是从零开始生成整体效率会高很多。验收通过之后主控代理把所有子任务的输出汇总成最终的物料包包括文案、关键词列表、视觉描述、合规检查报告。这些输出通过协调层写入最终交付目录一个完整的端到端流程宣告结束。我在实践里还会做一步把整个流程的“面包屑轨迹”保留下来包括每一步执行代理的输入输出引用、任务状态变化时间点、返工原因作为后续流程优化和历史复核的依据。这一步看似额外开销但在问题回溯和结果解释上价值极大。5. 踩坑实录与优化策略那些文档里不会写的问题5.1 坑一“全权授出”导致三个代理互相确认战点前面提到过“全权授出模式”我后来在另一个需求里又一次踩进了同一个坑。当时我的本意是让主控代理更聚焦于高维规划结果调度时把一个包含数据清洗、特征构建、模型训练三个环节的完整任务直接丢给了三个执行代理没有拆解任务边界。这三个执行代理收到任务后各自用不同的方式理解“我该从哪一步开始”。清洗代理认为特征构建代理会检查数据质量所以没有做充分的缺失值处理特征构建代理看到数据里存在脏值认为清洗代理已经处理完所以直接跳到建模特征映射训练代理收到特征时发现某些字段类型与预期的张量形状完全对不上。三个代理之间的消息来回持续了一个多小时实际上没有一方真正把责任接到底。我在日志里看到大量REPORT_PROGRESS消息但没有一条TASK_DONE。这次经历让我彻底明白了任务边界和完成定义必须由主控代理在下发任务时明确给出同时需要对所有执行代理接收消息执行完整性前置校验任何输入不合格就直接拒绝不能带着隐患往下传。5.2 坑二上下文注入策略与消息风暴的权衡多代理系统里还容易出现一种问题消息风暴。当子任务多、代理之间交互频繁时协调层会持续收到海量状态更新消息消息队列积压、处理延迟上升整个系统响应速度变慢。我被迫采取了两项调整。第一项是消息合并多个REPORT_PROGRESS消息在短时间内到达时协调层只保留最新一条并附带最近更新时间戳。第二项是引入心跳与阈值判断执行代理不再针对每个微小步骤发送消息而是每隔一段固定时间汇报一次状态或者在某一步耗时超过阈值时才上报。这里的核心权衡是可观察性确实会下降但系统的稳定性大幅提升。在上下文注入策略上我也做了同样的减法。早期每个执行代理的上下文包都会附上主控代理的完整任务历史造成大量token浪费。后来我只在小范围场景里保留完整历史包括需要深度语义理解的场景和分析类需求而标准化的步骤型任务一律使用精简上下文包。这样既保留了深度学习场景的上下文优势又不会拖累高频步骤类任务的处理速度。5.3 坑三幻觉式拒绝代理“看似勤奋实则摆烂”执行代理有时会在遇到不熟悉的问题时不直接说明困难而是生成一段看起来合理的兜底结果相当于把任务“貌似完成”了。这类问题比显式失败更隐蔽。我遇到的具体现象是一个数据抽取代理在处理非结构化文本时遇到表格结构严重损坏的输入没有发出失败信号而是生成了一个根据上下文推测的抽取结果还在摘要中标注了置信度。这个结果如果没有人工复核会被下游直接当作真实数据使用。针对这种问题我引入了三招第一要求执行代理在输出结果时附带“置信度字段”和“不确定点列表”从机制上让代理承认自己的不确定性第二在验收环节加入面向异常值的抽查机制主控代理会随机抽取部分输出字段回溯对应原始输入检验逻辑一致性第三为每个执行代理预置一组超过自身边界的问题提前标记出“已知未知”清单。通信契约里也加了一条规则当置信度低于阈值时执行代理必须先发ESCALATE消息禁止携带低置信度结果直接返回TASK_DONE。5.4 从观察性到可扩展一条长期演进路径回到可追踪性这个基础问题。多代理系统里最影响开发效率的瓶颈往往不是模型能力而是排障的困难程度。我在这套架构里把所有跨代理消息都接入了结构化日志每条消息携带任务ID、代理ID、消息类型、时间戳和引用字段。排查问题时只需要按任务ID筛选就能还原一次任务从下发到完成的完整链路。有一个细节值得单独说一下日志不是只记录成功路径就够了失败路径、重试路径、超时路径同样重要。我专门为失败和重试建立了一套独立的追踪标记确保任何一次任务异常都能追溯到具体环节和触发原因。长期来看这套三角色架构让我可以持续替换和扩展执行代理池而不会对编排系统产生破坏性影响。在模拟项目X的后续迭代里我换了三次底层执行代理实现从通用对话模型换到专门调优过的专业模型整个切换期间编排系统几乎零改动。这种“编排逻辑与执行能力解耦”带来的可扩展性才是这套设计最值钱的部分。最后再分享一个小技巧判断一套多代理系统设计得够不够好我一般会找一个“偏执型用户”来测试它总是给不完整、有歧义、带陷阱的输入。优质架构能在这种极端输入下仍然保持边界清晰、消息流转有序并且每个代理都知道自己的职责边界。如果测试过程中出现互相推诿、无意义等待或低置信度结果直接交付说明这套结构还远没有成熟。