做多智能体应用时间久了你会发现真正难的从来不是单个智能体怎么写而是多个智能体怎么协作。MAF系列写到这里是第13篇前几篇陆续讲过工具调用、记忆管理、上下文控制这篇集中把“智能体编排”这件事拆开聊。所谓编排说白了就是决定谁先跑、谁后跑、谁跟谁并行、结果汇给谁、出错之后怎么重来。同一组智能体编排方式不同产出的质量和成本可以差好几倍。这篇内容主要基于我最近做的售后客服数据分析项目六种常见编排模式都在里面用到了边讲实现边讲踩坑适合已经写过单智能体、准备往多智能体迁移的开发者也适合做技术选型时想快速了解编排风格差异的同学。1. 为什么需要编排单个智能体搞不定的事1.1 编排解决的三个核心问题先拿一个很常见的场景举例把一堆客服工单变成一份周报。单个智能体也能干把“清洗数据、分类问题、提取摘要、生成报告”全部塞进一个提示词里让它一次输出。实测下来这类做法非常不稳任务一多模型就会顾此失彼要么分类漏了某个类别要么摘要写得像流水账要么报告结构跑偏。这不是模型能力不够而是职责混在一起推理负担太重。多智能体的思路是把任务切碎各司其职让一个智能体只做一件小事。但切碎之后立刻冒出一堆新问题谁来安排执行顺序每个智能体的输入输出怎么衔接如果某个子任务失败了是重跑还是跳过多个结果怎么汇总成一个整体这些问题没有一套机制管起来多智能体就会变成多智能体“事故现场”。编排要解决的核心问题归纳起来是三类第一是任务分解把复杂目标拆成可执行的子任务第二是结果整合把多个智能体的产出合并成统一结果过程中不丢信息、不冲突第三是异常控制单个智能体出错了不能拖垮整条链路。这三件事不解决好多智能体系统就是脆弱的积木看着结构漂亮碰一下就塌。1.2 编排不是画流程图而是管状态很多人一开始把编排理解成“画一张流程图”哪个环节完了就走下一个箭头。流程图能说明顺序但说明不了更关键的东西上一步的结果从哪来、以什么格式传给下一步、这一步失败了退回哪里、多个并行结果怎么对齐。这些本质上全是状态问题。运行中的多智能体系统至少有两类状态。一类是任务状态记录当前执行到哪个节点、哪些子任务已完成、哪些还在等结果。另一类是对话上下文也就是各智能体之间传递的数据和中间产物。编排框架的作用就是帮你把这些状态管起来同时定义好智能体之间的“交接协议”。我见过不少失败的案例问题都出在没有协议。两个智能体之间直接说“你把结果发给我”结果一个返回了纯文本一个返回了JSON数组下游没法解析只好靠提示词硬猜一猜就崩。正确做法是提前定义每个交接物的字段名和格式让成功和失败路径都结构化。这就像团队协作光说“你把材料给我”没用得约定好给的是表格还是文档、列名是什么、缺数据怎么标注。智能体之间也是一样的道理。对比维度单智能体多智能体编排职责边界一个提示词承担全部逻辑每个智能体只做一件事上下文负担所有信息堆在一个对话里各智能体只需看自己那部分错误影响一次错误可能污染全部输出可以用重试、降级局部处理调试成本只能整体调提示词可以逐节点定位排查开发成本低写起来快高需要设计协议和状态这也是为什么单智能体原型很容易做多智能体落地却难。你真正在设计的不是几个提示词而是一套任务协作规则。2. 六种常见编排模式先认个脸熟2.1 六种模式一句话定义多智能体编排没有几十种花样常用的模式我总结下来就六种。各自解决不同的协作问题顺序链Chain上一步的输出就是下一步的输入按顺序一道道走。适合流程固定、前后依赖强的任务。并行扇出Fan-out一个任务拆成多个互不依赖的子任务同时执行最后汇总。适合能并行处理的批量场景。路由分发Router先判断输入属于哪个类别再走向不同分支处理。适合意图区分明显的场景。编排器-工作者Orchestrator-Worker由一个主智能体动态拆解任务、分配子任务、收集结果并汇总。适合任务内容不确定、需要动态规划的复杂场景。群聊协作Swarm多个角色智能体围绕一个主题各自发言、互相讨论由主持人收敛结论。适合需要多视角讨论和意见碰撞的场景。反思循环Reflection先生成初稿再用另一个智能体做质检发现问题就打回重改直到满足标准。适合质量要求高的生成类任务。这六种模式可以单独用也可以嵌套组合。我那个客服数据分析项目里六种全部用上了不同的环节选不同的模式后面会具体讲。2.2 选型不是选最好的是选最合适的很多同学第一次接触这六种模式容易陷入选择焦虑到底哪种最牛我的回答是没有最牛的模式只有最合适的场景。选型主要看四个维度任务流程是否固定、子任务之间有没有依赖、需不需要动态决策、以及你能接受多少延迟和成本。举个直观对比如果一个任务的流程完全写死在业务规则里顺序链就是最佳选择稳定且便宜。但如果流程本身要根据用户输入变化那顺序链就做不了得靠路由或者编排器。同样是做客服问答判断“查订单”和“办退货”用路由就够了但如果用户的请求是“帮我分析一下本周退货率为什么升高”背后的分析路径完全不确定就必须上编排器。我把六种模式的适用场景整理成一个对照表做方案设计时直接对着查模式适用场景优点缺点典型例子顺序链流程固定、强依赖稳定、易调试、成本低不能动态调整数据清洗→分类→出报告并行扇出子任务独立、量大延迟低、吞吐高上下文重复消耗token多渠道客服记录分别分析路由分发意图区分明显精准分流、分支可管理路由错了连带下游错退货流程 vs 订单查询编排器-工作者流程不固定、需动态拆解灵活、适应复杂任务依赖大模型指令遵循能力开放式数据分析任务群聊协作需多视角讨论信息互补、考虑全面容易发散、token消耗大报告多角色审核反思循环质量优先、可多次生成输出质量高耗时耗token质检报告、正式文案生成我自己的选型经验是能用简单模式解决的就不要上复杂模式。顺序链能跑通就先用顺序链真到了流程必须动态变化的那天再升级到编排器或者群聊不迟。3. 顺序链和并行扇出最常用的两种组合3.1 顺序链式编排实操让每个智能体只干一件事先讲我在售后客服数据分析项目里用得最多的顺序链。原始数据是各类客服工单目标是生成一份可读的分析报告。这条链路被拆成四段读取工单并标准化字段、清理无效数据、对问题分类汇总、生成周报摘要。每一段的输出都是下一段的输入中间的字段格式必须严格对齐。在MAF里顺序链的配置写起来比较直观大致是这样chain: name: after_sale_analysis steps: - agent: loader_agent input: raw_tickets output: normalized_tickets - agent: cleaner_agent input: normalized_tickets output: clean_tickets - agent: classifier_agent input: clean_tickets output: classified_tickets - agent: reporter_agent input: classified_tickets output: weekly_report这里最需要注意的一点是每一步都必须定义明确的输入输出字段而且字段名从第一步到最后一步要保持一致。比如ticket_id、issue_type、channel这些字段loader 返回什么名字后面所有环节就要沿用同一个名字。实际操作中我踩过最大的坑就是让一个智能体同时做“清洗”和“分类”。表面看都是文本处理但清洗需要关注格式、去重、补全分类需要理解语义、归并同类问题两个职责混在一起模型经常把分类结果写到清洗字段里去下游一解析就报错。后来老老实实拆开每个智能体只做一件事输出立刻稳定了。给这个环节定参数时单步超时我设的是30秒整条链最大重试次数是2因为链路本身有四步单步重试太多会导致整体延迟线性叠加。3.2 并行扇出实操多路同时跑延迟直接下降客服数据不止一个来源线上会话、邮件、电话工单各是一路。如果都走同一条顺序链数据量一大整条链路要跑十几分钟。这里就可以引入并行扇出把不同渠道的数据分给各自的worker智能体去处理最后再汇总。MAF里我用的是 FanOut 和 FanIn 两个组件配合写法大致这样from maf import FanOut, FanIn async def analyze_all_channels(pipeline_state): channels [online, email, phone] results await FanOut( workers[channel_worker(ch) for ch in channels], concurrency3, # 同时跑3路 timeout120, # 单路超时120秒 ).run() return await FanIn(results).merge(modesummarize)延迟的收益很直观。原来三条渠道的数据依次跑完需要约5分钟改成三路并行后整体耗时取决于最慢的那一路实测在90秒左右快了不止一倍。时间是省下来了但要注意几个细节。第一FanOut 的concurrency别盲目调高。并发数受模型接口限流影响一次开10路如果触发了限流反而全部失败我一般会留一点余量限额允许10就只开8。第二汇总阶段要做空结果兜底。某个渠道当天可能没数据worker 返回空列表FanIn 的 merge 需要处理这种情况不能直接报错。第三每条渠道的 worker 仍然是内部按顺序链跑的FanOut 和 Chain 不是互斥关系而是嵌套关系。先并行拆渠道渠道内部再串行处理这种组合非常常见。3.3 并行不是免费的成本与延迟的平衡并行扇出省了时间但有一个隐性代价公共上下文被每个 worker 重复拷贝。每个渠道的分析都需要知道项目背景、分析标准这些公共信息并行跑三个 worker这些信息就在上下文里被重复计费了三次。如果公共信息很长token 消耗涨得非常快。我的优化做法是把公共背景尽量放进 system prompt 而不是每个 worker 的输入上下文里同时让每个 worker 只输出“差异信息”也就是本渠道的处理结果不要把它们各自拿到的公共信息再复述一遍。另一个办法是把公共信息提炼成一段精简摘要放进 worker 的 system prompt而不是把完整的任务说明甩给每个 worker。选串行还是并行的判断标准其实只有一个子任务之间有没有依赖。完全独立就走并行有先后依赖就走顺序链。依赖关系没捋清之前强行并行只会把串行问题变成并发问题调试起来更头疼。4. 路由分发与编排器-工作者动态决策的两档4.1 路由分发实操先分类再分流客服系统里最常见的场景是用户进来一句话系统得先判断他想干什么然后走对应流程。退货退款是一套流程查订单物流是一套流程投诉建议又是另一套。这种场景用路由分发最合适。路由判定的实现我一般用“关键词粗判 模型细判”双重保险。先用关键词把明显意图过滤掉比如出现“退货、退款、换货”就归到退货意图关键词命中不了或者多个关键词冲突时再交给一个轻量分类模型做判定。MAF 里的路由规则可以写成这样route_rules [ { intent: return_request, match: {keywords: [退货, 退款, 换货]}, target_flow: return_flow, }, { intent: order_inquiry, match: {keywords: [订单, 物流, 到哪了]}, target_flow: order_flow, }, { intent: complaint, match: {keywords: [投诉, 差评, 态度差]}, target_flow: complaint_flow, }, ]路由最怕的是判定错误。分错分支后面跑得再顺也是白搭。所以我给路由结果加了一个confidence阈值判定置信度低于0.7的请求不硬走某个分支而是落到一个兜底的人工处理流程。这个习惯帮我们拦住了大量边界情况比如“你们这个快递怎么还没到我要投诉”这种同时命中物流和投诉两种意图的句子。4.2 编排器-工作者实操让主智能体当项目经理路由分发适合分支固定的场景但如果用户提的问题根本没法预先枚举分支就需要编排器-工作者模式。这个模式的核心是有一个主智能体充当项目经理它拿到总任务后自己判断需要哪些子任务把子任务分发给不同的 worker 智能体收集结果后汇总成最终答案。我在项目里遇到过的典型问题是“帮我看一下这周客服数据有什么异常”。这个问题没有一个固定流程能对应可能是退款率异常可能是某个客服响应时间异常也可能只是某个品类咨询量激增。用路由根本没法提前写死分支这个时候编排器就派上用场了。给编排器的系统提示词大致这样写你是任务编排器。用户给了一个总任务你需要判断拆成哪些子任务。 每个子任务必须交给专门的 worker 智能体处理。 worker 返回的结果必须使用统一 JSON 格式 {task_id: ..., result: ..., confidence: 0.0} 只有全部子任务返回后你才能综合生成最终结论。编排器在项目里实际发挥的作用是把“统计各渠道咨询量”“检测退款异常”“生成分析结论”三个子任务分配给了三个不同的 worker。这里有个关键实操细节编排器不要自己做重活。它的职责是拆解和汇总具体分析计算让 worker 去做。一旦编排器自己下场处理子任务它既当裁判又当运动员输出质量和稳定性都会下降。另外worker 返回的结果必须统一格式这是编排器能否有效汇总的前提。我一开始让 worker 自由发挥结果有的返回文本有的返回表格编排器强行汇总时逻辑就乱了。改成结构化输出后所有 worker 都返回 JSON编排器的工作就变成了拼装和提炼稳定性提升非常明显。4.3 路由 vs 编排器什么时候升级路由和编排器都带“判断”性质但判断的粒度不同。路由是在一组提前定义好的分支里选一个分支数和规则是固定的编排器是面对未知任务自己生成分支和子任务。选哪个核心看你对任务空间是否足够了解。如果你能把所有可能出现的情况列成一张清单用路由就够了成本低、响应快。如果你发现清单越列越长新情况层出不穷分支之间还互相干扰那说明任务本身不可枚举这时候才值得引入编排器。我见过不少项目一开始用路由跑了一两个月规则从十几条膨胀到上百条还经常漏判后来实在维护不动了才换成编排器-工作者。这不是说路由不好而是任务复杂度到了一定阈值后让模型动态决策比人肉维护规则更划算。5. 群聊协作和反思循环高投入高回报的进阶玩法5.1 群聊协作实操多人开会主持人收尾群聊协作是我在项目后期才加入的模式。原因很简单报告初稿出来后只经过一个生成智能体难免有视角盲区。数据组觉得统计口径不对业务组觉得分析不贴合实际文案组觉得表达不够清晰。单个智能体没办法同时站在三种视角审视同一份报告那就让三个角色智能体一起讨论。MAF 实现群聊协作时我用的是主持人模式。三个专家智能体围绕报告各抒己见主持人智能体负责控制节奏、总结分歧、最终收敛。给主持人的提示词是这样的你是讨论主持人。下面有若干专家智能体它们会依次发言。 你的职责是 1. 控制讨论不超过3轮每轮总结关键分歧 2. 每轮结束后归纳共识和未解决问题 3. 最终必须收敛成一个可执行的修改意见清单。 不要自己发表专业意见你的任务是归纳和仲裁。实操中最容易翻车的地方是讨论发散。专家智能体不像真人开会它会顺着上一个发言者的内容越扯越远三句话就偏离主题。解决办法就是在提示词里硬性限制轮次上限并且要求主持人每轮都必须产出“关键分歧总结”。这样即使中间偏题了主持人也会拉回主线。另外一个经验是群聊适合做质量打磨不适合做第一轮产出。首次生成直接用顺序链或编排器产出初稿后再上群聊让专家们针对已有内容提修改意见质量和成本都更可控。5.2 反思循环实操让质检智能体把关群聊协作是人多力量大反思循环则是让一个质检智能体专门挑毛病。流程很简单生成智能体产出初稿质检智能体拿着检查清单逐项审核发现问题就打回给生成智能体修改修改后再审直到通过或者达到最大轮次。我实现反思循环时写了这样一段逻辑for round_no in range(MAX_ROUNDS): draft reporter_agent.run(state) review checker_agent.run(draft) if review.get(passed, False): return draft state[feedback] review[issues] state[retry_reason] review[summary] # 超过轮次后返回最后一版草稿并附加未解决项 return draft, {unresolved: state[feedback]}质检智能体审核时不是给一句“这里不太好”就完事而是按检查清单逐项打分。我把检查清单做成了结构化数据比自然语言提示词稳定得多。比如报告质量检查清单包含数据口径是否一致、结论是否有数据支撑、分类是否覆盖所有工单、表述是否客观中性。每一项都有通过标准和修改建议字段。质检智能体按这个清单执行时输出格式稳定修改意见可直接回流给生成智能体效率比让质检智能体自由评论高出不少。5.3 循环终止条件没有终止条件的反思是烧钱机器反思循环最大的坑是无限循环。生成-质检-修改-再质检如果终止条件设得不好它会一直绕下去。我的经验是设置三种终止条件任何一个满足就停。第一是最大轮次我通常设3轮超过就返回最后一版草稿并附上未解决项第二是质检通过也就是passed字段为真第三是满意度评分达到阈值比如满分10分制达到8分就当合格。成本上也要有心理准备。反思循环每增加一轮token 消耗不是线性增长接近翻倍。因为每一轮都要把上一轮的草稿和质检意见重新放回上下文。所以我只在“最终报告生成”这个最看重质量的环节用了反思模式中间那些清洗、分类环节一律不用。钱要花在刀刃上这句老话在 token 消耗上也成立。6. 编排实操错误处理、上下文和成本控制6.1 错误处理与重试策略失败不能靠运气多智能体编排跑久了你会发现错误处理才是真正决定系统稳定性的地方。模型接口调用会有偶发超时worker 偶尔会输出解析不了的格式FanOut 里某一个 worker 可能莫名失败。没有一套重试策略整个编排就会经常性夭折。超时和重试参数我是这样设的单次模型调用超时30秒超过就重试重试采用指数退避第一次等2秒第二次等4秒第三次等8秒最多重试3次。这个策略能有效避免接口抖动时的连锁失败。伪代码大致是这样import time def call_with_retry(fn, max_retries3, base_delay2): for attempt in range(max_retries 1): try: return fn() except TimeoutError: if attempt max_retries: raise delay base_delay * (2 ** attempt) time.sleep(delay)并行扇出里部分失败的策略更讲究。我按关键程度把 worker 分为两类关键路径上的 worker 失败会导致整个任务重跑非关键路径上的 worker 失败可以降级处理。比如售后分析里数据清洗是关键路径清洗不过后面全白搭气象数据或商品价格这类辅助信息则是best-effort拿不到就用默认值继续跑。这样处理之后整体成功率明显上升不再因为一个不重要的数据源挂了就全盘失败。6.2 上下文传递与长度控制别让每个智能体都背全量信息多智能体编排里上下文长度是比 token 费用更棘手的问题。每个环节都带全量历史很快就会超出模型上下文窗口而且越到后面的智能体越容易被前面一堆中间结果干扰判断。我的做法是分级传递。关键的中间结果用全量传递比如清洗后的完整工单数据传给分类器次要信息用摘要传递比如分类器跑完只需把每类的数量和代表性工单编号传给报告生成器而不是把分类明细全部倒过去。要求每个 worker 的输出摘要控制在200字以内这不仅节约上下文还逼着 worker 提炼重点后续环节反而更容易抓到关键信息。上下文长度可以做一个简单估算并行扇出时公共上下文长度乘以并发数就是单轮的上下文总消耗再乘以轮次就是整体消耗。比如公共上下文2000字跑3路并行2轮处理光公共部分就是约12000字。所以公共上下文一定要精简能放系统提示词的不要放每轮输入。如果发现某条链路上下文超限优先做摘要传递而不是简单粗暴加窗口大小。6.3 成本与延迟优化编排粒度的性价比编排粒度越细系统越稳定但成本越高。拆得越碎每个智能体的任务越小越专注可模型调用次数变多每次调用都有固定开销。项目里我做过一个对比把清洗和分类合并成一个智能体成本约省30%但格式错误率上升约20%。谁更重要取决于业务容忍度。几个降低成本的实操经验一是简单任务用便宜的小模型路由判断、意图分类这类任务用小模型完全够用效果差不了多少费用差好几倍。二是公共数据做缓存那些不太变动的公共背景和说明命中缓存就不要重新构造。三是能并行就并行看似并行会增加 token但把多路串行改成并行后总 token 其实差不多延时的收益却非常直观。我自己的平衡原则是先跑通一个合理粒度的编排观察哪些环节经常出错、哪些环节成本占比高再针对性调整。一上来就把流程拆到极致细粒度成本失控是大概率事件反过来流程跑都跑不通谈省钱也没有意义。7. 常见问题与排查速查表7.1 六类高频故障排查多智能体编排的报错排查跟传统开发有很大不同。报错可能来自格式解析、上下文溢出、路由误判、循环控制也可能来自某一次模型抽风。我把高频问题整理成一张速查表方便照着排。症状可能原因排查方法下游收到格式乱上游未用结构化输出给上游配 JSON Schema强制字段格式并行结果丢了一路FanOut 超时设太短看每个 worker 的时间日志逐个确认完成状态上下文超限链太长或全量传结果中间结果改摘要传递公共信息移入 system prompt无限循环最大轮次没设终止条件不触发检查循环控制逻辑增加通过标志和轮次上限路由判定错误关键词冲突、阈值太低调高 confidence 阈值冲突关键词走兜底流程单步偶发失败模型接口抖动加指数退避重试关键路径与非关键路径分级处理排查这些问题时我最大的体会是日志一定要记全。每次编排执行都生成一个链路ID每个节点的输入、输出、耗时、错误信息都记一笔。没有链路ID多智能体系统一跑起来就像在暗室里找一根掉在地上的针完全无从下手。7.2 定位问题的笨办法也是最快的好办法遇到奇怪问题别急着调提示词。先手动跑单个 worker确认它能正常返回结构化结果再丢进编排里。七成问题的根源不在编排逻辑而在某个 worker 的输出格式不满足下游预期。所以我会先把每个 worker 单独拿出来用测试用例跑一遍确认它按协议返回数据然后再组装编排。另一个实用手段是把中间结果打印或者落盘。每跑完一个节点都把关键的中间输出摘要写出来看一眼。数据在哪一步开始变样的问题就在哪一步。这个习惯看起来笨但在多智能体这种环节多、依赖隐蔽的系统里比任何花哨的调试工具都管用。我个人在实际项目里最大的体会是编排模式不是越复杂越好而是越合适越好。第一版尽量用顺序链把全流程跑通等积累足够的运行数据再按需引入并行、路由、编排器这类动态能力。群聊和反思模式效果好但对时间和 token 的消耗很夸张一定要用在最重视质量的环节。还有一个小技巧可以分享给每个智能体定义输出格式时不要把成功路径的格式定义完就了事一定也要把失败路径的输出格式定义清楚比如“解析失败返回 {error: reason}”。成功和失败都是结构化数据排查问题时会轻松非常多。MAF这个系列从单智能体到这里已经覆盖了从入门到编排的完整路径后面再遇到具体业务场景就可以拿这些模式自由组合了。