☰
多智能体协作架构实战:单体Agent到Subagent的拆分演进与避坑指南
2026/10/21 11:58:15 网站建设 项目流程

上周把一个多轮深度调研类Agent从单体结构拆成了“主代理+多个子代理”的协作架构,跑通之后回头看,最深的感受就一句话:多智能体协作不是把Agent放到一个群里开会,而是把复杂性拆成边界清晰的子树。顺手把这一路踩过的坑和最后沉淀下来的协作姿势整理成文,希望能给正在折腾同类结构的同学一点参考。

1. 为什么要引入Subagent:从单体到协作的必然演进

1.1 单Agent的天然瓶颈

单体Agent是最容易上手的一种形态,一个系统提示词,带上若干工具描述,模型自己决定什么时候调工具、什么时候输出。但用久了就会发现几个绕不过去的瓶颈。

第一个瓶颈是上下文窗口的物理约束。做调研类任务的时候,需要同时塞进去背景资料、历史对话、候选工具描述、中间结论,而当前主流模型的上下文窗口虽然动辄几十万token,看起来很大,但真正的可用上下文可能只有其中一部分。实测下来,当一轮任务需要持续爬取并归纳多个来源的时候,单单是工具调用记录和中间观察结果就能把上下文顶到三四万token,再叠加原始对话,核心推理区域就开始出现注意力衰减,模型会明显表现得“记不住”早期的关键约束条件。

第二个瓶颈是任务聚焦度。模型在同一轮上下文里既要做网页分析,又要做数据提取,还要做结论归纳,本质上是在一个上下文里反复切换多个上下文模式。切换次数一多,模型就开始犯迷糊,常见表现是正在执行提取任务的时候,突然按照归纳任务的格式输出了一份总结,或者反过来。

第三个瓶颈是失败观测与恢复机制缺失。单体Agent一旦在某一小步上输出格式异常,或者工具返回了意料之外的数据结构,整个任务状态就开始失控。原因是单体Agent没有一个清晰的层级责任划分,任何一步出了问题都会污染全局状态。

这三个瓶颈叠加起来,正好对应一个现象:单体Agent做“小而纯”的任务,比如单页摘要、单轮查询、简单分类,表现很稳,但一旦任务变成“多步骤、多数据源、需要中间校验与状态维护”的复合式任务,成功率就明显下降。与其在一个上下文里硬扛复杂度,不如干脆引入Subagent,把一个大任务拆成若干互相独立的小任务,由主Agent统筹调度,每个子Agent在干净的小上下文里专注解决一个子问题。

1.2 Subagent到底解决了什么问题

引入Subagent本质上做的是把“单脑多线程”改成“多脑单线程”加“主管协调”。每个Subagent拥有独立的系统提示词、独立的任务边界、独立的上下文历史。主Agent只负责目标理解、任务拆解、调度分配、接收结果、合并输出。

用实际数据来说话。我之前用单体结构跑同一个行业调研任务,约10个数据源、需要输出结构化结论,成功率在65%左右,主要表现为中间有1到2个数据源提取失败,或者结论里的数字对不上。同样的任务,改成“一个协调Agent加五个专项Subagent”之后,成功率提升到91%。更重要的是,每一次失败都能精准定位——是哪个子Agent在哪个环节出错,而不需要从头到尾翻一遍上下文。

Subagent解决的问题可以用一句话概括:把不可控的长链路,切成一串相对可控的短链路。长链路之所以不可控,是因为每一步都可能出小错,而小错会在长链路上不断累积放大。切短链路之后,每一步的输入输出边界都收窄了,错误能被及时拦截,状态能被及时修正。

不过必须说清楚一个反直觉的点:并不是所有任务都适合上Subagent。任务本身很简单的时候,引入Subagent只会增加调度开销和额外的API消耗。我见过一个同学,让Agent帮他把一句话翻译成英文,也拆了一个翻译子Agent,结果白白多出两次模型调用,翻译质量并没有变得更好。所以设计的前置判断是:这个任务的复杂度,是否已经超过了一个Agent在一个上下文里能稳定承载的规模。

2. 踩过的那几个坑:Agent协作中的典型翻车现场

