ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

从单体对话到多智能体协作:Agent、Skills、Teams三层架构演进实践

从单体对话到多智能体协作:Agent、Skills、Teams三层架构演进实践

如果要对齐主流的推理范式,单线程的口头指令在今天已经不太够用了。过去一年我带着团队把一套以单体对话为核心的系统,分三个阶段重构成了Agent、Skills、Teams三个层次分明的架构,这期间踩了很多坑,也亲手扔掉过不少看似先进的方案。这篇文章就是把我们这一路的演进流程、技术选型判断依据,以及大量试错后沉淀下来的实操方法做一个完整的复盘。如果你正打算把一个普通的对话服务升级成Agent系统,或者已经开始了多智能体协作的改造,这篇文章里的一些取舍逻辑和细节控制可以直接拿来参考。

1. 从单体Agent到组件化:演进动因与路线全景

1.1 单体Agent的典型痛点

先说为什么一定要演进。很多团队的起点和我当时一样:用一个大模型做成了类似聊天助手的系统,所有流程都写在同一个提示词里,逻辑简单,确实能跑。但一旦需求复杂起来,问题就很具体:

  • 提示词越来越长,从最初几千字膨胀到几万字,可维护性急剧下降。
  • 功能复用难。三个场景都需要"查订单",只能复制三份提示词,改逻辑要同步改三处。
  • 能力边界模糊。模型什么都能答,但答得不可控,业务上很难验收。
  • 排障困难。一次错误响应出来,分不清是模型理解错了、工具参数错了,还是业务流程写错了。

这些问题本质上是"耦合度过高"。单体Agent把知识、推理、执行三种逻辑全部塞进了同一个模型上下文里,而这种高耦合在需求稳定时是没问题的,可一旦需求开始迭代,每一次改动都需要重新评估整个提示词对全局的影响。

我能给出的建议是:不要等系统完全跑不动了再去重构,而是设定几个信号——当你发现自己开始在提示词里写"如果用户说的是A就调用A服务,如果是B就调用B服务"这类硬编码分支时,就是应该动手拆分的时候了。

1.2 三条演进主线:Agent、Skills、Teams

基于上面的痛点,我们把整个系统抽象成三个层级,这也是整个演进的核心主线:

  • Agent层:负责决策和规划,它是"大脑",理解用户意图、拆解任务、决定下一步动作。这个层次的核心指标是决策准确率和规划合理性。
  • Skills层:负责具体执行和领域知识封装,它是"手脚",把每一个可复用的能力沉淀成参数化、标准化的接口。这个层次的核心指标是执行成功率和接口稳定性。
  • Teams层:负责多个Agent之间的协作与调度,它是"组织架构",解决的是单Agent能力天花板的问题。这个层次的核心指标是任务完成时间、资源利用率和协作损耗。

这三个层次的关系不是物理隔离的三个模块,而是逻辑上的分层。你把一个Agent换掉,Skills层和Teams层不应该受影响;你新增一个Skill,也不需要去改Agent的判断逻辑。我在设计原则里给团队定了一条红线:层与层之间只通过标准接口通信,不在上游逻辑里直接写下游的实现细节。这条红线在后面重构时帮我们省掉了大量不该有的连带修改。

1.3 演进节奏的把握:重构与业务并行

很多团队会踩一个坑:架构演进期间业务停摆。

我的经验是把演进拆成"能力防腐层"和"渐进式替换"两条线走。先建立一个兼容层——老的单体接口仍然保留,但内部开始支持以Skill形式注册新能力。新功能上线时强制走新架构,老功能在验证稳定后再搬迁。这样每走一步都是可控的,业务方不会感知到明显风险。

用数字来说明这个节奏:我们迁移第一个核心场景用了两周,后续每个场景平均三天,到后期基本上半天就能接完一个Skill。整个过程大约持续三个月,业务侧的反馈是零故障、零感知。这个节奏是可以复制的,关键就在于前期防腐层是否舍得投入。

2. 技术选型的三层判断框架

2.1 第一层判断:Agent内核是选择编排引擎还是自由发挥

现在市面上的Agent框架很多元,但按内核的编排方式划分,无非两大类:一类是"流程编排优先"的,另一类是"上下文行为优先"的。前者适合流程确定的场景,后者适合开放决策场景。

