多智能体协作全景:Agency-Agents设计与工程实践 说实话我第一次看到“多智能体协作”这个概念的时候第一反应是这又是一个包装出来的炒作词。直到我自己把Agent从单个往外拆才发现事情没那么简单单体Agent能做的事再多它也只有一双眼睛、一个脑子任务一多必然顾此失彼。上个月我把一个内部项目整理成开源Demo取名叫 Agency-Agents目的就是想在一套可视化看板上把多个智能体如何分工、通信、汇总结果的过程完整摊开给人看。这篇文章就把这个项目从设计思路到运行效果完整过一遍适合正在做Agent应用、或者纠结单Agent和多Agent到底怎么选的人。我会把整套协作机制、代码实现、踩坑记录都放出来内容偏工程实操不讲虚的。1. Agency-Agents 项目全景为什么需要“一群”智能体1.1 单体Agent的“全能幻觉”与协作的必要性单体Agent最大的问题说白了不是能力不够而是上下文一旦被塞满行为就变得不可控。我在项目早期做过一个很典型的实验让一个Agent同时干市场调研、竞品分析、内容撰写、风险检查四件事结果它经常写到一半就忘了前面的数据甚至会在调研阶段直接开始输出结论。这种问题不是通过调prompt能彻底解决的本质上是“一个脑子同时处理太多角色”的结构性缺陷。打个比方一个人既能当产品经理、又能当UI设计师、还能当测试听起来很全能可一旦项目复杂起来他一定会漏需求、忘细节。多智能体协作解决的就是这件事把一个大任务拆成多个小任务每个Agent只专注一个角色行为边界清楚了输出质量自然稳定。Agency-Agents 这个项目从一开始就围绕三个问题设计任务怎么拆、拆完之后的Agent之间怎么通信、最后的结果怎么汇总。这三个问题解决掉“一群”智能体才不是噱头而是真正能落地的东西。在这个项目里我把“协作”抽象成了一个可以实时观测的过程。每个智能体做了什么、想了什么、交出了什么结果全部通过事件流推送到前端看板。你不需要读代码只要盯着屏幕就能看到整个协作链条是怎么跑起来的。这也是“全景展示”这个名字的由来——不是给你看一堆日志而是把协作过程本身做成交互式可视化的产品。1.2 Agency-Agents 的整体架构Agency-Agents 的核心架构可以拆成四块调度中心Orchestrator、协作总线Message Bus、Agent运行时Runner、前端看板Dashboard。每一块职责非常清晰我列一下各自具体负责什么调度中心负责接收用户输入调用Planner智能体生成任务树然后对任务树做合法性校验。校验的内容包括任务依赖有没有环、每个任务需要的角色是否都有对应的Agent、哪些任务可以并行执行。任务树通过校验后调度中心再按依赖关系逐个派发任务并负责超时重试和失败降级。协作总线这是所有智能体之间通信的“高速公路”。Agency-Agents 没有让Agent之间直接互相喊话而是统一走消息总线。所有事件任务开始、任务结束、失败、心跳都打到总线上调度中心监听总线驱动流程前端看板也订阅总线做实时渲染。Agent运行时这是真正调用模型执行任务的模块。每个Agent实例由角色定义、任务输入和独立上下文三部分组成。Agent只知道自己要干什么不知道其他Agent的中间状态这种隔离是多Agent协作稳定性的基石。前端看板一个纯静态页面加WebSocket客户端。收到总线事件后把任务进度、Agent状态、中间产出、调用耗时全部渲染成可视化的面板支持按角色分栏、按时间回放。技术栈上服务端用 Python 3.11 搭配 FastAPI 和 WebSocket前端刻意没有引React、Vue这类重型框架就用了原生HTML加JavaScript。原因很简单这个项目的目标之一是让人能“下载后5分钟跑起来看效果”前端越轻越好。团队内部如果要接企业级平台可以单独换前端后端接口不需要动。1.3 为什么不直接套用现成多Agent框架在动手自研之前团队内部把市面上主流多Agent框架都过了一遍包括 LangGraph、CrewAI、AutoGen。它们各有优点但我最终决定用自研的轻量编排层理由其实很务实一是演示项目需要让人看懂协作协议框架封装太深反而把核心逻辑藏起来了二是业务侧的Agent有些已经上线协议和运行方式都是自有的直接套通用框架要改业务代码成本不低。我把讨论过程整理成了表格方便大家对照自己的场景判断框架/方案核心优势不适合我这个场景的点最后取舍LangGraph有状态图编排支持复杂分支与循环学习成本高调试时需要理解图结构和状态机生产可选演示太重CrewAI角色扮演自然封装友好协作协议固化想改事件流得看框架源码试用后放弃AutoGen多Agent对话机制灵活对话式协作可控性弱出问题不好排查不适合业务型流水线自研编排层协议完全可控事件全部可观测每个功能都要自己写投入人力最终选用这个选型过程给我的经验是多Agent项目里框架不是重点“协作协议怎么设计”才是重点。你用了别人封装好的框架如果协议设计理念跟你的业务不符后面会越调越别扭。自研一个轻量编排层初期多花一两天后面扩展起来反倒轻松。2. 核心协作机制拆解分工、通信与上下文隔离2.1 任务规划与拆解到底是谁在“想”网上有个很热门的问题“任务的规划与拆解是Agent的能力还是模型的能力”我在做Agency-Agents时有很明确的体会**拆解是模型的推理能力但拆完能不能执行、依赖关系对不对是框架的职责。**只听明白这句话多Agent的设计思路基本就对了。Agency-Agents 里的Planner智能体会把一个综合任务输出成一棵任务树。我举一个真实的例子假设用户提的是“为某智能手环新品做一份上市前内容包”Planner生成的计划长这样{ plan_id: plan_20250112_001, tasks: [ {id: research_1, type: research, role: researcher, depends_on: [], prompt: 调研目标用户对健康手环的核心需求}, {id: research_2, type: research, role: researcher, depends_on: [], prompt: 收集三款竞品的定价与功能参数}, {id: analysis, type: analysis, role: analyst, depends_on: [research_1, research_2], prompt: 基于调研结果输出差异化定位建议}, {id: writing, type: write, role: writer, depends_on: [analysis], prompt: 撰写产品上市宣传文案}, {id: review, type: review, role: reviewer, depends_on: [writing], prompt: 检查文案中的数据引用与合规风险} ] }调度中心拿到这个任务树之后不会直接无脑执行而是先做四步校验和编排拓扑校验检查任务依赖是否成环。成环的任务树直接打回让Planner重规划避免启动后死锁。角色映射把任务树里的role字段映射到已经注册的Agent列表上缺人的角色要显式报错。资源分配看哪些任务没有依赖、可以并行系统按max_concurrent_agents参数控制并发数防止一次性把模型服务打爆。失败预案每个任务都预设重试次数和降级动作。比如调研失败一次就重试重试还失败则跳过该任务并在汇总里标记“该环节数据缺失”。这四步做完任务树才真正成为一个可执行的工作流。很多人在做多Agent时只关注“让模型输出计划”却忽略了计划需要被工程化成可调度、可监控、可恢复的状态机。Agency-Agents 把这一步做成强约束极大减少了线上任务的随机失败。2.2 多智能体之间的消息协议Agent之间怎么讲话是多Agent项目里最容易拍脑袋决定、最后却最影响排错体验的部分。Agency-Agents 没有发明复杂协议就是用JSON加内存队列但每一条消息都带完整的链路追踪信息。消息里必需的字段包括 event_id、trace_id、event_type、agent_id、task_id、payload、created_at。核心事件类型我固定成六种事件类型含义谁产生关键载荷plan_created计划生成成功Planner任务树全文task_assigned任务已派发调度中心任务信息、输入提示task_started智能体开始执行Agent运行时任务状态标记task_result智能体产出结果Agent运行时结构化结果、token消耗task_failed智能体执行失败Agent运行时失败原因、重试次数agent_heartbeat智能体心跳Agent运行时当前状态、存活标记一条真实的任务完成事件长这样{ event_id: evt_8f3a2c, trace_id: trace_20250112_001, event_type: task_result, agent_id: researcher_01, task_id: research_1, payload: { summary: 目标用户最关注续航与心率监测精度, sources: [user_survey_2024, forum_analysis] }, usage: {prompt_tokens: 2200, completion_tokens: 640}, created_at: 2025-01-12T10:02:13Z }我说一个掏心窝的教训第一次写这个系统的时候我没给消息加 trace_id结果一个任务走完日志里全是不同Agent的输出根本分不清哪条消息对应哪次任务调用排查问题全靠猜。后来我花了半天把所有链路串起来每个任务从诞生到最终汇总都共享同一个 trace_id前端看板也能按 trace_id 回放整条执行链路排错效率提升了不止一个量级。2.3 上下文隔离与共享记忆多Agent协作里最隐蔽的坑是上下文串味。很多人做出来的多Agent实际上是把多个角色的prompt全塞进同一个上下文窗口里这跟单一Agent没有任何区别。Agency-Agents 规定了一套严格的隔离规则每个Agent的System Prompt里只能有自己的角色定义用户输入里只能有当前任务要求的输入字段任务之间的上下文默认完全隔离。但完全隔离也不行Agent之间总要共享一些信息。我的做法是引入一个“公共黑板”Blackboard的概念它本质上是一个只读共享层。任何Agent完成任务后调度中心会把经过校验的结果写入黑板后续依赖该任务的Agent只能从黑板读取已经汇总好的结论不能直接翻看任意Agent的私有草稿。用团队协作来类比就很好懂黑板相当于团队公共Wiki上面只写结论和必要的数据来源每个Agent自己的工作笔记相当于个人草稿别人不该也不允许看到。这样设计后两个Agent互相踩数据的概率大大降低而且每个Agent的输入输出边界都非常清楚。实现层面Demo版本直接用了一个内存里的字典加文件快照生产环境换成Redis或者外部数据库都很方便。3. 实操从零搭建一套可演示的协作效果看板3.1 环境准备与依赖安装Agency-Agents 的服务端代码我尽量做成了零花哨依赖。你不需要装数据库不需要装消息队列一台普通开发机能跑Python就行。前端就是两个静态文件不依赖Node生态。准备环境时建议直接创建一个干净的虚拟环境避免跟其他项目冲突。我用的是Python 3.11第三方依赖主要就四个mkdir agency-agents cd agency-agents python -m venv .venv source .venv/bin/activate pip install fastapi0.110 uvicorn[standard] websockets12.0 openai1.0模型这一层代码里接的是兼容OpenAI接口的服务。如果你用本地模型比如调Ollama或者其他开源模型服务只需要在配置里改base_url和api_key协议一样就能跑。我在项目里既用过大模型做规划和创作也用小模型做分类和抽取下面“演示任务”章节会讲怎么混用。目录结构非常简单甚至可以说是极简风agency-agents/ ├── app.py # FastAPI入口WebSocket服务 ├── orchestrator.py # 调度中心核心逻辑 ├── agent.py # Agent运行时与角色定义 ├── blackboard.py # 公共黑板实现 ├── static/ │ ├── index.html # 看板页面 │ └── dashboard.js # WebSocket客户端与渲染逻辑 └── .env # 模型服务配置3.2 核心代码调度中心与Agent运行时调度中心是整个系统的“大脑”。它的核心逻辑不复杂本质就是一个循环从任务队列里取出依赖已满足的任务交给对应Agent执行收到完成事件后更新黑板再派发后续任务。以下是精简版伪代码async def run_plan(plan: dict, ws: WebSocket): tasks {t[id]: t for t in plan[tasks]} done set() failed {} # 初始派发所有没有依赖的任务 ready [t for t in tasks.values() if not t[depends_on]] while ready or pending_has_runnable_task(tasks, done, failed): # 控制并发同一时刻最多跑3个任务 batch, ready take_up_to(ready, max_concurrent_agents3) results await asyncio.gather(*[run_agent(t, ws) for t in batch]) for t, r in zip(batch, results): if r.ok: done.add(t[id]) blackboard.set(t[id], r.value) # 解锁依赖它的后续任务 ready.extend(collect_new_ready(tasks, done, failed, t[id])) else: failed[t[id]] r.error return summarize(done, failed)关键参数我在代码里写成了三个max_concurrent_agents3因为实测同时跑5个以上时模型服务端的排队时间会明显变长总耗时反而没缩短所以并发不是越大越好task_timeout180单个任务超过3分钟直接判失败max_retries2只重试瞬时错误像prompt写错导致的失败重试也没意义。Agent运行时的最小实现核心就是封装一次模型调用。但每一个Agent在调用前要完成三件事组装自己的System Prompt、从黑板拉取依赖结果、把私有上下文和新任务输入拼起来发送给模型。伪代码如下class Agent: def __init__(self, role: str, system_prompt: str, model: str): self.role role self.system_prompt system_prompt self.model model async def run(self, task: dict) - str: deps_context blackboard.read_many(task.get(depends_on, [])) user_prompt task[prompt] \n\n参考信息:\n deps_context resp await chat_completion( modelself.model, messages[ {role: system, content: self.system_prompt}, {role: user, content: user_prompt} ] ) return resp.content有朋友问我为什么不直接在这个环节加一个复杂的记忆模块。我的建议是第一版千万别加先把任务跑通、把过程可视化做好后面再按需加记忆。记忆一多消息体就会膨胀token成本和不稳定性都跟着涨。前端看板这部分逻辑比想象中简单浏览器里创建一个WebSocket连到/ws收到事件后判断event_type然后把任务状态更新到页面对应的角色卡片上。调模型的过程是异步的所以看板能实时看到每个Agent是空闲、排队中、正在执行还是已经完成。3.3 演示任务设计从“市场调研”到“风险核查”的流水线光有框架还不行得有一个能体现“多智能体协作价值”的演示任务。我最后选定的场景是“智能手环新品上市前内容包生成”因为它足够综合既有数据搜集又有逻辑分析还有创意写作和风险控制每个环节交给不同Agent角色差异特别明显。整个任务树的编排和模型分配如下任务ID承担角色使用模型类型依赖任务产物research_1用户研究员小模型无用户核心需求清单research_2竞品分析员小模型无竞品定价与功能对比analysis产品分析师大模型research_1, research_2差异化定位建议writing内容创作者大模型analysis产品宣传文案review风控质检员大模型writing风险提示与修改建议选择大小模型混合纯粹是成本驱动。像用户需求和竞品参数这种任务本质上是从给定内容里抽取要点用小模型就能做得比较稳而定位建议和宣传文案需要更强的推理和创作能力这时候才值得用大模型。我测试下来混合方案比全部用大模型的成本低了接近一半最终产出质量没有明显差异。4. 协作效果全景一次完整任务能看见什么4.1 看板上的协作过程回放Agency-Agents 最直观的“全景展示”体现在前端看板上。任务一启动你不需要去看日志只要盯着页面就能看到整条协作链路像流水线一样滚动起来。我把自己实测时看到的事件流贴出来还原一下真实画面[10:00:01] orchestrator → plan_created 计划生成成功共5个任务 [10:00:02] orchestrator → task_assigned 派发 research_1 给 researcher_01 [10:00:02] orchestrator → task_assigned 派发 research_2 给 researcher_02 [10:00:03] researcher_01 → task_started 用户调研开始执行 [10:00:03] researcher_02 → task_started 竞品分析开始执行 [10:00:21] researcher_01 → task_result 用户核心需求清单已产出 [10:00:25] researcher_02 → task_result 竞品参数对比表已产出 [10:00:25] orchestrator → task_assigned 派发 analysis 给 analyst_01 [10:01:02] analyst_01 → task_result 差异化定位建议已写入黑板 [10:01:02] orchestrator → task_assigned 派发 writing 给 writer_01 [10:01:35] writer_01 → task_result 宣传文案初稿完成 [10:01:35] orchestrator → task_assigned 派发 review 给 reviewer_01 [10:02:10] reviewer_01 → task_result 风险检查通过无合规问题 [10:02:10] orchestrator → plan_completed 全链路完成共耗时129秒如果你在现场看会发现两个很有意思的现象前两个调研任务是真的在并行执行页面里两个角色卡片同时亮起“工作中”状态而写作任务必须等定位建议产出后才亮起依赖关系一目了然。这种可视化能力带来的最大好处是团队里不懂代码的同事也能理解Agent在工作流里到底做了什么。4.2 单Agent与多Agent协作的横向对比做这个项目之前团队内部有人提出一个很尖锐的问题多个Agent拆来拆去最终不还是要串行跑吗多Agent到底比单Agent强在哪为了回答这个问题我拿演示任务做了20轮对比测试控制变量用同一个模型服务分别用单体Agent和Agency-Agents各跑10次结果让我挺意外对比维度单体AgentAgency-Agents协作模式任务完成率70%100%平均端到端耗时95秒129秒总token消耗基线增加约38%输出结构完整率60%100%需要人工介入的轮次4轮0轮定位信息缺失导致的返工3次0次看完数据就明白了多Agent协作不是用来“加速”的而是用来“锁质量”的。花费更多的时间和token换来的是任务完成率从70%提升到100%而且每一轮输出都是结构完整的。如果把返工和人工纠错的时间算进去单Agent方案的真实总成本反而更高。我看板里印象最深的一次跑动是Writer在文案里写了一句“续航达14天”Reviewer独立核查时发现竞品调研表里最高端的竞品也只有10天续航这个数据可信度存疑于是它在结果里标注了“数据待与产品团队确认”。这种跨角色的纠错在单个Agent身上几乎不可能出现因为它不会拿着自己的输出去反复质疑自己。4.3 关注哪些指标才算“效果变好”搭建多Agent系统之后如果只用“报告好不好看”来评价效果很容易被偶然性误导。Agency-Agents 在后端落了一套结构化事件日志每条记录都是JSON Lines格式方便后续做统计。我建议重点盯这几个指标任务完成率最终成功产出并满足输出校验的任务比例。低于90%说明规划或角色分配有问题。重试率单个任务平均重试次数。重试率偏高说明prompt不稳定或者依赖数据质量差。端到端耗时从提交任务到产出最终结果的时间。这个指标要按环节拆分统计方便定位瓶颈。token消耗分布哪个角色消耗最多token是否需要换成小模型。人工修正量最终报告需要人工改几处才能交付。这是评估“协作质量”最有说服力的指标。很多人只关心“最终输出好不好”忽略了过程数据。实际上多Agent的价值恰恰在过程可控、失败点清晰。一次运行结束你能明确说出是哪个Agent在哪个环节出了问题而不是面对一个黑盒干瞪眼。Agency-Agents 还做了个“回放模式”读日志就能重建看板动画不用重新调模型这对排查历史问题非常管用。5. 常见问题与排查技巧实录5.1 “串味”Agent输出里混入了别人的角色内容现象Reviewer的输出里出现了“已完成调研并生成计划”Writer的回答里出现了“作为竞品分析员我建议……”。这种问题在多Agent系统里特别常见几乎每个第一次自己搭的人都会碰到。我排查下来原因基本逃不出两类一是全局上下文被污染比如把所有Agent的System Prompt拼在了一个公共变量里二是模型在长对话里产生了角色漂移任务之间的分隔不够显式。我解决的办法也是两手抓。代码层面强制每个Agent只能拿到自己的System Prompt和当前任务的输入不让任何历史消息堆积提示词层面在每个角色的System Prompt末尾加了一句很直白的要求“你的身份是固定的你只负责你职责范围内的事务不要代替其他角色做决定。”这样处理之后串味发生率从最初的每三轮一次降到了近一个月一次。5.2 并行任务死锁与依赖环现象看板上一堆任务都亮着“已派发”但迟迟没有进展日志里全是task_assigned没有一条task_started。这种问题多半是任务依赖关系写错了形成了环比如A依赖B、B依赖C、C又依赖A所有任务互相等待谁都不可能先执行。调度中心里的拓扑校验就是为这个问题设计的。每轮派发之前系统会对任务依赖做一次环检测发现环就直接打回给Planner重新生成计划而不是让任务卡死在那里。我在代码里还加了个看门狗定时器每隔30秒扫一遍执行中的任务如果发现某个任务超过timeout还没返回就强制判失败尝试重试一次不行就跳过并记录。跑了一个月看下来这个保险机制至少救了我三次线上任务。5.3 成本翻倍与token浪费现象账单出来了多Agent模式比单体Agent在token上多烧了接近40%。原因很好理解每个Agent都带着一个相对完整的上下文规划、汇总等环节还会重复读取公共黑板里的内容消耗自然大。省成本的方案我验证下来有三条是有效的。第一能用小模型的环节绝不用大模型比如抽取类、分类类任务用小模型性能完全够第二给任务结果加缓存同样的trace_id或同样的输入就不再重复调用模型尤其是Reviewer检查同一份文案时直接命中缓存第三公共黑板只放结论不放过程草稿避免后续Agent读入大量无用信息撑爆上下文。经过这三项优化我在第二个演示项目里把多Agent的额外成本从40%压到了15%左右。5.4 调试经验与工具清单最后分享一些调试工具和习惯能让你少走不少弯路。WebSocket调试本地起了服务之后用wscat -c ws://localhost:8000/ws或者浏览器控制台都能直接连上去看事件流。前端看板没渲染出来问题的时候先连WebSocket确认后端事件有没有正常推送。结构化日志每个Agent一个独立logger输出文件按trace_id命名。这样排查问题时直接按trace_id定位不用在混杂的日志里捞针。回放模式Agency-Agents 支持读取历史日志重建看板动画。出问题以后不用重新调一遍模型直接加载上次的日志就能复现当时的执行过程定位效率非常高。最小复现遇到Agent行为诡异时不要急着调全局配置先写一个最小测试用例只跑单个Agent用固定输入反复测试确认这个角色没问题再把多个Agent合起来跑。分层排查是效率最高的方法。最后说几句私房话Agency-Agents 这个项目跑到现在我最大的转变是再也不会把一个综合体任务硬塞给单个Agent了。它不会让单次任务变得更快但让复杂任务的产出变得稳定、过程变得透明这两个价值在真实业务里比“快”重要得多。如果你正准备尝试多智能体协作我建议不要一上来就搭复杂的生产系统先像这个项目一样把一个你自己最熟悉的任务拆成三个角色差异足够大的Agent跑一轮亲眼看到它们分工、等待、互相纠错的那一刻很多设计上的问题你就自然会懂了。