ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

多智能体协作系统Agency Agents:从架构设计到落地实操的工程化指南

多智能体协作系统Agency Agents:从架构设计到落地实操的工程化指南

1. 从"一个人扛所有"到"一群Agent各管一摊":这套协作系统到底在解决什么

如果你带过三人以上的开发小组,大概率经历过这种场面:需求评审时人人点头,排期表拉出来漂漂亮亮,真到联调阶段却发现接口对不上、字段命名各写各的、前端等后端、后端等数据、测试等所有人。问题不在于谁不努力,而在于协作本身没有被当成一个工程问题来对待。

Agency Agents 这套多智能体协作系统,本质上就是把"团队协作"这件事从口头约定变成可编排、可观测、可回滚的工程结构。它不是一个具体的业务框架,而是一层协作调度层——你可以把它理解成给一群各有所长的 AI Agent 配了一个项目经理、一套工单系统和一本共享的接口手册。

它适合谁?三类人最该关注:一是正在做 AI 应用落地、被"单 Agent 能力天花板"卡住的开发者;二是想用多智能体思路重构内部工具链的技术负责人;三是对 Agent 编排感兴趣、但被各种概念绕晕、想找一个能跑起来的参照系的人。这篇文章不讲空泛的"智能体时代来了",只拆架构、讲取舍、给能抄的配置。

先说清楚一个前提:多智能体不是"Agent 越多越强"。我见过太多项目一上来就堆七八个角色,结果调度开销比任务本身还大,最后退化成一个 Agent 干活、六个 Agent 围观。Agency Agents 的设计哲学恰恰相反——先定义协作边界,再决定要不要拆角色。这个顺序搞反了,后面全是坑。

2. Agency Agents 的骨架:角色、消息总线与共享状态三件套

2.1 角色定义不是写人设,是划职责边界

很多人第一次接触多智能体,第一反应是给每个 Agent 写一段"你是一位资深后端工程师"的人设提示词。这在 Agency Agents 里只是最表层的东西。真正决定系统能不能跑通的,是每个角色的输入契约和输出契约。

我拿一个典型的开发场景举例:一个"需求拆解 Agent"、一个"接口设计 Agent"、一个"实现 Agent"、一个"校验 Agent"。这四个角色的关键不在于提示词写得多花哨,而在于:

  • 需求拆解 Agent 的输出必须是结构化的任务列表,每条任务带明确的验收标准字段;
  • 接口设计 Agent 只接受任务列表作为输入,输出是接口定义(字段名、类型、必填性);
  • 实现 Agent 只认接口定义,不允许自己发明字段;
  • 校验 Agent 拿接口定义和实现产物做比对,输出差异报告。

看出来了吗?角色之间的耦合点全部落在数据结构上,而不是自然语言上。这是整套系统能稳定的根本原因。自然语言在 Agent 之间传递时信息损耗极大,同一个"用户ID"能被写成 userId、user_id、uid、用户标识四种形态,联调时就是灾难。

提示:定义角色时,先画一张表,列出每个角色的"输入字段"和"输出字段",字段对不上的地方就是未来必炸的地方。这张表比任何提示词工程都重要。

2.2 消息总线:为什么不用简单的函数调用串起来

初学者最容易想到的实现是:Agent A 跑完,把结果直接传给 Agent B 的函数。简单直接,但一旦角色超过三个、出现分支和重试,代码就会变成一团意大利面。

Agency Agents 采用的是消息总线 + 订阅的模式。每个 Agent 不直接调用下一个 Agent,而是把结果作为一条消息投递到总线上,带上类型标签(比如task_list、interface_spec、impl_artifact)。需要这类消息的 Agent 订阅对应标签。

这样做换来三个实打实的好处:

第一,解耦。新增一个"文档生成 Agent"只需要订阅interface_spec,完全不用改动接口设计 Agent 的代码。第二,可观测。所有消息都在总线上留痕,出问题时你能完整回放"谁在什么时候产出了什么",而不是靠日志里零散的 print 去猜。第三,可重试。某条消息处理失败,重新投递即可,不需要从头跑整条链路。

代价也很明显:调试时链路变长,一个 bug 可能藏在消息序列化的某个角落。我的经验是,在开发阶段一定要给总线加一个"消息快照"开关,把每条消息的原始内容落盘,排查效率能提升一个数量级。

