1. 从单兵作战到团队协作:多智能体协同到底在解决什么问题
做过 AI 应用开发的人都有一个共同感受:单个 Agent 能做的事情,天花板其实很低。你给它一个提示词,它帮你写一段代码、查一条信息、生成一段文案,这都没问题。但一旦任务链条拉长,比如“先调研竞品、再输出技术方案、然后写代码、最后跑测试并生成报告”,单个 Agent 就开始顾此失彼了——上下文窗口不够用、注意力被稀释、前面步骤的输出到后面就“忘了”。
多智能体协同(Multi-Agent Collaboration)要解决的核心问题就一个:把一个复杂任务拆成多个子任务,交给不同的 Agent 分别负责,再通过一套协作机制把它们串起来。这跟人类研发团队的组织方式本质上是一回事——你不会让一个人同时做产品调研、架构设计、编码和测试,而是分成不同角色,各司其职,通过规范化的接口和流程来协作。
我最初接触多智能体是在一个代码辅助开发的项目里。当时的需求是:给定一个业务需求描述,自动生成后端接口代码、对应的单元测试、以及一份接口文档。单 Agent 方案跑下来,代码质量忽高忽低,测试覆盖率经常不达标,文档更是敷衍了事。后来改成三个 Agent 分别负责编码、测试和文档,每个 Agent 有自己的系统提示词和工具集,通过一个调度器来协调,输出质量立刻上了一个台阶。
这篇文章适合哪些人看?如果你正在做 AI 应用开发、Agent 开发,或者你是一个技术团队的负责人,正在考虑怎么把 AI 能力工程化地落地到研发流程里,那这篇内容应该对你有直接参考价值。我会从架构设计、核心细节、实操过程、常见问题四个维度展开,把多智能体协同从“概念”落到“能跑起来的工程方案”。
2. 多智能体协同的架构设计与核心思路拆解
2.1 为什么不是“一个更强的 Agent”而是“多个协作的 Agent”
很多人第一反应是:我把单个 Agent 的模型换得更强、上下文窗口扩得更大、提示词写得更精细,不就行了吗?为什么要搞多个 Agent?
这个问题我认真想过,也做过对比实验。结论是:单 Agent 的能力提升是线性的,而多 Agent 协作带来的能力提升是指数级的。原因有三:
第一,上下文隔离。每个 Agent 只需要关注自己那一部分信息,不需要把整个任务的上下文都塞进去。编码 Agent 只需要知道接口规范和数据结构,不需要知道竞品调研的细节。这样每个 Agent 的上下文利用率极高,不容易出现“注意力涣散”。
第二,角色专精。一个 Agent 如果同时被要求“既要严谨地写代码,又要发散地做创意”,它的提示词就会自相矛盾。而多 Agent 可以让每个 Agent 的提示词高度聚焦,编码 Agent 就强调代码规范和边界处理,测试 Agent 就强调覆盖率和异常场景。
第三,可验证性。多 Agent 之间可以互相校验。测试 Agent 发现编码 Agent 的输出有问题,可以打回去重做。这种“交叉验证”机制在单 Agent 模式下很难实现,因为它自己检查自己往往会“护短”。
用一个生活化的类比:单 Agent 就像一个全能但精力有限的人,多 Agent 就像一个分工明确的小团队。团队不一定每个人都是最强的,但协作起来能完成远超个人能力的任务。
2.2 三种主流协作拓扑:流水线、辩论式、层级式
多智能体协同不是只有一种模式。根据任务特点,常见的协作拓扑有三种:
流水线模式(Pipeline)是最简单也最常用的。Agent A 的输出作为 Agent B 的输入,B 的输出给 C,依次传递。适合步骤明确、依赖关系清晰的任务,比如“需求分析 → 代码生成 → 测试 → 文档”。优点是实现简单、调试方便;缺点是如果中间某个环节出错,后面全崩,而且没有反馈回路。
辩论模式(Debate)是让多个 Agent 对同一个问题给出各自的答案,然后通过投票或仲裁来选出最优解。适合需要高质量决策的场景,比如技术方案选型、代码审查。我试过用两个 Agent 分别扮演“支持者”和“反对者”来评审一个架构方案,效果比单 Agent 自说自话好很多,因为它被迫考虑了反面意见。
层级模式(Hierarchical)是有一个“管理者 Agent”负责拆解任务、分配子任务、汇总结果,下面有多个“执行者 Agent”各司其职。适合复杂的大型任务,比如一个完整项目的自动化开发。管理者 Agent 不直接干活,它只做调度和决策。
实际工程中,这三种模式往往是混合使用的。比如顶层用层级模式做任务拆解,每个子任务内部用流水线模式执行,关键决策点用辩论模式做质量把关。
2.3 通信机制:Agent 之间怎么“说话”
多 Agent 协同的另一个核心问题是通信。Agent 之间怎么传递信息、传递什么格式的信息、信息丢了怎么办,这些都需要设计。
最常见的通信方式是结构化消息传递。每个 Agent 的输出不是一段自由文本,而是一个结构化的 JSON 对象,包含任务状态、输出内容、置信度、依赖项等字段。这样做的好处是下游 Agent 可以程序化地解析,不需要用自然语言去“猜”上游的意思。
我踩过的一个坑是:早期让 Agent 之间用自然语言通信,结果上游说“这个接口大概需要三个参数”,下游理解成“必须三个参数”,最后生成的代码参数数量对不上。后来改成结构化消息,明确字段param_count: 3,问题就消失了。
另一种通信方式是共享内存/黑板模式。所有 Agent 共享一个工作区,每个 Agent 把自己的输出写到工作区的指定位置,其他 Agent 按需读取。这种方式适合 Agent 数量多、通信关系复杂的场景,但需要设计好读写锁和版本控制,否则容易出现数据竞争。
提示:通信协议的设计要遵循“最小必要信息”原则。上游 Agent 只传递下游 Agent 真正需要的字段,不要一股脑全传过去。信息越多,下游 Agent 的解析负担越重,出错概率也越高。
2.4 工具选型:从框架到自研的取舍
市面上已经有不少多智能体框架,比如 AutoGen、CrewAI、LangGraph 等。这些框架各有特点,但我在实际项目中的体会是:框架适合快速验证,生产环境往往需要自研或深度定制。
原因在于,框架提供的抽象层虽然方便,但当你需要精细控制 Agent 之间的通信时序、错误重试策略、上下文裁剪逻辑时,框架的“黑盒”就会成为障碍。比如某个框架默认在 Agent 之间传递完整对话历史,这在长任务中会导致 token 消耗爆炸,而你想改成只传递摘要,就得改框架源码。
我的建议是:先用框架跑通一个最小可行原型,理解多 Agent 协作的基本模式;然后在生产项目中,把框架中验证有效的部分(比如消息路由、状态管理)抽出来,结合自己的业务逻辑做定制化实现。这样既不会重复造轮子,也不会被框架绑死。
3. 核心细节解析与实操要点
3.1 Agent 角色定义:提示词怎么写才不“串味”
多 Agent 协同的第一步是定义每个 Agent 的角色。角色定义的核心是系统提示词(System Prompt)。一个好的角色提示词应该包含四个部分:
- 身份声明:你是谁,你的专业领域是什么。
- 职责边界:你负责什么,不负责什么。
- 输出规范:你的输出格式是什么,必须包含哪些字段。
- 协作约定:你如何与其他 Agent 交互,遇到问题找谁。
我见过很多项目在角色定义上偷懒,编码 Agent 的提示词就一句“你是一个程序员,请写代码”。这种提示词写出来的 Agent,行为极其不稳定。后来我把它改成:“你是一个后端开发工程师,专注于 Python FastAPI 框架的接口实现。你只负责根据接口规范生成代码,不负责测试和文档。你的输出必须是一个完整的 Python 文件,包含类型注解和异常处理。如果接口规范中有不明确的地方,你需要在输出的questions字段中列出,而不是自行假设。”
这样改完之后,编码 Agent 的输出质量明显提升,而且它不会再“越界”去写测试代码或者文档。
3.2 任务拆解粒度:拆到多细才算合适
任务拆解的粒度是一个需要反复调试的参数。拆得太粗,单个 Agent 的任务还是太复杂,质量上不去;拆得太细,Agent 之间的通信开销和协调成本会急剧上升。
我的经验法则是:每个子任务应该能在单个 Agent 的一次推理中完成,且输出结果可以被独立验证。比如“生成用户登录接口”这个任务,如果拆成“生成路由定义”“生成请求模型”“生成业务逻辑”“生成数据库操作”四个子任务,就太细了,因为这四个部分高度耦合,分开生成反而容易接口对不上。但如果拆成“生成登录接口代码”和“生成登录接口测试”两个子任务,就比较合适,因为代码和测试可以独立验证。
另一个技巧是:先粗拆,再根据实际运行情况细拆。不要一上来就追求完美的拆解方案,先跑起来,看哪个环节经常出错,再针对性地细化。
3.3 上下文管理:怎么防止 Agent “失忆”
多 Agent 协作中,上下文管理是最容易被忽视但又极其关键的环节。每个 Agent 在接收任务时,需要知道哪些信息是必要的,哪些是可以丢弃的。
我常用的策略是分层上下文:
| 上下文层级 | 内容 | 是否必须传递 |
|---|---|---|
| 全局上下文 | 项目背景、技术栈、编码规范 | 所有 Agent 共享 |
| 任务上下文 | 当前子任务的目标、输入、约束 | 仅当前 Agent 需要 |
| 历史上下文 | 前序 Agent 的输出摘要 | 按需传递 |
| 临时上下文 | 当前 Agent 的中间推理过程 | 不传递,仅本地使用 |
关键点是:历史上下文不要传原始输出,要传摘要。比如编码 Agent 的输出是一份 500 行的代码文件,传给测试 Agent 时不需要把 500 行全传过去,只需要传接口签名、关键数据结构和业务逻辑摘要。测试 Agent 根据这些信息生成测试用例,需要看完整代码时再通过工具去读取。
这样做的好处是 token 消耗大幅降低,而且下游 Agent 不会被无关细节干扰。
3.4 错误处理与重试:Agent 失败了怎么办
多 Agent 系统中,Agent 失败是常态而不是异常。模型输出格式错误、工具调用超时、任务理解偏差,这些都会导致 Agent 执行失败。关键是怎么处理。
我的方案是三级错误处理机制:
第一级是格式校验重试。Agent 输出后,先做格式校验(比如 JSON 是否能解析、必填字段是否齐全)。如果格式不对,把错误信息反馈给 Agent,让它重新输出。这一级通常能解决 60% 以上的问题。
第二级是任务重试。如果格式没问题但任务执行结果不符合预期(比如测试 Agent 发现代码有 bug),把测试报告反馈给编码 Agent,让它修复后重新提交。这一级设置最大重试次数,比如 3 次。
第三级是人工介入。如果重试 3 次仍然失败,系统标记该任务为“需要人工处理”,并生成一份详细的失败报告,包括每个 Agent 的输入输出、错误信息、重试历史。人工处理完后,可以把修正结果反馈回系统,让系统从中学习。
注意:重试次数不是越多越好。我试过把重试次数设成 10 次,结果一个简单的格式错误反复重试了 10 次,浪费了大量 token 和时间。后来改成 3 次,效率反而更高,因为大部分问题在前 3 次就能解决,解决不了的说明是系统性问题,需要人工介入。
4. 实操过程与核心环节实现
4.1 环境准备与基础框架搭建
假设我们要搭建一个“需求到代码”的多智能体协同系统,包含四个 Agent:需求分析 Agent、编码 Agent、测试 Agent、文档 Agent。下面是我实际使用的搭建流程。
首先确定技术栈。我选择 Python 作为主语言,因为 Agent 生态最丰富。核心依赖包括:
pip install openai langchain pydantic fastapiopenai用于调用大模型,langchain提供一些基础抽象(虽然我后面会自研大部分逻辑,但它的文档加载和文本分割工具很好用),pydantic用于定义结构化消息格式,fastapi用于暴露 HTTP 接口。
然后是目录结构:
multi_agent_system/ ├── agents/ │ ├── base.py # Agent 基类 │ ├── analyst.py # 需求分析 Agent │ ├── coder.py # 编码 Agent │ ├── tester.py # 测试 Agent │ └── writer.py # 文档 Agent ├── core/ │ ├── message.py # 消息定义 │ ├── orchestrator.py # 调度器 │ └── context.py # 上下文管理 ├── tools/ │ ├── file_io.py # 文件读写工具 │ └── code_runner.py # 代码执行工具 └── main.py # 入口这个结构的好处是每个 Agent 独立一个文件,方便单独调试和替换。调度器负责协调,不掺和具体业务逻辑。
4.2 消息格式定义与调度器实现
消息格式是整个系统的“血液”。我定义了一个通用的AgentMessage类:
from pydantic import BaseModel from typing import Any, Optional from enum import Enum class MessageType(str, Enum): TASK = "task" RESULT = "result" ERROR = "error" FEEDBACK = "feedback" class AgentMessage(BaseModel): msg_type: MessageType sender: str receiver: str task_id: str payload: dict[str, Any] context_summary: Optional[str] = None retry_count: int = 0payload是具体内容,不同 Agent 有不同的 schema。context_summary是给下游 Agent 看的上下文摘要。retry_count用于控制重试。
调度器的核心逻辑是一个状态机:
class Orchestrator: def __init__(self, agents: dict): self.agents = agents self.task_queue = [] self.results = {} def run(self, initial_task): self.task_queue.append(initial_task) while self.task_queue: msg = self.task_queue.pop(0) agent = self.agents[msg.receiver] try: result = agent.process(msg) self.results[msg.task_id] = result # 根据结果决定下一步 next_msgs = self.route(result) self.task_queue.extend(next_msgs) except Exception as e: if msg.retry_count < 3: msg.retry_count += 1 self.task_queue.append(msg) else: self.handle_failure(msg, e)这个调度器很简单,但足够跑通大部分场景。关键在route方法,它根据当前 Agent 的输出决定下一个 Agent 是谁、传递什么消息。
4.3 编码 Agent 的完整实现与参数选择
编码 Agent 是整个系统中最核心也最复杂的部分。下面是我实际使用的实现要点。
首先是系统提示词。我反复调试了十几版,最终稳定下来的版本包含以下关键指令:
你是一个资深后端开发工程师,专注于 Python FastAPI 框架。 你的任务是根据接口规范生成完整的接口代码。 要求: 1. 代码必须包含类型注解,使用 Pydantic 模型定义请求和响应。 2. 必须处理异常情况,返回统一的错误格式。 3. 必须包含日志记录,使用 logging 模块。 4. 如果接口规范中有不明确的地方,在输出的 questions 字段中列出,不要自行假设。 5. 输出格式为 JSON,包含 code、questions、dependencies 三个字段。然后是模型参数。我对比过不同参数组合的效果:
| 参数 | 取值 | 效果 |
|---|---|---|
| temperature | 0.2 | 代码稳定性最好,几乎不会出现语法错误 |
| temperature | 0.7 | 代码更有“创意”,但偶尔会出现不存在的库调用 |
| max_tokens | 4096 | 足够生成大部分单文件接口代码 |
| max_tokens | 8192 | 对于复杂接口更安全,但成本翻倍 |
最终我选择temperature=0.2、max_tokens=4096。代码生成任务不需要创意,需要的是稳定和准确。4096 对于单个接口文件足够了,如果接口特别复杂,我会在任务拆解阶段就把它拆成多个子任务。
4.4 测试 Agent 与反馈闭环的搭建
测试 Agent 的职责是验证编码 Agent 的输出。它的工作流程是:
- 接收编码 Agent 的代码和接口规范。
- 生成测试用例,覆盖正常场景和异常场景。
- 执行测试用例,收集结果。
- 如果测试不通过,生成反馈报告,发回给编码 Agent。
测试 Agent 的系统提示词重点强调“边界条件”和“异常场景”:
你是一个测试工程师,专注于接口测试。 你的任务是为给定的接口代码生成测试用例。 要求: 1. 必须覆盖正常场景、边界场景、异常场景。 2. 每个测试用例必须包含输入、预期输出、实际输出。 3. 如果测试不通过,必须给出具体的失败原因和修复建议。 4. 输出格式为 JSON,包含 test_cases、passed、failed、feedback 四个字段。反馈闭环的关键是反馈信息要具体。我早期让测试 Agent 只说“测试不通过”,编码 Agent 根本不知道哪里错了。后来改成必须包含“哪个测试用例失败了、输入是什么、预期是什么、实际是什么、可能的原因是什么”,编码 Agent 的修复成功率从 40% 提升到了 85%。
4.5 文档 Agent 与最终产物的组装
文档 Agent 相对简单,它的输入是编码 Agent 的代码和测试 Agent 的测试报告,输出是一份 Markdown 格式的接口文档。
文档 Agent 的提示词:
你是一个技术文档工程师。 你的任务是根据接口代码和测试报告生成接口文档。 要求: 1. 文档必须包含接口描述、请求参数、响应参数、示例请求、示例响应。 2. 必须包含测试覆盖率信息。 3. 输出格式为 Markdown。最终产物组装时,我把代码文件、测试文件、文档文件分别写入不同的目录,并生成一个manifest.json记录每个文件的来源 Agent 和生成时间。这样方便追溯和版本管理。
5. 常见问题与排查技巧实录
5.1 Agent 之间“踢皮球”怎么办
这是多 Agent 系统中最常见的问题之一。编码 Agent 说“接口规范不明确,我无法生成代码”,需求分析 Agent 说“我已经写得很清楚了,是编码 Agent 理解能力不行”。两个 Agent 互相推诿,任务卡死。
我的解决方案是引入仲裁机制。当两个 Agent 对同一个问题有分歧时,启动一个仲裁 Agent,它的职责是阅读双方的输出,判断责任方,并给出明确的裁决。仲裁 Agent 的提示词强调“基于事实,不偏袒任何一方”。
另一个技巧是在任务拆解阶段就消除歧义。需求分析 Agent 的输出必须经过一个“完整性校验”步骤,检查是否所有必要信息都已提供。如果缺少信息,需求分析 Agent 必须向用户提问,而不是把问题留给下游。
5.2 Token 消耗失控的排查与优化
多 Agent 系统的 token 消耗很容易失控。我遇到过一次,一个简单的任务消耗了 50 万 token,成本直接爆炸。排查后发现三个问题:
第一,上下文重复传递。每个 Agent 都把完整的历史对话传给下一个 Agent,导致信息重复。优化方案是只传摘要,原始信息存在共享存储中,按需读取。
第二,重试没有上限。某个 Agent 陷入死循环,反复重试同一个失败任务。优化方案是设置最大重试次数和总 token 预算,超预算直接终止。
第三,提示词过长。系统提示词写得太详细,每次调用都要消耗大量 token。优化方案是把提示词拆成“核心指令”和“参考文档”,核心指令每次都传,参考文档按需检索。
优化后,同样的任务 token 消耗降到了 8 万左右,成本降低了 80%。
5.3 Agent 输出格式不稳定的解决思路
即使提示词里明确要求输出 JSON,Agent 还是经常输出带 Markdown 代码块的 JSON,或者干脆输出一段自然语言。这个问题困扰了我很久。
后来我总结出三个有效手段:
手段一:使用结构化输出功能。如果模型支持 function calling 或 JSON mode,优先使用。这能从底层保证输出格式。
手段二:后处理解析。写一个健壮的解析器,能处理常见的格式偏差。比如自动去除 Markdown 代码块标记、自动补全缺失的括号、自动修正常见的 JSON 语法错误。
手段三:格式校验反馈。如果解析失败,把错误信息和原始输出一起反馈给 Agent,让它重新输出。通常一次反馈就能解决。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| Agent 输出为空 | 提示词冲突或模型超时 | 检查提示词是否有矛盾指令 | 简化提示词,增加超时时间 |
| Agent 输出格式错误 | 模型未遵循格式要求 | 打印原始输出检查 | 使用结构化输出,增加格式校验 |
| 任务卡死 | Agent 互相推诿 | 查看消息队列和重试记录 | 引入仲裁机制,设置超时 |
| Token 消耗过高 | 上下文重复传递 | 统计每个 Agent 的 token 消耗 | 使用摘要传递,设置预算 |
| 测试通过但代码有 bug | 测试用例覆盖不足 | 检查测试用例的边界条件 | 增强测试 Agent 的提示词 |
| 文档与代码不一致 | 文档 Agent 未读取最新代码 | 检查文档 Agent 的输入来源 | 确保文档 Agent 读取最终版本代码 |
5.5 几个让我少走弯路的实操心得
第一个心得:先跑通单 Agent,再扩展多 Agent。不要一上来就设计复杂的多 Agent 架构。先用单 Agent 把任务跑通,识别出单 Agent 的瓶颈在哪里,再针对性地拆分成多 Agent。这样每个 Agent 的存在都有明确理由,不会为了“多 Agent”而多 Agent。
第二个心得:日志要详细,但不要打印全文。每个 Agent 的输入输出都要记录,但记录的是摘要和关键字段,不是全文。全文存在单独的文件里,需要时再查。否则日志文件会大到无法阅读。
第三个心得:定期人工抽检 Agent 的输出。自动化系统跑久了容易“退化”,Agent 可能会形成一些错误的模式。我每周会抽检 10 个任务的完整执行记录,看看有没有异常模式。这个习惯帮我提前发现了好几个潜在问题。
第四个心得:版本控制要覆盖提示词。提示词的修改对 Agent 行为影响巨大,必须像代码一样做版本控制。每次修改提示词都要记录修改原因和效果对比,否则出了问题根本不知道是哪个版本引入的。
6. 多智能体协同的边界与我的个人体会
多智能体协同不是银弹。它适合的是任务链条长、角色分工明确、输出可验证的场景。如果你的任务本身很简单,或者输出质量很难量化评估,那多 Agent 反而会增加复杂度和成本。
我在实际项目中的体会是:多 Agent 系统的价值不在于“用了多 Agent 这个技术”,而在于它强迫你把任务拆解清楚、把接口定义明确、把验证机制建起来。这些工作即使不用多 Agent,对项目本身也是有益的。多 Agent 只是让这些工作变得更加必要和显性化。
另外,多 Agent 系统的调试成本比单 Agent 高一个数量级。单 Agent 出问题,你看一个输入输出就行;多 Agent 出问题,你要追踪多个 Agent 之间的消息流转,定位是哪个环节、哪个 Agent、哪次调用出了问题。所以配套的可观测性工具(日志、追踪、指标)必须跟上,否则系统一旦复杂起来就会变成黑盒。
最后分享一个我最近在尝试的方向:让 Agent 之间互相写“交接文档”。就像人类团队交接工作一样,上游 Agent 在完成任务后,写一份简短的交接说明,告诉下游 Agent “我做了什么、结果在哪里、有什么需要注意的”。这个简单的机制显著降低了 Agent 之间的信息丢失率,尤其是当任务链条超过三个 Agent 时,效果非常明显。