“agency-agents”这个名字乍看像某些公司内部的组织架构目录。但如果你常关注AI应用开发大概能猜到我指的是什么——把多个AI代理Agent组织成一个“代理机构”让它们像一间公司里的员工一样协作干活。我最近一半的业余时间都砸在这个名为agency-agents的模拟项目上核心就一句话让一堆各怀绝技的AI Agent通过一套轻量级的协作框架完成靠单个Agent很难独立完成的任务。这项目不是某个大厂出品也不是什么规范框架就是我自己从零开始搭的一套多代理协作原型。折腾它的起因是某次我用单一Agent做“从选题到成稿”的内容流程时发现这家伙既要理解需求、又要检索资料、还得保证文风统一、兼顾润色校对结果就是上下文越塞越满、指令越加越乱输出质量肉眼可见地往下掉。我当时的想法很简单既然人干活时讲究分工为什么AI不能也搞个“部门”出来于是agency-agents就诞生了——给Agent配角色、配工具、配工作台再安排它们相互配合。这篇文章就记录我从零搭这套多代理协作系统的完整思路、核心代码细节以及我实测下来踩过的那些坑。它适合谁看想自己动手组一套多Agent系统的开发者或者正在纠结“单体Agent挺好到底要不要拆成多代理架构”的朋友们。1. “agency”到底解决什么问题单体Agent的膨胀危机先说一个比较反直觉的结论单个Agent在能力上并不弱真正拖垮它的是“既要又要还要”这个状态本身。1.1 一个Agent同时干十件事必然哪件都干不利索我做内容类Agent的时候给它的系统提示词里写了七八条职责——你要负责分析用户意图、搜索最新的行业信息、搭建文章结构、组织语言、检查错别字、还要优化SEO关键词。前几轮对话还好一旦任务时间线拉长问题就全冒出来了它会在“搜索资料”这一步过度投入把大量搜索片段堆进上下文真正落笔时反而忘了用户最开始的核心要求。不同职责之间互相抢权重。今天多强调一下文风它就少搜了两条资料明天多提示两句格式它又忽略了数据准确性。上下文长度是硬天花板。一次长流程跑下来光是把历史记录、搜索结果、写作草稿全塞进去就得占掉几万token如果再让它反复调整很快就什么都“记不住”了。这就像一个人同时当厨师、服务员、收银员、洗碗工偶尔还能应付但凡店里稍微忙一点厨房的菜凉着前台的单排着后厨的碗堆着——全线崩溃。1.2 从“一个全能Agent”到“一组专职Agent”的转变我再看同事那边接近工业级的Agent应用发现大家早就不追求“单兵作战”了。主流做法是把任务拆成多个Agent各管一摊再编排起来单体Agent模式多Agent协作模式agency一份上下文承载所有职责每个Agent只维护自己的小上下文指令互相干扰优先级要靠“押注”角色专职指令边界清晰失败后整条任务链断掉单个环节失败可重试、可替换想加能力就要改系统提示词越改越乱新能力新Agent即插即用这个表一下子击中了我的痛点。我当时手里正好有一个“从乱七八糟的原始资料里提炼一篇结构清晰的行业综述”的需求单靠一个Agent去处理时它要么写出通稿式的干瘪总结要么被原始资料带着跑。我意识到应该把“调研”“撰写”“审校”拆成三个阶段让不同的Agent各管一段。于是agency-agents的第一版架构图就成了这样一个调度核心我管它叫Eve加上若干专职Agent它们之间通过统一的消息格式沟通共享一块工作区按流程接力干活。写代码之后我才体会到这个“拆”字才是整个项目最关键的设计判断。2. 给Agent们设岗位角色划分与消息协议设计真正动手写代码前我先花了两个晚上想清楚一件事这个“代理机构”里到底需要几个“员工”。人开公司讲究组织架构搞多Agent系统也一样角色分得清后面才不会打架。2.1 四个基础角色Eve、Planner、Executor、Critic我第一版只设了四个角色都没有起什么花哨名字就按职能命名Eve调度员是整个系统的大脑入口接收外部任务把任务分派给合适的Agent收集结果必要时启动多轮流程。它不负责具体干活但所有消息都从它这里流转。Planner规划师负责把一个模糊的、宽泛的目标拆解成可执行的步骤清单。比如“帮我写一篇关于某技术的科普文”Planner会拆成“收集背景资料”“找出三个核心概念”“规划章节目录”“逐章撰写”“整体校对”。Executor执行者按步骤干活。它可以是一个写作者、一个程序员、一个数据分析师取决于我们给它的系统提示词和可调用的工具。一套agency里通常有多个Executor分别执行不同类型步骤。Critic评审员负责挑毛病。它拿到产物后从准确性、完整性、风格一致性等角度做检查给出修改建议或判定“通过”。你可能注意到这个结构其实模拟了一个小型内容工作室的运作方式。我刻意没有在某一步就上“记忆模块”“长期向量库”这些重装备因为第一个版本要验证的事情只有一个——多角色协作是不是真的比单体Agent能打装备越简单越能看出问题本身。2.2 消息格式一段JSON让Agent之间“说同一种话”多Agent系统最重要的一点就是把Agent之间的通信协议定义清楚。我这里既没用复杂的RPC框架也没上消息队列而是先定义了这样一个轻量级的消息结构{ message_id: msg_00123, timestamp: 1734591234567, from_role: planner, to_role: executor.writer, msg_type: task_assign, task_id: task_001, content: { instruction: 根据规划大纲撰写第二章核心技术原理, context_refs: [memory:plan_v1, file:docs/outline.md], priority: normal } }字段逻辑很直白message_id用于追踪每一条消息的来源排障的时候要靠它from_role和to_role定义了这条消息从哪里来、到哪里去msg_type则区分了这是“任务分配”“结果回传”“修改建议”还是“流程完成”。context_refs是个很关键的设计——Agent之间不直接传长文本而是传一个引用路径具体内容放在共享工作区里。为什么这么设计我一开始也图省事直接把任务描述和上下文一股脑塞进消息体结果一条消息几千字转手之间就把目标Agent的上下文撑爆了。改成引用路径之后Agent按需读取消息体轻多了上下文管理也清爽得多。这个设计后来帮我避免了一整类“上下文污染”的问题。2.3 给AI的角色系统提示词别讲长篇大论把约束讲清楚每个Agent都是由一个底层大语言模型我是用的某个通用API模型加上系统提示词组成的。系统提示词的关键不是写得多花哨而是把角色边界和工作方式写清楚。我以Executor里的“写手”角色为例最初的版本是这样的你是一位科技类文章写手。你的职责是根据规划大纲和资料片段撰写清晰、有逻辑、信息密度高的文章段落。 工作规则 1. 只处理与本文相关的资料不要自行搜索无关内容。 2. 每完成一个章节将产物保存到共享工作区的 writing/chapters/ 目录文件名需包含章节号。 3. 输出前做一次自查是否覆盖大纲要点是否有事实性表述未经资料验证 4. 收到评审员的修改建议后逐条修改并在回信中说明修改结果。 5. 禁止在回复中讨论任务之外的话题。对写手这类的Executor而言最重要的一条是“只处理相关输入”这能避免它在协作流程里产生幻觉或跑偏。而第4条是协作的关键——Executor和Critic之间会有至少一轮来回这条规则保证了修改是有闭环的不是改了白改。3. 编排层的实现让Agent之间“工作交接”而不是“乱炖一锅”角色和消息格式定完后接下来是重头戏——编写编排层也就是那个让整个agency转起来的主循环。这一层是很多自己搞多Agent的开发者的第一个坎搞着搞着系统不是跑不起来而是跑起来了却收不住——Agent之间要么谁也不理谁要么全体都在抢同一件事。3.1 主循环事件驱动还是顺序流水线我先说结论如果你的任务流程是相对固定的比如“先规划再执行后评审”那顺序流水线是最稳的起点。事件驱动虽然灵活但第一版就上它大概率会把时间都花在调试消息丢失和状态错乱上。我的第一版编排器就用了个很朴素的主循环用Python的asyncio实现class AgencyOrchestrator: def __init__(self): self.message_queue asyncio.Queue() self.agent_registry {} # role - agent instance self.task_log [] async def submit_task(self, initial_task: dict): 入口接收用户任务生成task_id转交给Planner task_id str(uuid.uuid4())[:8] planner_msg self.build_message( from_roleuser, to_roleplanner, msg_typetask_initial, task_idtask_id, content{raw_request: initial_task} ) await self.message_queue.put(planner_msg) self.task_log.append({task_id: task_id, status: created}) return task_id async def run_loop(self): 主事件循环持续从队列取消息并按角色分发 while True: msg await self.message_queue.get() target_role msg[to_role] if target_role not in self.agent_registry: # 找不到对应Agent记录异常并回退 self.handle_dropped_message(msg) continue agent self.agent_registry[target_role] response_events await agent.handle_message(msg) for event in response_events: await self.message_queue.put(event)核心就是那个看似简单的队列。任何一条消息进来都先入队再由run_loop根据to_role字段分发给对应Agent。Agent处理完后返回的可能是新的任务分配消息比如Planner把步骤分给Executor、结果回传消息Executor把产物给Critic或者终止信号全流程结束。3.2 共享工作区Agent之间不直接递“原件”只递“档案编号”刚才在消息协议里提到的context_refs落到实现上就是配套的共享工作区。我这里用一个本地目录充当“共享档案柜”每个task一个子目录Agent写产物前先落盘再在消息里传文件路径。workspace/ ├── task_001/ │ ├── planning/ │ │ └── plan_v1.md │ ├── research/ │ │ ├── sources_01.md │ │ └── sources_02.md │ ├── writing/ │ │ ├── chapters/ │ │ │ ├── chapter_01.md │ │ │ ├── chapter_02.md │ │ └── draft_v1.md │ └── review/ │ ├── review_comments_v1.md │ └── approved_sign.md每个Agent手上的上下文只包含它自己当前负责的那一小段文件路径和摘要信息。评审员审稿时不需要阅读浩浩荡荡的全部资料只需要读writing/draft_v1.md和planning/plan_v1.md再返回修改意见文件就好。这样每个Agent的上下文都保持短小、专业这也是整个系统能不能长久运行的关键。毫不夸张地说共享工作区这个看似不起眼的文件结构设计承担了系统一半以上的稳定性。3.3 工具注册机制让Agent能“动手”而不是只“动嘴”Executor如果只能生成文本那就是个纯聊天机器人干不了实事。所以我给agency加了一个简单的工具注册表让Agent通过函数调用的方式来执行“搜索某某内容”“读取某某文件”“用某某模板渲染”等操作。举一个搜索工具的例子tool_registry { WEB_SEARCH: { description: 搜索特定主题的最新公开信息返回若干条结构化结果, parameters: { query: {type: string, required: True}, limit: {type: integer, default: 5} }, handler: handle_web_search }, FILE_WRITE: { description: 把文本内容写入工作区的指定子目录, parameters: { relative_path: {type: string, required: True}, content: {type: string, required: True} }, handler: handle_file_write } }在系统提示词里我会给相关Agent列出可用工具清单并明确说“需要获取信息时使用WEB_SEARCH工具而不是凭空编造”。这一步是给Executor提供“动手能力”的关键设计也决定了它到底是“只会聊天的嘴”还是“能干活的手”。项目最早期我用的是普通文本格式描述工具但模型偶尔会把参数写错后来改成JSON Schema格式模型返回的调用参数就规范了很多成功率稳步提升。4. 真实跑起来以后一次“内容生产项目”的全程复盘流程搭建好、工具注册完就到了最兴奋也最容易翻车的环节——跑真实任务。我用agency-agents跑了很多个测试其中比较有代表性的是一个“从零写一篇科技产品体验稿”的任务因为它的流程足够长牵扯到的角色多能充分检验系统协作能力。4.1 第一轮尝试整体顺畅但死板Critic给出了“低质量”评分任务原始需求就一句话写一篇关于“某型号智能手表”的体验稿目标读者是运动爱好者要求突出续航和运动监测的实用感受字数3000字左右。Planner把它拆成了五步收集手表基础参数与官方宣传资料、搜索真实用户运动场景反馈、搭建稿件大纲、撰写全文、进行事实核查与风格校准。随后Planner把前两个步骤派给了负责调研的Executor第三个给了“大纲起草”第四、五个给了写手和评审员。第一天跑完整个流程结果是成功的——各部分都顺利产出最后Critic给出的结论是“通过”。但当我真的去读那篇稿子时发现它写得太官方了。全篇都是“具备50米防水”“支持GPS双频定位”这种产品说明书语言完全没有“运动爱好者”期待中的使用体感和场景代入感。问题出在哪我复盘发现调研Agent抓回来的资料几乎全是官方宣传页和厂商通稿真实用户反馈占比太少。我在调研Executor的指令里只说了“搜索真实用户运动场景反馈”却没人定义“真实用户反馈应该从哪些渠道获取”也没说要优先采信论坛、评论区、测评文章里的具体体验描述。4.2 针对“信息源质量”的修复给调研角色加约束条件找到问题后我调整了调研Executor的系统提示词着重加了三条规定关于体验类内容优先采用三类来源第三方评测文章、电商平台的已购用户评价、运动论坛的真实使用讨论。每获取一个观点必须标注来源类型来源不明或仅企业官方宣传的信息要单独归类为“厂商宣传仅供参考”。最终输出的调研简报中至少有70%的内容来自非官方来源否则视为调研不合格需要补充搜索。重新跑一次后调研简报的质量明显提升出现了很多具体细节——比如“某用户反馈在户外跑步时心率监测的准确性跟佩戴松紧度关系很大”“连续使用GPS模式约11小时后电量剩18%”——这些才是写手想要的素材。最终产出的稿子里也终于能看到“跑完一场半马后电量还剩一半”这种有画面感的句子而不再是参数复读机。4.3 踩过的坑Critic打分过低导致循环超时并不是每一次运行都这么顺利。有一轮我测试的是“产品说明书改写为面向普通用户的教程”流程跑了整整四轮Executor改完Critic打“不通过太专业”Executor再改Critic又打“不通过引用参数格式不统一”……如此循环六次直到触发超时机制。观察日志后我找到了病根Critic的评分规则写得不够明确“是否专业”这种主观判断没法给Executor可执行的修改方向。后来我在Critic的系统提示词里把审查项改成了结构化的检查清单每个专业术语第一次出现时是否在同一段落给出了通俗解释每个操作步骤是否按时间顺序编号且步骤数不超过7个是否包含至少一个“常见误区”提示全文是否避免出现三个以上未解释的英文缩写当审查标准从“感觉”变成“清单”修改建议才真正变得可执行。这个经验后来被我沿用到了所有角色的提示词设计里效果相当明显。5. 多Agent协作里的五个暗坑与我的对症下药跑了几十次任务、看了几百条日志之后我总结出多Agent系统里最常见的五个问题。这里不写理论直接说症状和解法每一条都是用调试时间换来的。5.1 暗坑一Agent之间互相“踢皮球”任务陷入死循环症状Planner规划完任务后Executor说“这个步骤需要更多信息才能执行”把问题抛回给PlannerPlanner又让Executor“先根据现有资料做一个初始版本”于是两队消息来回踢流程原地打转。解法我在编排器里加了一个max_iterations计数。针对单个task_id如果某个环节的协调循环达到8次就强制触发“人工接管”事件——把目前的中间产物和双方分歧汇总成一份报告停止自动流转。跑业务系统时我们会说“超时熔断”这里本质也是同一个思想先止损再分析别再让两个模型你来我往地空转。5.2 暗坑二上下文被“历史包袱”拖死症状任务跑到第三轮时某个Executor的prompt里自动拼进来的历史消息已经有好几万字响应速度变慢而且开始出现跟当前步骤毫无关系的记忆碎片——比如写手正在写“第三章”却突然引用起“调研阶段看到的一篇英文论文”。我的处理方式分三层第一层设置每条消息的最大长度超过部分强制截断第二层各Agent独立维护上下文窗口默认只保留当前任务编号下的最近10条消息更早的内容若需要改为通过context_refs文件引用第三层关键规则重新固化在系统提示词里而不是依赖对话历史——比如“你只负责第三章内容相关背景资料请读取指定文件”。这三层下来上下文膨胀的问题基本绝迹。5.3 暗坑三角色越界谁都想当“统筹全局的人”症状Executor明明只负责写段落非要给Planner提意见“我建议调整大纲顺序把第四章提到第二章”。一次两次还好次数多了系统就乱套了。后来我在统一的全局规则里写了一条硬性约束每个Agent只处理分派给它的任务只反馈任务要求的输出。对任务本身以外的任何计划调整、流程优化建议请在定时汇总的“改进建议箱”里提交不要直接改流程。这一条简单粗暴但极其管用。它把角色边界从“自觉遵守”变成了“系统约定”。5.4 暗坑四评审环节变成“无情的退稿机器”这个和前面Critic循环那个坑本质相同但更隐蔽。Critic偶尔会把一些小措辞问题当成大问题要求反复修改导致流程无限拉长。我的对策是给Critic的输出规定了统一格式任何一条修改意见必须有“问题位置问题描述具体修改建议”三要素问题严重级别也只分“阻塞”“非阻塞”两档只有存在“阻塞”级问题时流程才会暂停否则直接进入下一环节。这一招直接砍掉了大量无意义的来回。5.5 暗坑五失败之后没有“B计划”单Agent模式失败重跑一次只需几秒。多Agent系统跑了一堆协作流程某一环挂了想整体重来代价很高。我的做法是给每个环节都设计了“降级处理”如果调研Agent搜索失败自动降级为“使用已有工作区资料”如果写手生成内容为空从草稿缓存中恢复上一版如果调用某工具超时把该步骤标记为“信息不足”并继续推进后续不影响全局的步骤。说白了当你的系统里有多个角色时就要像管理一支小团队那样去设计容错机制不能把每个环节都当作不可替代的核心节点。6. 这套agency模式到底适合什么不适合什么写到这肯定有人会问那我是不是所有任务都应该组个agency跑我的答案很明确不是。搞清楚agency模式的边界很重要否则你会在明明单体Agent就够用的事情上白白浪费大量token和时间。我根据自己的实测经验列了一个判断清单适合切换为多Agent协作的情况更适合保持单体Agent的情况任务有多个明显不同的阶段且各阶段需要不同技能一问一答的交互式对话单个Agent的prompt已经写到2000字以上还嫌不够用任务只需要一次或两次工具调用产出物需要经过严格的质量审查才能交付不需要中间产物用户只看最终结果任务过程中需要依赖外部信息且信息源种类杂、数量大所有知识都在模型本身已充分掌握投入产出比可以接受多跑几轮流程不会拖垮成本对延迟极度敏感需要秒级响应我用这套清单反观自己手头的项目最后总结出一句话agency的价值不是让AI变得“更像人”而是让任务的复杂度跟上下文管理能力解耦。单Agent的问题不在于笨而在于所有复杂度都堆在同一个上下文里多Agent的价值也不在于“人多力量大”而是让每个上下文都保持精简以此对抗大语言模型在长上下文下遗忘和指令遵从度下降的固有缺陷。6.1 什么时候我会果断放弃多代理架构有一次我要做一个很简单的任务——把一段会议纪要里的待办事项提取成结构化清单。我尝试用Planner加Executor双角色跑结果光是消息分派、角色初始化、工作区落盘就花了几十秒而单Agent直接一句“提取重点”快进快出两秒完成。那一次给了我很大的冲击架构是有代价的不是所有问题都需要组织协同。如果你评估下来任务本身只有一步或者一个Agent就能用有限次工具调用搞定那就老老实实用单体Agent省时省力省钱。6.2 规模往上走的自然演化方向等基础版的原型跑稳了我开始思考更大的图景。当然不是说要重写代码而是顺着当前思路往几个方向演化多级agency当前版本还是“单小小队”未来可以让一个“计划部门”整体负责拆解任务把子任务分派给多个二级agency。这样每级内部的协作简单稳定层级之间只需传递目标和检查点复杂度会被有效控制住。动态角色招募现在角色是写死的未来我希望让它像项目制一样灵活——Eve可以根据任务类型动态创建角色给它配工具、配参考资料、设定退出条件任务完成即解散。与外部能力对接给Agent接入更多可调用的工具链——比如表格分析模型、专业绘图模型、数据处理脚本——让“代理机构”里的不同角色真正具备跨模态的生产能力。现在回头看agency-agents这个项目带给我的最大收获不是那几百行能跑的编排代码而是反复验证了一种思考方式与AI协作时与其试图把所有复杂度塞进同一个上下文不如先设计清晰的角色、消息和交接规则让每个上下文都专注于自己真正擅长的那一小件事。就像团队管理一样好系统的标志不是你写了多牛的系统提示词而是谁来接手都能立刻看懂每个Agent在干什么、为谁干、干完交给谁。如果你也在纠结要不要上多Agent架构我的建议是先别急着找框架。拿一张纸画出你现有任务的流程标出需要被不同能力处理的阶段算一算每个阶段的信息量会不会互相污染——然后你再决定是继续用单Agent硬扛还是给它组织一个agency。等你的架构图能画清楚的那一天代码反而是最简单的一步。