ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

多Agent编排实战:从单Agent瓶颈到协作系统搭建

多Agent编排实战:从单Agent瓶颈到协作系统搭建 这个系列写到现在很多朋友都来问同一个问题单个Agent已经能写代码、查资料、做表格了为什么真扔到工作流里还是觉得差点意思我今年做了几个AI自动化的项目最深的感受是——单个Agent再聪明遇到复杂业务一样会乱套但一旦把不同角色的Agent编排起来事情突然就变得靠谱了。今天这篇是系列第十篇就专门聊多Agent编排什么时候必须上多Agent、常见的编排模式怎么选以及一个能跑通的竞品分析Agent小组是怎么一步一步搭起来的。想从“调接口”迈到“做系统”的朋友这篇应该能帮你少走不少弯路。先说结论多Agent不是把更多大模型API堆在一起而是把任务拆解、角色分配、状态流转、异常恢复这套工程问题理顺。技术成熟窗口已经打开了剩下的就看谁的拼装更合理。1. 多Agent编排到底在解决什么问题1.1 单Agent的天然天花板为什么一个人干不了一整个项目先看一个特别常见的场景让一个Agent自动生成一份行业竞品分析PPT。刚上手的时候你可能会觉得这事交给大模型就行。真跑起来才发现第一步让它去网上搜资料它还能给你列几个链接第二步让它对数据做整理和清洗它就开始胡编第三步让它把结论排版成PPT又要调用各种工具指令一长上下文就乱成一锅粥。问题出在哪底层原因有三个。第一大模型的上下文窗口再大也是有限的。你让一个Agent既当研究员又当分析师又当文案所有历史对话、中间结果、工具返回信息全堆在同一个上下文里。很快前面搜到的有效信息就被后面的对话挤掉了模型只能“记忆模糊”地往下编。第二一个角色很难同时具备多重专家属性。写周报的prompt和写代码的prompt在语气、输出格式、约束条件上完全不一样。你把它们揉到一个Agent里要么互相干扰要么模型只能折中处理最后两头都不像。第三串行执行让故障被不断放大。单Agent处理复杂任务时一旦中间某一步调用工具失败了后续所有步骤都会建立在错误结果之上。而且它没有内置的“审查”机制错了也不知道只会继续一本正经地输出错误结论。所以单Agent适合的是那些目标明确、链路短、不需要反复校验的任务。一旦任务需要多步骤、多渠道信息、多轮验证的时候就要认真考虑多Agent了。1.2 多Agent协作的真实收益从单打独斗变成团队作战多Agent说白了就是把“一个全能选手”拆成“一队专业选手”。比如刚才说的竞品分析可以拆成三个角色研究员负责查资料、数据分析师负责整理数据、编辑负责把结论写成通顺的报告。每个Agent只干自己那一摊prompt简单、职责清晰、输出稳定。这样做的好处非常具体。首先是并行效率。研究员去查竞品A和竞品B的时候完全可以拆成两个更小的Agent并行去做整体耗时能从串行的几分钟压缩到几十秒。我在实际项目里测过单纯把检索节点拆成3个并行分支整条链路的耗时能下降60%以上。然后是天然的校验机制。让一个Agent写得报告再让另一个Agent专门负责挑错这比让写报告的人自己检查靠谱得多。我给一个数据分析Agent配了一个“审查Agent”之后虚构数据出现的概率明显下降。还有一点很多人会忽略可观测性。单Agent做复杂任务你很难知道它这步在干嘛、为什么得出这个结果。但多Agent下每个节点都有明确的输入和输出状态可追踪出问题能精准定位到具体环节。当然代价也有额外增加编排开销、开发复杂度变高、链路延迟变长。所以多Agent不是银弹下面我放一个对比表方便大家按场景选型。维度单Agent多Agent编排任务复杂度适合低于5步的短链路适合长链路、多分支的复杂任务上下文压力所有信息集中在一个上下文易溢出每个Agent只维护局部上下文压力分散错误扩散错误沿单链路直接传递可以通过校验节点发现并纠正执行效率串行执行慢可并行快开发成本低一两小时能调通高需要设计状态和流转可维护性改需求成本高改一个节点相对独立我的判断是如果任务能在一次模型调用里完成绝不上多Agent如果任务涉及5个以上步骤、多种工具、多处事实校验那就值得上。2. 编排模式与框架选型先选结构再写代码2.1 四种主流编排模式及适用场景多Agent怎么编排不是一个框架问题而是一个架构问题。做技术选型之前得先想清楚Agent之间是什么关系。我总结了目前项目里最常见的四种模式。第一种是流水线模式。Agent按顺序执行前一个的输出就是后一个的输入。比如“收集资料 → 提炼要点 → 撰写文案 → 格式排版”。这种模式适合流程固化、阶段明确的场景优点是逻辑简单、容易排查问题缺点是前面一旦出错后面全跟着错所以每个环节之间最好加一个数据校验节点。第二种是路由模式。一个“调度Agent”接收用户请求判断这是哪一类任务然后分发给对应的专用Agent执行。比如一个客服系统里售后问题转给售后Agent产品咨询转给产品Agent。这种模式适合入口统一、子任务类型丰富但彼此独立的场景。路由节点不需要很强的推理能力关键是分类准确率高。第三种是协作模式。多个Agent共享同一个对话上下文像群聊一样互相讨论、提出意见最后通过投票或共识得出结果。这种模式适合头脑风暴、方案评审、复杂决策。但问题也明显上下文消耗大token成本高讨论容易发散。我一般会限制讨论轮数并让一个主持人Agent负责收敛结论。第四种是监督者模式也是我目前的主力。一个Supervisor Agent负责把大任务拆解成子任务调度多个子Agent去执行再回收结果进行汇总、仲裁。子Agent之间不直接通信所有信息都经过监督者中转。这样状态流清晰便于监控和恢复非常适合复杂的业务项目。缺点就是中心化节点会成为瓶颈所以监督者只负责调度不要让它干太多重活。2.2 框架对比LangGraph、AutoGen、CrewAI、自研编排选框架之前我一直提醒团队一句话框架只解决“怎么跑”不解决“怎么想得对”。角色定义、状态设计、异常处理这些核心工作框架帮不了你。但选对框架确实能少写很多底层代码。我最近用过的几个框架简单对比一下。框架支持的编排模式状态管理上手难度适用场景LangGraph流水线、路由、监督者灵活度高显式状态图支持分支循环中高生产级复杂流程AutoGen协作模式、对话式多Agent会话驱动内置对话管理中研究原型、群聊式协作CrewAI流水线、协作角色定义友好基于Task和Process封装低快速搭建角色化Agent团队自研编排全部可控自己定义状态协议高高并发、定制化需求强的业务LangGraph是我目前项目里使用最多的。它把工作流建模成一张图节点是具体的处理函数边是状态流转的规则。它对分支、循环、并行支持都很好适合需要精细控制的任务。AutoGen我很喜欢它的群聊模式多个Agent可以你一言我一语地讨论最适合做“评审会”这种场景。但它的会话调度对新手来说稍显黑盒出了问题不好定位。CrewAI胜在“快”定义几个角色和任务就能跑起来适合验证想法但生产环境里对状态和异常的控制相对弱一些。如果你追求极致性能、或者业务流程特别定制自研编排也完全可行。核心就是把每个Agent包装成函数定义好标准输入输出再用一个事件循环来调度。我之前在数据处理项目里就自己写过一个极简编排器反而比引入大框架稳定得多。所以我的建议是快速验证用CrewAI生产级开发用LangGraph特殊需求考虑自研。3. 实操搭一个多Agent竞品分析系统3.1 任务定义与角色拆分说太多理论容易飘我直接拿一个最近做过的项目拆给大家看。任务是输入若干竞品官网链接自动生成一份结构化的竞品分析报告。先定义角色。这里不用贪多三个角色刚刚好研究员Researcher负责抓取每个链接的页面信息提取产品功能、定价、目标客户等原始数据。数据分析师Analyst负责把研究员拿到的原始数据整理成统一的对比表格发现差异点和优劣势。编辑Editor负责审阅分析师产出的表格补充逻辑细节最终生成通顺、完整、可发布的Markdown报告。如果不放心质量还可以再加一个“审查Agent”放到最后但最开始搭建的时候三个角色已经足够跑通。每个角色的Prompt我建议不超过200字重点放在“职责边界”和“输出格式”上。千万别在角色Prompt里写一堆背景故事模型很容易把自己带入成虚拟人格忽略你的实际任务。任务的输入输出也要提前约定好。我设计了一个共享状态结构每个Agent只负责更新自己对应的字段避免互相污染。流程上先让研究员串行或者并行抓取然后把结果交给分析师最后让编辑整合。整体是一个典型的流水线加监督者模式。3.2 基于LangGraph的编排实现代码方面我用的是LangGraph因为它的状态管理最直观。下面是一份精简版的实现为了控制篇幅我把抓取和分析的具体逻辑省略了只保留编排骨架。from typing import TypedDict from langgraph.graph import StateGraph, END class AgentState(TypedDict): sources: list[str] # 输入竞品链接 raw_data: list[dict] # 研究员产出原始数据 analysis: dict # 分析师产出对比分析 report: str # 编辑产出最终报告 def researcher_node(state: AgentState): # 实际实现里会调用爬虫或搜索工具 raw_data scrape_sites(state[sources]) return {raw_data: raw_data} def analyst_node(state: AgentState): # 将原始数据整理成对比表格 analysis build_analysis_table(state[raw_data]) return {analysis: analysis} def editor_node(state: AgentState): # 根据分析结果生成报告 report generate_report(state[analysis]) return {report: report} graph StateGraph(AgentState) graph.add_node(researcher, researcher_node) graph.add_node(analyst, analyst_node) graph.add_node(editor, editor_node) graph.set_entry_point(researcher) graph.add_edge(researcher, analyst) graph.add_edge(analyst, editor) graph.add_edge(editor, END) app graph.compile()这里有几个细节值得注意。第一每个节点函数的入参是当前的整体状态返回值Dialog会合并到状态里。所以我只在每个节点里更新自己负责的字段不去动别的字段。第二状态类AgentState里的类型提示很重要LangGraph会用它做序列化也方便调试。第三实际生产环境里每个节点内部要做重试、超时、异常回退处理不能像示例里这样裸奔。跑起来之后你只需要调用app.invoke({sources: [https://...]})最终就能拿到一个完整的Markdown报告。如果你想把研究员节点拆成两个并行分支可以改用LangGraph的条件分支和并行节点代码改动也不大。3.3 把Agent服务容器化与Web服务统一编排多Agent的任务编排只是第一步真正想要稳定运行还得把Agent服务部署起来。这里说的“编排”和前面的“Agent编排”是两个层面Agent编排管的是任务流程容器编排管的是进程和资源。实际项目里我通常把Agent后端、前端Web、向量数据库这些服务用Docker Compose放到一个网络里统一管理。给一个典型的docker-compose.yml示例version: 3.8 services: agent-service: build: ./agent env_file: .env ports: - 8000:8000 depends_on: - vector-db environment: - AGENT_MODEsupervisor - MAX_ROUNDS10 - LOG_LEVELINFO web: image: your-web-image ports: - 3000:3000 environment: - API_URLhttp://agent-service:8000 vector-db: image: qdrant/qdrant ports: - 6333:6333把Agent服务单独拆成一个容器有几个好处。一是可以独立扩缩容高并发时多开几个Agent服务实例。二是环境隔离Agent依赖的Python包和Web前端依赖的Node包互不影响。三是便于监控每个服务单独打日志谁出问题一目了然。这里有两个踩过的坑。第一个不要把API密钥直接写进compose文件一定用env_file或者秘钥管理服务。第二Agent服务如果是有状态的比如需要记住上一轮的会话就不要简单依赖容器重启恢复最好把状态外部化到Redis或数据库里否则容器一崩溃会话就全丢了。容器编排和Agent编排配合好系统才算真正能交付。4. 常见问题与排查技巧实录4.1 Agent互相“踢皮球”死循环与上下文污染多Agent跑起来之后第一个容易遇到的问题是“死循环”。两个Agent各自拿到对方的结果后觉得“有问题”于是反复退回重做或者在对话里反复纠缠同一个话题。我遇到过最离谱的一次两个Agent来回执行了17轮直到把token额度耗尽才停下来。排查思路很简单先看日志里的state流转记录找到在哪两个节点之间打转。如果是任务语义不清导致的就重新划分职责边界明确“什么情况算通过”。如果是流程控制问题就在编排层加硬性限制for round in range(MAX_ROUNDS): result app.invoke(state) if result.get(report) or result.get(done): break else: raise TimeoutError(Agent loop exceeded)同时在每个节点里加一个current_step字段强制要求节点执行后更新这样状态图就能识别出异常跳转。千万别指望模型自己“意识到该结束了”规则一定写在编排层。4.2 上下文太长导致早期信息丢失另一个高频问题就是上下文溢出。多个Agent协作时每轮对话都会把之前的历史拼进上下文如果中间还穿插着长文本工具结果很快就爆了。我遇到过Analyst分析到一半把Researcher第一轮检索到的正确数据忘了自己编了一个“看起来差不多”的数字最后报告出现严重错误。我的解决办法是“摘要层”。在每个Agent内部只让它们看到自己需要的那部分状态而不是整个历史。比如Analyst看到的只有raw_data的摘要和结构化的字段列表不需要知道Researcher抓取的网页原文。对于必须长期保留的信息我会把它们写入向量数据库按需检索而不是一股脑塞进上下文。经验是节点间传递的数据尽量做到“结构化、精炼化”能用JSON绝不用长篇文本能传字段摘要就不传完整网页。上下文管理要从设计状态结构时就开始不要等到运行期再救火。4.3 实测避坑清单5条经验最后整理几条这段时间实战下来最值钱的避坑经验每条都是用真金白银的token换来的。第一角色Prompt要短而明确。我见过有人给一个Agent写800字的人设背景结果模型每轮回复都在“扮演角色”完全不关心任务目标。其实只要说清楚“你是什么角色、负责什么输入、输出什么格式、遇到什么情况算完成”就够了。第二每个Agent的输入和输出尽量用JSON结构化。自由文本看似方便下游解析很容易出bug。我统一要求Agent输出{status: success, data: {...}}或{status: error, message: ...}这样编排层判断状态极其省事。第三并行节点务必隔离状态。多个Agent同时跑的时候不要共享同一个可变对象否则会出现数据互相覆盖。我给每个并行分支拷贝一份独立的状态副本跑完再合并。第四给每个Agent独立配置错误恢复策略。有的Agent可以重试两次有的Agent调用第三方接口出错时需要降级到本地规则。不要在编排层统一catch这样会让正常节点也背上不必要的等待。第五日志一定要打到文件。多Agent跑起来之后终端输出根本刷不过来。我给每个Node都加了log_state(state)把每一轮的关键状态写入本地JSONL文件出问题直接按trace_id检索。没有这套日志排查死循环和下游数据错误会痛苦十倍。这几点如果能在开发初期就落地后面上线运维会轻松非常多。多Agent编排说到底不是模型能力的比拼而是工程纪律的比拼。把状态管好、把边界划清、把日志留下这套系统才能真正扛住业务压力。我在多次项目里的感受是别一上来就追求Agent数量多能把两三个Agent的配合调顺已经是及格线再往上加角色每多一个都要重新审视状态流和异常流。如果现在的任务模式还没跑稳建议先把手上的流程用单Agent加工具调用优化到极致再考虑扩展。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进