我当时的选择逻辑是这样的:如果任务流程固定、变化频率低,就用轻量级编排方案,甚至直接用代码写好流程;如果任务开放、需要模型动态决定步骤,再用真正的Agent框架。很多人一上来就选重型框架,是因为没先问自己到底要的是确定性还是灵活性。

一个值得注意的点:即便现在有了很好的Agent开发框架,我依然建议核心流程尽量用代码显式编排,只把真正需要模型判断的分支留给Agent。原因是可观测性——显式流程每一步都能打日志、设断点、做指标监控,但Agent自由发挥时,模型走了哪个分支,依赖的是它的内部决策,出了问题很难复现和定位。

2.2 第二层判断:Skills体系选函数调用还是外部知识库

Skills层的技术选型是最容易被低估的。很多团队把Skill想成简单的"给模型挂工具",但真正落地后才发现,你面对的是大量的参数校验、结果验证、上下文截断、权限控制这些问题。

关于参数化格式,业界主流是JSON Schema,这没有悬念。关键在于如何设计Schema的粒度:

  • Skill的输入参数不要超过7个,否则模型在函数调用时会出现参数遗漏和误填。
  • 枚举类型的字段要提供详细的枚举描述,而不是只有代码值。
  • 描述性字段放在参数描述里,不要放在参数名前缀里。

这一层还有一个选型是"外部知识库是否并入Skills"。我的做法是拆开:简单的、稳定的事实性查询走知识库直接检索;需要多步推理、需要结合上下文做判断的,才封装成Skill。因为如果知识库检索逻辑本身很复杂,硬塞进Skill里会导致Skill本身变成一个巨大的黑盒,反而失去了架构分层的意义。

2.3 第三层判断:Teams协作选共享上下文还是消息传递

到了Teams层级,选型的核心是Agent之间的通信协议。两种主流模式:共享上下文模式和消息传递模式。

  • 共享上下文模式:所有Agent读同一个状态块,优点是实现简单,缺点是一旦Agent数量上来,上下文冲突和带宽消耗指数级上升。我实测过,3个Agent以内还能接受,超过5个基本就乱套了。
  • 消息传递模式:Agent之间通过结构化消息交互,由调度器(或协调者)负责路由和汇总。优点是解耦好、可扩展性强,缺点是延迟增加、消息协议设计复杂度上升。

我们的选择是消息传递为主,共享上下文只用在极少数紧密配合的场景。具体来说就是:每个Agent有独立的上下文空间,Agent之间用指令消息和结果消息通信。这样每个Agent的上下文只有它自己关注的那部分数据,既省Token又降低相互干扰。代价就是需要多设计一套消息协议,但这是值得的——它决定了Teams能不能横向扩展。

2.4 量化决策:我给选型做的评分表

为了不让团队在选型时凭感觉,我做了一张简易评分表,每次选型都按这张表打分,四个维度权重分别是:可维护性30%、性能20%、扩展性30%、社区与生态20%。

  • 可维护性:代码可读性、结构清晰度、类模块依赖复杂度。这个我一般让团队两个工程师分别打分,取均值。
  • 性能:基于压测数据和架构特征综合判断。给定消息延迟、吞吐量、资源占用三个子项。
  • 扩展性:新增一个Skill、新增一个Agent所需要改动的位置数量。改得越少分越高。
  • 生态与社区:看重的是长时间不维护的风险、周边工具链完整度、文档质量。

比如我们评估两个候选Skill实现方案时,A方案功能强但依赖复杂,B方案功能弱但结构清晰。评分结果A是6.8分,B是8.2分,最后选了B。事实证明这个选择是对的——因为半年后业务需求变化,B方案只改了一个接口就完成了新场景接入,而A方案那时由于依赖复杂,已经很难改动。

提示:评分表是参考工具,不是决策唯一依据。不要因为分数而强行选择自己不熟悉的技术栈,团队的熟悉程度也是隐性成本。

3. 演进实操:四步落地Skills体系

3.1 第一步:盘点现状,拆分能力边界

动手写代码之前,先做能力盘点。我们把现有单体Agent里的所有能力列成一张表,标注:能力名称、调用频率、是否依赖外部服务、是否与其他能力共享数据、当前实现位置。

