“单 Agent 不够用”之后:我们怎么把 MultiAgent 搬进生产环境
先交代一下背景。我在团队里主要负责复杂任务型 AI 应用的架构设计,上半年接手了一个偏“流程编排”的项目:用户给一句含糊的自然语言需求,系统要自动拆解成子任务、调用多个内部服务去执行、最后汇总成一份可用的交付结果。最初用单个大模型 Agent 硬扛,Prompt 写了十几版,效果始终不稳定——不是漏步骤,就是中间状态没人管,稍微复杂一点的链路,模型就开始“自由发挥”。
后来我们把架构切成了典型的企业级 MultiAgent 方案:顶层有一个负责“规划”的主 Agent,下面挂了一批负责“执行”的子 Agent,整个运行过程用 Plan 模式驱动。这套体系稳定运行到现在,我才敢说踩完了该踩的坑。这不是一篇讲概念的文章,重点全部放在落地细节上:Plan 模式到底怎么设计、主子 Agent 之间怎么分工、哪些地方最容易翻车、以及我亲手试出来的调优方法。团队正在做 Agent 项目、或者对 MultiAgent 只停留在 Demo 阶段的朋友,这篇可以直接当参考。
1. 企业级 MultiAgent 落地的核心思路
1.1 单 Agent 为什么撑不住复杂业务
先聊一个很多人都忽略的事实:单个 Agent 的能力上限,往往不在模型本身,而在“单线程的上下文处理方式”。你让一个 Agent 同时承担“理解需求、拆分任务、调用工具、检查结果、处理异常”这五件事,它很快就陷入一种尴尬境地——模型确实能输出看起来合理的下一步动作,但整个链路缺少真正的“状态控制”。
我举个例子你就明白了。我们内部有个数据报表生成场景,用户会这样描述:“把华东区最近三个月的销售和库存按周汇总,找出异常波动的品类,做成 PPT 格式。”
单个 Agent 收到这句话后,理论上应该依次执行:
- 查销售数据
- 查库存数据
- 按周做聚合
- 对比异常
- 生成 PPT
但实际运行中,单 Agent 经常出现的问题是两个极端。要么它把所有步骤压缩成一次“大而全”的调用,某个子任务失败就得整体重来;要么它在步骤之间“自我发挥”,比如查到一半觉得“库存数据可能不准”,就自己决定跳过对比分析。这种不确定性在 Demo 里问题不大,但在企业环境里就是事故——业务方要的是稳定、可追踪、每个环节都能复核的执行过程。
这就是我们决定上 MultiAgent 架构的根本原因:不是追新,而是单 Agent 无法提供企业级任务执行所需要的确定性和可观测性。
1.2 MultiAgent 到底解决什么问题
MultiAgent 架构的核心价值,说起来很简单:把一个复杂的任务,拆成多个有明确职责边界的子任务,每个子任务由独立的 Agent 负责,再通过协作机制组合成最终结果。
但这里有一个非常关键的区别,很多人没搞清楚——MultiAgent 不等于“多个 Agent 聊天”。如果你只是让几个 Agent 互相发消息、互相传递结果,那不叫架构,叫“群聊”。企业级 MultiAgent 一定要有清晰的组织关系和任务流向:
- 有一个角色负责“看全局”,也就是规划者(Planner/主 Agent)
- 有一批角色负责“干具体活”,也就是执行者(Worker/子 Agent)
- 有一层机制保证任务从“规划”到“执行”再到“验收”的闭环
这套设计直接带来三个单 Agent 实现不了的好处。
一是分工之后,每个 Agent 的 Prompt 可以做得非常“窄”。窄 Prompt 意味着更少的歧义、更稳定的输出、更容易调试。我们的子 Agent 几乎每个都只需要关注一件事情的输入输出,有的子 Agent Prompt 只有几十行,比原来单 Agent 数百行的“万能 Prompt”可控得多。
二是任务状态可追踪。主 Agent 规划出子任务后,每个子任务在系统中的执行状态都是明确的:等待中、执行中、成功、失败、重试中。这个状态是系统层面的,不依赖模型“记不记得住”。
三是单个环节可以独立优化。某个子 Agent 效果不好,直接替换那个子 Agent 的实现,不影响整体链路。这在工程上的收益是巨大的——我们的报表子 Agent 迭代了四次,主 Agent 一次都没改过。
1.3 什么时候应该考虑 Plan 模式
多 Agent 方案不是银弹,我见过不少团队一上来就奔着复杂架构去,结果被自己的设计拖垮。要不要用 Plan 模式、要不要上主子 Agent 协作,我建议你先用下面这个标准判断:
- 任务是否包含多个明确顺序依赖的步骤?
- 任务执行过程中是否需要动态决定下一步做什么?
- 单个 Agent 的上下文是否经常被中间过程撑爆?
- 业务方是否要求每个执行环节都可见、可查?
这四个问题里,如果命中两个以上,Plan 模式就是合适的。反过来,如果任务本身只有一两步、输入输出非常固定,老老实实用单个 Agent 加工具调用就好,不要为了架构而架构。
我们最终选择的方案是:主 Agent 负责任务理解与计划生成,子 Agent 负责具体执行,计划和执行之间通过一个明确的任务队列衔接。整个模式属于当前工业界落地性较强的“Plan-then-Execute”路线,后面我详细拆。
2. Plan 模式深度拆解
2.1 Plan 模式的核心机制
Plan 模式,英文全称通常叫 Plan-and-Execute(规划-执行),和另一种常见的 ReAct 模式(推理-行动)形成鲜明对比。ReAct 是“边想边做”,模型每走一步就想一下下一步干什么;Plan 模式是“先想清楚再做”,模型在真正执行之前,先产出一份完整的任务计划,然后按计划逐步执行。
打个生活的比方。ReAct 模式像一个路痴开车,每到一个路口,停下来看一眼导航再决定往哪走。Plan 模式像一个闭环计划的工程队,先由总工勘察现场、画出施工图、列出每个施工步骤和验收标准,然后交给各班组按图施工。遇到图纸之外的情况,再回到总工那里修订计划。
在企业场景下,Plan 模式的优势非常明显:
- 减少上下文漂移。执行阶段不需要反复把完整任务描述灌输给模型,只需要按计划逐项执行即可。
- 可预期性更强。计划本身可以展示给用户或业务方确认,也可以在系统里留档。
- 支持并行和重试。计划生成后,多个相互独立的子任务可以并行执行,某个失败也不需要重跑整个链路。
从技术实现上看,Plan 模式的完整生命周期是这样的:
用户请求输入 ↓ 主 Agent(规划器)分析请求 → 生成结构化的任务计划(Plan) ↓ 计划校验器(可选)检查计划是否覆盖用户需求 ↓ 任务分发:根据计划逐个/并行分发子任务给子 Agent ↓ 子 Agent 执行各自任务 → 产生结果回传 ↓ 主 Agent(汇总器)聚合所有子任务结果 → 生成最终回复 ↓ 中途异常 → 回到主 Agent 重新规划注意这里有个细节:主 Agent 实际上承担了两个角色——规划器和汇总器。在真正的工程实现里,这两个角色可以共用同一个模型实例,但建议在 Prompt 层面分开成两套指令,避免“既要又要”导致行为混乱。
2.2 Plan 的生成与结构化表示
Plan 模式最关键的一步是“计划生成”。它决定了整个任务执行的走向,如果计划错了,后面的执行再完美都没有用。
我在实践中最开始遇到的坑是:让模型自由输出计划。比如直接说“请列出完成这个任务的步骤”,结果模型输出的计划五花八门——有的是一段话,有的是几个词。这种非结构化输出没法被程序可靠地解析和分发。
后来我们定了硬性规范:计划必须以严格的 JSON 格式输出,并且每个计划项包含四个字段:
task_id:任务唯一标识,用于追踪状态description:这个子任务要做什么,描述要足够具体depends_on:前置依赖的任务 ID 列表,用于决定执行顺序assignee:负责执行的子 Agent 名称
实际的计划输出长这样:
{ "plan": [ { "task_id": "task1", "description": "从数据库中按华东区、近三个月条件查询销售明细数据", "depends_on": [], "assignee": "data_fetcher" }, { "task_id": "task2", "description": "从数据库中查询同期库存明细数据", "depends_on": [], "assignee": "data_fetcher" }, { "task_id": "task3", "description": "将 task1 和 task2 的数据按周维度聚合,计算周销售额和周库存量", "depends_on": ["task1", "task2"], "assignee": "data_processor" }, { "task_id": "task4", "description": "分析聚合数据,找出异常波动品类并生成说明", "depends_on": ["task3"], "assignee": "analyst" } ] }这套结构化格式带来一个额外的好处——主 Agent 生成计划后,程序可以自动做依赖检测。比如发现有任务循环依赖、或者依赖了不存在的任务 ID,可以及时让主 Agent 修正,而不是带着错误的计划往下跑。这在复杂任务场景里非常有必要。
2.3 计划执行的动态修订机制
很多人以为 Plan 模式是“一条路走到黑”,实际不是。企业级落地时,计划是“静态生成、动态修订”的。也就是说,主 Agent 先生成一份初步计划,但执行过程中如果发现某项任务结果异常,必须要有“回到规划层”的机制。
我们当时设计的修订流程有三类触发条件:
- 执行失败重试超过阈值。比如某个子 Agent 连续重试三次都没有成功返回数据,系统判定当前计划可能有问题。
- 执行结果校验不通过。子 Agent 返回了结果,但结果缺失关键字段、格式校验失败。
- 汇总阶段发现矛盾。主 Agent 汇总多个结果时,发现两份数据之间存在明显冲突,无法整合。
一旦触发修订,系统会把当前所有中间状态(已完成的任务、失败的原因、部分结果)打包传给主 Agent,要求它基于最新情况输出“修订版计划”。修订版的格式和初始计划完全一致,只是可能增加了新的任务、修改了某个任务的目标、或者调整了执行顺序。
这里有一个我强烈建议做的设计:修订时保留原始计划的任务 ID 前缀,只做增量变更。比如原本是 task1 到 task4,修订后新增的从 task5 开始编号,已经完成的 task1 到 task3 不必重新执行。这样能极大节约成本和时间,也方便做执行历史审计。
3. 主子 Agent 协作机制设计
3.1 主 Agent 与子 Agent 的职责边界
讲完 Plan 模式,接下来到主从 Agent 协作的部分。这套协作机制的设计原则其实非常简单,就一句话:主 Agent 做决策,子 Agent 做执行,两者职责严格分离。
主 Agent(主管/Planner)的工作清单:
- 理解用户原始意图,判断任务类型
- 生成初始执行计划和修订计划
- 把子任务分发给对应的子 Agent
- 收集子 Agent 结果并进行汇总校验
- 在最终回复中组织语言,给用户一个可读的答案
子 Agent(执行者/Worker)的工作清单:
- 接收一个明确的、独立的子任务
- 在自身职责范围内调用必要的工具和数据源
- 返回结构化结果给主 Agent
- 在执行失败时返回错误码和错误描述
这种职责分离带来的最直接好处是:你不必再担心“Agent 之间的上下文污染”。主 Agent 不需要知道子 Agent 内部究竟调用了什么 API、查询了什么表、传了什么参数,只需要看到子 Agent 返回的标准化结果。子 Agent 也不需要理解用户的完整需求,只需要聚焦在自己那一个步骤上。
3.2 主子 Agent 之间的通信协议
主子 Agent 之间用什么方式通信,是一个被很多人低估的设计点。我见过一些项目直接让主 Agent 把任务描述用纯文本发给子 Agent,然后子 Agent 也用自由文本回复。这种方式在 Demo 阶段跑得通,但进入企业级就会发现:自由文本通信意味着无法可靠解析,无法自动校验,更无法做流程追踪。
我们最后定了一套“任务单 + 结果单”的通信协议,本质上是把任务下发和结果回传都格式化成 JSON。
任务单的格式:
{ "instruction": "查询华东区近三个月的销售明细数据", "constraints": { "time_range": "2026-01-01~2026-03-31", "region": "华东区", "fields": ["date", "category", "sales_amount", "inventory"] }, "output_format": "json_array", "deadline_seconds": 30 }结果单的格式:
{ "status": "success", "data": [...], "summary": "共返回 1280 条销售记录,覆盖 15 个品类", "error_code": null, "metrics": { "cost": 0.032, "latency_ms": 2140 } }定义清晰的协议后,任何子 Agent 的替换都变得异常轻松。我们后来把一个自研的数据查询 Agent 替换成了基于某个外部服务封装的 Agent,主逻辑零改动,只需要让新子 Agent 符合同一个“任务单 → 结果单”协议。
3.3 任务分发和并行控制
企业级场景中,任务的并行执行是提升整体吞吐量的关键手段。我们最初是“串行执行计划里的每个任务”,效果当然正确,但效率不高——用户要等十几秒才能得到结果。后来改造成“按依赖关系做并行调度”,效果立竿见影。
并行控制的核心是依赖图(DAG)调度。主 Agent 输出计划后,程序先解析depends_on字段构建依赖图,然后按拓扑序执行:没有依赖的任务先跑,依赖完成的任务随后跑。
实际执行的效果是:一个包含 6 个子任务的计划,如果有两个任务是独立的数据查询,它们可以并行执行;如果有三个任务依赖同一份数据,则它们必须等前面那个数据任务完成后再并行执行。
不过我要提醒一句:并行执行不是越多越好。我实测下来,并行度设置为 3 到 4 通常性价比最高。原因很现实:
- 并行度太高,底层数据源和 API 容易被并发打满
- 并行度太高,如果某个任务失败引起重试,和并行任务之间的竞争关系会更难排查
- 成本控制上,并行任务的 token 计费是叠加的,需要评估性价比
3.4 汇总与结果整合策略
子 Agent 都执行完后,主 Agent 的最后一个核心动作是“汇总”。这个环节的难度经常被低估,因为多个子 Agent 返回的结果之间可能存在冲突、缺失、格式不一致等问题。
我在实践中把汇总策略分成两层。
第一层是程序级合并。这一步不依赖模型,用代码把子 Agent 返回的结果统一合并成一个结构化的总结果集。比如所有子 Agent 返回的都是 JSON 数组,程序先做数组合并、字段对齐、缺失字段填充。这样主 Agent 拿到的汇总输入是规整的,而不是一堆乱七八糟的原始结果。
第二层是模型级汇总。主 Agent 基于已规整的结果,站在“全局视角”写一个连贯的交付文本。这一步的本质是“写作”而不是“拼装”,所以 Prompt 里要强调:
- 不要重复罗列所有数据,要提炼关键信息
- 如果多个子任务结果之间存在矛盾,要明确说明
- 最终输出要覆盖用户原始需求中的每一个要点
4. 实操落地过程全记录
4.1 框架选型与技术栈方案
聊完了设计,我们来点实操的。开头先回答一个很多人问我的问题:多 Agent 框架用什么?
市面上的主流选择有 LangGraph、AutoGen、CrewAI,以及自研编排。我们的选择是基于 LangGraph 做底层编排,自研上层业务逻辑。原因有三点:
一是 LangGraph 对“状态机”的支持做得比较成熟。它允许你定义一个图结构,节点是 Agent 或工具,边是状态转移条件。这和我们前面讲的“计划生成 → 任务分发 → 子 Agent 执行 → 汇总”这个流程天然匹配。
二是 LangGraph 支持显式的状态持久化。每轮执行的中间状态可以保存下来,计划、任务状态、执行结果都可以随时回溯。这是 LangGraph 相比 AutoGen 等框架在企业场景下最大的优势。
三是上层的业务逻辑我们希望完全可控,比如用户认证、权限管理、敏感信息脱敏、数据查询逻辑,这些不适合写在框架层,应该自研。
我们的技术栈大概是这样:
| 组件 | 选型 | 说明 |
|---|---|---|
| 编排引擎 | LangGraph | 负责 Agent 节点调度、状态管理 |
| 大模型 | GPT-4o / 内部私有化模型 | 两个模型轮询,根据任务类型切换 |
| 服务框架 | FastAPI | 提供对外 API 接口 |
| 数据存储 | PostgreSQL + Redis | PostgreSQL 存执行记录,Redis 存实时状态 |
| 任务队列 | Redis Stream | 子任务分发和状态同步 |
| 监控 | LangSmith + Prometheus | 链路追踪、性能指标采集 |
4.2 主 Agent 的 Prompt 设计实战
这是整篇文章里最值得抄作业的部分。放一个我们主 Agent 的 Prompt 核心结构,你可以直接改改拿去用:
你是一个企业级任务规划器。用户会提出一个复杂需求,你需要完成以下工作: 第一步:理解用户需求,判断是否涉及多个子任务。 第二步:如果任务简单,只需要一个步骤,直接输出单任务计划。 第三步:如果任务复杂,将任务拆解成多个子任务。 输出要求: 1. 严格输出 JSON 格式,不允许输出任何解释文字。 2. JSON 必须包含 plan 数组,数组中的每个元素包含: - task_id:格式为 task1、task2、task3... - description:子任务的具体描述,必须包含执行所需的关键参数 - depends_on:前置任务 ID 数组,无依赖则为空数组 - assignee:建议负责执行的子 Agent 名称 3. 子任务拆解要遵循以下原则: - 每个子任务必须有独立、可验证的交付物 - 子任务之间不能有职责重叠 - 数据获取类任务优先拆到最前 - 分析、生成类任务依赖数据任务注意一个细节:我们没有让主 Agent 在同一个 Prompt 里既规划又汇总,而是准备了两个独立的 Prompt——PLANNER_PROMPT和SUMMARIZER_PROMPT。在 LangGraph 里,同一个模型节点根据当前阶段加载不同的 Prompt 模板。这个设计让模型的每一次调用目标都非常纯粹,实测下来比一个万能 Prompt 的效果稳定很多。
4.3 子 Agent 实现与工具挂载
子 Agent 的实现相对简单,因为它们职责单一。我以我们项目里的data_fetcher为例,它的完整功能就是:根据任务单里的参数查数据库,返回 JSON 数组。
这个子 Agent 的 Prompt 非常“窄”:
你是一个数据查询助手。你会收到一个 JSON 格式的任务单,任务单中包含查询所需的时间范围、区域、字段列表。 请根据任务单中的参数,判断需要调用哪个数据查询工具,并生成工具调用的参数。 工具会返回查询结果。你需要做以下处理: 1. 如果查询成功,将结果整理成 JSON 数组,并按任务单中的字段要求返回 2. 如果查询失败,不要重试超过 2 次,直接返回 status 为 failed 的结果单 3. 所有返回必须符合 result 格式为了让子 Agent 真正能干活,还需要给它挂载工具。这里我们遇到过一个非常典型的坑:把数据库连接字符串直接暴露给子 Agent,让子 Agent 自己去生成 SQL 去查。结果非常危险——模型生成的 SQL 偶尔会漏掉WHERE条件,直接把整张表查出来,差点搞挂数据库。
后来我们改了方案:不直接暴露数据库连接,而是封装成受限的查询接口,例如query_sales(start_date, end_date, region)。这样模型只能传参数,不能碰 SQL。这个方法强烈建议各位参考——Agent 手里的工具越受限,系统越安全。
4.4 状态管理与执行链路追踪
企业级落地还有一个绕不开的话题:状态管理。说白了,就是发生问题的时候,你能不能说清楚“这个任务执行到哪一步了?每一步什么结果?哪一步花了多少钱?”
我们当时落地了一套三层的状态追踪方案:
第一层:每次请求一个全局 request_id。用户发起请求时生成,整个执行链路的所有日志、状态记录都带上这个 ID。
第二层:任务级别的状态记录。每个子任务执行完成后,往 PostgreSQL 写入一条记录,包含 request_id、task_id、状态、开始时间、结束时间、token 消耗、子 Agent 名称。
第三层:实时缓存。执行中的状态实时写到 Redis,前端页面可以通过轮询或 WebSocket 看到“正在执行哪个步骤”,提升用户等待时的体验。
这个三层方案的好处是:既保证了执行结束后的历史可审计,又保证了执行过程中的实时可视化。我们排查线上问题的时候,基本都是靠 request_id 直接拉出来整条链路的日志,定位效率非常高。
5. 常见问题与排查技巧实录
5.1 计划生成阶段的典型问题
问题一:主 Agent 拆解任务过多或过少
现象:用户需求很简单,主 Agent 拆出了十几个子任务;或者需求很复杂,只拆出了两三个。这种情况下,执行效率和结果质量都会受影响。
排查思路:先看主 Agent 的输出日志,确认拆解数量是否异常。如果拆解过多,通常是因为 Prompt 里没写“最小任务拆解原则”;如果拆解过少,通常是因为主 Agent 没有充分理解用户意图,我们可以要求它在输出计划前先写一句“我对用户需求的理解”。
问题二:计划格式解析失败
现象:主 Agent 偶尔输出 JSON 前面带了一段解释文字,或者 JSON 里混入了注释,导致程序解析失败。
解决方案:两招,第一是代码层面容错——用正则把输出里的 JSON 部分提取出来再解析;第二是 Prompt 层面强调“禁止输出任何非 JSON 内容”。两招缺一不可,只靠 Prompt 无法 100% 保证,代码容错必须兜底。
5.2 子 Agent 执行阶段的典型问题
问题三:子 Agent 结果与任务描述不符
现象:data_processor需要按周聚合数据,结果返回的数据还是日维度的。
排查思路:这种情况往往是子 Agent 的 Prompt 对“输出格式”的描述不够具体。我们的经验是:子 Agent 的 Prompt 里必须给出“输入样例”和“输出样例”,不能只写抽象要求。一个具体的样例比十句抽象描述都管用。
问题四:执行阶段上下文爆炸
现象:某些子 Agent 需要处理大量数据,比如一次查询返回了上万条记录,导致该子 Agent 的上下文窗口被占满,后续步骤质量急剧下降。
排查思路:这个问题非常典型。我们最后的方案是在子 Agent 前面加一个“数据预处理节点”,先用代码对结果做筛选、截断、聚合,只保留核心数据再交给模型分析。原则是:能让代码做的事,绝不让模型做。
5.3 协作链路的典型问题
问题五:计划修订陷入循环
现象:修订计划执行到一半又触发修订,连续四五次都搞不定,看起来像一个死循环。
排查思路:这是最棘手的问题之一。我们在设计上加了“最大修订次数”限制,默认是 2 次。超过次数后,系统强制终止执行,返回“当前任务过于复杂,需要人工介入”的提示,并附上最后的执行记录。宁可让用户知道任务失败了,也不能让它无限消耗算力。
问题六:主子 Agent 之间信息传递丢失
现象:子 Agent 执行结果里有用的信息,汇总阶段没有被主 Agent 采用。
排查思路:这类问题往往出在“结果单”的摘要字段太简略。我们后来给子 Agent 加了一个强制要求:summary 字段必须包含关键数字和结论,不能只写“执行成功”。比如“共返回 1280 条记录,覆盖 15 个品类,同比异常品类 3 个”——这种摘要越具体,主 Agent 在汇总时的利用率越高。
6. 写在最后的实操经验
从单 Agent 改造成 MultiAgent 架构到现在,个人最大的感受是:这套架构真正解决的,不是“模型不够聪明”的问题,而是“组织不够清晰”的问题。当每个节点都只做一件事、每件事都有清晰的定义和验收标准时,整个系统的确定性和可维护性会大幅提升。
最后分享一个我反复验证过的小建议。
刚开始搭建这套系统时,我建议你不要一上来就追求“完全体”——即完整的多 Agent、并行、动态修订。先跑通一条串行的最小闭环:一个主 Agent 规划,两个子 Agent 执行,所有任务串行。这个闭环能稳定跑通之后,再引入并行调度、再引入修订机制、再增加更多的子 Agent。
这是我们实际项目迭代的真实路径。先让系统“能跑”,再让系统“跑得聪明”。顺序搞反了,你会同时面对规划逻辑、并行控制、故障恢复、上下文管理四个维度的 bug,排查起来非常痛苦。
这套架构后续还有不少可以延展的方向,比如引入子 Agent 的“技能注册中心”实现更灵活的任务路由、或者接入更细粒度的成本与耗时优化。但基础的地基,就是文里这些朴素的道理:职责分清楚、协议定标准、状态留痕迹。以上。