
1. 从agency-agents这个标题说起一个多智能体协作框架的完整拆解第一次看到agency-agents这个命名我的直觉是这大概率是一个围绕代理agent概念构建的协作型系统而agency这个词放在前面暗示了它强调的不是单个代理的能力而是代理的能动性和代理机构式的组织协作。换句话说这不是一个简单的调用大模型API的玩具项目而是一个试图让多个智能体像一家小型机构一样分工、协作、互相审查、最终交付结果的框架。我在过去两年里陆续搭过七八个多智能体系统从最简单的两个角色对话到后来带工具调用、带记忆、带任务队列的复杂编排踩过的坑基本能写一本小册子。所以看到这个标题我脑子里立刻浮现出一堆问题它的代理是怎么定义的代理之间怎么通信有没有中心调度器任务怎么分解和回收失败重试怎么做这些问题的答案决定了一个多智能体框架到底是能跑demo还是能干活。这篇文章我会把agency-agents当作一个典型的多智能体协作框架来拆解讲清楚它的核心设计思路、关键实现细节、实操落地步骤以及我在类似项目里踩过的坑。不管你是刚接触agent概念的新手还是已经写过几个编排脚本的老手应该都能从里面找到能直接抄作业的东西。核心关键词会围绕多智能体协作、代理编排、任务分解、工具调用、状态管理这几个方向展开这些也是判断一个agency类框架是否合格的核心维度。需要先说明一点下面的很多实现细节是基于这类框架的常见工程实践做的合理补全因为原始信息比较零散我会明确标注哪些是通用做法、哪些是我个人的经验判断方便你对照自己的实际场景做取舍。2. 多智能体协作框架的整体设计与思路拆解2.1 为什么是agency而不是单个agent单个agent的问题在于它什么都得自己干理解需求、拆任务、查资料、写代码、检查结果。这就像一个公司只有一个人既当老板又当员工还当质检能力上限被死死卡住。更麻烦的是单agent在长任务里特别容易跑偏——上下文一长它就开始遗忘早期约束或者陷入自我确认的循环自己写的东西自己觉得没问题。agency这个词的精髓在于组织化。一家真正的代理机构里有接单的人、有执行的人、有审核的人、有统筹的人。多智能体框架要解决的核心问题就是把这套组织关系用代码固化下来。我见过做得好的框架通常会把代理分成几类角色**规划者Planner**负责把大目标拆成子任务**执行者Executor**负责具体干活**审查者Critic/Reviewer**负责挑毛病**协调者Orchestrator**负责调度和汇总。这种分工不是花架子它实实在在解决了单agent的三个死穴上下文过载、缺乏外部校验、任务无法并行。2.2 中心化调度 vs 去中心化协商设计多智能体系统时第一个要拍板的架构决策就是代理之间怎么组织主流有两种路子。一种是中心化调度有一个主控代理或者叫编排器它持有全局任务状态负责给各个子代理派活、收集结果、决定下一步。这种模式的好处是逻辑清晰、状态可控、调试方便缺点是主控容易成为瓶颈而且主控本身如果能力不行整个系统就废了。另一种是去中心化协商代理之间直接对话通过消息传递达成共识没有单一控制点。这种模式理论上更灵活、更接近真实组织但实际落地时状态极难追踪容易出现两个代理互相等对方的死锁调试起来让人抓狂。我的经验是绝大多数生产场景应该选中心化调度。原因很现实——你需要可观测性。当系统跑出问题时你得能一眼看出是哪个环节卡住了。去中心化系统里问题可能藏在任意两个代理的对话历史里排查成本高得离谱。agency-agents这类框架如果做得好通常会提供一个显式的编排层把谁在什么时候做什么记录得清清楚楚。2.3 任务分解的粒度控制任务分解是多智能体框架里最考验设计功力的地方。分得太粗子任务还是太大执行者照样跑偏分得太细代理之间通信开销爆炸而且容易丢失任务之间的依赖关系。我一般遵循的原则是每个子任务应该能在一次独立的代理调用里完成且产出物是可验证的。比如写一个用户登录接口就太粗因为它包含设计、编码、测试多个环节而把这段伪代码翻译成Python函数就合适因为输入输出明确结果对错一眼能看出来。框架层面通常会用一个规划代理先输出一个任务列表JSON格式每个任务带id、描述、依赖的前置任务id、预期产出格式。这个结构化的中间产物非常关键它是后续调度和校验的基础。2.4 状态管理与上下文传递多智能体系统里状态管理是最容易被低估的部分。每个代理都有自己的上下文窗口但任务的整体状态必须有一个统一的存储。常见做法是维护一个共享状态对象Shared State里面包含原始目标、任务列表及各自状态、每个任务的产出、全局的中间结论。代理在执行时只从共享状态里取自己需要的那部分执行完再把结果写回去。这里有个关键技巧不要把所有历史都塞给每个代理。我早期犯过这个错把完整的对话历史传给每个子代理结果token消耗巨大而且代理被无关信息干扰表现反而变差。正确做法是给每个代理做上下文裁剪——只传它当前任务相关的输入和必要的前置产出。这个裁剪逻辑往往是一个框架好不好用的分水岭。3. 核心细节解析与实操要点3.1 代理角色的定义与提示词工程每个代理本质上就是一段系统提示词 一个模型 一组可用工具。听起来简单但提示词的质量直接决定系统上限。我总结下来一个好的代理定义应该包含四个部分角色身份、能力边界、输出格式约束、失败处理指引。角色身份要具体不能只说你是一个助手而要说你是一个负责代码审查的资深工程师只关注逻辑错误和边界条件不关注代码风格。能力边界要明确告诉它你不能做什么比如审查代理不应该去改代码只输出问题清单。输出格式约束最好用结构化格式JSON或固定Markdown模板方便下游程序解析。失败处理指引则是告诉它如果信息不足不要瞎猜直接输出NEED_MORE_INFO并说明缺什么。提示代理提示词里一定要有不确定就说不确定的指令。我见过太多系统因为执行代理硬编了一个错误答案导致后续所有环节全盘皆输。3.2 代理间通信协议的设计代理之间怎么传消息是个看似简单实则讲究的事。最朴素的做法是传自然语言字符串但这样下游解析起来很痛苦。我的建议是结构化消息 自然语言补充的组合核心字段用JSON比如task_id、status、result、confidence需要解释的部分放在一个description字段里用自然语言写。消息里必须包含的几个字段发送方、接收方、关联的任务id、消息类型请求/响应/通知、时间戳。别小看时间戳调试并发问题时它是救命稻草。另外消息要有明确的终止信号否则调度器不知道一个任务什么时候算真正结束。我通常用status字段的枚举值来标记PENDING、RUNNING、DONE、FAILED、NEED_REVIEW。3.3 工具调用的封装与权限控制多智能体系统里工具调用是最容易出安全事故的地方。一个执行代理如果能随意调用文件写入、网络请求、命令执行那它一旦跑偏破坏力是惊人的。所以工具必须做权限分级规划代理通常不需要工具只需要模型推理执行代理按任务类型分配最小必要工具集审查代理一般只读不写。工具封装上我强烈建议每个工具都做三件事输入校验、超时控制、结果截断。输入校验防止代理传进来乱七八糟的参数超时控制防止某个工具卡死拖垮整个系统结果截断防止工具返回超长内容把上下文撑爆。这三点看着基础但真正做全的框架不多而它们恰恰是系统稳定性的关键。3.4 循环控制与终止条件多智能体系统最怕的就是无限循环——A等BB等A或者审查代理一直不满意执行代理一直改改到天荒地老。所以必须设置硬性的终止条件。我一般会设三层保险单任务最大重试次数比如3次、全局最大步数比如50步、全局超时时间比如10分钟。任何一层触发系统就强制收敛输出当前最好的结果并标记为未完全完成。这里有个经验重试次数不要设太高。我试过设5次重试结果发现第3次之后代理基本就是在原地打转改来改去还是那些问题。3次是个比较甜的点超过3次还没解决说明要么任务本身有问题要么需要人工介入。4. 实操过程与核心环节实现4.1 环境准备与依赖选型搭一个agency-agents类的系统基础依赖其实不多。核心是一个支持函数调用function calling的模型接口、一个状态存储初期用内存字典就够生产环境上Redis或SQLite、一个日志系统这个千万别省后面全靠它排查问题。模型选型上我的建议是规划用强模型执行可以用稍弱的模型。因为规划环节对推理能力要求最高一旦规划错了后面全错而执行环节往往是相对确定的工作弱一点的模型也能胜任这样能显著降低成本。工具层面如果涉及代码执行一定要用沙箱隔离别直接在宿主机上跑代理生成的代码。# 一个最小化的代理定义示例 class Agent: def __init__(self, name, role_prompt, tools, model): self.name name self.role_prompt role_prompt self.tools tools self.model model self.history [] def run(self, task_input, shared_state): # 上下文裁剪只取相关部分 context self._build_context(task_input, shared_state) response self.model.chat( systemself.role_prompt, messagescontext, toolsself.tools ) return self._parse_response(response)4.2 编排器的核心逻辑实现编排器是整个系统的心脏。它的主循环大致是这样从任务队列取出一个就绪任务所有前置依赖已完成找到对应的执行代理调用它拿到结果更新共享状态然后判断是否需要审查、是否需要触发后续任务。def orchestrate(goal, shared_state, max_steps50): planner Agent(planner, PLANNER_PROMPT, [], strong_model) tasks planner.run(goal, shared_state) # 输出结构化任务列表 shared_state[tasks] tasks step 0 while step max_steps: ready [t for t in tasks if t.status PENDING and all_deps_done(t, tasks)] if not ready: break task ready[0] executor pick_executor(task) result executor.run(task, shared_state) task.result result task.status DONE if result.ok else FAILED if task.needs_review: reviewer Agent(reviewer, REVIEWER_PROMPT, [], strong_model) review reviewer.run(task, shared_state) if not review.passed: task.status PENDING # 打回重做 task.retry_count 1 if task.retry_count 3: task.status FAILED step 1 return aggregate_results(tasks)这段代码看着简单但每一行背后都有讲究。比如pick_executor这个函数它要根据任务类型路由到不同的执行代理这个路由表的设计直接影响系统的灵活性。再比如审查环节不是所有任务都需要审查简单任务审查反而拖慢速度所以要有个needs_review的标记逻辑。4.3 一个完整的任务执行实例假设我们要让系统完成分析一份销售数据并生成报告这个目标。规划代理会先把它拆成加载数据、清洗数据、计算关键指标、生成图表、撰写报告文字、整合成最终文档。然后执行代理逐个完成其中计算关键指标和撰写报告这两个环节会触发审查代理介入。我在实际跑这类流程时发现数据加载和清洗环节最容易出问题因为真实数据往往有各种脏东西。所以我会给执行代理配上数据探查工具让它先看一眼数据长什么样再动手。这个先探查再执行的模式能显著降低后续环节的返工率。4.4 结果聚合与输出所有任务完成后需要一个聚合环节把零散的产出拼成最终交付物。这个环节看似简单其实也有坑不同任务产出的格式可能不一致需要做归一化有些任务的产出是中间结果不应该出现在最终输出里还有些任务可能失败了聚合时要优雅降级而不是直接报错。我的做法是让规划代理在最初拆任务时就为每个任务标注一个output_role字段标明它是最终产出还是中间产物。聚合时只取最终产出的部分按预设顺序拼接。这样即使中间某个环节失败只要核心产出还在最终结果依然可用。5. 常见问题与排查技巧实录5.1 代理跑偏了怎么办这是最高频的问题。表现是执行代理输出的东西跟任务要求对不上或者审查代理挑了一堆无关紧要的毛病。排查思路是先看传给代理的上下文是不是有问题是不是混入了无关信息再看提示词是不是有歧义最后看任务描述本身是不是太模糊。我的经验是80%的跑偏源于任务描述不清晰。比如优化这段代码代理根本不知道优化目标是什么是提速、是省内存、还是提高可读性改成在不改变功能的前提下把这段代码的时间复杂度从O(n²)降到O(n)代理立刻就明白了。所以与其在提示词上反复调不如先把任务描述写清楚。5.2 系统卡死或无限循环前面提过一定要设硬性终止条件。但除了终止条件还要能定位卡在哪里。我的做法是给每个代理调用都打上详细的日志输入是什么、输出是什么、耗时多久、调用了哪些工具。当系统卡住时看日志就能发现是哪个代理在反复重试或者哪两个代理在互相等待。有个隐蔽的坑是依赖环。规划代理拆任务时可能不小心拆出A依赖B、B又依赖A的情况这时候调度器永远找不到就绪任务系统就静默卡死了。解决办法是在任务列表生成后先做一次拓扑排序检测发现环就报错让规划代理重做。5.3 成本失控多智能体系统的token消耗是单agent的好几倍因为每个代理都要带上下文还有审查环节的额外开销。我见过一个项目跑一个任务烧掉几十万token成本高得吓人。控制成本的关键是上下文裁剪和模型分级。上下文裁剪前面说过只传必要信息。模型分级则是把简单任务交给便宜模型。我一般会统计每个环节的token消耗找出消耗大户重点优化。通常规划环节和审查环节是消耗大头因为这两个环节需要看全局信息。执行环节如果任务拆得够细每个子任务的上下文其实很小。5.4 常见问题速查表问题现象可能原因排查方向解决建议代理输出格式错误提示词格式约束不明确检查系统提示词用JSON schema强约束任务反复重试任务描述模糊或超出能力看重试日志细化任务或换更强模型系统静默卡死依赖环或就绪任务为空检查任务依赖图加拓扑排序检测成本异常高上下文未裁剪统计各环节token裁剪上下文模型分级审查代理过于苛刻审查标准不明确看审查提示词明确审查维度和阈值工具调用失败参数校验缺失看工具调用日志加输入校验和重试5.5 几个我踩过的坑第一个坑是过度信任审查代理。我早期设计时审查代理说不行就打回重做结果审查代理有时候会挑一些主观性的问题导致执行代理反复改却永远过不了。后来我改成审查代理必须给出具体的、可验证的问题比如第15行的变量未定义而不是代码质量不高这种模糊评价。第二个坑是忽略了代理的个性。同一个提示词不同模型的表现差异很大。有的模型倾向于过度解释有的倾向于直接给答案。在多智能体系统里这种差异会被放大。所以选模型时不能只看单次任务表现要看它在整个协作流程里的稳定性。第三个坑是没有做幂等设计。系统重试时如果某个工具调用不是幂等的比如发邮件、写数据库重试就会造成重复操作。所以所有有副作用的工具都要做幂等处理比如用任务id做去重键。6. 工具选型与扩展方向6.1 状态存储的选型小规模实验用内存字典完全够用但一旦要支持断点续跑或者多进程就得上持久化存储。SQLite适合单机场景轻量且无需额外服务Redis适合需要快速读写的场景但要注意持久化配置如果任务量很大可以考虑PostgreSQL这类关系库方便做复杂的任务查询和统计。我的建议是从SQLite起步它的迁移成本最低等真的遇到性能瓶颈再换。别一上来就上重型方案那是给自己找麻烦。6.2 可观测性建设多智能体系统的调试难度远高于单agent所以可观测性必须从第一天就建。最低要求是结构化日志每个代理调用记录代理名、任务id、输入摘要、输出摘要、耗时、token消耗、工具调用列表。进阶一点可以接一个追踪系统把整个任务的执行链路可视化出来。我个人的习惯是每跑一个新任务先看日志确认流程符合预期再看结果。很多时候结果不对但流程是对的那问题就在某个代理的输出质量上如果流程都不对那就是编排逻辑有问题。这个二分法能快速缩小排查范围。6.3 后续可以扩展的方向这套框架搭起来之后有几个自然的扩展方向。一是加入长期记忆让代理能记住之前处理过的类似任务避免重复劳动。二是支持人工介入在关键节点暂停等待人工确认这在生产环境里很实用。三是多任务并行把没有依赖关系的任务并发执行能大幅缩短总耗时。四是代理能力自评估让代理在接任务前先判断自己能不能做做不了就上报避免硬着头皮瞎做。我个人在实际操作中的体会是多智能体系统的价值不在于炫技而在于它能把一个复杂任务拆解成可管理、可验证、可重试的小步骤。真正决定成败的往往不是模型有多强而是任务拆得够不够细、上下文管得够不够干净、失败处理做得够不够稳。把这几点做扎实了哪怕用的是中等能力的模型系统也能跑出靠谱的结果。反过来如果这些工程细节没做好再强的模型也救不了你。