2.3 共享状态:哪些东西该共享,哪些坚决不能共享

共享状态是双刃剑。共享得好,所有 Agent 看到同一份事实;共享得烂,就是并发写冲突和状态污染。

Agency Agents 的实践原则是:只共享"事实",不共享"过程"。具体来说,接口定义、任务清单、最终产物这些是事实,放进共享状态;而某个 Agent 的中间推理、草稿、临时变量,坚决留在 Agent 内部,不往共享区写。

我踩过的一个坑:早期把"当前进度百分比"也放进共享状态,结果多个 Agent 同时更新,数值来回跳,前端展示像抽风。后来改成每个 Agent 只上报自己的状态,由调度器统一汇总,问题立刻消失。共享状态应该是只读为主、写入受控的,谁都能随便写的共享区迟早出事。

3. 调度策略:串行、并行还是动态编排,选错了全盘皆输

3.1 三种调度模式的适用边界

调度策略决定了 Agent 之间的执行顺序,选错了要么慢得离谱,要么错得离谱。Agency Agents 支持三种基本模式,我把它们的适用场景整理成表:

调度模式执行方式适用场景主要风险
串行流水线A→B→C 依次执行强依赖链路,如需求→设计→实现单点阻塞,一个环节慢全链路慢
并行扇出A 同时触发 B、C、D独立子任务,如多模块同时实现结果汇总时的冲突合并
动态编排由调度器按状态决定下一步复杂分支、需要重试和回退调度逻辑本身容易变复杂

大部分团队的真实项目是混合模式:需求拆解到接口设计是串行(强依赖),多个模块的实现是并行(互相独立),校验和修复是动态编排(失败要回退重做)。

3.2 动态编排的判定逻辑怎么写才不失控

动态编排最诱人的地方是"智能",最危险的地方也是"智能"。如果让一个 LLM 自由决定下一步调谁,系统会变得不可预测。Agency Agents 的做法是把决策规则显式化,LLM 只负责在规则框架内做有限选择。

举个具体的判定逻辑:校验 Agent 输出差异报告后,调度器按以下规则走——

  • 差异为空:任务完成,进入汇总;
  • 差异仅涉及字段命名:触发"轻量修复",只重跑实现 Agent 的格式化步骤;
  • 差异涉及接口结构:回退到接口设计 Agent 重新产出;
  • 连续两次修复失败:升级为人工介入标记,停止自动重试。

这套规则是写死的,不是让模型自由发挥的。规则负责"能不能做",模型负责"怎么做",这个分工是多智能体系统稳定的关键。我见过让模型全权决定调度顺序的项目,跑十次有三次进入死循环,最后不得不加一个"最大步数"硬性熔断。

3.3 熔断与超时:没有这两样,系统迟早挂死

任何多智能体系统都必须有熔断机制。Agency Agents 里我建议至少配三层保护:

第一层是单 Agent 超时,比如单个 Agent 处理超过 60 秒强制中断,避免某个环节卡死拖垮全局。第二层是链路总步数上限,比如整条任务链最多 20 步,超过就判定异常。第三层是重试次数上限,同一个环节最多重试 2 次,第三次直接标记失败并保留现场。

这三层看起来简单,但缺任何一层都会在真实项目里翻车。我印象最深的一次是某个 Agent 因为输入格式异常陷入"重试-失败-再重试"的循环,一晚上烧掉了大量调用额度,第二天看账单才发现。熔断不是可选项,是必选项。

4. 落地实操:从零搭一个能跑的最小协作系统

4.1 环境与依赖准备中最容易被忽略的两件事

搭这套系统,技术栈其实不挑,Python 或 Node 都能做。但有两件事新手几乎必踩:

第一,消息序列化格式要统一。Agent 之间传的如果是 JSON,就全程 JSON,别一会儿 JSON 一会儿 YAML 一会儿纯文本。格式不统一,解析代码会写到你怀疑人生。我建议用 JSON Schema 把每种消息类型的结构固定下来,投递前先校验。

第二,给每个 Agent 配独立的超时和重试配置,不要全局一刀切。接口设计 Agent 可能要想很久,实现 Agent 相对快,用同一个超时值要么误杀要么白等。

