如果你最近在折腾AI Agent,八成遇到过这种场面:让一个智能体一口气完成“调研-分析-写报告”整个流程,结果它调研完就忘了结论,写到一半开始编数据,最后交出来一份前后矛盾的东西。我早期做自动化任务时被这种问题折磨得不轻,直到接受了“一个Agent干不了所有事”这个现实,开始尝试把任务拆给一组各司其职的Agent去做。这篇文章就来聊聊我基于agency-agents这套思路搭建多智能体协作系统的完整过程,包括架构怎么设计、消息协议怎么定、最小Demo怎么跑通,以及那些不实际跑一遍根本发现不了的坑。如果你正在做Agent应用开发,或者准备从单Agent往多Agent协作方向过渡,这篇应该能帮你省下不少试错时间。
1. 先搞清楚agency-agents是什么:一套“智能体办事处”式的编排框架
1.1 它不是又一个模型封装,而是把Agent当成“岗位”来编排
很多人听到“多智能体框架”第一反应是:这不就是把Prompt拆成几段、串起来调用吗?还真不是。agency-agents更像是一个虚拟组织——每个Agent有明确的岗位职责、自己的记忆空间、独立的工作日志,它们之间通过消息通信,而不是共享同一个上下文。你可以理解成开了一家“信息处理办事处”:有人负责接活,有人负责查资料,有人负责写初稿,有人负责校对复核,各管一段又互相配合。
这个项目名字里的“agency”我是这样理解的:它强调“代理机构”的组织属性。单个Agent只是工具人,而一组Agent组合起来,就成了能承接完整项目的小团队。项目里运作的每个Agent都像一个雇员,有职位描述、有汇报对象、有完成任务的验收标准。这种建模方式最大的好处是,复杂任务的复杂度从“一个大Prompt”转移到了“一套协作规则”上,而规则是可以用代码控制的。
1.2 它和“链式调用”“多轮对话复用”的本质区别
我用一个实际例子说明。以前做“行业调研报告”这类任务,最常见的做法是写一个很长很长的Prompt,要求模型依次完成检索、提炼、总结、排版。这个方案的问题在于:模型每一步都在同一个上下文窗口里工作,前期调研得到的文档摘要会越积越多,到写报告时重要信息已经被淹没了,甚至模型为了“连贯”会主动遗忘或篡改早期事实。
而agency-agents的思路相反:把任务拆成“调研—分析—写作”三段,每个阶段由独立Agent负责,前一段Agent只输出结构化的结果备忘录,后一段Agent只基于这份备忘录工作。上下文被刻意“截断”,反而保证了关键信息不被稀释。这里的关键词是结构化交接与上下文隔离,这两个设计贯穿了整个项目。
1.3 这套设计适合谁
如果你手头的任务是固定流程型——比如每天定时抓取信息、生成摘要、发报告;或者是内容生产型——比如先搜集素材、再列大纲、再成稿、再校对;又或者是需要多个知识面配合的复合型任务——比如既需要技术分析又需要市场洞察,那么agency-agents这套模式非常适合你。反过来,如果任务只是一个简单问答、一次文本转换,强行拆多个Agent只会增加延迟和成本,完全不划算。
2. 设计agency-agents的底层逻辑:单Agent在三类任务上的崩溃模式
2.1 长任务里的“上下文稀释”
我最初做单Agent自动化时,遇到的最大问题是长上下文中的注意力衰减。让一个模型连续处理2万Token的资料和10万Token的资料,它在后段任务里记住前置结论的可靠性完全不是一个量级。更严重的是,当早期信息与后期推理有关联时,模型可能会把最早的细节给“挤”出注意力范围,导致结论建在缺失信息上。
举个具体场景:我让一个Agent先读20个项目文档,然后总结技术选型建议。它前8个文档分析得头头是道,但从第12个文档开始明显敷衍,最后的总结只覆盖了最近读的几份材料。这不是模型能力问题,是单上下文处理长任务的天然天花板。agency-agents的做法是让一个Agent只负责“读文档并输出结构化摘录”,一次只处理有限份文档,摘录结果落到共享记忆区,下一环节的Agent只面对摘录而非全部原文。
2.2 多技能叠加时的“角色混沌”
另一个崩溃模式是角色冲突。如果你让一个Agent同时扮演“严谨的审核员”和“创意十足的文案”,它往往会摇摆不定,产出风格拧巴的内容。我调试过不少Prompt,无论怎么强调“先审核后创意”,模型还是会混着来——因为它本质上是在同一套参数和激活路径里切换角色,切换成本极高。
在agency-agents里,这个问题从根本上被规避了:审核Agent的System Prompt全程只有审核要求,它看不到创意阶段的上下文;文案Agent也看不到审核意见。每个Agent都活在“信息单行道”里,角色一致性大幅度提升。这就好比一个公司不会让财务兼着做市场策划——不是能力不够,是两种思维模式放在一起会互相干扰。
2.3 并行需求下的“串行浪费”
第三种崩溃更像效率问题。单Agent处理一批任务时永远只能顺序执行:查完A资料才能查B资料,写完全部代码才能开始测试。如果任务本身天然可并行,比如同时调研三个竞品、分别生成五个章节的初稿,单Agent模式就是在用昂贵的串行时间换本不该存在的等待。
在我做的模拟项目X中,用agency-agents把这类任务拆分给3个并行Agent后,耗时从原来的20分钟降到6分钟,质量没有下降,因为每个子任务的上下文更干净了。当然,并行也带来了新问题——多个Agent同时写共享资源时会发生冲突,这个我在第5部分专门讲。
2.4 为什么是“办事处”而不是“流水线”
流水线是固定的工位顺序,A做完传给B,B做完传给C,中间没有回退和协商。办事处则更像一个带有主管调度的混合结构:主管Agent接收任务后,根据任务类型动态组建临时小组,有些环节可以并行,有些环节需要评审。项目里我建议把架构设计成“主管+专家”的星型结构,而不是单向线性流水线,这样扩展性更强——新增一个专家Agent不影响已有流程,主管只需要更新自己的任务路由表。
3. 系统骨架拆解:主管Agent、专家Agent、共享记忆三层如何配合
3.1 主管Agent(Planner):拆任务、派活、回收结果
主管是整个系统的核心协调者。它不负责具体执行,只做四件事:理解用户意图、把目标拆解为子任务、为子任务匹配合适的专家Agent、汇总子任务结果做最终交付。在代码实现上,主管Agent本质上是一个“决策循环”:读任务、查可用Agent清单、生成执行计划、下发指令、监听结果、判断是否需要调整计划。
我给它定义了比较严格的输出格式,必须返回一个JSON计划数组,每个元素包含agent_id、task_brief、depends_on、output_key四个字段。depends_on用于表达依赖关系——比如“分析员”必须等“调研员”完成后才能开工。主管Agent的System Prompt里写明了它的输出只允许是合法JSON,其他任何附加解释都不接受。这样后续代码解析起来非常省心。
3.2 专家Agent:每个只维护一套私有记忆和工具
专家Agent是真正干活的人。每个专家Agent的System Prompt只描述它那一亩三分地的职责,不会让它了解整个项目的全貌。比如调研员的Prompt只是“根据给定查询词,检索资料,输出结构化条目列表”,它不需要知道最终报告面向什么读者、需要什么深度。
每个专家Agent有自己的private_memory字段,存放它在本次任务中的中间推理和临时笔记。这些内容不进入共享区,避免噪音污染其他Agent。只有当它产出最终节点(比如一份摘录、一份代码文件)时,这个成品才会被提交到共享记忆区。这个“私有记忆/公共成品”的区分,是整个系统上下文干净的关键设计。
3.3 共享记忆与消息总线:三种写法与两种隔离策略
共享记忆区我用一个键值存储来实现,键是output_key,值是上一步Agent产出的结构化数据。围绕这个存储,有三种提交方式:
- 全量写入:直接把Agent的完整输出存进去,适合短小的结果。
- 摘要写入:Agent自己先产出一份摘要,把摘要存进去,原文留在本地或外部存储。这个方式对控制Token成本非常有效。
- 指针写入:只在共享区存一个路径或ID,原始结果放到独立的文件/数据库里。适合大块数据,比如抓取的网页快照。
隔离策略也很重要。第一层是“按任务隔离”:不同用户请求产生的共享记忆必须分目录存放,绝不能互相串;第二层是“按阶段隔离”:同一个任务的中间产物与最终产物区分权限,只允许特定专家读取对应阶段的输出。我见过不少多Agent项目出幻觉问题,追根溯源都是共享区写入太随意,低级Agent写了一半的垃圾笔记也被后续Agent当素材读进去了。
3.4 工具注册表:不要让Agent自己猜该调用什么
专家Agent需要调用外部工具时(比如搜索引擎API、代码解释器、文件读写模块),我不会让它在对话里自由想象函数名,而是引入一个工具注册表。注册表里每个工具声明三件事:名称、参数Schema、用途描述。专家Agent的Prompt中附带当前可用工具的列表,它只能从列表中选择,生成工具调用请求,由系统侧统一执行。
这样做的好处有两个:一是避免模型幻觉出根本不存在的工具名,二是方便做权限控制——比如只有调研员能调用搜索工具,写作员无权触网。系统里维护一个权限矩阵,把工具按Agent角色做白名单映射,防止一个Agent通过工具越权影响其他Agent的产出。
4. 亲手搭一个最小可运行的协作Demo:角色定义、消息协议与执行循环
4.1 定义三个Agent:调研员、分析员、写作员
为了讲清楚实现细节,我以模拟项目X为例,搭建了一个最小闭环:调研员负责搜索并整理资料,分析员负责提炼观点和判断,写作员负责把观点写成可发布的文章。每个Agent都是一个独立类实例,拥有各自的System Prompt和模型参数。
这个案例里三个Agent的职责划分如下表:
| Agent | 职责 | 输入 | 输出 |
|---|---|---|---|
| 调研员 | 搜索原始资料,产出结构化条目 | 查询词列表 | JSON条目,每条含标题、来源、摘要 |
| 分析员 | 阅读条目,归纳核心观点与趋势 | 调研员产物 | JSON观点列表,含佐证条目ID |
| 写作员 | 根据观点成文 | 分析员产物 | 最终Markdown文稿 |
这里有个细节:写作员看不到调研员的原始条目,它只基于分析员提炼后的观点工作。这有意隔离了细节,让写作员不会被一堆原始链接干扰行文逻辑。
4.2 约定团队内部的消息协议:一个带信封的JSON结构
多Agent通信最大的坑是“各说各话”。我给系统定义了一个统一的消息信封格式:
{ "protocol_version": "1.0", "message_id": "8f3a...", "sender": "researcher_agent", "recipient": "analyst_agent", "task_id": "task-20240601-001", "message_type": "task_execute", "payload": { "content": "...", "metadata": { "source_count": 12, "elapsed_seconds": 45 } } }所有Agent之间的通信都走这个信封,message_type区分任务下发、结果回传、错误报告、心跳确认四种类型。协议版本号是为了以后升级不破坏兼容性。这个设计一开始看着繁琐,但在调试时救了我很多次——我只要看sender和recipient就能快速定位是哪两个Agent在对话。
4.3 主管的执行循环:分发—监听—合并—再分发
主管Agent的调度逻辑我写成了这样一个简单循环:
class AgencyOrchestrator: def __init__(self): self.agents = {} self.shared_memory = {} def dispatch(self, task_plan): pending = task_plan[:] running = [] while pending or running: completed = self._collect_results() running = [r for r in running if r["id"] not in completed] for result in completed: self.shared_memory[result["output_key"]] = result["data"] self._mark_dependencies_satisfied(result["output_key"]) for task in list(pending): if self._dependencies_met(task): agent = self.agents[task["agent_id"]] self._send_task(agent, task) running.append({"id": task["id"], "agent": agent}) pending.remove(task) time.sleep(1) return self._merge_results()核心逻辑是维护一个“待执行”集合和一个“执行中”集合,每次循环先收集已完成Agent的结果写入共享记忆,再检查哪些待执行任务的依赖已满足,满足则立即派发。这个循环简单到没有用任何复杂调度库,可读性极佳,也方便我后面加超时控制和重试逻辑。
4.4 一个完整的任务跑通案例
我用一个实际跑通的案例说明效果。任务是:“调研某新兴技术的应用趋势,输出一篇约800字的技术观察文章”。
主管收到任务后生成三步计划:
- 让调研员针对“技术名称 应用场景”“技术名称 企业实践”两组查询词进行检索,产出12条结构化条目。
- 让分析员阅读12条条目,归纳出三个主要趋势和对应的佐证条目ID。
- 让写作员基于三个趋势,输出一篇约800字的观察文章。
整个执行过程耗时约6分钟,其中调研占用大头,写作不到1分钟。最终文章质量比我用单Agent跑出的版本高很多——核心论点和查到的数据能一一对上,没有出现“编造引用”的问题。这让我确信“上下文隔离+职责单一是保证事实准确性”的组合拳是有效的。
5. 跑通之后才会遇到的真实坑:上下文污染、循环死锁与成本失控
5.1 “传声筒”效应:主管被共享记忆撑爆
第一次跑通Demo后,我又尝试增加Agent数量,很快遇到新问题:主管Agent为了向专家下发任务,习惯性地把共享记忆里的所有内容都塞进每条消息,美其名曰“让下属掌握完整背景”。结果专家Agent非常容易被大量无关信息干扰,产出质量严重下降,Token消耗也成倍上升。
后来我在主管的System Prompt里加了硬性规则:只允许传递当前任务所需的字段,严禁附带不相关记忆。实现上我加了一个filter_context函数,根据任务计划中声明的输入字段,从共享记忆里只取指定键。举个例子,分析员需要的只是research_results,主管就只把这个键的值放进消息,其余一概不传。
5.2 意见不合的Agent会无限循环
多Agent系统里最经典的失控场景是“评审回路”:写作员改完一版,审核员打回,写作员再改,审核员再打回……如果两边没有达成共识的机制,循环可以无限进行下去。我在模拟项目X里就遇到过,审核员连续三次提出修改意见,但每次意见角度不同,写作员改完一项另一项又不满意。
解决办法是给评审类任务设置最大迭代次数(我设置为3轮),超过之后由主管Agent介入做最终裁决。主管的裁决逻辑很简单:对比最新一版和上一版,如果修改幅度较小,就强制通过;如果修改幅度较大,则抽取分歧点,让两个Agent各自提供依据,主管再综合判断。这个“上级仲裁”机制非常像真实公司里的跨部门争议处理流程。
5.3 工具竞争:两个Agent同时写同一个文件
启用并行后我发现,如果两个专家Agent同时被派发“写报告的不同章节”任务,它们会试图以追加方式写同一个文件,导致文件内容互相覆盖。排查了半天,根因是共享文件系统没有做锁机制。
我用一个简单的文件锁解决了这个问题:输出路径由主管分配,每个文件责任到人,Agent只能写属于自己的路径;如果确实需要合并,由主管在收集阶段统一执行合并,而不是让Agent自己去写公共文件。这也是一个非常重要的分工原则——Agent只应该向自己的输出槽位写数据,任何跨Agent的数据拼装都应交给编排层完成。
5.4 Token成本:让专家只读摘要,别把原文糊脸
刚开始我图省事,调研员每次把搜索到的网页全文塞给分析员。结果一次完整任务消耗的Token量高得吓人,成本大约是精简方案的六倍。实际上分析员真正需要的只是网页标题、核心观点摘要、来源链接这几个字段,全文对总结趋势几乎没有帮助。
我把调研员的输出格式调整为强制摘要模式:每条结果只保留150字以内的摘要,并鼓励它提炼“与该技术相关的关键事实”,而不是照抄网页导语。分析员基于摘要工作后,Token消耗降低了将近70%,而且由于摘要已经去除了大量无关营销内容,分析质量反而提高了。这里的关键词是给每个Agent设计“刚刚够用”的输入,而不是“多多益善”。
5.5 调试技巧:先跑单Agent黄金路径,再加协作
多Agent系统调试难度比单Agent高出几个量级,因为出问题时你不知道是哪个Agent理解错了。我建议的调试顺序是:先把每一个专家Agent当作独立脚本单独测试,给它一组标准输入,检查输出是否稳定达标。只有当所有“单兵”都合格了,再串联起来跑协作测试。
我还会在编排层开一个“录音模式”,把每个Agent收到的消息和产出的结果都落盘。出问题时直接回放链路,对比哪个环节的输出偏离预期。这套方法帮我减少了大量排查时间,比盯着日志猜半天高效得多。
6. 进阶玩法与落地建议:动态编排、竞价制与可观测性
6.1 从“固定剧本”到动态路由
最初的任务计划是写死在代码里的,后来我让主管Agent根据任务描述动态生成计划,但很快发现它生成计划时偶尔会漏掉必要环节,或者给某个子任务匹配了错误的专家Agent。解决方式是维护一个“任务模板库”:把常见任务类型(调研报告、代码评审、竞品分析、文章生成)的计划模板预置好,主管Agent先匹配模板,再针对本次任务做参数填充和微调。
这种方式兼具稳定性和灵活性——模板保证下限,动态调整负责上限。对多数业务场景来说,完全自由生成计划并不必要,因为任务模式通常是有限几种,模板匹配远比你想象的更可靠。
6.2 用“成本预算”替代“无限循环”:竞价与裁减
多Agent系统的成本控制很难靠事后优化,必须在编排层做前置约束。我在主管Agent的任务计划里增加了一个cost_budget字段,每次派发任务时根据子任务复杂度分配Token预算。专家Agent在回复中必须声明estimated_tokens,如果累计接近预算,主管会主动压缩后续任务的要求,比如要求调研员减少搜索条数、分析员只输出结论不输出推演过程。
这本质上是一种预算竞价机制:哪个子任务对最终交付更重要,就给它分配更多资源。在资源有限的前提下,这个机制能保证系统不会因为某个非核心环节的过度消耗而整体失败。我试过让所有Agent不限量调用模型,结果一次任务烧掉大量Token,产出却没有任何改善——预算约束反而逼着每个Agent只做最必要的事。
6.3 日志、追踪与审计:每个Agent做了什么必须可回放
多Agent系统天然是一个分布式系统,哪怕跑在一台机器上,也存在并发、超时、状态不一致等问题。我给所有消息和共享记忆的读写都加了结构化日志,包含时间戳、Agent ID、任务ID、操作类型、数据长度。这样做有一个额外的好处:可以让最终用户看到一份“处理过程审计报告”——每个中间结论是怎么来的、哪个Agent产出的、依据了哪些资料。
这个机制在内容生产类应用中特别重要。有一次我的写作员写出的观点存在偏差,通过回查日志发现分析员在归纳时漏掉了一个关键的负面信息。定位到具体环节后,我只调整了分析员的Prompt,整个流程就不再复现同类问题。如果系统里的过程不可见,这种纠错几乎只能靠猜。
6.4 适合agency-agents的场景清单与不适合的边界
基于这些实践,我总结了一份适用范围清单,供参考:
| 适合场景 | 不适合场景 |
|---|---|
| 调研、写作、分析、总结等多阶段任务 | 一句话问答、即时翻译 |
| 需要多知识面交叉的复合任务 | 强实时交互、低延迟要求任务 |
| 可以并行处理的批量任务 | 单一确定性计算任务 |
| 需要追踪中间过程的审计型任务 | 资源极其受限的轻量环境 |
| 流程相对稳定的长期运行任务 | 临时一次性简单任务 |
判断标准很简单:如果拆成多个步骤后,每步都需要不同的提示词、不同的工具、不同的上下文,那就值得用多Agent;如果全流程就是一次调用的事,为Agent化而Agent化只会增加成本和延迟。
我现在的习惯是每接到一个新的自动化需求,先画一张“任务分工表”——列出需要哪些岗位、各自输入什么、输出什么、谁和谁有依赖关系。这张表画完,agency-agents的编排逻辑基本就清晰了。个人最大的体会是:多Agent系统的复杂度不在模型,而在如何设计信息流动的边界。边界画得好,模型各干各的活,协同自然顺畅;边界画得模糊,再强的模型也会被互相污染的信息拖垮。后续你可以从你自己的任务场景出发,先搭一个最小三Agent闭环跑通,再加上主管调度、共享记忆、工具注册表这几层,很快就会感受到“一支AI团队”和“一个AI员工”之间的巨大差别。