这个盘点表是最重要的输入。完成后我做了两个判断:

  • 高频率+低耦合的能力优先拆成Skill,比如"查天气""查汇率""看订单状态"。
  • 低频率+强依赖的能力暂时保留在Agent内,等架构稳定后再迁移。

别贪多。第一次演进,我建议只拆分3到5个核心高频能力,跑通一条完整链路,验证架构可行性。千万不要试图一次把所有能力全拆完,那会导致改动面巨大,出了问题根本定位不了。

3.2 第二步:定义Skill接口规范

接口规范是整个Skills层的基石,设计得好,后续开发就顺,设计得差,后续到处补丁。

我定下的Skill接口包含四段结构:

  • name:Skill唯一标识,用"领域.动作"的命名规则,例如order.query、user.verify。
  • description:给模型看的能力描述,写清楚这个Skill是做什么的、什么时候该用它。这部分要站在模型的视角写,而不是站在程序员视角写。
  • parameters:入参JSON Schema,严格要求类型、枚举值和字段描述。
  • result:出参结构,同样用JSON Schema定义。

在接入格式上,我选了业界主流的函数调用格式。这个格式的好处是生态支持好,主流大模型都有针对性的训练和调优,模型特别擅长从对话里提取参数填充到函数调用结构里。实测下来,同样的参数提取任务,用这个格式比纯文本提示的成功率高了大约12个百分点。

这里有一个反直觉的细节:不要为了追求"灵活"而把参数定义得很宽泛。比如"查询时间范围"这个参数,如果你定义成string类型,模型会传"上周一"、"最近三个月"、"2024年初至今"这些五花八门的写法,你的下游解析逻辑会写到崩溃。正确定义方式是拆成两个字段:start_date和end_date,类型都是date,模型在提取时会自动把语义转换成标准日期格式。这一点在你做多Agent协作时尤其重要,因为Agent生成参数的风格比人更难预测。

3.3 第三步:渐进式切换,灰度放量

Skill写完不是直接全量替换老逻辑。我的标准流程是:

  1. 新Skill在老逻辑旁边并行运行,输入和输出同时记录。
  2. 跑一周左右的影子验证,比对Skill结果和老逻辑结果是否一致。
  3. 差异率降到阈值以下(我们当时定的1%)后,开始按5%、20%、50%、100%比例灰度切换。
  4. 全量切换后,保留老逻辑代码一个月再删,期间出问题可快速回滚。

这套流程虽然看起来慢,但它最大化地保证了演进过程的风险可控。Teams协作也一样——新的协作流程先在测试环境跑通,再切一个影子副本去处理线上真实流量的副本,确认没问题再真实引流。

很多团队忽视这个步骤,直接全量切换结果线上事故,这是得不偿失的。架构演进的本质是控制熵增,而不是追求速度。

3.4 第四步:建立评估与回归闭环

上了Skills体系之后,需要一套评估机制来防止"改了这个Skill,那个场景崩了"的回归问题。

我们的做法是构建了一套模拟用户请求的回归测试集,覆盖每个Skill的典型输入、边界输入和异常输入,每次Skills代码有变更时自动跑一遍,看识别准确率、参数提取正确率和执行成功率三个指标。

说说三个指标怎么定义:

  • 识别准确率:该调用Skill的时候,Agent是否成功调用了正确的Skill。这个指标反映的是Skill与Agent意图识别之间的配合质量。
  • 参数提取正确率:调用Skill时,模型从对话中抽取的参数值是否全部正确。这个指标直接反映接口Schema的设计质量。
  • 执行成功率:Skill内部执行后返回结果是否成功,包括依赖服务是否正常、返回结构是否合法。

这三个指标形成闭环后,就能把架构演进从"拍脑袋"变成"看数据"。

注意:回归测试集的覆盖范围要及时跟进。每次新增业务场景,都要同步加入回归集,否则三个月后,你的回归集就会变成一道摆设。

4. Teams协作架构的实现要点

4.1 角色Agent的划分:按场景还是按职能

Teams层级的第一个问题是:到底怎么拆多个Agent。拆法有两种:按场景拆(比如"售前Agent""售后Agent"),或者按职能拆(比如"需求分析Agent""代码生成Agent""测试Agent")。

