
你把编排器部署在一个多 Agent 系统里认认真真跑了半年每天看着它调 Agent、传消息、接结果觉得一切尽在掌握。直到某个周二下午你打开最近三天的调用链路日志突然意识到一件事它和开工第一天几乎没有区别所有任务都按同一条固定流程走完某个关键 Agent 失败时它只会原样报错处理不了的东西就丢给人工兜底。它从指挥交通的调度员变成了只负责送信的邮差。这个现象并不罕见。多 Agent 系统的编排器Orchestrator早期还能看出“智能”的影子运行几个月后业务压力上一来反而越来越像一个笨重的消息中转站拆包、转发、等结果、再转发。真正需要判断、决策、应变的地方它全都让位给了写死的规则和预设的流程。这篇文章就聊聊我观察到的“编排器邮差化”过程以及怎么在半年这个节点上把编排器拉回轨道。它适合正在长期维护多 Agent 系统的架构师、后端开发、AI 应用负责人参考。无论你的系统是客服助手、内容生产流水线还是企业内部知识问答平台只要编排器已经跑了一段时间这篇文章里的诊断方法和重构路径都可以直接拿来用。1. 编排器从指挥者变成邮差到底哪里出错了1.1 邮差化编排器的三个典型特征我见过不止一个团队把编排器做成“邮差”而不自知。最典型的特征有三个。第一个特征是转发大于决策。编排器把每个请求原样丢给固定的 Agent 链比如先调用意图识别再调用搜索再调用生成最后套模板输出。整条链路看起来逻辑完整但编排器本身没有做任何实质性的判断。用户问“你们退款政策是什么”和用户问“我的订单为什么还没发货”会被送进同一套流程只是参数不同。编排器不关心输入是否触发了新的分支、是否需要跳过低效步骤只负责按顺序把消息往后传。第二个特征是规则全部写死。半年下来业务新增了很多需求Agent 也拆了好几轮但编排层的变化仅限于往旧链路里多加一个 if 分支。业务说“如果用户情绪很激动就转人工”代码里就多一句判断业务说“如果搜索结果小于三条就补充一轮扩写”代码里又长了一截。这些规则表面上是编排逻辑实际上是把智能决策硬编码进了业务代码让它失去了适应新场景的能力。第三个特征是对结果质量毫无感知。编排器只关心 Agent 是否成功返回了结果不关心结果本身靠不靠谱。第一个 Agent 返回了一篇看似完整但事实错误的内容第二个 Agent 直接把它当成素材继续加工编排器依然一路放行。长期运行的系统里这类问题最隐蔽因为它不在报错日志里出现只会在用户投诉或业务复盘时突然爆发。这三个特征叠加起来编排器就彻底退化成了邮差。消息从它手里过它不知道内容里有什么坑也不知道目的地是否真正正确。半年后你想让它做一些更智能的调度会发现它早就没有这个能力了。1.2 深挖根因不是模型能力退化是系统设计提前还债很多人一开始会怀疑是不是大模型能力不够导致编排器“变笨”了。实际上模型的能力没有退化甚至因为版本升级变得更强了。真正的问题在于编排器的设计在系统运行半年之后开始集中偿还技术债。第一个根因是原型阶段留下的短期方案被长期沿用。刚开始做多 Agent 系统时为了快速跑通 Demo大家习惯在编排器里直接写规矩。这本身没问题Demo 删掉重写也不心疼。但真实业务不会等你把代码重构完再上线。那套写死的 if-else 随着业务一起成长半年后已经复杂到没人敢动。一个在我看来是短期跳板的东西最终成了系统最牢固的枷锁。第二个根因是上下文在编排层被掐断了。长期运行的系统往往面临高并发为了控制成本和延迟很多团队会大幅压缩传给编排器的上下文。一次请求进来只保留当前这一步需要的字段历史对话、之前 Agent 产出的中间结论要么缓存过期要么直接被丢弃。编排器每次决策都像失忆的人看一张便利贴只能看到当下一丁点信息自然做不出高质量的调度判断。第三个根因是缺少结果反馈回路。真正合格的编排器应该像项目负责人分配任务、检查交付质量、发现不合格就打回重做。但很多系统在设计时只考虑了正向流程Agent 返回什么就接受什么没有在编排层设置质量检查点。没有检查就没有反馈没有反馈就没有优化依据编排器只能当一条传送带。第四个根因更隐蔽团队对编排器形成了一种“少动少错”的默契。系统跑得久了出问题的人多改坏过的人更多。大家都在编排器旁边小心翼翼地修边幅却不敢动核心逻辑。长期下来编排器这块代码区就成了雷区所有人都在它外面打补丁里面变得越来越陈旧。这四个根因单拎出来任何一个都不难解决但它们捆在一起就会在半年左右的时间里把表面上还完整的多 Agent 系统变成一个内部已经僵化的空架子。2. 半年后再看编排器该管什么不该管什么2.1 编排器和协调器的边界很多人一开始就分得不对聊编排器邮差化之前必须先把边界盘清楚。多 Agent 系统里有两类控制角色一类叫 Orchestrator另一类叫 Coordinator中文经常都翻译成“协调器”或“编排器”但它们的职责完全不同。Orchestrator 的核心是编排它决定一次复杂任务要拆成哪几步、每步交给哪个 Agent、步骤之间如何衔接、失败时如何回退。它的输出是一个执行计划并且对计划的整体结果负责。Coordinator 的核心是协调它处理的是多个 Agent 平等的、持续的协作关系避免死锁分配资源调解冲突更像会议主持人。很多团队在架构初期把编排器当成一个什么都能干的中心节点既做任务分解又做资源协调还兼职消息转发。一个小模块塞了太多角色结果必然是哪样都不精。真正合理的拆分是让 Orchestrator 只管“这一单任务怎么执行”把资源分配、并发控制、服务发现这些事交给独立的协调层去做。半年后回头看凡是编排器表现得很别扭的系统多半是把这两层焊死在了一起。2.2 编排器的核心价值是“决策”不是“转发”如果我们把编排器的职责收敛到一个点上那一定是决策。它应该像一个经验丰富的项目经理拿到需求后快速判断这个请求的意图是什么现有哪个 Agent 能完成最核心的一步需不需要并行发起多个能力中间结果要不要做质量检查如果核心 Agent 失败有没有合适的备选路径每一项都是决策每一项都需要编排器结合当前上下文、历史记录和 Agent 状态来做判断。反过来看邮差式编排器它从头到尾只做了一个决策就是“按既定顺序发送”。这不是编排这只是循环。我在实际重构项目里会把编排器塑造成一个“计划生成器 执行监督者”。计划生成器负责把用户请求变成一条可执行的步骤序列执行监督者负责在执行过程中观察每步结果动态调整剩余计划。转发只是执行计划时的副作用不是核心。如果哪天发现代码里编排器主要在做转发那基本可以判定它已经偏离了正确的角色。2.3 邮差模式也有存在价值别一棒子打死话也得说回来。邮差模式在某些场景下是合理的甚至是最优解。如果业务本身就是强流程的比如订单处理创建订单、扣库存、开发票、通知物流每一步的前置条件都不可跳过用固定的编排链路反而是正确选择。还有一种情况是 Agent 数量少、任务模式单一比如一个内部工具只做文本润色先调用摘要 Agent再调用改写 Agent半年都没变。这种稳定、低变化的场景里动态决策的收益很小强行上复杂路由只会增加延迟和维护成本。所以我反对的不是邮差模式本身而是“明明场景已经变化了编排器还停留在邮差模式”。判断依据很简单你对一个用户的请求是不是真的只需要按固定顺序转发就能完成如果是保持现状没问题。如果系统已经出现越来越多的分支需求固定的顺路早就意不到了那就要考虑升级编排器。这种灰度思维很重要。实际项目里最危险的做法是听到“编排器要智能”就推倒重来把所有确定性流程全部改成动态决策。结果只会换来一个更难排查、更不可预测的黑盒效果可能比原来的邮差还差。3. 把编排器从“邮差”拉回“决策者”的实操路径3.1 诊断先行用三类指标锁定“邮差化”程度重构之前先别动手改代码先把系统当前的“邮差化程度”量化出来。我常用三类指标做判断。第一类叫路由多样性指标。统计一段时间内编排器把请求分发给不同 Agent 的组合数量。在一个运行健康的多 Agent 系统里同样一批请求应该产生多种调用路径。如果数据显示 95% 的请求都走同一条固定链路那就说明编排器根本没有在做路径决策。采集方式是把编排器的路由动作全部打在 trace 日志里用 trace_id 聚合统计主链路的占比。第二类叫决策有效性指标。人工或者用评测模型抽查一批已经结束的任务看编排器选的路径是不是当前条件下最优的。实际案例里我经常用“是否发生了不必要的长链路调用”做判断。用户在问简单的价格问题编排器却调用了搜索、检索、对比、生成四个 Agent原本一个直接问答就能解决这就是决策有效性差。第三类叫上下文连续性指标。检查编排器传给每个 Agent 的内容是否包含了完成该步骤所需的完整上下文。最典型的糟糕模式是用户提供了一个订单号编排器只把“查订单”这个指令传给订单 Agent却没带上订单号。最后订单 Agent 只能回一句“缺少参数”。这类错误越频繁说明编排器越像一个不管不顾的信使封装了内容却不检查内容。把这三类指标统计出来基本就知道编排器退化到哪一档了。指标结果差不要紧至少你能明确问题在哪而不是靠感觉做判断。3.2 引入意图驱动的动态路由如果诊断结果显示编排器太“邮差”当务之急是把写死的路由规则替换成以意图为基准的动态路由。具体做法分两步。第一步是建立“意图到能力”的映射。把业务里真实会出现的请求类型穷举出来比如“咨询订单状态”“申请退款”“投诉建议”“闲聊扯淡”每种意图对应一个或一组 Agent 技能。这个映射表不用一开始就做得特别大先覆盖 80% 的高频场景剩下的交给兜底逻辑。第二部是在编排层用一个轻量的路由函数来判断意图并选择路径。所谓轻量不一定是非得调用大模型。很多团队有个误区觉得动态路由必须每个请求都让 LLM 做意图识别。其实不必。你完全可以先做一层规则过滤命中已知关键词就直连对应 Agent规则匹配不上再交给 LLM 做意图判断。这样既保留动态性又不至于把成本拉得很高。我实际用的路由逻辑大概是这样的。def route_request(req): if req.contains_entity(order_no) and req.intent_hint refund: return RefundAgent, {priority: normal} if req.intent_hint is None: intent classify_by_llm(req.user_text) return skill_map.get(intent, FallbackAgent) return skill_map.get(req.intent_hint, FallbackAgent)这里有三个关键点。第一个是 entity 提取要前置第二步路由如果缺少关键实体就会走错路径。第二个是 FallbackAgent 不能省它至少要当住了无法识别的请求而不是直接报错。第三个是把原有的固定链路降级为“默认路径”只有路由不明朗的时候才走它。实践下来这套逻辑的准确率能接近 92% 以上而且重构成本比想象中低核心的 mapping table 一个下午就能整理出来。3.3 给编排器装一个“结果反馈回路”动态路由解决的是“把任务交给谁”的问题下一个要解决的是“任务结果靠不靠谱”。没有反馈回路的编排器就像只发菜不管口味的厨师做完就上桌好坏全凭客人自认。我强烈建议在编排层加入一个轻量质检节点。不是每个步骤都要检查那太贵了而是挑对整个任务成败影响最大的关键步骤做质量门禁。比如内容生成链路里第一步检索到的资料是以后所有生成内容的事实基础那就在检索结果返回后、送进生成 Agent 前加一道校验是否有空结果、是否明显和问题不相关、来源是否足够可信。质量门禁可以用规则也可以用 LLM-as-Judge。规则适合硬性条件LLM-as-Judge 适合判断“回答是否完整覆盖用户核心问题”。两种结合效果最好。质量门禁发现问题后编排器不能只记录日志必须能触发重试、换路或者降级。我在项目里常用的做法是定义统一的重试策略class RetryPolicy: def should_retry(step_result): if step_result.quality_score THRESHOLD: return swap_to_backup_agent(step_result) if step_result.timeout: return prune_step_and_continue(step_result) return proceed一个容易被忽略的点是重试不能原样再跑一遍相同逻辑那大概率得到相同结果。正确做法是换一个备选 Agent、调整指令上下文、或者把任务拆小。否则重试反馈回路只是一个昂贵的自我重复还会把延迟拉高一倍。3.4 用编排配置化替代代码化动态路由和反馈回路加进来之后编排器代码会明显变复杂。如果业务下一步又有新需求靠改代码来加分支的老路又回来了。为了避免这个循环我的经验是把编排规则尽量配置化。具体来说把“意图映射表”“Agent 能力清单”“重试策略参数”“质量门禁阈值”这些内容都从代码里剥离出来放到一份独立配置里。配置可以是一个 YAML 文件、一个数据库表或者一个配置中心。运行时编排器只需要读取配置后续增删 Agent、改路由关系都不需要动核心代码。配置化的好处除了维护方便还有一层更关键的价值可回滚。你会发现多 Agent 系统的变化频率远远高于传统服务。今天你给 Agent 换了提示词明天你上了新模型这些都会影响 Agent 的实际表现。配置化之后一旦某次调整导致效果崩了可以快速回退到上一个稳定版本而不用从代码层面重新部署。我见过一个做得不错的团队把整套编排策略做成了可视化的表格意图、技能、兜底策略、阈值全部在 UI 上维护。运行半年后他们已经迭代了十几个版本但核心编排代码几乎没改过。这才是动态编排该有的样子。4. 六个月大考一份可以直接抄的编排器健康检查清单4.1 周度巡检和月度深度 Review分开做与其等到半年后才发现编排器邮差化不如把检查做成常规动作。我的经验是周度做巡检、月度做一次深度 Review两者关注的颗粒度完全不同。周期检查项核心关注点通过标准每周编排器调度延迟各环节耗时是否异常升高P99 延迟不超过基线 1.5 倍每周Agent 调用成功率是否有 Agent 频繁超时或报错成功率 ≥ 99%每周路由分布占比主链路占比是否突然上涨主链路占比保持稳定区间每月路由决策召回率目标 Agent 是否正确命中决策准确率 ≥ 95%每月质量门禁拦截率质检环节拦下多少劣质结果拦截后有明确替代动作每月上下文完整率传给下游 Agent 的参数是否齐全缺参错误趋近于 0周度巡检可以用监控告警来实现把它嵌到 CI/CD 和监控体系里。月度深度 Review 则需要人工介入我一般会拉取两周的 trace 日志随机抽样 100 个完整任务逐个看路由决策是否合理、Agent 输出是否可靠、编排器有没有在关键节点做出正确判断。体检的意义不在于发现多少问题而在于让团队的每一个人都知道编排器不只是一段消息转发代码它承载着系统的质量和智慧。只要定期量它就不会悄无声息地烂掉。4.2 已经“邮差化”的编排器重建还是渐进重构如果检查做完发现编排器已经深度邮差化这时候最大的选择难题来了直接重写还是在旧代码上打补丁。我的建议是除非系统规模真的很小否则不要选择推倒重来。多 Agent 系统最值钱的往往不是代码本身而是代码里沉淀的一套规则和异常处理经验。那些写死的 if-else拆开来看其实是业务团队用几个月的踩坑换来的积累。你把代码删了经验也丢了。正确思路是渐进重构。先保留原有链路作为默认路径在旁边搭建一个新的编排组件只把新的请求切到新组件上跑。新组件跑稳了再把旧链路慢慢收窄直到最后只剩兜底功能。这样每一步都可回退、都可观测风险会被控制在一个让团队能接受的范围。我特别喜欢渐进重构的另一个原因是它能逼着团队把路由逻辑真正想清楚。因为旧链路还能兜底新组件你会更从容地设计上下文传递、反馈回路和配置化这些细节。半年后你收获的会是一个结构清晰、有测试覆盖、能快速演进的编排系统而不是又一个刚刚诞生的“伪智能”。4.3 别把编排器和 Agent 技能耦合到动弹不得体检时还要顺带检查一项隐性问题编排器和具体 Agent 技能是不是已经深度耦合了。我见过最极端的情况编排器代码里直接引用了某个 Agent 的内部数据结构Agent 把返回值的字段名一改编排器立刻崩溃。这种耦合一旦出现编排器就不再是独立的控制层而变成了某个技能模块的扩展代码。以后想换一个实现更好的 Agent牵一发而动全身根本动不了。六个月的节点上这个问题的杀伤力会被放大很多倍因为 Agent 技能已经迭代过不止一轮了。如果要给一个原则的话我对编排器与 Agent 的通信契约只有一个要求像调用外部 API 一样严格字段定义清楚版本独立不共享内部变量。编排器应该只基于 Agent 的输入输出接口做决策永远不要深入某个 Agent 的实现细节。5. 真话分享一个实践者对编排器的一些体会每次聊到编排器邮差化我都会想起一句话建筑不是材料的堆叠而是空间的组合。多 Agent 系统也是一样。一堆能力很强的 Agent如果中间没有一个能真正做判断的编排层它们只是被简单拼接在了一起而不是被有机组合起来。从我自己的实践来看编排器能不能长期保持“决策者”的状态关键不是模型参数调得多好也不是路由策略写得多花哨而是有没有把反馈闭环、上下文连续、配置化和定期体检这四件事真正落地。这些看起来都是基础工程但它们恰恰是防止系统老化的最有效手段。最后分享一个小技巧。我在团队里规定编排器代码里不允许直接出现“永远”和“必须”这两个词对应的硬编码逻辑。任何一条路由规则都要能回答一个问题如果这个 Agent 在下一次调用时不可用系统该去哪。能回答得了这个问题的编排器才配叫编排器回答不了的不过是一个跑得再快也改变不了本质的邮差。