2.1 坑一:子Agent上下文过载,两头不讨好

第一次改造的时候,我犯了一个很典型的错误:为了让子Agent“拥有足够的信息”,把主Agent的上下文原封不动地传给了每个子Agent。系统提示词、工具描述、历史用户对话、背景资料、目标说明,全部一起塞进去。结果每个子Agent都背着十几万的上下文跑,调用速度慢不说,产出质量还下降——它们在自己的结果里反复复述背景资料,而不是真正聚焦在执行任务上。

这个坑背后的原因是对“上下文隔离”的理解不够到位。引入Subagent,想要的核心价值就是让每个子Agent只关注自己那部分的小上下文。如果我把整个上下文都传给子Agent,那上下文隔离就形同虚设,整个结构退化成了带接口的单体Agent。

正确的做法是:主Agent传递给子Agent的,只需要三个要素——任务目标、输入素材、输出格式要求。任何背景信息如果跟子任务没有直接关系,就不要传过去。举一个实际例子,在某代码审查类Agent中,主Agent只需要告诉子审查Agent“这是被审查文件的内容、这是项目编码规范、请以列表形式输出违反规范的代码位置”,而不是把整个项目的技术选型讨论过程、用户最初的需求描述都传给子Agent。

2.2 坑二:任务边界模糊,子Agent频繁越权

第二次调整遇到的问题是子Agent“管得太多”。典型场景是:我让一个子Agent做“网页内容提取”,结果它在返回提取结果的时候,顺带给我做了一版内容摘要、改写了标题、还附加了一段SEO建议。从结果上看,内容本身没错,但这种越权行为在主Agent合并结果的时候会造成非常棘手的问题——主Agent不知道哪些内容是可靠的原始数据,哪些已经被加工过了。

这个坑的本质是子Agent的“任务边界声明”不够刚性。模型天然倾向于“多做一点”,尤其是对话式模型,你让它做A,它经常会把B也做了,然后期待你夸它贴心。在Agent协作场景中,这种“贴心”恰恰是最危险的东西。

排掉这个坑需要两步:第一步,在子Agent的系统提示词里明确写清楚“你只负责XX任务,不需要做任何其他事情,不要输出额外建议,不要进行无关的分析”。第二步,在子Agent的输出格式里做严格约束——如果是提取类任务,就直接给JSON模板,模板里只有数据字段,不给结论字段。

2.3 坑三:上下文复用不当,子Agent被“脏数据”污染

第三个坑,也是隐蔽性最强的一个——上下文污染。有一次跑一个数据归纳类任务,第一个子Agent读取了一个数据结构,后续任务需要复用这批数据。我图省事,直接把第一个子Agent的观察输出作为素材,丢给第二个子Agent去用。结果是,第二个子Agent不仅使用了数据,还把第一个子Agent在观察过程中的中间判断也当作事实依据,最后生成的结论里混入了第一个子Agent的猜测。

排查了很久才定位到这个问题的根源:模型分不清“事实”和“推理中间态”。如果你给它的上下文里有推理过程和原始数据混在一起,四舍五入等于给模型植入了一个认知偏差。

解决方式是做出明确的“事实层”和“推断层”分离。任何一个子Agent在执行任务的时候,只输出两种状态:Observed(观察事实)和Inferred(推断结论)。主Agent在决定后续调度的时候,只能把Observed状态的数据作为下一轮子任务的输入,Inferred状态的数据作为参考,不能作为事实依据。操作上,我用JSON输出格式做了一个硬约束,让子Agent的返回结果必须包含一个evidence_type字段,标记是observed还是inferred。就这一条规则,后续任务的结论准确率提升了不少。

2.4 坑四:失败重试策略粗糙,一次错误无限放大

还有一个常见的失误,就是失败重试策略设计得太粗糙。我第一次实现的时候,遇到子Agent调用失败或者返回格式解析异常,就直接让主Agent重新发一次同样的调度指令。结果遇上模型由于随机性导致的同一类错误,重试两三次还是错的,白白消耗API成本不说,还拖慢了整个流程。

