十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从单Agent到MultiAgent:Plan模式与主子Agent协作的企业级落地实践

从单Agent到MultiAgent:Plan模式与主子Agent协作的企业级落地实践 先交代背景吧。前阵子我们团队在推进一个自动化客服场景的 Agent 项目单 Agent 的 Demo 跑得很顺用户问“我的订单到哪了”它调一下物流查询工具把结果组织成话术返回挺像那么回事的。但是一旦把这些 Agent 丢到真实的售后链路里用户问的是“我买了 7 天还没发货想退款运费谁出”整个就乱了——Agent 一会儿去查物流一会儿去翻售后政策中间还要判断谁的责任上下文一长就开始出现工具调用错乱、输出结论前后矛盾甚至把模型自己猜出来的“已签收”当成真实数据继续往下推。这个阶段我们才意识到企业级场景其实根本不是一个 Agent 能扛下来的它需要一套有明确分工、有规划能力、能互相协作的多智能体系统。也就是这个标题里写的 MultiAgent配上 Plan 模式再配上主从主子Agent 的协作方式才会走得通。这篇文章就围绕这几件事展开为什么单 Agent 撑不住企业任务、Plan 模式到底改变了什么、主子 Agent 的协作骨架怎么画、以及真正落到生产环境时会踩到的几个坑。内容偏实操适合已经在做 Agent 应用、想往 MultiAgent 方向升级的团队参考。1. 单个 Agent 在企业任务面前为什么不够用1.1 Demo 顺畅接真实流程就崩先别急着上 MultiAgent先搞清楚“单 Agent 为什么会在企业任务里崩掉”。我们最初设计的是一个“大而全”的客服 Agent给它十几个工具从订单查询、物流轨迹、售后退款计算到用户画像读取、优惠券补偿全部塞进同一个系统提示词里。单轮问答、单工具调用的时候效果很好因为模型只需要在有限上下文里做一次工具选择。但真实业务里用户诉求往往是多跳的。比如上面那个“退款谁出运费”的场景完整链路是查订单信息、判断当前履约状态、匹配售后政策、计算责任归属、输出补偿方案。每一步都需要前一步的结果作为输入。单 Agent 在这种多跳任务里有两个绕不开的问题。第一个是上下文过长导致的“失焦”。对话记录、工具返回结果、中间推理过程全部堆在同一个上下文里模型越到后面越容易忘记最初的用户目标走着走着就开始按自己的“惯性”发挥。第二个是工具选择的不稳定性。工具一多同一种意图有时候调 A 工具有时候调 B 工具输出格式也可能忽好忽坏这在 demo 里看不出来但在线上会直接反映成业务方眼中的“不稳定”。1.2 单 Agent 的三个典型症状我总结了我们内部经常观察到的三个症状你也可以拿这三个症状去对照自己的项目上下文失焦任务执行到第 4、5 轮之后Agent 开始围绕某个中间结果反复纠结忘了最初要解决什么问题。工具调用抖动同一类问题不同会话里 Agent 选择的工具路径不一致导致结果口径不统一。边界感缺失所有能力都堆在一个 Agent 里提示词、工具权限、限流策略完全无法按业务模块隔离。一旦某个工具出问题整个 Agent 一起遭殃没法做精细化的降级。这三个症状本质上指向同一个结论当一个任务需要明确拆分成多个子任务、并且每个子任务都有自己的专业规则和评估标准时一个 Agent 承接所有能力并不是好的选择。这就好比一个全科医生什么病都能看但在复杂手术面前还是需要不同专科医生配合还需要一个主刀医生来做整体判断。这也是我们决定切换到 MultiAgent 架构的根本原因而不是为了追概念。2. Plan 模式把“边想边干”改成“先想清楚再干”2.1 ReAct 和 Plan 的本质差异很多人一提到 Agent第一反应是 ReAct 模式也就是“思考一步、执行一步、观察结果、再思考下一步”的循环。这个模式非常灵活但在企业级任务里有明显短板它没有一个显式的全局规划执行过程中的每一步都可能被局部信息带偏最后产生一个完整但跑偏的路径。Plan 模式则相反它在执行之前先让模型生成一份完整的任务计划把所有要执行的步骤、依赖关系、每个步骤的产出标准提前定义清楚。之后才进入执行阶段。两种模式的区别拿开车来类比最直观ReAct 是开到路口才决定往哪拐Plan 是出发前先看地图定好路线再按路线开。当然这里的“先定计划”不等于计划一成不变。后面我会讲到我们在实践里是把 Plan 模式做成“计划-执行-校验-再计划”的循环而不是一次性生成计划就闷头执行。两者核心差异我整理成了下面的对照表维度ReAct 模式Plan 模式推理时机每步边想边做先全局规划再执行上下文压力全程高频推理上下文增长快规划阶段集中产出执行阶段上下文更聚焦错误恢复依赖局部观察纠偏依赖步骤校验和全量再计划适用任务探索性强、步骤不确定有明确流程、需要稳定输出的企业任务成本特征多次往返Token 消耗较高规划开销集中执行过程更可控2.2 我们是怎么定义一份“任务计划”的既然要 Plan第一步就是定义计划的数据结构。我们早期踩过“计划就是一段 Markdown 文本”的坑用自然语言让 Agent 规划结果后续解析和校验都很痛苦。后来统一成结构化 JSON每个步骤都带明确的输入、输出和校验字段。一个实际简化过的 Plan 结构大概是这样的{ plan_id: plan_20250211_001, goal: 处理用户退款运费争议, steps: [ { step_id: s1, name: 查询订单基本信息, agent: order_query_agent, input: {order_id: 来自用户输入}, expected_output: {order_status: string, delivery_status: string}, timeout_ms: 3000, retry: 2 }, { step_id: s2, name: 匹配售后责任规则, agent: policy_agent, input: {order_status: 来自 s1.order_status, delivery_status: 来自 s1.delivery_status}, expected_output: {responsible: platform|user, reason: string}, timeout_ms: 3000, retry: 1 } ], max_replan_count: 2 }注意这里有一个关键点每个步骤的agent字段指向的是具体的子 Agent而不是“让主 Agent 自己去执行”。这就是第 3 节要讲的主子 Agent 协作的接口基础。计划里还必须明确expected_output因为它是执行后校验是否通过的依据。没有这一栏计划就只是一张写着步骤的纸无法驱动后续的自动纠偏。2.3 计划不是一次性生成的要动态修正我们在实际运行中发现如果只做“生成计划 - 按序执行 - 返回结果”一旦中间某一步的实际输出和预期不符后续步骤会被带偏。所以我们在 Plan 模式外面套了一层“执行-校验-再计划”的循环。用一个简单的伪代码描述的话大致是这样plan planner.generate(goal, context) for step in plan.steps: result execute_step(step) if not validate(result, step.expected_output): step.retry_count 1 if step.retry_count step.retry: plan planner.replan(goal, context, executed_steps, errorresult.error) break else: context.update_with(step.step_id, result)这里的核心是replan。当某个步骤执行失败我们不会从头重启整个任务而是把已经执行的步骤结果、失败原因和原始目标一起重新交给规划器让它生成一个“剩余步骤”的新计划。这样做的好处是既保留了 Plan 模式的前瞻性又避免了执行中途计划失效后整条链路全部重来的巨量开销。这个机制落地的门槛不高但非常值得做。否则“先计划后执行”就会变成一个新的僵化问题计划很完美现实根本不听计划走。3. 主子 Agent 协作架构主 Agent 拆活子 Agent 干活3.1 为什么选择“主子”而不是“平等交互”MultiAgent 的协作方式有几种常见形态平等对话式、环形传递式、中心化调度式。我们在企业环境里最终选择了中心化调度也就是“主子”结构原因很直接企业级任务需要明确的责任边界、权限边界和异常边界。平等对话式最大的问题是“没有人对最终结果负责”。多个 Agent 互相讨论、来回传递信息看起来灵活但一旦某个环节失控很难判断是哪个 Agent 的提示词有问题也很难做权限隔离。中心化调度式里主 Agent 是唯一的中枢它负责拆解任务、派发、回收结果、校验质量子 Agent 只做自己那一个垂直领域的事。这样出了问题定位责任非常清晰要么是主 Agent 拆解得不合理要么是某个子 Agent 的执行能力不足。在实际系统里这个结构很像一个项目组主 Agent 是项目经理子 Agent 是负责不同模块的工程师。项目经理不写具体代码但要对整体进度负责工程师不关心其他模块只把自己那块做扎实。3.2 主 Agent 和子 Agent 的职责划分在落地的时候职责切分必须严格否则就会出现“主 Agent 插手子 Agent 的细节”这种混乱。主 Agent 负责四类事情解析用户意图判断是否需要拆解任务调用规划器生成任务计划把每个步骤派发给对应的子 Agent回收结果汇总所有子结果做最终质量校验和话术生成子 Agent 只做一件事把自己垂直领域内的步骤执行完返回结构化结果。比如我们的“售后政策 Agent”只会根据订单状态和用户诉求去匹配规则它不需要关心这个订单是哪个仓库发的货也不需要在最终回复里组织完整话术。职责清晰之后提示词设计也变得简单。子 Agent 的提示词长度可以控制得很短因为它的目标非常聚焦上下文里只需要包含当前步骤的输入不需要承载整个用户会话。3.3 主子之间怎么通信消息协议的设计主子 Agent 之间不是“函数调用”而是走消息通信。我们设计了一套很轻量的 Agent 消息协议本质上是一个 JSON 信封里面固定携带几个字段{ message_id: msg_20250211_0001, trace_id: trace_88f3a2c1, msg_type: TASK_ASSIGN, from: master_agent, to: order_query_agent, task_id: plan_20250211_001_s1, timestamp: 1739241102000, payload: { order_id: PO20250211001 } }消息类型一共就四类别搞得太复杂TASK_ASSIGN主 Agent 给子 Agent 派发任务。TASK_RESULT子 Agent 执行成功后返回结果。TASK_FAILURE子 Agent 执行失败返回错误码和错误信息。PLAN_UPDATE主 Agent 在运行过程中更新了计划通知相关子 Agent。这套协议的最大价值体现在可观测性上。每个消息都有trace_id日志系统可以根据trace_id拉出整条任务的完整链路——用户诉求是什么、主 Agent 分了几步、每一步派给了谁、结果是什么、卡在哪一步。排查线上问题的时候没有这个 trace 链路你会陷入“黑盒”里无从下手。4. 企业级落地的四个硬骨头4.1 工具权限与数据隔离MultiAgent 上了生产之后第一个绕不开的问题是权限。主 Agent 需要能调用规划能力和汇总能力但可能不需要直接访问支付相关数据子 Agent 查订单的只能查订单算优惠券的只能算优惠券。如果我们把所有 Agent 都放到一个完全信任的进程里等于把账户体系的安全边界全部打穿了。我们的做法是给每个 Agent 维护一个独立的“工具白名单”这个白名单注册在服务启动时从配置中心拉取。子 Agent 在执行时拿到的不是泛化的“能调用所有工具”的权限而是一串只包含它业务域内工具的列表。同时底层集成层会根据trace_id和 Agent 身份做二次校验就算某个 Agent 的提示词被诱导去调用工具集成层也会直接拒绝。数据隔离方面我们坚持的原则是“子 Agent 只看到它需要的字段”。比如物流查询子 Agent 的返回结果如果包含仓库地址但后续政策子 Agent 并不需要仓库地址那就由主 Agent 在汇总时做字段裁剪而不是把完整结果原样传给下一个子 Agent。4.2 上下文策略该共享什么、该隔离什么MultiAgent 落地时上下文策略是最容易被忽略的一个点。很多团队会把整段用户对话原封不动地丢给每一个子 Agent理由是“让子 Agent 更了解上下文”。但这样做有两个问题一是成本高每个子 Agent 都在处理大量无关信息二是信息干扰无关信息可能把子 Agent 的注意力带偏反而影响准确性。我们的策略是分层上下文管理。主 Agent 保留完整的用户会话和任务计划子 Agent 只接收“当前步骤的输入”加上“主 Agent 从全局上下文中提取出来的必要背景”。比如退款计算子 Agent 接收的输入就是订单金额、退款类型、用户选择的退款原因它不需要知道用户在聊天里抱怨过物流慢也不需要知道前面的客服转接过程。这个策略一开始会增加主 Agent 的提示词复杂度因为它需要在派发时做一次简单的“上下文裁剪”。但长期看收益非常大子 Agent 的输入变短、性能变稳、Token 成本也下降整体上非常划算。4.3 失败恢复与任务重试链路Agent 任务和多活架构里的服务调用有一点很像都会失败而且失败是常态。我们在线上把任务执行的状态机设计成了这样状态含义进入条件可流转状态PENDING等待调度计划生成完成PLANNING, FAILEDPLANNING生成或更新计划主 Agent 开始规划RUNNING, FAILEDRUNNING子 Agent 执行中计划下发RETRY, SUSPENDED, DONE, FAILEDRETRY步骤失败可重试子 Agent 返回失败且未超重试次数RUNNING, FAILEDSUSPENDED任务等待人工介入重试耗尽或需要人工审批RUNNING, FAILEDDONE全部步骤执行完成最后一个步骤成功终态FAILED任务失败重试/校验失败终态其中SUSPENDED这个状态值得多说一句。企业级场景里不是所有任务都应该一路自动化到底。比如退款金额超过某个阈值或者模型判断存在较大的风险争议我们会让任务挂起转人工客服处理。这个设计的价值在于Agent 不是用来替代人的而是用来替人处理重复性任务的。遇到它处理不了的事情要能体面地退给人类而不是硬着头皮生成一个用户看不懂的回复。分布式环境下状态不能只存在进程内存里否则进程一重启整个任务丢了。我们用的是把任务状态和中间结果写入 Redis 并加分布式锁同一个订单维度只允许一个主 Agent 实例在跑避免并发调度导致状态覆盖。4.4 成本控制与模型选型很多人容易忽略MultiAgent 架构天然比单 Agent 费 Token。任务多了主 Agent 和子 Agent 之间的消息传递每一轮都要消耗输入输出。所以模型选型不能“全家桶都用同一个最强模型”。我们简单粗暴的分层原则是主 Agent 用综合能力更强的模型因为它的规划、裁剪、校验都直接影响任务上限子 Agent 尽量用参数量更小、成本更低的模型因为子任务目标明确不需要太强的推理能力。比如订单查询子 Agent本质上就是“从接口参数里提取订单号调工具返回结果”用一个大模型纯属浪费。成本控制的另一个手段是减少不必要的消息往返。如果一个子 Agent 已经能返回结构化结果主 Agent 就不需要再“复核一遍”子 Agent 的中间推理过程而是直接把结果作为事实输入到下一步。把这个逻辑写进提示词和编排逻辑整体 Token 消耗能下降不少。5. 踩坑实录三个真实问题的完整排查过程5.1 子 Agent 把上游的中间结论当成真实数据这个坑是我们在早期联调时最头疼的。现象退款计算子 Agent 返回的退款金额明显算多了而且理由写着“商品已签收用户选择七天无理由退款运费由平台承担”。但实际查单用户根本没有签收记录。排查链路先查trace_id关联的消息日志发现退款计算子 Agent 的输入里有一项delivery_status 已签收。这个字段不是物流查询接口返回的而是上一个子 Agent 在返回结果时附带的一句自然语言总结。主 Agent 在派发下一步时把子 Agent 的自然语言输出里面的“推测信息”直接当成了事实数据传下去了。根因子 Agent 的任务结果没有区分“事实字段”和“模型生成字段”。物流子 Agent 可能在想“这个订单应该早到了”就把这个推测写进了 summary而下游完全不设防。修复方案强制所有子 Agent 的结果采用结构化输出事实字段必须来自工具返回值模型自动补齐的内容单独放在inferred字段里并且下游默认忽略inferred字段除非主 Agent 显式决定使用。代价是每个子 Agent 的提示词都要反复调优但这条规则上线后因为假数据引起的连环错误基本绝迹了。5.2 计划执行到一半计划本身已经过时了现象有个任务是“用户申请换货系统自动给用户生成一个新的退货地址”。计划的前三步是先让用户确认商品已寄出再校验物流单号最后生成退货地址。结果用户在任务执行过程中临时说“我不换了改退款”。这在本地测试时完全没暴露因为测试里用户不会中途变卦但真实对话里太常见了。排查链路日志显示用户的最新消息已经到达消息队列但主 Agent 还在按旧计划执行第三步因为主 Agent 的编排逻辑是“把当前步骤跑完再处理新消息”。结果就是用户听到了一个完全过时的回复。根因 Plan 模式做的是“基于过去信息的最优计划”但它没有把“新信息随时会到”这个约束考虑进去。修复方案在每步执行前增加一个“意图漂移检查”主 Agent 会快速判断是否有用户新消息进入如果有先暂停执行更新计划。同时在编排层增加了一个循环上限防止“更新计划-执行-再来新消息-再更新计划”陷入死循环超过上限就让机器人说“我们换一个更直接的方式帮您处理”然后转人工。5.3 并发子 Agent 写同一个缓存导致状态错乱现象线上某个时段同一笔订单同时存在两个主 Agent 实例在跑一个在处理退款计算另一个在处理优惠券补发。两个子 Agent 都会更新订单的“售后处理中”状态缓存结果后写入的那个把前面那个的任务 ID 覆盖了导致其中一个任务重回 PENDING开始重复执行用户收到了两条客服回复。排查链路查 APM 监控看到订单维度有并发调用把两条任务实例的起止时间对齐后发现是从消息队列重复消费开始的。消费者在极端情况下没有及时提交 offset触发了重启重放同一个消息被投递了两次。根因任务状态没有做订单维度的互斥锁而且消息处理逻辑没有做成幂等。修复方案两条防御。一是消费侧加幂等校验同一trace_id已经存在执行记录就直接丢弃新消息。二是任务状态写入改用SET NX分布式锁锁的 key 用订单 ID时间设置为 30 秒流程结束再释放。同时给长任务增加了锁续期机制防止流程没跑完锁先没了。这三个问题排查下来我们的一个整体感受是MultiAgent 的故障点往往不在单点的生成能力上而在系统级的状态管理、数据契约和编排逻辑上。这些坑都是传统分布式系统里已经解过一遍的题放到 Agent 场景里依然要老老实实再做一遍。6. 落地效果的评估口径与后续扩展方向文章到了后面还是说点实际的东西。我们并没有用一个“唯一成功率”来衡量 MultiAgent 落地因为不同任务的复杂度差异太大单独看一个数字没有意义。我们内部主要看三个指标任务完成率、每任务平均工具调用次数、人工介入率。任务完成率衡量“自动化链路把用户问题彻底解决的比例”工具调用次数侧面反映规划的合理性过多调用往往意味着规划碎片化人工介入率则反映自动化和人类的交接边界是否合理。从实测情况看切到 Plan 模式和主子 Agent 协作之后变化最明显的是“长尾任务”的表现。单 Agent 时代那些“能跑通但其实跑偏”的任务会被步骤校验尽早拦下来要么重试要么转人工而不是等到最后输出一个看着完整、实际错误的答复。这个改进对业务方的体感影响很大——错误率下降比响应变快更让人放心。后续的扩展方向上我们在尝试两件事。一件是在主 Agent 的规划能力里引入更细的规则兜底比如电商场景里一些强约束规则不走模型判断直接用代码规则引擎校验让模型的灵活性和代码的确定性结合起来。另一件是加强对子 Agent 输出质量的分级评估不是简单地“成功/失败”而是把“部分成功”“结果存疑”“需要人工复核”都区分出来让主 Agent 有更细的决策空间。最后说一句个人体会MultiAgent 这套东西最难的从来不是画一个漂亮的架构图而是把主子之间的数据契约、失败语义、状态流转这些“脏活”咬牙做掉。只要能把任务拆得清楚、把结果定得标准、把失败管得妥当即使每个 Agent 单独看不怎么惊艳组合起来也能在企业场景里跑出稳定的价值感。
返回列表