这两年做AI应用,最大的感触就是:单Agent是玩具,多Agent才是工程。可一旦把多个Agent真正放到一起跑,你很快会发现,真正难的不是模型能力,而是怎么把它们组织起来、让它们协作不互相踩脚、出了问题还能快速定位。agency-agents这个项目,说白了就是冲着这个痛点去的——它想解决的,不是"怎么让一个Agent更聪明",而是"怎么让一群Agent像一个团队那样工作"。
如果你也在折腾Agent编排、正在为上下文互相污染、任务分配不清、Agent跑着跑着就死循环这些问题头疼,或者只是想把最近很热的"多智能体"概念落地成能用的东西,这篇文章应该能给你一份可以直接照着做的参考。
1. 这个项目到底要解决什么问题:单Agent的天花板与多Agent的混乱
1.1 单Agent的可见瓶颈
先说个我自己的经验。去年我做过一个内部用的调研工具,最开始就是单个Agent:给一个大Prompt,让它自己去搜索、总结、写报告。一开始效果还凑合,但用了一两个月问题就全暴露了。
最典型的是上下文污染。搜索回来的资料、中间推理过程、临时笔记全部混在一个上下文窗口里,Agent写到最后经常忘了最初的任务目标。比如我让它调研"社区团购的市场规模",它搜到一堆生鲜电商的数据,写到一半居然开始分析前置仓,而且理直气壮地觉得这就是用户要的。
还有一个问题是工具调用的复杂性。单个Agent一旦挂了五六七八个工具,它自己都不知道该先调哪个。有时候它会在一个步骤里反复调用同一个搜索工具,把同样的结果搜索三遍,浪费时间和token。
这不是模型笨,是结构问题。上下文窗口再大,也装不下一个真实工作流的所有中间状态。单Agent看起来简单,但所有复杂度都压在一个上下文里,这本身就是不可持续的。
1.2 多Agent的天花板不是模型,是编排
后来我改为多Agent协作,比如一个负责搜索、一个负责整理、一个负责写报告。改了之后确实好一些,但没过多久就发现新的问题。
多Agent不是把多个Agent丢进一个系统里就完事了。它们之间的通信怎么做?谁来分配任务?A Agent的输出什么时候该给到B Agent?如果B Agent解析不了A的输出怎么办?如果某一个Agent挂了,整个流程是不是就断了?
这些问题的本质是:我们缺少一层"指挥和管理"的机制。模型本身只是执行者,真正决定多Agent系统成败的,是这层编排机制。你可以把它类比成一个编剧团队的分工——有人负责大纲、有人负责初稿、有人负责润色,但如果没有主编去统筹、派活、审稿,团队效率反而不如一个人闷头写。
我做调研工具时最头疼的就是这部分。任务分配靠写死在代码里,Agent之间传递数据靠共享一个JSON,出错了只能翻日志一点一点查。整个系统跑起来像几个人在同一个房间里各喊各的,乱成一团。
1.3 agency-agents的项目定位
正是在这个背景下,我接触到了agency-agents这个框架思路。它的核心想法很直接:把一群Agent当成一家"代理机构"里的成员来管理。每个Agent有自己的角色说明书(system prompt)、技能列表(tools)、工作边界(权限),然后由系统统一调度——谁能干什么、谁来分配任务、任务成果交给谁。
"agency"这个词很有意思,它本身有"代理机构"的意思,也有"自主能动性"的意思。把这个词用在Agent编排里,恰好表达了两层含义:一是Agent作为"代理"替用户执行任务,二是我们要给每个Agent一定的"自主性",让它能在自己的职责范围内做决策,而不是每一步都被外部流程死死绑住。
这个项目适合谁?我觉得主要是这几类人:
- 已经在用单个Agent做应用,发现效果和工作流复杂度不成正比的人;
- 团队里有多个人在各自调Agent,急需一套统一编排方式的人;
- 想研究多Agent系统、但不想从零开始造轮子的学习者。
下面我会按"架构设计-关键机制-完整Demo-踩坑记录-扩展方向"的顺序,把我做的过程中总结出来的东西完整写出来。
2. 核心架构拆解:把Agent当作"员工"来管理
这一章是整个项目最值得细看的地方。我直接说结论:agency-agents的架构核心就三件事——注册中心、路由机制、状态隔离。
2.1 注册中心:每个Agent都要"上户口"
在这个框架里,每个Agent在启动时都要向中心注册自己的元信息。我见过很多自己在代码里写死Agent列表的做法,看起来简单,但改起来非常痛苦——新增一个Agent要改代码、重新部署、还得通知所有相关模块。注册中心解决的就是这个问题。
我实际使用的注册信息大概长这样,以一份平台无关的描述性结构为例:
{ "agent_id": "research_specialist", "name": "调研专员", "description": "负责信息检索、资料筛选、数据整理,输出结构化调研笔记", "capabilities": ["web_search", "url_fetch", "data_extract"], "max_concurrent_tasks": 2, "temperature": 0.3, "routing_weight": 0.8 }注意description和capabilities这两个字段,它们是路由决策的关键依据。description要让调度器一眼看出这个Agent擅长什么,capabilities列的是它能调用的工具列表。注册中心会维护一张全局的Agent清单,路由模块每次要派任务时,都会实时查这张清单,而不是走一遍写死的if-else。
这里有我踩过的一个小坑:description一定要写得具体,不要写"负责各种任务"这种废话。调度器是根据description做语义匹配的,写得越模糊,任务派错的概率越大。我一直用的思路是"像一个HR在给岗位写JD,要写清楚需要什么样的人来干这个活"。
2.2 路由与消息传递:任务怎么找到对的Agent
注册中心解决的是"有哪些Agent可用",路由机制解决的是"任务该给谁"。agency-agents不是简单地按预设的DAG(有向无环图)走流程,而是允许任务在多个Agent之间动态流转。
路由的关键在于任务意图与Agent能力的匹配。我自己实现过两种匹配方式:
| 匹配方式 | 原理 | 适用场景 | 准确性 | 查询成本 |
|---|---|---|---|---|
| 关键词匹配 | 任务标签与Agent标签做文本匹配 | 任务类型少、边界清晰 | 中 | 极低 |
| 语义向量匹配 | 用嵌入模型计算任务描述与Agent描述的表达距离 | 任务开放、描述模糊 | 较高 | 中等 |
| LLM路由 | 用一个轻量模型判断任务应该交给谁 | 需要理解复杂意图 | 最高 | 高 |
我实际做下来,推荐的做法是前两者结合:先用关键词做一轮粗筛,缩小候选范围;剩下的边界模糊的任务,再用语义向量排序。至于LLM路由,适合任务非常开放的场景,但要小心它本身会引入延迟和额外成本——我用过一次,发现每个任务多出2到3秒的决策时间,在小流量场景还能接受,往上走就有点吃不消了。
消息传递这一层,我强烈建议不要用"直接调用另一个Agent"的方式。Agent之间一旦直接互相调用,依赖关系就会变成一团乱麻,你根本不知道谁在等谁。agency-agents的做法是引入一个消息中枢:A Agent把产出投递到一个消息队列里,路由转发器根据投递目的地把消息转交给下一个Agent。所有Agent只跟中枢通信,不关心下游是谁。这是多Agent系统能保持可维护性的关键,没有这个"解耦",后面加Agent的时候你会非常痛苦。
2.3 状态隔离:每个Agent只看到自己该看的东西
多Agent系统之所以容易乱,一个重要原因是状态共享被做成了状态大锅饭。所有Agent共用一个全局context,谁都能改,导致没法判断一个输出到底是基于什么信息产生的。
agency-agents对状态的思路是"每个Agent有一个自己的工作台"。Agent在工作台里维护自己的上下文、中间结果、临时笔记。外部给它的任务是"一份简报"而不是"整个项目的上下文"。只有当它明确声明"我的工作完成了",它的产出才会被提交到全局的产物区,供下游使用。
这个"工作台+产物区"的隔离设计,带来一个很大的好处:你可以精准地追溯每一个Agent的每一句话是基于什么信息说的。出了问题只需要查它自己的工作台,不用把一堆Agent的日志混在一起大海捞针。
我另外还会给每个工作台设置一个"上下文周长"限制,避免Agent在单个任务里越聊越远。比如一个调研Agent,单次任务上下文最多3万token,超过就强制它输出阶段性总结、清理中间内容、然后开始下一阶段。这属于工程上的取舍,但非常实用。
3. 三大关键设计:任务分解、协作协议、错误隔离
架构确定之后,真正决定这个系统好不好用的,是三个机制层面的设计。
3.1 任务分解:把大目标拆成"能给单个Agent执行"的粒度
注册中心、路由都只是载体,核心难点在于任务分解。任务分解做不好,后面全是白搭。
一个典型的错误是:把任务分解当成"把一个标题分成几个小标题"。比如让调研Agent干的是"制定报告大纲",调研Agent直接列了五个章节就完事了——这不是真正的任务分解,这只是把一个大任务复制成了五个同样大的任务。
我对任务分解的要求是三层标准:
- 每个子任务必须有明确的产出物(比如"一份带来源链接的要点清单",而不是"研究一下市场");
- 子任务之间尽量互不依赖,能并行的就并行;
- 单个子任务的执行时间控制在可预期范围内,如果一个子任务预计要跑很久,说明拆得还不够细。
实际完成任务分解的有两种方式:一种是用一个规划Agent来做,另一种是让路由层先用规则判断。我的经验是,对于边界相对固定的场景(比如调研、报告生成、内容整理),规则式就够用了,而且稳定可控。等到任务类型变得开放、需要临场判断时,再上规划Agent不迟。
再说一个细节:任务分解时要考虑到"分派人手"的成本。每个子任务的拆分,本质上是在为后续的路由创建输入。子任务描述的质量直接影响路由成功率,所以我一般会让分解模块输出一个标准化的任务卡,里面包含任务ID、目标、输入信息、期望产出、约束条件这几项,后面路由和Agent都不用再去猜任务意图。
3.2 Agent间的协作协议:没有协议的协作就是互相甩锅
任务分解之后,各个Agent之间要通过"协作协议"来衔接。我用的是很轻的三阶段协议:
- 任务领取:Agent收到任务卡后,先返回一个acknowledgement(明确表示"收到,我理解我要产出一个XX"),如果理解不了就返回clarify请求,由调度器补充或调整;
- 过程回报:Agent在工作过程中,如果有重大发现、卡点或产出不符合预期的情况,主动发一条状态消息给调度器,调度器决定是继续还是换人;
- 最终交付:Agent提交最终产物,并附带一段"自查说明"——它认为自己的产出满足了哪些要求、还有哪些局限。
这个协议看起来简单,但它解决了我之前遇到的"Agent A以为B会做某件事,结果B根本没做"的问题。所有衔接点都有明确的确认环节,这个系统才谈得上"协作"。
有一点要注意:协作协议里要明确"不做交接的边界"。也就是说,如果一个Agent的产出需要下游继续处理,那它必须在交付时给出足够的上下文说明,否则下游接手时会一头雾水。实践中我要求在交付物里附一个"to_next"字段,里面写清楚"我交付的东西是什么、下一步建议怎么处理",这就省了下游Agent大量重新理解的时间。
3.3 错误隔离与重试机制:别让一个Agent拖垮整条流水线
多Agent系统的故障跟单Agent不太一样。单Agent挂了,整个任务失败,状态很清楚。多Agent里一个环节出错,可能后面所有Agent都在处理一个错误的前置结果,最终产出一个看起来正常、实际完全不可用的东西——这种"静默失败"是最坑的。
我设计的容错策略分几个层级,每个层级针对不同的失败类型:
| 失败类型 | 检测方式 | 处理策略 |
|---|---|---|
| 单次调用超时 | 调用超时监控 | 重试一次,换更低温度 |
| 输出格式不符合协议 | 交付物校验不通过 | 返回给原Agent要求重新产出一次 |
| 同一Agent连续失败两次 | 失败计数阈值 | 任务改派给同类别的备用Agent |
| 整个流水线卡死 | 全局超时看门狗 | 触发降级路径,返回部分结果 |
检测方式里,最容易被忽略的是"交付物校验"。这步一定要做,而且不能只看"格式对不对",还要看"内容是否真的满足任务卡要求"。我以前犯过一个错:只用JSON Schema校验字段,结果是数据合法但内容不是用户要的——比如用户要竞品分析,Agent给了一堆竞品公司介绍,格式完全没问题,但方向偏了。后来在校验层加了一个简单的语义校验(比如"输出里是否覆盖了任务卡的所有问题"),效果好很多。
错误隔离的另外一个关键是"下游Agent的输入快照"。每次把产出投递给下游之前,我都要给这个输入打快照存起来。万一整条链路出错,回溯时就有一条完整的证据链,能快速定位是在哪一步开始歪的。
4. 从零搭建一个实际Demo:多Agent协作完成"行业调研报告"
前面讲了一堆理论,这章我用一个实际跑通的Demo来演示,目标是:三个Agent协作产出一份行业调研报告。一个负责搜索资料,一个负责分析整理,一个负责撰写成文。这个场景有代表性,又不至于太复杂,方便看清整个流程的运作方式。
4.1 定义Agent与注册信息
第一步,把三个Agent的信息填进统一的注册格式里。我实际用的定义比前面示例更完整,会把提示词基础模板也放进去:
{ "agent_id": "searcher", "name": "资料搜集员", "description": "根据用户给定的调研主题,搜索并筛选相关网页、新闻与行业报告,输出带来源索引的信息清单", "capabilities": ["web_search", "web_extract"], "workbench_config": { "max_context_tokens": 30000, "output_style": "bullet_list" } }注意这个描述有几个关键词:"搜索并筛选""带来源索引的信息清单"。它的作用是告诉调度器和下游:这个Agent的产出物是清单,不是分析结论。后面我会看到,正是因为定义清楚,下游才知道拿到的是一份原材料,需要再做加工。
类似地,分析Agent和撰写Agent的注册表里,我只突出它们各自"吃什么样的输入、吐什么样的输出",尽量少写它们"要怎么做"的细节——怎么做是模型的职责,注册表要管的是边界。
4.2 编排与启动流程
注册完成之后,我把三个Agent和一个调度器组成了一个"工作班组"。调度器的主要职责是:收取用户请求,将其分解成子任务,分派给对应Agent,收集产出并在合适的时机触发下一个环节。
整个编排我没有用代码去"显式编排",而是给调度器配了一条规则链:
- 收到用户主题后,先向搜索Agent派发任务卡"search_by_topic";
- 等搜索Agent交付信息清单后,将清单作为输入,给分析Agent派发任务卡"analyze_and_organize";
- 等分析Agent返回结构化大纲后,再把大纲给撰写Agent,让它输出完整报告。
这三步是顺序依赖的,所以被定义成了一条有向链。但如果你是想做并行调研,完全可以把这个规则链拆成"搜索A给主题1、搜索B给主题2、各自完成后分别进入分析"这样的分叉结构。规则链只是描述了调度逻辑,它的下层仍然是注册中心和消息队列,所以改流程不需要重新部署Agent。
启动环节要注意一个细节:所有Agent要能并行接收任务,所以通信模块必须设计成异步的。我用的是异步事件循环模式——每个Agent在独立的任务循环里处理消息,而不是一个Agent处理完再启动另一个。这个设计带来的体验是:如果三个Agent分别要处理三个不同子主题,它们可以同时开工,整体等待时间能缩短为原来串行的三分之一左右。
4.3 运行效果与调优过程
第一次跑通Demo时,我预期每个环节各自花几十秒,整条链走完大概几分钟。实际跑起来发现几个问题,挑两个最有代表性的说说。
第一个是搜索Agent产出的清单信息密度过高,导致下游分析Agent需要花大量精力去分辨"哪条是真正重要的"。我让搜索Agent一口气输出50条搜索结果,分析Agent要读完50条再做归纳,效率非常低。调优方案:把搜索Agent的产出要求改成"最多保留15条,每条必须附一行为什么值得保留的理由",这个改动让分析Agent的质量明显提升,处理时间也下降了将近40%。
第二个问题出现在撰写Agent上。它拿到分析Agent的大纲之后,会自行扩写细节,但经常把大纲之外的内容也写进去,导致报告篇幅失控。我的处理方式是给撰写Agent增加一条强制约束:"只允许扩展大纲中已有的要点,不得新增主题",调整之后报告结构稳定多了。
调优的方向总结起来就是三句话:控制中间产物体积、明确每个环节的产出边界、严格要求格式标准化。这三个点做好,整个系统的稳定性会有质的提升。
5. 生产环境落地时最容易踩的五个坑
Demo能跑通是一回事,真正要用到生产环境又是另一回事。这一章写的是我在从Demo走向实际使用过程中踩过的坑,每一条都对应过我真实翻车的历史。
5.1 上下文污染:最隐蔽的系统性风险
第一个坑就是上下文污染。刚才已经提过分工作台的设计,但分工作台并不等于完全隔离。如果你在实现时只是简单地把上下文切开来,却没有在每个任务结束时做"收尾归档",Agent会用着用着忘记前面的工作成果——然后重新开始思考,产出新的、不一致的内容。
具体表现是:分析Agent在处理完第一部分数据后,上下文里还留着第一章的笔记,到了写第三章总结时,它会把第三章写得好像还在讲第一章,或者干脆把第一章的结论再复述一遍。
我的解决方案是任务隔离快照:每个子任务完成时,把当前工作台上下文导出成一个快照文件;下一个任务开始时,只给它加载快照的核心结论部分,而不加载全部对话历史。这等于每个Agent"换一张新纸开始下一段工作",但手里还拿着上一张纸的摘要,能有效避免内容串味。
5.2 死循环与失控token消耗
第二个坑是死循环。多Agent系统里,一个"回复-再回复"的循环一旦触发,两个Agent就会互相发消息,永远不结束,直到你把它手动掐死。我第一次遇到时,两个Agent为了一个"数据来源是否可靠"的问题来回争论了二十分钟,token烧掉几十万。
深层原因是消息缺乏终止条件。应对方案是设置三把锁:
- 每轮协作消息加一个"最大轮次限制",比如同一对Agent之间最多来回5轮,超过就由调度器介入裁决;
- 每个任务设置累计token上限,超出后直接截断并返回已完成部分;
- 给每个Agent的回复加"确定性指令":一旦Agent意识到自己在重复讨论同一问题,必须主动发起"请求外部裁决"而不是继续回复。
前两把锁是硬性的,第三把是软的。实际效果很好,死循环基本绝迹。
5.3 工具权限边界没管好
第三个坑是工具权限。多Agent系统里,如果每个Agent都能调用所有工具,你会在日志里看到一些奇怪的组合:搜索Agent调用了代码执行工具,分析Agent自己去发邮件……在Demo阶段没问题,但一旦接上真实的业务系统和外部服务,这就是安全事故的隐患。
我的做法是三重限制:
- 注册表里明确每个Agent的
capabilities,不是简单的名字列表,而是"允许调用的具体工具白名单"; - 在工具层做二次校验:每个工具调用请求要携带发起Agent的ID,工具执行器会校验该Agent是否有权限;
- 对高权限工具(能修改数据、能外发消息的)增加人工审批开关,默认关闭,确需开启时走审批流。
这三重限制看着繁琐,但能挡掉绝大多数误操作。有一次内部测试时,一个语义理解Agent因为提示词注入被诱导去调用发消息工具,结果被权限校验拦下来了。要不是有这层,还不知道要搞出什么乱子。
5.4 并发与速率限制:别让Agent们"打架"
第四个坑是并发与速率限制。当多个Agent同时开始执行时,如果都去调用同一个上游API,很容易触发限流。而我最初设计时没有做全局的并发协调,于是就出现了搜索Agent和分析Agent同时抢着调用同一个模型接口,导致大量请求排队超时。
后来引入了一个简单的"请求闸门":所有对外部API的请求走统一通道,通道里维护一个并发窗口(比如同时最多5个请求),超过的请求自动排队。这样虽然偶尔会增加延迟,但整体稳定性提升非常明显。另一个点是对上游接口的失败要有一个"稍微退避"的机制,不要在限流恢复的一瞬间一窝蜂重试——我用的是指数退避加抖动,成功率从60%出头提升到95%以上。
5.5 可观测性:多Agent系统的"仪表盘"
最后一个坑跟排错有关。多Agent系统最大的问题是"黑盒"——你看到最终报告出来了,但不知道它中间经历了什么。一次报告里出现一个错误引用,要定位是搜索Agent搜错了,还是分析Agent选错了,还是撰写Agent自己编的,就得靠日志回溯。
所以从第一天起就要建设可观测性。我给每个Agent的每一步工作都生成了结构化的trace:任务ID、输入消息摘要、调用工具列表、关键决策点说明、输出消息摘要、耗时、token数。
最有用的是"决策点说明":让Agent在每次做重大决定时,写一句话说明"我为什么做这个选择"。比如搜索Agent选择不采纳某篇高权重文章时,它要写"因为这篇文章的信源不明确"——这样排错时,你不用去猜它为什么这么做,它已经主动告诉你了。
我还做了一个简单的可视化界面(用一套日志系统实现的),按时间轴把每个Agent的工作串起来看。排查问题的效率提升了不止一倍。
6. 后续可以扩展的方向
上面几章基本把agency-agents的框架和实操讲透了。最后聊聊我看到的几个扩展方向和我在继续折腾的思路。
6.1 从"链式编排"到"混合路由"
我们的Demo是链式编排,三个Agent顺序执行。但实际业务里,很多时候不是简单的线性依赖,而是混合形态——有些环节要并行,有些环节要条件分支,有些环节还要动态回退。
我下一步想做的,是让调度器支持"混合路由":给定一个任务卡,可以配置前驱和后继条件,调度器自动算出满足条件的路径组合。这样加一个新场景时,不需要重写调度逻辑,只改配置就够了。这个思路其实类似工作流引擎,只是节点从"任务"换成了"Agent"。
6.2 引入"人机审批"节点
多Agent系统最容易让人担心的,是Agent在无人监管的情况下自行做出有副作用的操作。我的计划是在高影响操作链路中,增加一个"人机审批"节点——当某个Agent准备执行一个高权限动作时,它需要向一个队列发出审批请求,相关人审核通过后才能继续。
这个设计本质上是对"Agent自主性"的一种补充:我们既希望Agent有一定的自主决策能力,又希望在风险阈值边界上保留人的控制。这个平衡是生产系统能否被信任的关键。
6.3 长期记忆与跨任务学习
目前每个任务组都是相对独立的,任务跑完,Agent不会把经验留到下一个任务里。这其实很浪费,因为同类型的调研任务,跑十遍之后,Agent都已经很熟练了,但每次还得从头摸索。
我给这个项目规划的下一步,是加入一个跨任务的"经验库":Agent在每次任务结束时,除了交付产物,还要提炼两条"下次做类似任务时值得注意的点",存入经验库。经验库会在注册或路由时被加载,作为Agent的"背景知识"。说白了,就是让系统越用越聪明。
6.4 容器化与横向扩展
多Agent系统天生适合横向扩展,因为每个Agent都是无状态的独立工作单元,只要把工作台的快照放到共享存储上,就可以随时起一个副本来分担任务。
目前我用的是比较简单的多进程部署,下一步计划做容器化封装,让每个Agent跑在独立的容器里,通过消息队列通信。这样一个Agent升级时,其他Agent完全不受影响,整个系统还能通过自动伸缩来应对大流量。
最后说点实在的。我在做agency-agents的过程中,最大的感悟是:多Agent编排的核心不是模型,而是管理。模型的能力提升确实能带来单点效果提升,但一个多Agent系统能不能稳定、可控、可维护地长期运转,取决于你有没有把角色边界、通信协议、错误隔离、可观测性这些"管理问题"想清楚。
如果你也想动手做一个类似的东西,我建议别急着写代码,先花一晚上把下面三个问题写清楚再动手:
- 你的系统里有哪些角色,每个角色的输入、输出、边界分别是什么?
- 角色之间通过什么协议协作,谁有权给谁派任务?
- 某个角色出错时,你的系统怎么发现、怎么恢复、怎么防止错误扩散?
这三个问题想透了,工程实现反而是水到渠成的事。接下来去写代码的时候,你会有一种到处是"抓手"的感觉,而不是像无头苍蝇一样在Agent和工具之间打转。