我的经验是前期按场景拆,后期按职能拆。

为什么?按场景拆的好处是每个Agent的职责边界天然清晰,用户意图识别准确,不需要额外的路由逻辑。但坏处是场景之间有大量共享逻辑时,会重复开发。按职能拆的好处是复用性高,但需要设计复杂的角色编排逻辑,否则Agent之间不知道谁先谁后。

我们的实际方案是混合制:对外承接用户请求用场景Agent(售前、售后、运营),内部的做事环节用职能Agent(检索Agent、计算Agent、撰写Agent),场景Agent负责调度职能Agent。这个结构跑下来体验比纯场景拆或纯职能拆都要好。

4.2 协作模式选型:星型还是流水线

Teams协作模式下,Agent之间的消息流拓扑是核心决策。三种主流模式:

  • 星型:一个主控Agent接收所有请求,分发给各个子Agent再汇总结果。优点是流程统一、便于监控,缺点是主控容易成为瓶颈。
  • 流水线:Agent链式传递,A做完传给B,B做完传给C。优点是职责单一、上下文干净,缺点是总延迟是各环节之和,链条长了容易超时。
  • 动态路由:由调度器根据请求内容动态决定走哪条链路。灵活但调试复杂。

我们最初做的是星型,因为实现简单。后来业务复杂度上来,发现主控Agent需要处理的元任务太多,既要理解用户意图又要拆分任务还要汇总结果,决策负担过重,响应质量和速度都下降了。后来重构为"场景级星型+任务级流水线"的混合模式:场景Agent是星型中心,它拆分出子任务后,子任务内部再用流水线协作。

举一个实际的例子:处理"用户投诉订单配送延迟"这个请求时,场景Agent(售后Agent)把任务拆成三个子任务:核实订单状态、核实配送记录、生成答复建议。这三个子任务分别走不同的职能Agent,其中核实配送记录这个子任务内部又是流水线:先从快递系统拉轨迹,再匹配延误原因,最后生成时间节点说明。这样每个Agent的上下文都很干净,任务边界清晰。

4.3 上下文传递与任务编排:共享内存还是独立堆栈

这部分是我认为Teams协作架构里最容易翻车的地方。

先说上下文传递。我们试过两种方式:一种是所有Agent共享一个全局上下文对象,另一种是每个Agent维护自己的上下文堆栈,任务完成后只把必要的结构化结果传给下一个Agent。

共享全局上下文的问题在Agent数量达到5个以上时特别明显——每个Agent读到的都是全部信息,但每个Agent真正关心的只是其中一小部分。这不仅是Token浪费,更大的问题是无关信息会干扰Agent的决策质量。

最终解决方案是:

  • 每个Agent有自己的上下文窗口。
  • Agent之间只传递结构化结果对象,不传递原始长文本。
  • 全局仅保留一份"任务档案",记录任务状态和关键结果摘要,供调度器和监控使用。

这样设计后,Agent上下文长度平均降低了60%以上,决策准确率反而提升了,因为模型不再被无关信息干扰。

4.4 预算控制:Teams协作的Token成本管控

多Agent协作最大的隐形成本是Token消耗。N个Agent协作一次,Token消耗不是线性叠加,而可能是指数级膨胀——因为每个Agent都要读取任务描述、上游结果、自身指令。

我们在实践中总结了三层控制手段:

  1. 结果压缩层:每个Agent在返回结果时,先通过一层压缩逻辑把详细过程转换为摘要,只保留对下游有意义的结论和关键数据点。
  2. 任务拆分控制层:场景Agent在拆分任务前先预估一个"协作预算",如果预估总Token消耗超过阈值,就主动收敛策略(比如将开放式问题转为规则化处理)。
  3. 模型分层使用层:不是所有Agent都用最强最贵的模型。简单任务用轻量模型,复杂任务用重量模型,规划类任务用推理模型,执行类任务用快速模型。

这层设计做完之后,我们的整体成本消耗比初版架构下降了约35%,而任务完成率却提升了,因为轻量模型的快速响应减少了协作链路中的等待时间。

5. 常见问题与排查技巧实录

5.1 Skills调用的参数错位问题

