1. 从"agency-agents"这个名字说起:一个多智能体协作框架的定位
第一次看到"agency-agents"这个项目名,我的直觉是:这大概率是一个围绕"代理(agent)"和"代理机构(agency)"概念做文章的多智能体协作框架。拆开来看,"agency"在英文里既有"代理机构"的意思,也有"能动性、自主行动能力"的含义,而"agents"则是复数形式的"代理/智能体"。把这两个词拼在一起,指向的应该是一套让多个智能体像一家机构里的不同角色那样协同工作的系统。
这个判断不是凭空来的。近两年围绕大模型的应用开发,已经从"单次问答"快速演进到"让模型自己规划、调用工具、分步执行任务"的阶段。单个智能体再强,遇到复杂任务也会力不从心——上下文窗口有限、单点能力有短板、无法并行处理子任务。于是"多智能体协作"成了很自然的方向:把一个大任务拆给多个各有分工的智能体,让它们像团队一样配合。agency-agents 这个名字,恰好踩在这个方向上。
需要先说明的是,我拿到的项目正文、关键词和摘要描述都是空的,所以这篇内容不是对某个具体代码仓库的逐行解读,而是基于标题本身、结合多智能体协作这个领域的通用实践,做一次完整的拆解和落地推演。我会把"如果我要从零搭一个叫 agency-agents 的多智能体协作框架,我会怎么设计、怎么踩坑、怎么调优"这件事讲透。对于正在做类似事情的人,这篇可以直接当设计参考;对于刚接触多智能体的读者,也能借此建立一套完整的认知框架。
适合读这篇的人大概有三类:一是想给自己的产品加上"多角色协作"能力的开发者;二是被单个智能体"越做越乱"困扰、想找结构化方案的人;三是对 agent 编排(orchestration)感兴趣、想了解工程落地细节的技术负责人。下面我会从核心概念、架构设计、角色分工、通信机制、实操步骤、避坑经验几个层面展开,尽量把每个"为什么"都讲清楚。
2. 多智能体协作到底解决了单智能体的哪些死结
2.1 单智能体的三个典型瓶颈
在动手设计 agency-agents 之前,得先想明白一件事:为什么非要多个智能体?单个大模型加几个工具调用,很多时候也能跑通任务。我实际做过不少单智能体的项目,总结下来它有三个绕不过去的瓶颈。
第一个是上下文污染。当一个智能体既要理解用户意图、又要规划步骤、还要执行具体操作、最后还要检查结果时,所有这些信息都堆在同一个上下文里。任务一复杂,上下文里就混进了大量中间过程的噪音,模型很容易"忘记"最初的目标,或者在执行细节时被之前的规划思路带偏。这就像让一个人同时当项目经理、程序员和测试,脑子里的角色切换会互相干扰。
第二个是能力无法专精。单智能体的系统提示词(system prompt)往往要兼顾多种能力,结果就是每种能力都写得比较泛。你很难让同一个提示词既擅长严谨的代码审查,又擅长天马行空的创意发散。提示词越长越全,模型对每一条的遵循度反而越低,这是很常见的现象。
第三个是无法并行与隔离。有些子任务之间没有依赖关系,本可以同时做,但单智能体只能串行。更麻烦的是,一个子任务失败会污染整个上下文,导致后续步骤全部受影响,缺乏故障隔离。
2.2 多智能体带来的四个结构性收益
agency-agents 这类框架的价值,正是针对上面三个瓶颈给出的结构性解法。我把它归纳成四点收益。
职责分离带来的上下文纯净。每个智能体只关心自己那一段,规划者只管拆解任务,执行者只管干活,审查者只管挑毛病。各自的上下文里只有和自己相关的信息,噪音大幅减少,遵循度自然提升。
角色专精带来的提示词优化空间。因为每个智能体只干一件事,它的系统提示词可以写得非常聚焦、非常具体,甚至可以为每个角色单独调参、单独选模型。便宜的模型干简单的活,贵的模型干关键的活,成本也能优化。
并行执行带来的效率提升。没有依赖关系的子任务可以分发给多个执行智能体同时处理,最后汇总。这在批量处理类任务上收益非常明显。
故障隔离带来的鲁棒性。某个执行智能体跑挂了,不会直接毁掉整个流程,编排层可以重试、可以换一个智能体、可以把失败信息交给审查者处理。这种隔离是单智能体很难做到的。
理解了这四点,后面所有的架构设计其实都是在回答一个问题:怎么把"职责分离、角色专精、并行执行、故障隔离"这四件事在工程上落地。
3. agency-agents 的架构骨架:编排层、角色层与共享状态
3.1 三层结构的分工
如果让我来设计 agency-agents,我会把它拆成三层:编排层(Orchestration Layer)、角色层(Agent Layer)、共享状态层(Shared State Layer)。这三层各司其职,边界清晰,是整个框架能稳定运行的基础。
编排层是"大脑",负责接收用户任务、决定用哪些角色、按什么顺序调度、处理失败重试、判断任务是否完成。它本身通常也是一个智能体,或者是一段确定性的调度逻辑。我的经验是:能用确定性代码写的调度逻辑,就不要交给模型去决策。比如"先规划再执行再审查"这种固定流程,用代码写死比让模型自由发挥稳定得多。模型只应该用在真正需要判断力的地方,比如任务拆解、结果评估。
角色层是"干活的团队",每个角色是一个独立的智能体实例,有自己的系统提示词、工具集、模型配置。角色之间不直接互相调用,而是通过编排层或共享状态来交换信息。这一点很关键——如果让角色之间随意互相调用,很快就会变成一团乱麻,调试时根本追不清调用链。
共享状态层是"公共白板",存放任务描述、中间产物、各角色的输出、执行日志等。所有角色读写这块共享区域,而不是把信息塞在消息里传来传去。这样做的好处是信息有单一可信来源,任何角色都能看到全局进展,也方便做持久化和断点恢复。
3.2 为什么用共享状态而不是纯消息传递
这里我要专门解释一下为什么倾向共享状态而不是纯消息传递,因为这是很多人在设计多智能体系统时容易走偏的地方。
纯消息传递的模式下,智能体 A 把结果发给 B,B 处理完发给 C。听起来很清晰,但实际跑起来问题很多:消息格式要严格约定,一旦某个环节格式不对,整条链就断了;中间结果无法复用,如果 D 也需要 A 的输出,就得让 A 再发一遍或者层层转发;调试时你只能看到一条条消息,很难还原"当前全局状态到底是什么"。
共享状态模式则像一块大家都能读写的白板。A 把结果写到白板的某个区域,B、C、D 谁需要谁去读。信息只写一次,多处复用。调试时直接看白板快照,全局一目了然。当然,共享状态也有代价——需要处理并发写入的冲突,需要设计好状态的结构。但对于 agency-agents 这种多角色协作场景,共享状态的收益远大于成本。
我通常会把共享状态设计成一个结构化的对象,大致包含这几个字段:任务目标、任务拆解后的子任务列表、每个子任务的状态(待处理/进行中/已完成/失败)、每个子任务的产出、全局的执行日志、以及一个"当前阶段"标记。这个结构用 JSON 或类似格式序列化,方便持久化和恢复。
3.3 编排层的调度策略选择
编排层的调度策略,直接决定了整个系统的行为模式。常见的有三种:顺序流水线、动态路由、混合模式。
顺序流水线最简单:规划者拆任务,执行者逐个做,审查者最后检查。适合流程固定的场景,比如"写一份报告"这种线性任务。优点是稳定可预测,缺点是灵活性差。
动态路由更灵活:编排层根据当前状态,动态决定下一步该叫哪个角色。比如执行者报告某个子任务失败,编排层可以决定是重试、是换个角色做、还是叫审查者介入。这种模式适合任务结构不确定的场景,但调度逻辑复杂,容易出 bug。
混合模式是我最推荐的:主干流程用顺序流水线保证稳定,在关键决策点引入动态路由处理异常。比如正常情况按"规划→执行→审查"走,但执行阶段如果某个子任务连续失败两次,就动态插入一个"诊断"角色来分析原因。这样既有稳定性又有灵活性。
4. 角色设计:一个 agency 里到底该有哪些 agent
4.1 核心角色清单与职责边界
一个能跑通复杂任务的 agency,我建议至少包含这几类角色。注意,角色不是越多越好,每多一个角色就多一份调度复杂度和 token 成本,够用就行。
| 角色 | 核心职责 | 典型工具 | 建议模型档位 |
|---|---|---|---|
| 规划者 Planner | 把用户任务拆成可执行的子任务 | 无或仅任务管理工具 | 高能力模型 |
| 执行者 Executor | 完成具体子任务 | 搜索、代码执行、文件读写等 | 中高能力模型 |
| 审查者 Reviewer | 检查产出质量、发现错误 | 无或校验工具 | 高能力模型 |
| 汇总者 Synthesizer | 把多个子任务产出整合成最终结果 | 无 | 中高能力模型 |
| 诊断者 Diagnoser | 分析失败原因、给出修复建议 | 日志读取 | 高能力模型 |
规划者是整个流程的起点,它的输出质量直接决定后续所有环节。我踩过的坑是:规划者拆的任务粒度太粗或太细都不行。太粗,执行者拿到一个模糊的大任务还是不知道怎么做;太细,子任务数量爆炸,调度开销和 token 成本飙升。我的经验是每个子任务应该是一个"执行者能在一次交互内完成、且有明确验收标准"的单元。
执行者是数量最多的角色,可以按领域再细分。比如做数据分析的 agency,执行者可以分成"数据获取者""数据清洗者""统计分析者""可视化者"。细分的好处是每个执行者的提示词和工具集都能高度专精。
审查者的价值经常被低估。很多人觉得模型自己产出的东西自己检查就行,但实测下来,让一个独立的、带着"挑刺"心态的审查者来检查,发现问题的概率明显高于让执行者自查。因为执行者有自己的思维惯性,容易对自己的产出过度自信。审查者的提示词要明确要求它"假设产出有问题,主动找证据",而不是"看看有没有问题"。
4.2 角色提示词的写法:三个必须写清楚的要素
每个角色的系统提示词,我建议必须写清楚三件事:身份与目标、能力边界、输出格式。
身份与目标要具体。不要写"你是一个助手",而要写"你是一个负责把复杂任务拆解成可执行步骤的规划专家,你的目标是产出粒度适中、依赖关系清晰的子任务列表"。越具体,模型越能进入状态。
能力边界要明确说"不做什么"。比如执行者的提示词里要写"你只负责完成分配给你的子任务,不要尝试规划整体流程,不要修改其他子任务的内容"。这条极其重要,因为模型天然有"越界"的倾向,不明确禁止它就会去干不属于自己的活,导致职责混乱。
输出格式要严格约定。多智能体系统里,角色之间的输出是要被程序解析的,格式不对整个流程就断了。我通常要求角色输出结构化的 JSON,并在提示词里给出完整的 schema 示例。如果模型经常不遵守格式,可以在提示词末尾加一句"只输出 JSON,不要有任何其他文字",效果通常不错。
4.3 角色数量与成本的权衡
这里分享一个很实际的经验:角色数量要跟着任务复杂度走,不要一上来就搭一个十几个角色的庞大体系。
我见过有人一上来就设计了规划、执行、审查、汇总、诊断、记忆管理、工具路由等七八个角色,结果调试了两周还没跑通一个完整任务。原因是角色越多,交互路径越多,出问题的地方越多,而且 token 成本是线性甚至指数级增长的。
我的建议是从最小可用集开始:规划者 + 执行者 + 审查者,三个角色先跑通一个简单任务。跑通之后再根据实际遇到的瓶颈逐步增加角色。比如发现执行者经常失败但不知道为什么,再加诊断者;发现多个执行者产出需要整合,再加汇总者。这种渐进式的做法,比一次性设计一个大而全的体系要靠谱得多。
5. 智能体之间的通信:消息格式、状态同步与冲突处理
5.1 结构化消息格式的设计
角色之间传递的信息,必须是结构化的,不能是自由文本。我通常设计一个统一的消息信封(envelope),包含这几个字段:发送者、接收者、消息类型、负载(payload)、时间戳、关联的子任务 ID。
消息类型我一般定义几种:任务分配、任务结果、任务失败、状态查询、审查意见。每种类型对应不同的 payload 结构。比如"任务结果"的 payload 里要有子任务 ID、产出内容、执行耗时、使用的工具列表;"任务失败"的 payload 里要有失败原因、错误堆栈、已尝试的方案。
为什么要这么细?因为编排层需要根据这些结构化字段做决策。比如看到某个子任务失败且错误类型是"工具调用超时",编排层就知道可以重试;如果错误类型是"输入格式错误",那重试也没用,得让规划者重新拆任务。这些判断都依赖结构化的错误信息,自由文本是做不到的。
5.2 共享状态的并发写入问题
前面说了用共享状态,但共享状态有个绕不开的问题:多个执行者并行写入时,怎么避免冲突?
我的做法是按子任务分区。共享状态里每个子任务有自己独立的区域,执行者只写自己负责的子任务区域,不碰别人的。这样并行写入时天然不冲突。全局性的字段(比如"当前阶段""整体进度")只由编排层写入,执行者只读不写。
如果确实需要多个角色写同一块区域(比如多个审查者都要往"审查意见"列表里追加),那就用追加(append)而不是覆盖(overwrite)的语义,并且给每条记录带上写入者标识和时间戳。这样即使并发写入,也只是顺序问题,不会丢数据。
5.3 状态同步的时机与粒度
状态同步的时机也很讲究。同步太频繁,开销大;同步太少,各角色看到的状态不一致,容易做出错误决策。
我的经验是在角色切换的边界做同步。也就是说,一个角色完成工作、把结果写入共享状态后,编排层做一次状态快照,然后再启动下一个角色。这样每个角色启动时看到的都是最新的、一致的状态。角色执行过程中不需要实时同步,因为它只操作自己那块区域。
对于并行执行的多个执行者,它们各自完成后分别写入自己的区域,编排层等所有并行任务都结束后再做一次汇总同步。这种"边界同步"的策略,在保证一致性的同时把同步开销降到了最低。
6. 从零搭一个 agency-agents 的实操步骤
6.1 环境准备与依赖选型
假设现在要动手实现,我会这样准备环境。首先是语言和框架的选择:Python 是这类项目的主流选择,生态成熟,各种大模型 SDK 都有官方支持。编排逻辑可以用纯 Python 写,也可以用现成的编排框架,但我建议核心调度逻辑自己写,因为现成框架的抽象往往和你的具体需求不完全匹配,改起来反而更费劲。
依赖方面,至少需要:一个大模型的调用 SDK、一个用于结构化输出的校验库(比如 Pydantic 这类做数据校验的)、一个持久化方案(初期用本地文件或轻量数据库就够)。如果涉及工具调用,还需要准备各个工具对应的客户端库。
配置管理要提前做好。模型 API 的密钥、各角色的模型选择、超时时间、重试次数这些,都应该放在配置文件里,不要硬编码。我见过太多项目把这些写死在代码里,换个环境就要改代码,非常痛苦。
6.2 定义共享状态的数据结构
第一步是定义共享状态。用 Pydantic 或类似工具定义一个状态类,包含任务目标、子任务列表、每个子任务的状态和产出、执行日志、当前阶段。每个子任务本身也是一个结构,包含 ID、描述、依赖的子任务 ID、状态、产出、错误信息。
定义好之后,写两个基础方法:一个是把状态序列化成 JSON 存盘,一个是从 JSON 反序列化恢复。这两个方法是断点恢复的基础,一定要在项目早期就做好。我吃过亏——项目做到一半才想起来要支持断点恢复,结果发现状态结构设计得不合理,序列化出来一堆问题,返工成本很高。
6.3 实现规划者:任务拆解的关键技巧
规划者的实现,核心是提示词设计和输出解析。提示词里要明确要求它输出一个子任务列表,每个子任务包含描述、依赖关系、验收标准。输出用 JSON 格式,方便解析。
这里有个关键技巧:在提示词里给规划者一个"拆解原则"。比如"每个子任务应该能在一次交互内完成""子任务之间的依赖关系要显式声明""每个子任务要有可验证的完成标准"。不给这些原则,规划者拆出来的任务质量参差不齐。
解析规划者输出时,一定要做校验。检查子任务数量是否合理(比如超过 20 个就要警惕)、依赖关系是否有环、每个子任务是否有描述和验收标准。校验不通过就带着错误信息让规划者重新拆,通常重试一两次就能得到合格的结果。
6.4 实现执行者:工具调用与结果回写
执行者的实现相对直接,但有几个细节要注意。执行者拿到子任务后,要根据任务描述决定调用哪些工具、按什么顺序调用。这里我建议给执行者一个明确的工具使用规范,比如"优先使用搜索工具获取事实信息""代码执行前先检查输入格式"。
执行完成后,执行者要把结果写回共享状态对应的子任务区域,并更新子任务状态。如果执行失败,要写入结构化的错误信息,而不是简单抛个异常。错误信息里至少要有:失败原因分类、详细描述、已尝试的方案。这些信息是后续诊断和重试的依据。
6.5 实现审查者与汇总者
审查者拿到执行者的产出后,对照子任务的验收标准做检查。它的输出应该是一个结构化的审查结果:通过/不通过、不通过的具体问题、修改建议。如果审查不通过,编排层要把问题反馈给执行者重新做,或者交给诊断者分析。
汇总者在所有子任务完成后启动,把所有子任务的产出整合成最终结果。它的提示词要强调"保持各部分的一致性""消除重复内容""确保整体逻辑连贯"。汇总不是简单拼接,而是真正的整合,这一步做不好,前面所有工作都会打折扣。
6.6 编排主循环的编写
编排主循环是整个系统的中枢。它的逻辑大致是:初始化状态 → 调用规划者 → 循环处理子任务(按依赖顺序,无依赖的并行)→ 每个子任务执行后调用审查者 → 全部完成后调用汇总者 → 输出最终结果。
循环里要处理各种异常:执行失败的重试、审查不通过的返工、超时的处理。每个异常处理分支都要有明确的策略和上限,比如"同一子任务最多重试 3 次""整个任务最多运行 30 分钟"。没有上限的循环是灾难,我见过因为一个子任务反复失败导致整个流程跑了几个小时、烧掉大量 token 的情况。
7. 实测中踩过的坑与调优经验
7.1 规划者拆任务粒度失控
最常见的坑是规划者拆出来的任务粒度不合适。要么太粗,执行者拿到"分析这份数据"这种模糊任务无从下手;要么太细,把"读取文件"和"解析文件"都拆成独立子任务,导致子任务数量爆炸。
我的解法是在提示词里给出正反例。比如正面例子是"计算过去 12 个月的月度销售增长率并生成趋势图",反面例子是"分析数据"(太粗)和"打开文件"(太细)。给了具体例子之后,规划者的输出质量明显提升。另外可以在解析阶段做自动检查,子任务描述少于一定字数就判定为太粗,打回重拆。
7.2 执行者越界修改全局状态
执行者越界是个隐蔽但危害很大的问题。表现是执行者在完成自己任务的同时,顺手改了共享状态里不属于它的区域,或者擅自修改了子任务列表。这会导致状态混乱,后续角色基于错误状态做决策。
解法是在执行者的提示词里明确禁止,同时在代码层面做权限控制——执行者只能写自己子任务对应的区域,写其他区域直接拒绝并记录警告。提示词约束加代码约束双保险,基本能杜绝这个问题。
7.3 审查者过于宽松或过于苛刻
审查者的尺度很难把握。太宽松,问题漏过去,最终结果质量差;太苛刻,什么都通不过,执行者反复返工,流程卡死。
我的调法是给审查者明确的判定标准,并且要求它区分"必须修改"和"建议优化"两类问题。必须修改的才触发返工,建议优化的记录在案但不阻塞流程。这样既保证了质量底线,又避免了无限返工。另外可以给审查者设一个"通过率"的软约束,如果它连续判定多个子任务不通过,就提示它重新审视标准是否过严。
7.4 token 成本失控的排查
多智能体系统的 token 成本很容易失控,因为每个角色都要消耗 token,而且共享状态越大,每个角色读取时消耗的 token 越多。
排查成本问题,我一般先看共享状态的大小。如果状态里堆积了大量中间过程的详细日志,每个角色读取时都要把这些日志读进去,成本自然高。解法是给共享状态做分层:角色默认只读和自己相关的部分,全局摘要信息单独维护一份精简版。这样每个角色读取的上下文就小很多。
另一个成本大头是重试。如果某个子任务反复失败重试,token 消耗会成倍增长。所以要给重试设上限,并且每次重试时把之前的失败信息精简后再传给执行者,避免上下文越滚越大。
7.5 并行执行时的资源竞争
并行执行多个子任务时,如果它们都要调用同一个外部工具(比如同一个搜索接口),可能会触发限流。这个坑我在实际项目里踩过,多个执行者同时发请求,结果被接口方限流,全部失败。
解法是给外部工具调用加一个统一的限流器,控制并发数。另外并行度不要设太高,一般 3 到 5 个并行就够了,再高收益递减而风险上升。并行度这个参数最好做成可配置的,方便根据实际情况调整。
8. 这套框架适合什么场景,以及后续可以怎么扩展
agency-agents 这类多智能体协作框架,最适合的是任务结构相对复杂、可以拆解、且对结果质量有要求的场景。比如长文档的撰写与审校、多步骤的数据分析流程、需要多轮验证的代码生成、复杂的信息调研与整合。这些场景的共同点是:单次交互搞不定,但拆开之后每个子任务都不算太难。
反过来,如果任务本身很简单,比如单轮问答、简单的格式转换,那用多智能体就是杀鸡用牛刀,徒增成本和复杂度。判断标准很简单:如果你用单智能体加几个工具调用就能稳定搞定,就不要上多智能体。
后续扩展方向,我觉得有几个值得尝试。一是引入长期记忆,让 agency 能记住之前处理过的类似任务,复用经验。二是动态角色生成,根据任务类型自动决定需要哪些角色,而不是预先写死。三是多 agency 协作,让多个 agency 各自负责一块,再有一个更高层的编排者协调它们。这些方向都很有意思,但每加一层复杂度,调试难度都会上一个台阶,建议在单 agency 跑稳之后再考虑。
最后分享一个我自己的体会:多智能体系统的调试,比单智能体难得多,因为问题可能出在任何一个角色、任何一次交互上。所以日志和可观测性一定要在项目早期就做好。每个角色的输入输出、每次状态变更、每个决策点,都要有清晰的日志。我现在的习惯是,在编排层加一个"回放"功能,能把一次完整执行的所有交互按时间顺序打印出来。有了这个,排查问题的时间能缩短一大半。这个投入,绝对值得。