# 消息结构定义示例(基于常见实践的补充) AGENT_CONFIG = { "requirement_splitter": {"timeout": 90, "retry": 1, "output_type": "task_list"}, "interface_designer": {"timeout": 120, "retry": 2, "output_type": "interface_spec"}, "implementer": {"timeout": 60, "retry": 2, "output_type": "impl_artifact"}, "validator": {"timeout": 45, "retry": 1, "output_type": "diff_report"}, }

4.2 核心链路跑通的最小步骤

不要一上来就追求完整功能,先把"需求→设计→实现→校验"这条最短链路跑通。具体步骤:

  1. 定义四种消息类型的数据结构,写死字段;
  2. 实现一个最简单的内存消息总线,支持投递和订阅;
  3. 每个 Agent 先写成"假实现"——输入固定、输出固定,验证链路能通;
  4. 逐个把假实现替换成真实的模型调用;
  5. 加上超时、重试、熔断;
  6. 最后接共享状态和可观测日志。

这个顺序的核心逻辑是:先保证结构对,再保证内容对。很多人反过来,先调模型效果,结果链路结构一塌糊涂,模型再好也白搭。第 3 步的"假实现"特别重要,它让你在没有任何模型成本的情况下验证整个骨架。

4.3 跑通 Demo 之后立刻会遇到的三个坑

Demo 跑通那一刻很爽,但接下来三个坑几乎躲不掉:

坑一:消息顺序错乱。并行扇出时,多个 Agent 的结果到达顺序不确定,如果你的汇总逻辑假设了固定顺序,就会偶发错误。解决办法是给每条消息带时间戳和序号,汇总时显式排序。

坑二:上下文膨胀。每个 Agent 都往共享状态里塞东西,跑几十轮之后上下文大到模型处理不动。解决办法是给共享状态设"生命周期",任务完成后清理中间产物,只保留最终结果。

坑三:错误传播。上游 Agent 产出了一个格式正确但语义错误的结果,下游全盘接受,最后错误被放大。解决办法是在关键节点加"语义校验",比如接口设计出来后,用一个独立的校验 Agent 检查字段是否覆盖了需求里的所有实体。

5. 协作质量的真正瓶颈:不在模型,在接口契约

5.1 为什么"字段命名"这种小事能拖垮整个系统

我在多个项目里反复验证过一个结论:多智能体系统的失败,八成不是模型能力不够,而是接口契约没定清楚。字段命名、类型、必填性、默认值、边界条件——这些在人类团队里靠"沟通"解决的东西,在 Agent 之间必须靠"契约"解决。

举个真实感很强的例子:需求里写"用户可以有多个标签",接口设计 Agent 理解成tags: string[],实现 Agent 理解成tags: string(逗号分隔),校验 Agent 按数组校验,三方各说各话。这种问题在人类团队里一个 Slack 消息就解决了,在 Agent 之间如果没有契约,会一直错到联调。

所以 Agency Agents 里我强烈建议:接口定义一旦产出,就冻结成契约文件,所有下游 Agent 只读不改。要改可以,走"契约变更"流程,重新触发受影响的下游。

5.2 契约的版本管理:别让"改一个字段"引发雪崩

契约冻结之后必然会有变更需求。如果没有版本管理,改一个字段名可能导致所有下游 Agent 的产物全部失效。

我的做法是给契约打版本号,Agent 订阅时声明自己依赖哪个版本。契约升级时,新旧版本并存一段时间,下游 Agent 逐步迁移。这听起来像微服务的接口版本管理,没错,多智能体协作本质上就是一套微服务架构,只不过服务是 Agent。把微服务那套契约治理经验搬过来,能少走很多弯路。

5.3 校验 Agent 该怎么设计才不是摆设

很多项目的校验 Agent 形同虚设,因为它只做"格式对不对"的检查,不做"语义对不对"的检查。真正有用的校验要分三层:

  • 格式层:字段类型、必填性、枚举值是否合法;
  • 语义层:接口是否覆盖了需求里的所有实体和关系;
  • 一致性层:多个 Agent 的产物之间是否自洽(比如实现里用到的字段都在接口定义里存在)。

第三层最容易漏,也最容易出问题。我建议校验 Agent 的输出必须是一份可定位的差异报告,精确到"哪个字段、哪一行、期望什么、实际什么",而不是笼统地说"有问题"。差异报告越精确,修复 Agent 越好干活。

