1. 从"一个人扛所有"到"一群Agent各司其职":这套协作系统到底在解决什么
如果你带过三人以上的开发团队,大概率经历过这种场面:需求评审时大家点头如捣蒜,真到排期落地,前端等后端接口、后端等数据库设计、测试等前后端联调,一条链路上任何一环卡住,整条线就停摆。更麻烦的是,很多决策散落在不同人的脑子里,没人说得清"这个字段当初为什么这么设计"。
Agency Agents 这套多智能体协作系统,本质上就是把上面这套"人类团队协作的痛点"搬到软件系统里,用一组职责明确的智能体(Agent)来模拟一个开发团队的运转方式。它不是那种"一个大模型包打天下"的方案,而是把需求分析、架构设计、编码实现、测试验证、代码审查这些环节拆开,每个环节交给一个专门的 Agent,再通过一套调度机制把它们串起来。
我第一次接触这类系统的时候,直觉反应是"这不就是把 prompt 拆成几段分别调用吗"。但真正跑起来才发现,难点根本不在单个 Agent 的能力,而在于它们之间怎么传递上下文、怎么处理冲突、怎么在某个环节失败时优雅降级。这跟带团队是一模一样的道理——单兵作战能力强的人凑在一起,如果没有好的协作机制,产出可能还不如一个中等水平但配合默契的小组。
这篇文章适合三类人看:一是正在做 AI 应用落地、想把单点能力扩展成流水线的开发者;二是对多智能体架构感兴趣、想搞清楚"编排层"到底在做什么的技术负责人;三是单纯好奇"AI 团队协作"这件事到底靠不靠谱的从业者。我会从架构分层、Agent 角色设计、上下文传递、冲突消解、落地踩坑几个角度,把这套系统拆开讲透,尽量给到可以直接参考复现的细节。
需要先说明一点:多智能体协作目前还没有"标准答案",不同团队的实现差异很大。下面讲到的很多设计选择,是基于我在实际项目中反复试错后总结出的常见实践,不代表唯一正确路径,但至少是踩过坑之后相对稳的那条路。
2. 编排层才是真正的核心:Agency Agents 的分层架构拆解
2.1 为什么不能把所有逻辑塞进一个 Agent
很多人做多智能体,第一步就走偏了:把系统提示词写得巨长,试图让一个 Agent 同时干需求分析、写代码、做测试。结果就是它在每个环节都表现平平,而且一旦某一步出错,你根本不知道是哪段逻辑出了问题。
Agency Agents 的做法是分层解耦。整个系统大致分成四层,从下往上依次是:
| 层级 | 职责 | 典型组件 |
|---|---|---|
| 模型接入层 | 统一封装不同模型的调用、重试、限流 | 模型适配器、Token 计数器 |
| Agent 能力层 | 定义单个 Agent 的角色、工具、输出格式 | 角色定义、工具绑定、输出解析器 |
| 编排调度层 | 决定谁先谁后、任务怎么分发、结果怎么汇总 | 调度器、状态机、消息总线 |
| 应用接口层 | 对外暴露 API、Webhook、可视化面板 | 任务提交接口、进度查询、日志 |
这个分层最大的好处是每一层可以独立替换。比如你今天用某个模型,明天想换另一个,只需要改模型接入层;今天用线性流程,明天想改成带反馈回路的流程,只需要改编排调度层,Agent 本身的定义不用动。
我见过不少项目把"调用哪个模型"硬编码在 Agent 逻辑里,结果想换个模型要改十几个文件,这就是分层没做好的典型症状。
2.2 编排调度层的三种常见形态
编排层是整个系统的大脑,它的设计直接决定了系统的灵活性和复杂度。实践中主要有三种形态,复杂度依次递增:
第一种是线性流水线(Pipeline)。需求 Agent 输出交给架构 Agent,架构 Agent 输出交给编码 Agent,一路往下。优点是简单、可预测、好调试;缺点是没有任何反馈,前面错了后面只能跟着错。适合流程固定、容错要求不高的场景。
第二种是带反馈的状态机(State Machine)。每个 Agent 完成后,调度器根据输出质量决定是"继续下一步"还是"打回上一步重做"。比如测试 Agent 发现代码有 bug,就把任务退回给编码 Agent,并附上失败原因。这种形态能显著提升最终产出质量,但需要设计好状态转移条件和最大重试次数,否则容易陷入死循环。
第三种是黑板模式(Blackboard)。所有 Agent 共享一块"黑板"(通常是结构化的共享内存或数据库),每个 Agent 读取自己需要的信息、写入自己的产出,调度器根据黑板上的状态决定激活哪个 Agent。这种模式最灵活,适合任务边界模糊、需要多个 Agent 反复协作的场景,但实现复杂度也最高,调试起来相当痛苦。
我的建议是:从线性流水线起步,跑通之后再逐步引入反馈。一上来就上黑板模式,大概率会在调试阶段耗光耐心。
2.3 状态管理:被低估的"记忆"问题
多智能体系统里,状态管理是最容易被忽视、又最容易出问题的地方。每个 Agent 执行时都需要知道"之前发生了什么",这个"之前"包括:原始需求是什么、上一个 Agent 产出了什么、有没有失败记录、当前是第几次重试。
常见的做法是维护一个任务上下文对象(Task Context),结构大致如下:
{ "task_id": "task-20240101-001", "original_requirement": "用户原始需求文本", "current_stage": "coding", "retry_count": 1, "artifacts": { "requirement_spec": "需求分析产出", "architecture_doc": "架构设计产出", "code_files": ["file1.py", "file2.py"], "test_report": null }, "history": [ {"agent": "requirement_agent", "status": "success", "timestamp": "..."}, {"agent": "coding_agent", "status": "failed", "reason": "..."} ] }这个对象在每次 Agent 调用时被传入,Agent 执行完再把更新后的版本写回。关键点在于:不是所有历史都要塞给每个 Agent。给编码 Agent 传需求文档和架构文档就够了,把测试报告也塞进去只会浪费 Token 还干扰判断。所以上下文需要做按需裁剪,这也是编排层的一个重要职责。
提示:上下文裁剪策略建议做成可配置的,不同 Agent 配置不同的"可见字段"。硬编码裁剪逻辑,后期改起来会很痛苦。
3. 角色设计:每个 Agent 该配什么"人设"和"工具箱"
3.1 角色定义的三要素:职责、输入、输出
一个 Agent 的定义,本质上就是回答三个问题:它负责什么、它需要什么、它产出什么。这三要素必须写得极其明确,含糊一点都会导致协作出问题。
以需求分析 Agent 为例,它的定义大致是这样:
- 职责:把用户模糊的自然语言需求,拆解成结构化的功能点列表,标注优先级和依赖关系。
- 输入:用户原始需求文本、可选的业务背景说明。
- 输出:固定格式的 JSON,包含功能点数组,每个功能点有 id、描述、优先级、依赖项。
这里有个关键经验:输出格式一定要强制结构化。如果你让 Agent 自由发挥输出一段文字,下游 Agent 解析起来会非常痛苦,而且容易解析失败。用 JSON Schema 约束输出,配合模型的"结构化输出"能力,稳定性会高很多。
我踩过的一个坑是:早期让架构 Agent 输出 Markdown 格式的设计文档,结果编码 Agent 解析时经常把标题层级搞错,导致模块划分混乱。后来改成 JSON 描述模块和接口,问题立刻消失。
3.2 工具绑定:给 Agent 装上"手"
光有大脑没有手,Agent 只能"纸上谈兵"。工具绑定就是给 Agent 装上能实际操作的手。常见的工具包括:
- 文件读写工具:让编码 Agent 能真正创建、修改文件。
- 命令执行工具:让测试 Agent 能跑测试命令、看结果。
- 检索工具:让所有 Agent 能查文档、查历史代码。
- 代码分析工具:让审查 Agent 能做静态检查。
工具绑定的原则是最小权限。编码 Agent 需要写文件权限,但需求 Agent 完全不需要;测试 Agent 需要执行命令,但最好限制在测试命令白名单内。权限给太大,一个 Agent 出错可能污染整个工作区。
# 工具注册的简化示意 agent_tools = { "requirement_agent": ["read_file", "search_docs"], "coding_agent": ["read_file", "write_file", "search_docs"], "test_agent": ["read_file", "run_command"], "review_agent": ["read_file", "static_analyze"] }3.3 角色之间的"接口契约"
多智能体协作最容易出问题的地方,就是角色之间的接口没定义清楚。A Agent 以为输出的是列表,B Agent 以为收到的是字典,一跑就崩。
解决办法是给每对相邻的 Agent 定义明确的"接口契约":上游输出什么结构、下游期望什么结构、字段含义是什么、必填还是选填。这个契约最好用代码里的数据类(dataclass)或 Schema 固化下来,而不是靠文档约定。
| 上游 Agent | 下游 Agent | 传递字段 | 格式要求 |
|---|---|---|---|
| 需求 Agent | 架构 Agent | 功能点列表 | JSON 数组,每项含 id/desc/priority |
| 架构 Agent | 编码 Agent | 模块与接口定义 | JSON,含 modules 和 interfaces |
| 编码 Agent | 测试 Agent | 文件路径列表 | 字符串数组 |
| 测试 Agent | 审查 Agent | 测试报告 | JSON,含 passed/failed/errors |
这张表看起来简单,但真正落地时,很多团队就是缺了这么一张表,导致联调阶段反复扯皮。
4. 上下文如何在 Agent 之间流转而不"失真"
4.1 信息衰减:为什么传着传着就变味了
多智能体系统有个隐蔽但致命的问题:信息在传递过程中会衰减。原始需求里一句"要支持批量操作",传到架构 Agent 变成"支持批量接口",传到编码 Agent 变成"实现一个批量方法",传到测试 Agent 可能就只剩"测试批量功能"——具体批量多少条、要不要分页、失败怎么处理,全丢了。
这跟人类团队里"传话游戏"是一个道理。解决办法有两个方向:
一是关键信息不靠传递,靠共享。把原始需求、核心约束放在共享上下文里,每个 Agent 都能直接读到原文,而不是读上游的"转述"。这样即使中间环节有损耗,源头信息始终在。
二是传递时附带"不可省略字段"。在接口契约里明确规定哪些字段必须原样传递、不允许改写。比如需求 ID、约束条件、验收标准,这些字段在每一跳都必须保留。
4.2 上下文窗口的取舍策略
上下文窗口是有限资源,不可能把所有历史都塞进去。实践中我总结了一个优先级排序:
- 原始需求:永远保留,这是"宪法"。
- 当前阶段的直接输入:必须保留。
- 最近一次失败的原因:如果处于重试状态,必须保留。
- 更早的历史产出:按需保留,能摘要就摘要。
- 无关 Agent 的产出:直接丢弃。
具体操作上,可以给每个 Agent 配置一个"上下文模板",明确它能看到哪些字段。这样既控制了 Token 消耗,又避免了信息干扰。
注意:不要用"把历史全部摘要成一段话"这种偷懒做法。摘要会丢失细节,而很多 bug 恰恰藏在细节里。宁可精确裁剪,不要粗暴摘要。
4.3 失败重试时的上下文处理
重试是最考验上下文设计的场景。当测试 Agent 发现 bug 打回给编码 Agent 时,编码 Agent 需要知道:上次写了什么、这次哪里错了、错误信息是什么。
如果只是简单地把"测试失败"四个字传回去,编码 Agent 根本不知道该改什么。正确的做法是传递结构化的失败信息:
{ "retry_reason": "test_failure", "failed_cases": [ {"case": "批量插入1000条", "error": "超时", "expected": "<5s", "actual": "12s"} ], "previous_code_ref": "commit-abc123", "suggestion": "考虑分批提交或加索引" }有了这些信息,编码 Agent 才能有针对性地修改,而不是盲目重写。这也是为什么我一直强调失败信息要结构化——它是重试能否成功的关键。
5. 冲突消解:当两个 Agent 意见不一致时听谁的
5.1 冲突的三种典型来源
多智能体系统里,Agent 之间产生冲突是常态,不是异常。常见的冲突来源有三类:
第一类是目标冲突。架构 Agent 追求"设计优雅、扩展性好",编码 Agent 追求"快速实现、少写代码",两者天然有张力。架构 Agent 说"这里要抽象一个接口层",编码 Agent 觉得"直接写死不就完了"。
第二类是事实冲突。两个 Agent 对同一个事实的判断不一致。比如需求 Agent 认为"用户要的是 A 功能",架构 Agent 理解成"用户要的是 B 功能"。
第三类是资源冲突。多个 Agent 想同时修改同一个文件,或者都想占用同一个工具资源。
5.2 优先级裁决机制
解决冲突最直接的办法是预设优先级。在系统设计阶段就明确:当架构 Agent 和编码 Agent 意见不一致时,以架构 Agent 为准;当需求 Agent 和架构 Agent 冲突时,以需求 Agent 为准(因为需求是源头)。
这个优先级链本质上是"谁更接近原始需求,谁的话语权更大"。听起来简单,但落地时必须写进调度逻辑里,不能靠"希望它们自己协商"。
| 冲突类型 | 裁决规则 | 兜底方案 |
|---|---|---|
| 需求 vs 架构 | 需求优先 | 人工介入 |
| 架构 vs 编码 | 架构优先 | 记录分歧,人工复核 |
| 编码 vs 测试 | 测试优先(有 bug 就得改) | 重试上限后人工介入 |
| 任意 vs 审查 | 审查优先(质量红线) | 人工介入 |
5.3 引入"仲裁 Agent"的时机
当冲突频繁发生、且优先级规则无法覆盖时,可以考虑引入一个专门的仲裁 Agent。它的职责是读取冲突双方的论据,结合原始需求做出裁决。
但我要泼盆冷水:仲裁 Agent 不是万能药。它本身也是模型,也会犯错,而且引入它会让系统复杂度再上一个台阶。我的经验是,只有当冲突模式稳定、可枚举时,才值得引入仲裁;如果冲突五花八门,不如老老实实加人工介入点。
一个折中方案是:让仲裁 Agent 只做"分类",不做"裁决"。它判断冲突属于哪一类,然后按预设规则处理,处理不了的才升级给人工。这样既降低了仲裁的难度,又保留了灵活性。
6. 落地实录:从 Demo 到稳定运行踩过的五个坑
6.1 坑一:Agent 输出格式不稳定
最开始跑 Demo 时,编码 Agent 十次里有三次输出的 JSON 解析失败——要么多了个逗号,要么字段名拼错,要么干脆输出了一段解释文字。下游 Agent 直接崩。
根因:模型的结构化输出能力不是 100% 可靠的,尤其是输出内容较长时。
解决:三层防护。第一层,用模型原生的结构化输出能力(如 JSON mode);第二层,加输出校验和自动修复(比如用 JSON 修复库);第三层,解析失败时自动重试,并在重试提示里附上"上次输出格式错误"的说明。三层下来,解析成功率从 70% 提到了 99% 以上。
6.2 坑二:无限重试烧光预算
有一次测试 Agent 和编码 Agent 陷入了"改-测-改-测"的死循环,一晚上跑了上百轮,Token 消耗直接爆表。
根因:没有设置重试上限,而且失败原因没有收敛——每次都是同一个 bug 换个说法。
解决:设置双重上限。一是单个任务的最大重试次数(比如 3 次),二是整个任务的最大 Token 预算。任一超限就终止并转人工。另外,重试时要检测"失败原因是否重复",如果连续两次失败原因相同,说明 Agent 卡住了,直接终止。
6.3 坑三:上下文越传越乱
跑到后期,编码 Agent 收到的上下文里混进了大量无关信息,导致它开始"自由发挥",改了一堆不该改的文件。
根因:上下文裁剪策略太粗糙,把历史产出全塞进去了。
解决:给每个 Agent 定义精确的"可见字段白名单",白名单之外的字段一律不传。同时给上下文加"版本号",确保 Agent 读到的是最新版本,避免读到过期数据。
6.4 坑四:工具调用权限失控
测试 Agent 有一次执行了一条删除命令,把工作区里的文件删了。
根因:命令执行工具没有做白名单限制,Agent 可以执行任意命令。
解决:所有工具调用走白名单 + 沙箱。命令执行只允许特定前缀(如pytest、npm test),文件操作限制在指定目录内。这个坑的教训是:永远不要相信 Agent 会"自觉",权限必须从系统层面卡死。
6.5 坑五:日志缺失导致无法复盘
系统跑挂了几次,但因为没有详细日志,根本不知道是哪一步出的问题,只能重跑,浪费大量时间。
解决:给每个 Agent 的每次调用都记录完整日志:输入上下文、输出内容、耗时、Token 消耗、工具调用记录、状态转移。日志按 task_id 聚合,出问题时能完整回放整个执行链路。这个投入在后期排查问题时回报巨大。
7. 性能与成本:多智能体系统的现实账本
7.1 Token 消耗的构成分析
多智能体系统的成本,主要来自 Token 消耗。它的构成和单 Agent 系统很不一样:
- 重复上下文消耗:每个 Agent 都要读一遍原始需求,这部分是重复的。
- 传递消耗:Agent 之间的产出传递,本身也是 Token。
- 重试消耗:失败重试会成倍放大消耗。
- 工具调用消耗:检索、分析类工具也会消耗 Token。
实测下来,一个五 Agent 的流水线,完成一个中等复杂度任务,Token 消耗大约是单 Agent 方案的 3 到 5 倍。这个账要提前算清楚,别等账单出来才后悔。
7.2 降低成本的四个实操手段
手段一:模型分级。不是所有 Agent 都需要用最强的模型。需求分析和架构设计用强模型,格式转换、简单校验用轻量模型,成本能降一大截。
手段二:上下文缓存。原始需求这类不变的内容,可以用上下文缓存机制,避免每次都重新计算。
手段三:并行化。没有依赖关系的 Agent 可以并行执行,比如多个模块的编码可以同时进行,缩短总耗时。
手段四:结果复用。相似任务的产出可以缓存复用,比如同一类需求的架构设计模板。
| 优化手段 | 成本降幅(实测) | 实现难度 |
|---|---|---|
| 模型分级 | 30%-50% | 低 |
| 上下文缓存 | 15%-25% | 中 |
| 并行化 | 不降成本,降耗时 | 中 |
| 结果复用 | 20%-40% | 高 |
7.3 延迟与吞吐的权衡
多智能体系统天然比单 Agent 慢,因为要串行经过多个环节。如果对延迟敏感,可以考虑:把非关键环节(如审查)做成异步;把可以并行的环节并行;对简单任务走"快速通道",跳过部分 Agent。
但要注意,不要为了降延迟牺牲质量。审查环节省掉,短期看是快了,长期看是技术债。
8. 这套系统适合谁用,以及我个人的几点体会
多智能体协作系统不是银弹。它适合的场景是:任务复杂度高、环节多、对产出质量有要求、且能接受较高成本。如果只是简单的单点任务,用单 Agent 甚至直接调模型就够了,上多智能体纯属杀鸡用牛刀。
我在实际项目里最大的体会是:这套系统的成败,八成取决于编排层和接口契约的设计,而不是单个 Agent 有多聪明。很多团队把精力全花在调 prompt 上,结果协作机制一塌糊涂,产出自然好不了。反过来,即使单个 Agent 能力一般,只要协作机制设计得好,整体产出也能达到可用水平。
另一个体会是:人工介入点必须预留。不要指望系统全自动跑通所有任务,在关键节点(如架构定稿、最终验收)设置人工确认,既能保证质量,又能在系统出错时及时止损。全自动听起来很美,但真出了问题是灾难性的。
最后分享一个实用技巧:从最小的两 Agent 系统开始。先做"需求 + 编码"两个 Agent 的流水线,跑通、跑稳,再逐步加 Agent。一上来就搭五六个 Agent 的复杂系统,调试成本会让你怀疑人生。多智能体这东西,是"长"出来的,不是"设计"出来的。