后来改成“分级重试”策略:第一级重试,原样再调度一次,用于应对偶发性的输出抖动;第二级重试,带上上一轮的失败输出和错误解析信息,让子Agent“自己看刚才错在哪里”,用于应对模型输出格式不稳定的情况;第三级重试,不再重试同一个子Agent,而是降级走一个更简单的备选工具路径,比如提取结构化数据失败,就退化为让主Agent先抓全页文本再做初步拆分,最后交给人类兜底。

这条经验可以说看起来简单,但实际上非常有效。切忌在一个子Agent上无限重试,三次不成功就降级或者放弃,这是多智能体架构设计的一个健康红线。

3. 正确的协作姿势:几种值得复用的架构模式

3.1 层级委派模式:主控负责拆解,Subagent负责执行

我目前最常用的模式就是标准的“主管-专员”层级结构。主Agent不直接执行任何子任务,它只做三件事:理解任务目标、把目标拆成子任务、把子任务分配给合适的Subagent,然后接收结果做最后整合。

层级委派模式的核心特征是“主不干活,专干细活”。比如做一个企业风险调研需求,主Agent会先把任务拆成:工商信息检索、司法风险检索、舆情分析、财务数据提取、合规审查,五个子任务。每个子任务对应一个专门的Subagent,各自拥有针对性的系统提示词和工具集。工商信息检索子Agent只被授权调用企业信息查询API,舆情分析子Agent只被授权调用新闻RSS源和关键词过滤工具。

这种模式最大的好处是可观测性极强。每个子Agent从输入到输出,都是结构化、可追踪的。如果最终报告里有一块数据异常,可以直接定位到对应的子Agent去查证,而不需要在长篇对话里大海捞针。

层级委派的关键参数是“每个子任务的最小充分上下文”。也就是说,传给子Agent的上下文,必须小到它刚好能高效完成子任务,同时又不缺必要信息。这个度的把握需要反复调测,我一般按这个步骤去做:先跑一版全量上下文,观察子Agent的输出质量和响应速度;再逐步裁剪上下文,每裁一块就重新跑一遍同样的子任务对比质量;直到输出质量开始明显下降,就回退到上一档。这个过程挺花时间的,但一次调好之后,整个架构的效率和稳定性会有显著提升。

3.2 并行拆解模式:互不依赖的子任务同时跑

层级委派解决的是任务纵向上“分工”的问题,并行拆解解决的是任务横向上“提速”的问题。当多个子任务之间没有数据依赖关系时,就可以用并行调度同时派发。

让我用一个案例来说明。某市场信息汇总类Agent需要覆盖多种信息源:行业公告、政策文件、媒体报道、学术摘要。这4类信息源互相独立,按顺序跑可能耗时40秒,并行跑只需要15秒甚至更短。方案就是让主Agent并行派发4个子Agent,每个子Agent查询一类信息源并返回结构化结果,最后主Agent再统一汇总并去重合并。

并行拆解模式需要注意的唯一问题是“结果冲突处理”。多个子Agent并行跑,结果之间往往存在冲突,比如两个信息源给出的同一家企业的营收数据不一致。主Agent在汇总阶段必须有一个明确的冲突处理策略,我在实践中用的是“数据源权威度优先级”:设定一个数据源评级表,评级高的数据优先采用,评级低的作为差异标注。

并行拆解模式不适合有状态依赖的任务。比如任务A的输出是任务B的输入,这种就千万别开并行,开了就等于人为制造了一个数据竞争条件,结果必然不靠谱。并行模式判断的依据,核心就一个:子任务之间除了共享同一个最终目标之外,有没有其他任何依赖?只要有,就不要并行。

3.3 动态规划模式:让Agent自己决定任务怎么拆

层级委派是主Agent按固定模板拆任务,动态规划则更进一步——让主Agent在执行过程中根据实际情况,动态决定任务怎么拆、先后怎么排、哪些任务需要合并或跳过。

这种模式适合探索性很强的任务,典型场景就是“研究一个不熟悉的主题”。这类任务没有一套固定的拆解模板,因为你不清楚该主题下有哪些子问题值得去挖。硬套模板会导致拆出来的子任务跟实际要解决的问题错位。