6. 实测中的意外与经验:那些文档不会写的事

6.1 模型调用成本会以你意想不到的方式增长

多智能体系统的成本不是线性的。角色越多、重试越多、上下文越大,成本涨得越快。我实测过一个四角色链路,正常跑一次的成本是单 Agent 的三到五倍,一旦触发重试,能到十倍以上。

控制成本的关键不是砍角色,而是砍无效上下文。每个 Agent 只给它完成任务必需的信息,不要把整个共享状态一股脑塞进去。我见过最夸张的配置是把所有历史消息都拼进提示词,成本高得离谱,效果还更差,因为模型被无关信息干扰了。

6.2 并行不一定比串行快

直觉上并行更快,但实测经常相反。原因是并行 Agent 之间如果有隐式依赖(比如都要读同一份共享状态),会产生锁竞争和等待,反而比串行慢。只有当子任务真正独立时才并行,判断标准是:两个 Agent 的输入集合有没有交集。有交集,就老老实实串行。

6.3 日志的可读性决定了你排查问题的速度

多智能体系统的日志如果只是流水账,排查问题时你会疯。我的经验是日志必须能回答三个问题:谁、在什么时间、基于什么输入、产出了什么输出。最好给每条日志带一个全局 trace_id,能把一次完整任务的所有消息串起来。这个投入在项目初期看起来多余,出第一个疑难 bug 时你就知道值了。

6.4 人工介入的口子一定要留

再智能的系统也需要人工兜底。Agency Agents 里我建议在调度器里预留"人工介入"状态:当自动修复连续失败、或者校验差异超过阈值时,系统暂停并通知人来看。不要追求 100% 自动化,追求"自动化处理 90% 的常规情况,把 10% 的疑难留给人",这才是可持续的落地方式。

7. 从能跑到好用:几个让系统更稳的进阶配置

7.1 给 Agent 加"置信度"输出

让每个 Agent 在产出结果时附带一个置信度(比如 0 到 1 的分数),调度器据此决定是否需要额外校验。置信度低的产物自动触发二次校验,置信度高的直接放行。这个机制能显著降低无效校验的开销,同时不放过可疑结果。

7.2 用"影子运行"验证新配置

想调整某个 Agent 的提示词或调度规则,别直接上线。让新旧配置同时跑,对比两者的产物差异,确认新配置确实更好再切换。这套"影子运行"的思路来自灰度发布,在多智能体系统里同样适用,能避免"改一处崩全局"。

7.3 定期回放历史任务做回归

把历史任务的消息序列存下来,定期用新配置回放,看结果是否退化。这相当于给协作系统做回归测试。我一般每周回放一批典型任务,一旦发现某个改动导致历史任务结果变差,立刻回滚。多智能体系统的改动影响面比单 Agent 大得多,回归测试不是可选项。

8. 我在这套系统上踩过的三个真实教训

第一个教训关于"过度拆分"。我曾经把一个本来一个 Agent 就能干好的任务拆成三个,理由是"职责更清晰"。结果三个 Agent 之间的消息传递和状态同步开销,比任务本身还大,整体耗时翻倍。后来合并回一个,效率立刻回来。拆分的唯一理由是"职责边界真的不同",而不是"看起来更专业"。

第二个教训关于"契约先行"。有个项目为了赶进度,先让 Agent 自由发挥,打算"后面再统一接口"。结果跑到一半发现各 Agent 的产物根本对不上,返工的成本远超当初定契约的时间。契约这件事,省下来的时间后面要加倍还回去。

第三个教训关于"可观测性投入"。早期觉得日志和 trace 是锦上添花,直到线上出了个偶发 bug,没有 trace 根本复现不了,排查了整整两天。从那以后我把可观测性当成第一优先级,宁可功能少做一点,也要把链路看清楚。

这套 Agency Agents 的架构和落地经验,说到底就一句话:多智能体协作的难点从来不在"智能",而在"协作"。把角色边界划清楚、把接口契约定死、把调度规则显式化、把熔断和可观测做扎实,剩下的模型能力问题反而好解决。反过来,如果协作结构一塌糊涂,再强的模型也只是在混乱里加速。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进