多智能体编排层混合架构:外层自研内层通用框架 先说结论我们最终把多智能体系统的编排层拆成了两层——外层流程编排自研内层Agent执行交给通用框架。这个决定不是谁拍脑袋定的也不是出于“技术洁癖”而是被生产环境里一连串事故和卡点逼出来的。这篇文章就把这次架构抉择的前因后果、对比过程、落地细节和踩坑记录完整写出来给正在做类似选型的朋友一个参考。从我的视角看多智能体系统的架构选型本质上不是“哪个框架更火”的问题而是“谁更适合管住生产环境里的不确定性”的问题。通用框架让起步很爽但到了线上状态一致性、可观测性、故障恢复这些事框架帮不了你多少而全自研又意味着你先把轮子造完才能跑业务。我们想要的是一个既能快速上线、又能在关键路径上完全可控的方案于是才有了这套混合架构。1. 先交代清楚我们要做的多智能体系统是什么1.1 系统形态与四个核心痛点我们做的系统本质上是一个面向客服场景的多智能体协作平台用来处理用户在售前、订单、物流、售后这几个环节的咨询。系统内部一共运行了五个职责不同的Agent意图路由Agent、订单查询Agent、物流跟踪Agent、售后处理Agent以及一个负责兜底的通用问答Agent。用户的一句话进来先由路由Agent判断意图再决定把任务分给哪个Agent执行执行过程中还可能涉及调用外部接口、判断结果、追问用户、甚至转人工等动作。这个系统从Demo走到生产环境之后我们陆陆续续遇到了四个非常具体的痛点。第一个是长流程编排像售后处理这种场景一个会话可能要经历用户描述问题、Agent查询订单、判断是否符合退货条件、询问用户意见、生成退货单这么多个步骤每个步骤还有可能分支流程一不小心就走乱。第二个是状态一致性多个Agent协作的时候任何一个环节超时或报错整个会话的状态就可能卡在一个不上不下的位置用户重试也好、换个说法再问也好都无法继续。第三个是可观测性我们很难回答“这个会话现在到底走到哪一步了”这个问题定位一次线上异常通常要翻半天日志。第四个是成本控制模型调用和工具调用的开销在生产环境里非常敏感流程编排不精准浪费的token都是真金白银。这四个痛点指向同一个结论多智能体系统能不能稳定跑起来瓶颈不在单个Agent的智商而在Agent之间的“编排层”——谁来决定下一步做什么、谁在状态异常时兜底、谁记录整个过程。1.2 为什么说“编排层”才是架构的分水岭我在内部评审会上反复强调一个观点如果把多智能体系统比作一家公司单个Agent就是某个岗位的员工而编排层就是这家公司的流程制度。员工再能干如果流程制度混乱——任务没人跟进、交接没有记录、出了问题没人负责——公司照样转不起来。技术选型的真正分水岭就在于你选择把“流程制度”交给谁。交给通用框架相当于直接买一套成熟的管理系统上线快、功能全但制度的细节你说了不算全自研相当于自己从零定制度最贴合业务但前期要投入大量精力。我们当时面临的选择其实就在这两端之间通用框架负责Agent的跑动逻辑和工具调用但把流程状态和步骤控制交还给自己的代码。这个想法听起来并不复杂难的是具体怎么切分职责、怎么封装接口、怎么保证切换之后系统的稳定性不会反而变差。后面几节就是这套方案从思路到落地的完整记录。2. 决策拆解自研编排和通用框架各自的底牌2.1 通用框架好在哪里又坑在哪里先说通用框架的优点这个不能否认。当时我们选型时重点看了两类开源方案一类偏图谱式编排把多智能体的流程画成一张图节点之间用边连接另一类偏角色式对话让多个Agent像开讨论会一样互相聊天完成任务。这两类方案我们都实际搭过Demo体感和宣传基本一致上手快、抽象层次高、内置了常用工具调用逻辑。一个最简单的意图路由加订单查询流程用通用框架大概一天就能跑通这在早期验证业务价值时帮了我们大忙。但通用框架的问题也是我们真正上了生产之后才深刻体会到的。第一是编排逻辑的“黑盒感”图编排框架虽然给了你可视化的流程图但流程真正跑起来时每一步状态切换、重试策略、异常分支的处理很多逻辑藏在框架内部你只能在有限的回调点里做文章想精确控制某个分支非常吃力。第二是可观测性不够细框架确实会输出一些日志和trace但粒度是它定义好的不是你的业务语言出了问题你很难回答“这个用户的退货单为什么在这个节点停住了”。第三是升级绑架框架版本迭代频繁我们因为需要某个新特性升级过一次结果一个不兼容的改动导致十几个流程要重新回归测试。还有一个更微妙的问题通用框架的“泛化能力强”反过来意味着“定制能力弱”。它为了适配所有用户抽象层级一定偏高而我们的业务其实有大量特殊情况比如某些节点必须人工审批、某些Agent调用外部接口有特殊的鉴权协议、某些流程步骤数不能超过限制。这些东西做进通用框架里要么等官方支持要么自己改源码维护分支两条路都不好走。2.2 自研编排的优势与真实成本正因为通用框架在生产环境的控制力不够我们一度产生了“干脆全自研编排”的冲动。这个冲动的起点其实很朴素我们的流程虽然多但并没有多到几十上百种的地步我们的状态虽然复杂但仔细梳理之后核心状态机是可以枚举清楚的。自研编排引擎的好处正是这种“完全可控”——每个节点做什么、状态怎么迁移、异常怎么兜底、日志怎么记录全部由我们自己的代码说了算。另外一个容易被忽视的好处是“无绑定”。用通用框架你的核心业务代码会被框架的抽象方式影响时间一长你会发现自己不是在写业务而是在适配框架的API。而自研编排引擎没有这个问题业务的表达方式就是你最自然的方式。但自研的真实成本我们也算了一笔账。首先是开发成本一个能支撑生产环境的编排引擎至少包含状态机、任务队列、重试机制、超时控制、持久化、可观测性这些模块从零写起一个三到四人的小组保守估计要六到八周才能达到可上线状态。其次是长期维护成本编排引擎一旦成为基础设施后续的每个改动都要非常谨慎这对团队的能力要求比用框架高一个级别。最后是“轮子风险”开源框架背后有整个社区在踩坑自研引擎的坑只能自己踩有一些场景——比如极端并发下的状态竞争——你没遇到过就永远不会知道问题在哪。2.3 一张决策矩阵表看清边界为了把两边的边界理清楚我们整理了一张决策矩阵核心就看几个维度开发成本、可控性、生态能力、可观测性、升级风险和适用阶段。这张表后来在我们内部评审会上反复被拿出来讨论也帮我彻底想明白了混合架构的方向。评估维度纯自研编排通用框架编排混合架构自研外层框架内层初期开发成本高约6~8周低约1周跑通Demo中约3~4周流程控制力完全可控受框架抽象限制核心路径完全可控工具生态能力自己对接开箱即用通过内层框架复用可观测性按需定制受框架粒度限制按需定制技术绑定风险无高低适合场景流程复杂且团队能力强快速验证/简单固定流程生产级复杂业务从这个表格能很明显地看出纯自研和纯用通用框架都不是最优解。我们真正想要的是把通用框架的生态能力和自研的流程控制力结合起来。这就是混合架构的出发点外层用自研引擎控制“流程怎么走”内层用通用框架解决“单个Agent怎么干活”两者各管一段职责分明。3. 混合架构怎么落地外层自研、内层通用的三层设计3.1 架构总览与职责划分混合架构落地之后整体分成了三层接入层、编排层、执行层。接入层负责接收用户请求、做简单的意图预处理、把请求标准化成任务对象编排层是我们自研的流程引擎负责状态管理和流程推进执行层则是通用框架的运行时负责承载具体的Agent处理工具调用和模型交互。三层之间遵循一个关键原则粗粒度编排细粒度执行。意思是说编排层只在“流程节点”级别做控制和决策——比如判断当前该调用哪个Agent、前一步的结果是否符合预期、下一步是继续还是终止——它不去管Agent内部怎么跟模型对话、怎么选择工具、怎么拼接上下文这些细粒度逻辑全部收口在执行层的通用框架里。这个切分的好处是边界非常清晰。一旦某一个Agent出了问题编排层能准确知道是哪个节点失败一旦流程需要调整我们只需要改编排层的配置不需要动Agent的实现。对我们来说这相当于把“公司的流程制度”和“员工的工作方式”彻底解耦了。3.2 自研编排引擎的核心实现自研编排引擎的核心是状态机。我们把一条业务流程抽象成一组节点每个节点有明确的输入、输出和状态状态迁移由事件驱动。最开始我们用的是现成的状态机库但后来发现状态机的语义跟业务不完全匹配干脆自己写了一个轻量版本核心代码并不复杂关键在状态转移规则的清晰定义。# 自研编排引擎的状态机核心骨架 from enum import Enum class AgentStatus(Enum): PENDING pending # 等待调度 RUNNING running # 执行中 WAITING waiting # 等待外部事件如人工审批 SUCCESS success # 执行成功 FAILED failed # 执行失败 TIMEOUT timeout # 超时终止 class WorkflowState: def __init__(self, session_id, current_node, status: AgentStatus): self.session_id session_id self.current_node current_node self.status status self.payload {} # 节点间传递的数据 self.attempts 0 # 重试次数 self.created_at now() self.updated_at now() def transit(self, event: str, context: dict): # 状态转移规则表每个(当前状态, 事件)对对应一个目标状态 rule TRANSITION_RULES[(self.status, event)] if rule is None: raise IllegalTransition(self.status, event) self.status rule.target self.payload.update(context) self.updated_at now()状态转移规则表是我们花了不少心思梳理的每种业务节点一张表。以售后处理为例用户描述问题后状态从PENDING切到RUNNING售后Agent执行成功返回SUCCESS此时判定是否需要人工审批如果需要就切到WAITING人工审批通过后事件是APPROVE才允许进入生成退货单的后续流程。任何一个状态收到不合法事件直接抛异常并触发告警这比静默吞掉错误要安全得多。调度器则是状态机的“发动机”。它维护一个优先级队列不断取出“已就绪”的任务分发给执行层同时处理重试和超时。为了避免一个慢Agent拖垮整个会话我们给每个节点都配了超时阈值超时后先触发一次重试仍然失败就进入降级分支由兜底Agent接替或者转人工保证用户的会话永远不会因为某个Agent卡死而永久中断。3.3 通用框架执行器的封装与协议统一外层编排引擎并不直接跟具体Agent打交道它面对的是一个统一的Agent执行器接口。每个Agent无论内部用的是什么模型、什么工具都要实现这个接口这样编排层就只依赖协议不依赖实现。# Agent执行器统一接口 class AgentExecutor(Protocol): async def run(self, task: Task) - AgentResult: ... async def cancel(self, task_id: str) - None: ... def healthcheck(self) - bool: ...接口统一之后具体实现就交给通用框架来完成。我们把原来跑在通用框架上的Agent原样保留只是在外面包了一层适配器从编排层接收任务转换成通用框架的输入格式调用框架的Agent运行时再把结果转换回标准结果格式返回给编排层。这个封装解决了一个很实际的问题模型供应商的SDK版本升级、内部某个Agent的prompt大改、工具调用的参数调整这些变动全部被隔离在执行层内部。编排层完全不受影响状态机也不需要跟着改。有一次我们为了适配新的模型上下文格式几乎重写了Agent内部逻辑但整个流程编排层一行代码没动那次之后团队里再没有人质疑混合架构的价值了。4. 从Demo到生产实操过程与关键数据4.1 基线验证与性能对比混合架构落地之后我们并没有直接全量切换而是先跑了一轮完整的基线验证。验证分两条线一条线是把原来的通用框架编排跑在测试环境作为对照组另一条线是新的混合架构跑同样的业务场景。测试数据来自脱敏后的真实会话样本一共覆盖五种意图、两千多条消息重点对比三个指标P95端到端延迟、任务失败率、故障恢复时间。对照组测出来的数据并不算好看P95延迟在8秒左右徘徊任务失败率接近5%而且有大约三成失败会卡住会话状态需要人工介入才能恢复——这个现场我们内部用“流程卡死”来形容用户的会话停在某个节点上你再怎么发消息都走不下去。混合架构跑同一批样本P95延迟降到了3秒左右任务失败率降到1.2%更关键的是会话状态不会再轻易卡死超时之后自动重试或者降级到兜底Agent绝大多数情况下用户无感知。延迟降下来主要是因为编排层省掉了大量不必要的框架内部开销流程明确之后节点之间的跳转变得非常直接。失败率降低则得益于自研状态机的精确控制和重试策略。恢复能力提升是混合架构最明显的收益故障恢复时间从人工介入的几十分钟缩短到自动重试的十几秒。4.2 踩到的三个真实坑这套架构并非一帆风顺有三个坑我想重点记录因为它们都不是靠看文档能避开的。第一个坑是状态机“死锁”。最初我们设计状态机时只考虑了正常事件流没有充分考虑“用户中途取消会话”这类边界事件。结果上线后出现了一个诡异的现象部分售后会话的状态变成了RUNNING但流程永远停在那里既不前进也不失败因为这个状态没有定义CANCEL事件的转移规则。定位了半天才反应过来——状态机写得太理想化把边界事件漏掉了。这次事故之后我们专门补了一张“全事件×全状态”的转移矩阵把所有非法组合全部排查一遍边界事件一个都不放过。第二个坑是上下文爆炸。外层编排层为了流程判断会把每个节点的输出都存起来导致一个长会话的payload越积越大。刚开始不觉得直到有用户连续咨询了二十多轮我们发现内存和token开销成倍增长甚至影响到了响应速度。解决办法是给payload做“摘要式保留”每个节点输出先做一次压缩只保留流程判断需要的关键字段完整内容存到外部存储需要时再按ID取回。第三个坑是回调风暴。多个Agent并发的场景下执行层完成的回调如果同时到达编排层我们最早的处理方式是每个回调都触发一次状态变更和持久化结果在压测时数据库连接直接被打满。后来改成事件批处理回调先写入内存队列编排层按批次统一处理、统一落库写压力直接降了一个数量级。这个改动让我深刻理解了“削峰填谷”在编排层的重要性。4.3 可观测性与稳定性建设经历过几次线上事故之后我们把可观测性提到了最高优先级。编排层每个状态迁移都产生一条结构化事件记录包含 session_id、节点名、旧状态、新状态、触发事件、耗时、payload摘要。这些事件写入独立的日志存储支撑两类查询一是按session_id拉出完整的流程轨迹二是按节点名统计成功率、耗时、重试次数。我们还为每个流程节点设计了“运行画像”记录最近一周的P50/P95耗时、失败率、重试率、降级率这些指标。每周Review一次哪个节点异常、哪个Agent不稳定一眼就能看出来。可以说混合架构最大的收益之一就是它让系统变得“可解释”了——每次线上问题都能快速定位到具体节点和具体原因而不是在黑盒里猜。稳定性建设方面除了前面提到的超时重试和降级分支我们还加了两个保险机制一个是“会话冻结恢复”如果工作进程崩溃了重启后能根据持久化的状态快照从最近一个稳定状态继续执行而不是从头开始另一个是“全链路超时兜底”单个节点超时是3秒整条编排流程还有一个总超时总超时到了之后不管流程走到哪都强制收敛到人工兜底节点宁可转人工也绝不让用户无限等待。5. 常见问题速查与排障经验5.1 问题速查表把实际操作中遇到的问题整理成了一张速查表方便团队内部排查问题时快速定位。这里挑几个高频问题分享出来。现象可能原因排查方法解决方案会话状态卡在RUNNING不动缺少边界事件的状态转移规则拉取该会话的完整事件轨迹查最后一条事件补齐状态转移矩阵增加超时强转多个Agent并发时延迟飙高执行层回调风暴打满数据库连接看数据库连接数和事件落库写入TPS改事件批处理削峰填谷相同业务请求重复执行重启后状态没有从快照恢复查进程启动日志和状态快照时间实现幂等键和状态快照恢复Agent返回结果格式不稳定模型输出解析失败查看执行层原始输出和解析失败率增加输出校验和自动重解析逻辑某节点重试依然秒败依赖的下游接口已不可用查节点指标里的下游API错误率增加熔断开关直接走降级分支5.2 三个最有价值的经验总结下来我觉得有三个经验如果一开始就意识到能少走很多弯路。第一个经验是“可观测性必须在第一天就设计进去”不要等出了线上事故再补。我们最开始用通用框架的时候就是因为可观测性不足导致排查问题非常痛苦。后来自研编排层第一件事就是把结构化事件日志设计好之后所有线上问题的定位时间都以分钟计。第二个经验是“兜底方案往往比主流程更重要”。用户的咨询路径不会总是按你想的流程走开发的重心很容易放在“正常流程跑通”上但生产环境里真正决定体验的是异常情况下的兜底超时怎么办、解析失败怎么办、下游接口挂了怎么办。这些分支一开始就要设计好而不是出事后再补。第三个经验是“架构是演进出来的不是选出来的”。刚开始用通用框架没有错它帮我们快速验证了业务价值后来纯自研的探索也有意义它帮我们看清了核心痛点在哪里。最终混合架构不是某一天的灵光一现而是逐步演变的结果。如果让我给一个建议那就是别等到“完美方案”再动手先把业务跑起来然后再在真实的反馈里优化架构。6. 混合架构的后续演进路径与个人体会6.1 后续可以做哪些改进混合架构当前版本已经稳定运行了一段日子但方向上还有不少可以继续深挖的地方。我们在内部规划了几个后续演进方向这里也列出来供参考。第一个方向是引入决策引擎替换硬编码的路由分支。目前的流程编排虽然相比之前灵活了不少但节点之间“走哪条分支”基本上还是靠代码写死的规则。后续我们打算让路由判断改成一个独立的决策模块基于用户画像、会话上下文和实时指标做更智能的路径选择让同一个节点在不同场景下走不同分支。第二个方向是完善“人工审批”这类异步节点的体验。目前WAITING状态虽然支持但人工审批的结果回填还是一个比较重的操作涉及跨系统回调。我们计划把审批流统一抽象成一种特殊Agent让它跟普通Agent走同一个执行器接口这样编排层不需要为人工流程单独维护一套逻辑。第三个方向是给编排层加一个“流程沙箱”。现在修改一条流程的编排配置必须走完整回归测试才能上线周期比较长。我们想做一个预生产环境新流程先在沙箱里跑用历史会话回放的方式验证正确性通过之后一键发布。这个如果能做好“编排治理”的体验会上一个台阶。6.2 个人体会最后聊一点私人的体会。这一年多下来我对“框架选型”这件事的理解变了很多。早些年我倾向于追新框架总觉得用了某个热门方案就站在了技术前沿踩过这么多坑之后我反而觉得好的架构不是选出来的是长出来的。通用框架给了你快速验证的翅膀自研代码给了你掌控细节的双手而混合架构的精髓恰恰是在两者之间找到那个最适合自己业务现状的平衡点。如果你也在做多智能体系统的架构决策我的建议很简单先用通用框架把业务跑通把核心痛点列清楚然后只对痛点最多的那一层做自研最后让框架和自研代码各管自己最擅长的事。千万别一上来就追求“纯自研”的极致控制也千万别为了省事把所有东西都压在通用框架里。生产环境的复杂性永远比我们预想的更曲折而混合架构至少给了我们一个既能快速应变、又能稳住底盘的务实选择。