动态规划的实现方式是基于“计划-执行-再计划”循环。主Agent先根据初步理解给出一版探索计划,然后逐个执行计划中的探索动作,每完成一个动作,就把新发现的内容追加到上下文中,再重新调整后续计划。相当于Agent一边做一边学习、一边修订计划。

动态规划和完全随机探索的区别在于,它依然需要约束在目标范围里。每一次计划修订都必须基于已经观测到的事实,而不是模型拍脑袋。我在实践里给主Agent加了一条硬性指令:“计划调整必须注明是由什么具体观察触发的,不允许进行无依据的计划变更。不得脱离最终目标发散方向。”

这条约束很重要,没有约束的动态规划,会演变成模型自己跟自己聊天,越聊越离谱,上下文越聊越长,最后生成的结论与最初目标八竿子打不着。

3.4 汇总合并模式:别让“最后一步”毁掉所有前期工作

最后一个常用的模式是汇总合并。这个模式看起来最不需要技术含量,实际上恰恰是最容易翻车的一步——主Agent在汇总子Agent结果时进行“二次加工”。什么叫二次加工?就是汇总的时候,不仅仅是把子Agent的输出拼在一起,还顺手加了主Agent自己的推断。如果主Agent推断的依据并不是所有子Agent结果的完整集合,只是抽样了几个,汇总结果就会失真。

举个例子,五个子Agent分别返回了五份风险清单,主Agent在汇总的时候决定:“基于我的判断,前三项风险最重要。”——问题在于,主Agent的“判断”到底依据了什么?如果它没有深入分析后两份清单的具体内容就下了这个结论,那这个汇总就是有偏的。

我目前的处理方式是把汇总合并分两个阶段。第一阶段叫“纯拼接”,主Agent把子Agent的结构化输出按约定模板拼接成一整份文档,不添加任何额外分析。第二阶段叫“交叉验证”,主Agent逐项检查拼接后的文档,对于需要保留分析判断的字段,必须引用对应的子Agent输出原文,否则不能写入。

这个“引用原文”的要求,是防止模型汇总时为了流畅性而丢掉关键细节的一个非常有效的方法。在很多模型上,加了“必须引用原文”这一条之后,汇总内容的精准度显著提升,原因是模型被逼着去检索和复述具体信息,而不是基于整体语义模糊概括。

4. 参数调优与工具选型:让协作跑得更稳

4.1 上下文窗口的动态管理

多Agent协作一旦上了规模,token开销就会成倍增长。五个子Agent各执行一次等于五个独立的调用量,任何一个子Agent重试一次,又叠加一次调用量。如果不做上下文窗口管理,成本会肉眼可见地失控。

我实际操作中的配置经验是:为每个子Agent设定一个独立的最大上下文上限,子Agent只保留与当前子任务相关的最新的N条消息,超出上限的早期历史直接“归档”。归档后的历史不会作为后续模型调用的输入,但如果最终报告需要引用早期信息,主Agent会从归档数据里检索并单独拼装给对应子Agent。

这里还有个细节,所谓“N到底取多少”,适配不同模型差异很大。经验值是这样:对上下文窗口为1万的模型,子Agent保留6~8轮对话足够;对上下文窗口为5万以上的模型,可以放宽到12~15轮,但尽量不要超过上下文总窗口的一半。因为模型在上下文过长时,对中间乃至较早部分的注意力会显著下降,而且请求延迟也会明显上升。

4.2 温度与采样参数:子Agent与主Agent要不要区别对待

很多人在做多Agent协作时,习惯全部Agent用同一套采样参数。我建议把主Agent和子Agent的参数分开设置,因为两者的任务性质完全不同。

主Agent负责的是调度决策,需要的是稳定和确定。温度设置偏低,比如0.1甚至0,因为我希望主Agent每次对同一个任务结构的判断都尽量一致,不要因为采样波动导致同一类任务今天拆三步、明天拆五步。

子Agent负责的是具体执行。执行类任务通常需要一定的精确性,温度一般也都偏低,但个别任务可以适当调高,比如需要生成开放式文案、头脑风暴式探讨论证的时候,子Agent可以把温度调到0.7以上,让产出更有发散度。

