
做多智能体快两年了从最早拿 LangChain 写 demo 玩到后来真刀真枪在公司业务里落地这中间踩过的坑、推倒重来的设计估计能写一本小册子。最近刚把一套沉淀下来的工程化方案整理完正好趁这个机会从需求拆解到上线部署把完整的思考链路和能直接抄作业的细节都摊开聊聊。这篇文章不聊花哨的概念只讲怎么把一个模糊的“我们要做个智能体系统”变成线上稳定跑的工程。1. 接手项目后的第一件事不是写代码而是拆需求很多团队犯的第一个错误是把“多智能体”当成一个炫技的技术标签上来就画出一堆 Agent 拓扑图。但工程化的核心永远是先搞清楚这一个系统到底要解决谁的什么问题。我拿到需求时习惯先问四个问题这个场景单一模型能不能解决如果能为什么非要多智能体架构任务拆解之后哪些环节确实需要独立的策略或上下文隔离整个流程的可失败边界在哪以我最近做的模拟项目X为例业务方最初的需求是“做一个智能客服能处理售前咨询、售后跟进、投诉安抚和数据分析”。这种描述如果直接丢给大模型你得到的只是一个自带人设的聊天机器人而不是一个可靠业务系统。所以第一轮拆解很重要我把原始需求拆成了三个层面任务层接待、问答、解决方案推荐、工单记录、数据汇总每个任务有独立的输入输出协议。决策层判断用户意图属于哪条垂直线需要调取哪些工具是否需要转人工。执行层对接订单系统、知识库、CRM 接口把大模型生成的结论转换成实际业务动作。这个拆分决定了整个系统的边界。一个多智能体系统最忌讳的就是职责混乱所有 Agent 共享一套提示词共享一个上下文窗口最后表现出来就是每个 Agent 都半吊子互相干扰。我当时定下的原则是每个子 Agent 只做一件明确的事上下文相互隔离上层统一调度。这种设计可能在一开始看起来不够“聪明”但对线上系统来说可维护性和可观测性远比炫技重要。另一个拆需求的关键是识别出那些本质上不适合智能体去做的环节。比如订单状态的实时查询这应该是接口调用而不是让 Agent 自由发挥因为 Agent 一旦去猜订单状态错误概率就会拉高。只有在无法调用接口或者需要综合多源信息做判断的场景才值得让 Agent 介入。这个边界划分得越清晰后续开发越省心。需求拆解的产出不应该只是一堆文档而是场景-流程-数据-接口四张表。场景表描述每个用户意图对应的完整链路流程表描述每一步的触发条件、输入输出、超时兜底数据表定义上下文流转的数据结构接口表列清楚每个 Agent 可能调用的外部服务。这四张表搭起来才算是从“模糊需求”跨到了“可实施方案”。2. 架构选型的取舍为什么不是每个团队都适合上多智能体我见过不少团队在单 Agent 都还跑不稳的情况下就急吼吼地上 Multi-Agent 框架。结果系统复杂度和错误率跟着一起起飞。这里必须先泼盆冷水多智能体的本质是把复杂决策拆分成多个可控环节而不是把简单问题复杂化。如果场景只需要一次大模型调用加一个工具函数老老实实用单 Agent 加工作流引擎就够硬拆成三个 Agent 只会徒增延迟和出错的概率。那什么样的场景真正值得上多智能体我自己归纳出三个硬指标第一任务涉及多个知识域比如售前问答和售后投诉处理知识库差异明显放一个上下文里互相污染第二需要不同粒度的策略控制比如权限、敏感词过滤、对话风格隔离设计上最好物理隔离第三流程有明确的分工结构比如先规划再执行再审查这个链路本身就是多角色协作模型。缺了这三条上多智能体基本是自找麻烦。选型明确的第二个维度是技术栈选择。去年我做过一轮相对系统的对比当时市面上主流的候选有成熟的编排框架、基于状态图的建模框架、自研代理解耦方案以及最原始的通过消息队列自己拼多角色。比较下来的结论是如果你团队能接受系统复杂度首选状态图框架因为它在流程控制上表达力最强适合业务规则相对确定的场景如果你更看重快速迭代和生态工具链编排框架上手快但对复杂状态的表达能力弱一些如果是搞研究怎么都行如果最终要上生产建议不要用完全自研的方案调试成本远超预期。此外还需要考虑部署形态。多智能体不只是代码问题还牵扯到算力分配和依赖管理。比如一些 Agent 需要 GPU 推理另一些只是做文本分类或格式整理用 CPU 就足够。部署时把它们混在一个服务里资源会被次线性利用。我后来是拆成独立的服务模块按调用量分配副本数吞吐立刻上来了。架构评审的时候我习惯画三张图。第一张是链路图描述从入口到出口的全流程包括分支、循环和终止条件第二张是上下文拓扑图标清每条消息在哪几个 Agent 之间流转什么字段被读取、什么字段被写入第三张是容错图标注每个节点失败后的降级路径和兜底策略。三张图齐了才说明这个架构是真想清楚了而不是画个拓扑糊弄人。3. 核心开发的关键上下文隔离、工具封装与编排策略这一节是全文实操含量最高的部分我会从代码层面把关键的几个设计决策讲透。3.1 上下文设计隔离是硬边界共享要显示声明多智能体系统最常见的崩溃原因是上下文串了。A Agent 产出的中间结果被 B Agent 当成了用户输入或者历史会话里无关的信息干扰了当前决策。解决这个问题的思路并不复杂我把上下文分成三类会话原始上下文用户的每一次原始输入这个保留给路由层做意图判断。工作上下文每个子 Agent 执行过程中的输入输出只对当前执行链路可见。全局状态需要跨 Agent 共享的业务数据比如订单 ID、用户等级、处理进度这些用显式的状态对象存储不允许 Agent 私自读写。用代码来表示就是每个 Agent 的输入是结构化的数据对象不是一大段聊天文本。比如售前 Agent 收到的内容是“用户在咨询某商品的价格和库存状态”而不是“请根据以下对话判断用户想干什么”。这样设计之后模型接收到的是结构化任务描述跑偏的概率大幅下降后续的评估和日志排查也有明确的字段可以对齐。我在项目里定义了一个统一的事件格式类似后端开发里的消息体class AgentMessage: msg_id: str agent_from: str agent_to: str payload: dict context_ref: str # 指向全局状态对象 timeout_ms: int每个 Agent 处理完消息后只输出新的 AgentMessage 或者结束信号不允许直接修改全局状态。全局状态的变更由一个专门的 StateManager 统一管理这相当于给系统加了事务性逻辑越复杂收益越明显。3.2 工具/API 封装别让大模型直接决定一切很多教程会让大模型自己选工具然后调函数。这在 demo 里没问题但生产环境必须做工具准入机制哪些工具对哪个 Agent 可见是需要在配置文件里显式声明的。不允许 Agent 调用清单之外的任何函数尤其涉及订单查询、退款操作这类有真实副作用的能力。我给模拟项目X 设计了一套工具注册体系代码大致长这样class ToolRegistry: def __init__(self): self._tools {} def register(self, agent_id, func, schema, criticalFalse): self._tools.setdefault(agent_id, []).append({ func: func, schema: schema, critical: critical }) def available_tools(self, agent_id): return [t[schema] for t in self._tools.get(agent_id, [])] def execute(self, agent_id, tool_name, args): for t in self._tools.get(agent_id, []): if t[schema][name] tool_name: if t[critical]: return self._execute_with_audit(t[func], args) return t[func](**args) raise ToolNotAllowed(...)一个细节值得注意凡是涉及用户资金、订单状态变更、对外发送消息的工具必须标记为 critical调用时要走审计日志而且关键动作的结果要回传给上层做二次确认不能由 Agent 自己拍板到底执行不执行。这个设计帮我避免过至少三次线上事故类似“Agent 自作主张给用户发了错误补偿”这种情况在开放工具权限的架构里太容易发生了。3.3 编排策略固定流程为主动态规划为辅多智能体的编排策略业内常见的就两类一类是预先定义一个标准作业流程按图执行另一类是让一个 Planner Agent 自己拆解步骤动态规划。每当我看到有人把动态规划作为默认策略时都会劝他把退路先想好。动态规划的问题在于不可预测你无法保证 Planner 在每次复杂度变化时都能走出合理的步骤这在有严格 SLA 的业务里是灾难。我的建议是混合编排主链路用固定流程保证 90% 的常规路径稳定可控只在少数需要发散的场景里才允许绘图 Agent 做动态规划。例如模拟项目X 的投诉处理链路正常情况下走标准工单流程只有当用户情绪激烈程度超过某个阈值时才启动安抚引导分支由专门的 Agent 来动态组织话术。这样既保留灵活性又控制住失控面。实现上我一直用语言化描述来定义状态流转逻辑清晰且可读性高有团队成员接手时不用靠猜。核心思路是每个节点是条件映射而不是硬编码跳转。比如ROUTING_RULES { start: lambda msg: attribution_agent if msg.need_type_check else qa_agent, attribution_agent: lambda msg: after_sales_agent if msg.intent after_sale else sales_agent, after_sales_agent: lambda msg: refund_agent if msg.need_refund else human_service, }这种语言化配置的好处是第一不用重新部署整个系统就能调整路由第二可以可视化地做流程审查团队成员能快速理解每一步在干什么第三链路执行完之后每一步的跳转原因都留在了日志里排查问题时极其有用。3.4 Prompt 工程要看重边界多智能体的 Prompt 设计和单 Agent 是完全不同的思路。单 Agent 的 Prompt 追求上下文融合和信息完备性而多智能体里的子 Agent Prompt 最在乎的是边界遵守明确告诉模型你只管什么、什么情况必须上报、哪些缩写和推断是禁止的。我每个子 Agent 的 Prompt 都固定包含四块内容角色目标、输入数据协议、输出格式约束、红线与上报条件。红线与上报条件多重要举个例子就清楚了售后 Agent 如果发现用户要求赔偿金额超出权限它不应该自行拒绝或者答应用户而是直接触发升级流程。这种规则写在 Prompt 里比写在逻辑代码里效率高太多。因为边界本来就有模糊性硬编码很难覆盖所有表达方式而模型正好擅长做这种模糊匹配。另一个经验是在子 Agent 的 Prompt 里尽量不给历史聊天对话只给数据提取后的最终结果。比如一个“订单查询 Agent”输入是 JSON 格式的订单号和查询意图输出是标准格式的查询结果不需要知道用户说过什么。这样 Prompt 大大的缩短模型的响应速度和稳定性都会提升费用也会低一些。4. 落地部署与压测调优不把问题留到夜间上线后开发侧工作完成之后真正的考验才开始。我把这个阶段分成四步模拟环境联调、压测与性能摸底、发布策略设计、监控报警体系建设。4.1 模拟环境联调先把接口都 Mock 掉多智能体系统本身的抽象是模型调用但它的不确定性来自外部依赖知识库、订单系统、消息推送。联调的第一步是把外部系统全部 Mock 成可控行为的数据源干扰项先剔除掉。Mock 数据要覆盖正常结果、空结果、超时、返回异常等类型重点验证编排层在外部系统各种表现下的路由决策。这一步很容易被省略。尤其是多 Agent 本身的日志又比较乱很多人图省事直接连真实接口测。但真实接口的不稳定会让定位问题的成本翻倍。我们当时模拟项目X 联调时专门搭了一个 Mock 服务把十几个接口全部虚拟化数据源造假造得相当细致并内置了各个异常分支的开关测试效率因此高了很多。4.2 压测与性能摸底关键指标是 P50 和 P95 延迟所有单测通过后就进入压测。多智能体压测有两个天然难点调用链路长外部依赖多。我在压测时重点关注三组数据链路端到端延迟的 P50 和 P95、每个子 Agent 节点的单独耗时、以及各种工具调用的耗时分布。收集这些数据的意义是找出真正的瓶颈所在。就拿模拟项目X 的排查经历来说压测结果当时端到端 P95 延迟达标但 P50 表现异常后来一查才发现原因是某个知识库接口在部分时段偶发慢查询。如果只看平均延迟这种人容易漏掉的偶发问题根本发现不了到线上爆发就被动了。按节点拆解的做法是快速定位复杂系统性能问题的最有效手段强烈建议建立指标看板除了常规观测指标还要专门盯每个环节的调用耗时。4.3 发布策略多智能体不要搞一键全量发布多智能体的一个隐藏风险是多个 Agent 之间可能存在隐性的依赖关系比如 A Agent 的输出格式被 B Agent 的输入协议隐式依赖。这种情况最怕全量发布一个小改动导致全系统崩掉类似连锁反应。我们的发布策略是灰度和链路级开关。每一个 Agent 的新版本先只在 5% 的流量上跑并对比新旧版本在完成任务率、回退率、延迟指标上的差异。但这里有个关键设计给每个 Agent 配上独立的版本开关这样即使整个系统是同一个流程编排也能做到某一个环节单独灰度其他环节保持不变。网关层做流量染色也很重要按用户 ID、会话 ID 或者其他标签把流量切分到不同版本链路上。观测到新版本超时时可以立刻切回旧版本而不是慌乱地回滚整个系统。这种精细控制在多人协作的多智能体系统里几乎可以说必需品。4.4 监控报警要留审计日志别只依赖云厂商自带监控很多系统上线前不看审计这点特别要提醒多智能体系统一定要有独立于模型厂商之外的日志体系。因为模型厂商的日志经常查不到 prompt 里业务字段的明细比如当时用户选了哪个商品Agent 调用了哪一步。自建日志要记录三层内容会话层日志、决策轨迹和审计明细。决策轨迹指的是记录路由判断、各个子 Agent 的输入输出摘要、工具的调用参数和结果。这个设置既能在线上出问题时做时间旅行回放又能帮助后续提炼评估集。有一次线上收到用户投诉说系统乱承诺如果当时没有决策轨迹日志整个过程就很难追溯还原。后来通过日志逐条回看了决策路径很快就定位到是某一个配置工具的 prompt 边界写得模糊所致半天内就修掉了。报警规则上这是另一种思路不要只设置响应时间或错误率的指标。多智能体最容易出问题的是**“任务未完成”**——链路跑了但最终没产出结果而表面上没有任何错误码。所以一定要设置一个专门报警链路的完成率包括成功走到终态的比例以及回退比例这两个指标能够第一时间暴露系统功能的“软性失败”。5. 常见问题与避坑实录挑几个我们上线后反馈最集中的问题整理成表格后面再展开几个特别有共性的细节。问题典型表现解决办法上下文污染子 Agent 答非所问混入上一轮闲聊内容上下文隔离只传结构化数据工具滥用Agent 调用不在权限内的操作工具准入清单 critical 审计超时累积外部依赖慢导致端到端超时每个子 Agent 独立超时熔断降级输出格式漂移模型偶尔输出非 JSON 导致链路断掉强制结构化输出格式纠错重试灰度影响面失控一个 Agent 的 bug 影响全链路链路级开关按 Agent 独立灰度5.1 为什么明明设置了超时系统还是全链路卡死这个坑大概率是子 Agent 的超时时间设置得比外部依赖的调用超时还短。A Agent 调 B Agent 的接口B 内部又调了外部 APIB 对外部 API 忍耐时间很长A 对 B 的忍耐时间却很短结果就是 A 疯狂重试B 还是慢吞吞地跑最后 A 被打爆。解决思路是超时时间必须按照调用链从外到内逐级递减。比如外部 API 设置为 5 秒内部 Agent 调用设置为 4 秒上层编排节点设置为 3 秒。同时要限制重试次数防止请求在 Agent 之间打乒乓球。多智能体系统里重试风暴的危险程度远高于单机系统因为一个请求可能触发多个 Agent 连环重试。5.2 模型输出不规范导致的“幽灵 bug”我用过好几家不同的大模型即便各家都声称支持结构化输出实测也偶尔会跑出格式异常可能是多了一个字段也可能是 JSON 截断。这种问题在单 Agent 里修复成本低在多智能体里会顺着链路散播做一个 Agent 的显示字段错误可能因此传染给所有下游节点。所以我在设计每个子 Agent 节点的时候都会加一个格式校正层。模型输出先经过一个轻量级校验器不符合协议就触发一次重试重试失败就走兜底逻辑而不是把异常结果发给下游。兜底逻辑的配置很灵活——可以用上一次的正常结果可以用模板答案也可以直接把链路标记为失败并转人工。反正原则就一个不符合协议的数据永远不允许下沉到下一环。5.3 上线前别疏忽“冷启动测试”冷启动对于依赖知识库召回的 Agent 来说是另一个重灾区。特别是知识库索引更新时如果某个子 Agent 依赖刚写入还未生效的数据结果就会和线上不一致。做系统设计时一定要明确数据一致性的边界哪些知识允许最终一致哪些必须强一致。还有一种冷启动发生在会话场景里新用户没有任何历史上下文所有 Agent 都拿不到必要的字段这时很容易出现流程空转。我在各个子 Agent 的输入协议里都设置了必填字段检查缺了关键字段直接触发补全会话流程而不是让 Agent 硬猜有效避免了很多没意义的空转。6. 这套方案后续还能怎么扩展这套工程化方案目前帮我撑起了模拟项目X 的完整业务流程包括售前、售后、投诉和数据汇总四条线全部是独立开发、统一编排、灰度发布。后续如果要继续扩展我自己的思路是这样的针对不同业务模块形成标准技能包包括 Prompt 集合、工具注册、评估集和监控面板方便新业务快速装配把链路执行轨迹沉淀成评估数据定期评测各子 Agent 的效果最后把人工审核的流程和 Agent 的自动决策串成一个半自动闭环尤其在售后赔偿、豁免这类敏感操作上保留人工兜底。前面这些经验本质是从“能跑通的 demo”向“能运营的系统”过渡时会遇到的一系列问题。如果在方案设计之初就开始有意识地做上下文隔离、工具准入、链路开关和审计日志这几件事后续踩坑的概率会小很多。我自己最大的体会是多智能体的难点早就不是让 Agent 回答得聪明而是让整个系统在复杂链路中仍然可预测、可排查、可控制。把这三点想透再复杂的项目也会有大致的稳妥解法。