这是Skills层上线后最高频的故障类型。典型表现:模型调用了正确的Skill,但参数传得离谱,比如把"下单时间"传成了"发货时间",或者把数值类型的参数传成了字符串。

排查思路分三步:

  1. 查看请求日志里模型实际返回的function_call参数值,判断是不是模型本身的参数提取错误。
  2. 如果是参数提取错误,先检查Schema里的字段描述是否足够清晰,按需补上设计时的枚举说明和格式规范。
  3. 如果字段描述已经很详细但模型仍然出错,检查是不是上下文里出现了多个相似的字段名,导致模型混淆。比如同时出现"收货时间"和"发货时间",模型很容易抓错。这时候可以通过改字段命名或者加约束词来解决。

我在工程上还加了一层保险:Skill执行前增加参数校验逻辑,校验失败的参数通过工具反馈给模型自行修正。这个机制就像大模型自纠错,实测能把参数提取失败后的最终成功率从60%左右拉回到95%以上。

5.2 多Agent协作中的死锁与循环调用

多Agent协作中,最难排查的问题是循环调用。典型现象:Agent A调用Agent B,Agent B又调用Agent A,两边都在等对方的结果,最终超时。

造成这种问题的原因多数是任务分派逻辑没有做去环处理。最直接的解决方式是在调度器层加一个调用深度限制,比如约定单次任务最多L3层级调用,超过即中断,并返回中间结果。另一个手段是给每个Agent注册自己可以发起的下游Agent列表,List本身静态配置,防止动态跳转导致的环路。

如果要在设计上根治,还是得细化Agent的职责边界。循环调用意味着你划的边界出现了重叠区域,要么A能做B的事,要么B能做A的事。把重叠区收敛,环自然就消失了。

5.3 经验那些感觉"先进"的方案为何被否决

有一说一,演进过程中我们否决过好几个看起来特别先进的方案。比如某个方案支持全自动的任务规划和工具编排,理论上能省很多人工配置。但实际测试时发现,这种全自动方案在最常见的稳定路径上表现确实好,但一旦出现异常分支,它的处理逻辑就变得无法预测,而且无法通过代码去精确控制特定业务的规则。这个案例让我形成了一个原则:投入产出比不确定的高大上方案,优先级永远低于虽然笨但可靠可控的方案。

还有一次评估某个新出的Agent框架,社区热度很高,我们也下载下来做了POC。但深入之后发现它的核心能力绑定了一个特定的模型服务,这意味着如果后期要换模型,整个框架都要推翻重来。这种风险我们不接受,果断放弃。

5.4 实战故障速查表

症状可能原因排查动作
Skill被调用但参数为空Schema字段无必填约束,模型产生幻觉检查parameters里的required数组
模型频繁调用无关Skilldescription写得太笼统,模型无法区分边界重写description,明确适用条件
多Agent任务超时某个Agent依赖的Skill执行耗时过长拆分流程:单独处理耗时调用,或改为异步
返回结构不合法模型输出未严格遵循result Schema后端强制校验,非法结果触发自动重试
某个Agent持续决策质量低上下文被上游塞入了过多无关结果优化上游结果压缩逻辑,精简传递内容

结尾:几点经验之谈

这次架构演进做下来,我最大的体会是:好的架构不是设计出来的,是不断演进出来的。不要指望一步到位,更不要迷信某个现成的框架能解决所有问题。快速试错、明确取舍、保留回退能力,这三点比任何花哨的技术本身都重要。

另外,团队协作模式的演进对我个人也有启发。以前我习惯一个人把所有技术决策都做了,但这个项目让我意识到,复杂架构需要不同视角的碰撞。我把选型评分表开放给整个团队成员,大家一起打分、互相挑战,最终选的方案往往比我一个人定的要稳健得多。一个人的盲区,在团队审视之下会暴露得很明显。

再分享一个小技巧:所有架构决策都要写下当时的原因背景和取舍依据。有一次我们想改掉一个看似不合理的Skill定义,翻记录才发现当初是基于某次线上特定故障场景的应对策略。如果没有记录,大概率会在未来某次迭代中把这条线改回去,然后重新踩一次同样的坑。这个文档成本很低,但它能阻止技术债的重复积累。

RELATED READING

延伸阅读

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