不过要强调一点:温度参数的任务适配性远比“高低偏好”重要。如果子Agent做的是“从文本中提取日期”,温度调到0.7那纯属自找麻烦;如果子Agent做的是“撰写一段品牌介绍文案”,温度调到0.1又会显得干巴无趣。参数选择必须跟着任务性质走。

4.3 工具集的分权管理:谁用什么,必须说清楚

多Agent协作中,工具管理一定要和单Agent时代区别开。单个Agent的时候,所有工具全部挂在一个上下文里,模型自己决定用什么。多Agent的时候,如果还把全部工具集合都暴露给所有子Agent,就相当于每一个子Agent都背了一整个工具清单,不仅浪费token,还增加错误工具调用的概率。

我目前的做法是:工具集合分成全局工具、组长工具、组员工具三层。全局工具只挂主Agent或“工具路由Agent”,负责通用检索、启动新子Agent、读取全局状态。组长工具跟在层级委派模式的主控Agent上,负责任务拆解与调度。组员工具跟具体执行类子Agent绑定,每个子Agent的系统提示词里只出现它在执行任务时可能用到的几项工具描述。

例如:在做某金融数据提取类Agent时,财务数据提取子Agent的工具清单里只放“解析PDF表格”和“读取表格文件”,而舆情分析子Agent的工具清单里只有“抓取网页正文”和“关键词匹配过滤器”。两个子Agent互不相通,既降低指令干扰概率,也减少了token开销。

4.4 结构化输出协议:治理Agent协作的第一抓手

多Agent协作,最需要统一的不是系统提示词写得多花哨,而是子Agent返回结果的统一结构。我花了一个下午专门设计了一套通用的结构化输出协议,支持所有子Agent按同一套模板返回自己的产出。

协议的核心字段包括这些:task_id(子任务标识)、status(成功/失败/部分成功)、output_type(observed/inferred/artifact)、data(实际结果内容)、error(错误信息或异常记录)、metadata(执行耗时、token占用、置信度等)。

这套协议带来的直接好处是主Agent的调度逻辑变得异常简单。它不需要针对每个子Agent单独解析不同的返回格式,只需要统一读取这些字段,根据status判断下一步是继续调度还是发起重试,根据output_type决定数据放入事实层还是推断层。

对比一下没有协议之前的情况——每个子Agent返回的格式都不一样,有的输出是Markdown,有的是JSON,还有的直接甩一段渲染好的HTML。主Agent做汇总阶段不得不为每种格式写一套处理逻辑,工作量奇大且新的子Agent每多一种输出格式就要多维护一段代码。统一协议之后,新增一个子Agent几乎零成本。

5. 常见问题排查与避坑清单

5.1 问题速查表

以上踩过的坑太多,顺手整理成了一张速查表,方便遇到问题的时候直接对号入座。

现象可能原因处理动作
子Agent返回结果质量下降上下文传得过多,注意力分散按最小充分上下文原则裁剪上下文,只保留任务直接相关素材
子Agent输出了不该输出的内容任务边界声明不够刚性系统提示词明确只做A不做B,输出格式强约束为JSON模板
多个子Agent结论互相冲突缺少冲突处理机制设定数据源权威度优先级,低优先级结果作为差异标注
主Agent汇总时丢失关键信息汇总时二次加工过度第一阶段纯拼接不加工,第二阶段逐项引用原文验证
子Agent反复调用工具但没有进展工具定义和任务目标匹配度低检查系统提示词中的任务意图与工具描述是否同频,甚至触发语义混乱
任务整体响应越来越慢上下文持续累积不归档设定上下文窗口上限,超出部分自动归档并只保存上限内内容

5.2 排查思路与调试技巧

排查多Agent协作问题,和排查单体Agent问题有一个根本差异:单体可以靠“重跑一次”来解决大部分问题,多Agent必须靠“链路追踪”来定位。

我的排查步骤是固定的。第一步看主Agent的调度日志——它调度了哪些Subagent、每个Subagent的输入输出是什么。第二步看失败的那个Subagent的原始返回——注意不是处理后的结构化结果,而是原始返回,因为很多问题出现在结构化解析丢失了部分信息。第三步看该Subagent的完整执行上下文——它的系统提示词、喂进去的素材、工具调用记录、中间观察,都会把问题根源暴露出来。

定位到问题之后,修复也分三层:第一层是“提示词修复”,调整该Subagent的系统提示词,把任务边界和输出要求说得更清楚;第二层是“上下文物料修复”,调整该SubAgent接受到的素材范围,去掉干扰性内容;第三层是“协议修复”,发现当前输出协议的结构没法区分某些精细状态,就去扩展协议字段。

这个调试流程里最常被忽略的是,大多数Agent协作问题的根源其实是“物料错配”,也就是喂给子Agent的内容里包含了不该包含的信息,或者漏掉了关键信息。判断物料是否错配的标准很简单:子Agent的失败模式是不是每次都在同样的一类输入上触发。是的话,物料配置大概率有系统性偏差。

5.3 避坑清单与经验备忘

最后一部分我把自己实际沉淀下来的避坑经验全部列在这里,都是直接用真金白银的API调用换来的教训,每一条都值得收藏。

首先,加Subagent之前先问自己一个问题:这个任务真的需要多Agent吗?如果单体Agent已经能稳定完成,不要为了架构好看而上多Agent。多Agent带来的token消耗、延迟增加、调度复杂度是实打实的成本,架构上的漂亮不能当饭吃。建议先做单体基线测试,确认单体确实有上有上限再拆。

第二,上下文隔离是底线,不是加分项。刚开始调的时候我反复提醒自己,子Agent不是主人的“分身”,而是独立的“同事”。你给“同事”的资料一定是跟任务直接相关的,而不是把整包档案丢过去让同事自己翻。

第三,协议先行。刚开始搭建多Agent协作时,不要急着写各个Agent的系统提示词,先把子Agent返回的统一协议定好。协议是代码层面的接口,接口没定好,后面写再多提示词也白搭。

第四,善用重试机制但别过度依赖。要设计好三级重试(原样重试、带错误反馈重试、降级处理),同时每级重试最多只能触发一次,不要无限循环。无限重试不仅是成本黑洞,还会让链路越跑越烂。

第五,新Agent上线之前必须单独测。不要直接把它挂到整个协作链路上跑,先在隔离环境里单独调用它执行自己的子任务,确认单点输出质量稳定之后再接进链路。接进链路之后还要单独观察至少三四次调度,确认主Agent给它的物料和它被单独测试时给的物料在质量上没有断层。

第六,及时给Agent版本打Tag。多Agent架构的系统提示词、工具定义、协议字段随时在变。每个Subagent在产出结果时可以在metadata里带上当前提示词版本号和协议版本号。后期排查时能快速定位到这个结果是旧版配置还是新版配置跑出来的,能省下大量跟自己对账的时间。

第七,做大任务时一定要定时清理上下文僵化。长时间运行的多Agent任务中,子Agent可能会执着于最初的小概率假设,忽略新进入上下文的更优证据。每隔一段时间让主Agent做一次“全局假设检查”,重新评估当前主假设和备选假设的证据支持比,必要时让对应子Agent重新规划。

第八,人类监督不可省,尤其是最终的综述报告。多Agent可以把你从繁琐执行中解放出来,但“最终报告是否准确、是否完整、是否符合用户意图”,这个责任还是得人来扛。我在整个流水线最后设置了一道人理校验关卡,主Agent生成的结果必须经过人工抽查才能进入交付环节。

最后再分享一个小技巧

最后分享一个小技巧:调试多Agent协作的时候,给每个子Agent起一个人类能看懂的代号,比如“工商检索专员”“舆情扫描器”“财务核对员”。代码里可以用这些代号作为任务标识,日志里也直接用代号输出。这样每次排查问题的时候,不需要去翻JSON字段去猜“这个task_007到底是干什么的”,日志一打出来,一眼就知道是哪个环节出了问题。虽然只是命名习惯上的小事,但对效率提升和心智负担下降真的很有帮助。

多Agent协作这条路,踩坑是正常的,不要因为一开始翻车就否定这个方向。架构在不断地被批评、被校准、被修复的过程中才能真正长成适合项目的样子。希望这篇踩坑总结能让你在第一版就走对路,少烧一点验证的钱。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询