
我一度觉得把一个大模型Agent当成万能助手去处理复杂任务这件事本身就是个错误。任务稍微复杂一点它要么忘记前面已经得出的结论要么卡在某个子环节上原地打转甚至一本正经地把完全错误的信息写进最终报告。后来我动手做了一个叫 agency-agents 的项目核心思路很直接与其让一个Agent硬扛全部工作不如做一个Agent的管理者。它借鉴了现实中咨询公司的运作方式——对外统一接单对内把需求拆解后分派给不同专长的执行人最后汇总成一份完整交付。这篇文章我会从为什么需要这个项目、总控与工作层怎么划分职责、任务怎么拆解与传递一直讲到最小可用版本的代码实现、实测踩坑记录和后续扩展方向。如果你正在做AI应用落地或者被单个Agent的长任务处理能力折磨过这篇应该能给你不少可复用的实践经验。1. 单体Agent看起来全能一碰长任务就露馅1.1 单条上下文链承载不了真实世界的并行度我先说一个很常见的场景。让一个Agent完成调研行业现状整理成报告检查格式一致性这类多步骤任务前几步通常表现不错可一旦步骤超过五六个问题就来了它可能在报告的第三部分重复第一部分的结论也可能把前面查到的数据引用错位置。这不是模型能力不够而是单体Agent在结构上有硬伤——所有信息都挤在同一条上下文链里。它每走一步都要把目标、历史过程、中间结果、最新反馈全部重新读一遍。上下文窗口再大也有装不下的那天。你可以把它想象成一个人同时负责接电话、写方案、跑客户和算账中间再穿插五个临时询问最后他大概率会把最早那件事的细节忘得一干二净。1.2 任务一长缺的不是能力是调度我曾经花时间观察一个Agent在长任务里的输出轨迹发现最典型的失败模式有两种一种是遗忘型早期结论被后续信息冲淡另一种是固执型在某个子任务上反复尝试同一套错误做法浪费大量token后才被迫放弃。这两种问题的根源都不是模型笨而是缺少调度。一个串行执行的Agent每一步都要重新拼接全部上下文既没有分工也没有优先级更没有人帮它决定这件事做到什么程度就该收手。多Agent协作的价值恰恰在这里——用多个专职执行者分担认知负担再用一个总控把任务和目标管理起来让专业的人做专业的事。1.3 agency-agents项目的三层边界我一开始也走过弯路以为多个Agent凑在一起就行结果是两个Agent互相纠正到天荒地老。后面把项目收敛成三层边界思路才清晰起来总控层Agency负责接收目标、拆解任务、分派工作、回收结果、校验质量。工作层Agents一组各自具备专长的智能体比如调研型、编码型、审查型。工具层Tools外部能力接口比如搜索、数据库查询、代码执行环境。这三层边界一旦划清楚系统几乎不会乱。总控不参与具体执行工作层不决策全局目标工具层不拥有记忆。各司其职整个系统的行为才可预测。2. Agency总控层与Agents工作层职责划分决定系统上限2.1 总控层像一个项目总监不是一个接电话的前台很多人在设计多Agent系统时容易把总控做成一个传话的用户说什么它就转发给下面的Agent然后等结果。这样的总控没有价值。真正好用的总控有四件事必须亲自负责任务解析、任务拆解、质量校验和结果汇总。任务解析是把模糊的用户诉求翻译成结构化需求任务拆解是根据需求产生一组可执行子任务质量校验是检查每个Agent交回来的结果是否达标结果汇总是把多个子结果合成最终交付。我在代码里把总控实现成一个循环收集输入 → 拆解任务 → 派发子任务 → 等待回执 → 校验结果 → 汇总输出 → 回到收集输入。这个循环本身很简单难点全在拆解和校验这两个环节后面专门讲。2.2 工作层每个Agent是提示词工具私有记忆退出条件的集合工作层的Agent不是简单地把同一个模型复制几份而是要真正角色化。我常用的角色有三种调研Agent擅长搜集资料、总结归纳工具集是搜索接口和网页内容抓取。编码Agent擅长编写和调试代码工具集是代码执行沙箱。审查Agent擅长找漏洞和不一致工具集是静态检查脚本。每个Agent的定义本质是一个配置文件用什么提示词定义角色、挂哪些外部工具、保留哪些私有记忆、满足什么条件就停止工作。这跟我们招人很像——岗位职责明确了才知道该看什么能力、配什么资源。2.3 任务单与回执Agent之间不闲聊只交换合同我踩过最大的一个坑是让Agent之间自由对话。调研Agent说了一段话编码Agent理解成另一个意思然后两个Agent开始来回澄清十分钟过去了任务还没开始。后来我把它们之间的通信全部改成结构化的任务单回执效果立竿见影。一份任务单长这样{ task_id: T-20240521-001, from: agency, to: research_agent_01, intent: collect_materials, payload: { topic: 社区健身角的典型配置, source_scope: [公开报告, 城市规划案例], output_format: markdown_list }, deadline: 2024-05-21 10:00:00, max_retries: 2 }回执则是任务单的补充多了状态字段done/failed/needs_help和结果摘要。这个设计的本质是把自由对话变成合同交互。合同的字段是固定的双方都不用猜对方在说什么出错时也有据可查。2.4 私有记忆和公共黑板必须分开多Agent系统最容易出现的诡异问题就是串味——做需求的Agent引用了做调研的Agent掌握的原始邮件内容调研Agent则可能把编码Agent的中间报错当成事实写进自己的结论。我的解决办法是设置一个公共黑板区和一个私有记忆区。公共黑板只有各Agent写入的结论摘要类似于项目周报私有记忆则存放每个Agent自己的原始材料和推理过程其他Agent不可见。任何Agent要读取公共信息必须通过总控的只读接口谁写了什么、谁读了什么全部留痕。有了这层隔离系统行为才不会像几个人共用同一张办公桌那样混乱。3. 任务拆分和上下文压缩这两个环节决定你能跑多远3.1 子任务粒度怎么定5步原则和3工具原则拆任务是总控最重要的能力之一。拆得太粗子任务仍然复杂到单个Agent处理不了拆得太细调度和上下文交换的成本又会超过任务本身的价值。我实践下来总结了两条判断原则。第一是5步原则一个子任务如果让执行Agent用不超过5次动作就能完成粒度就是合适的如果超过说明还需要继续拆。第二是3工具原则任何子任务需要调用的外部工具类型不超过3种否则就该进一步分化。举个例子调研AI在医疗影像中的应用听起来是一个任务实际上至少应该拆成调研市场现状调研技术路线调研落地案例三个子任务。每个子任务对应的Agent可以专注地使用搜索和网页读取两个工具5步内给出结论。3.2 上下文压缩的三种策略多Agent协作中有一对永恒的矛盾信息太少Agent没法干活信息太多上下文迅速膨胀。我常用三种压缩策略组合。第一种是摘要重写。子任务执行完Agent不只交原始结果还必须交一份不超过两百字的摘要。总控只把摘要放进公共区域原文留在私有存储里需要全文时再按需读取。第二种是选择性遗忘。在任务链条中很多中间推理过程是没有保存价值的。总控在分派下一轮任务时只保留与后续工作相关的结论明确丢弃无关的中间过程。这很像我整理笔记的习惯——只留下所以是什么不记当时怎么想的。第三种是外部存储替身。数据量大的中间结果比如整份报表、日志文件、代码输出统一存到外部文件或数据库里上下文里只保留一个类似路径指针的东西。上下文永远轻量数据永远完整。3.3 失败重试与人工介入不能让它无限循环Agent执行任务免不了失败。失败的应对策略我把它分成四级一级失败输出格式不对或者轻微信息缺失。让原Agent带着错误说明重试一次。二级失败重试一次后仍然失败把任务重新拆细换一个粒度更小的子任务派发。三级失败连续失败换一个Agent角色处理——比如调研Agent搞不定的数据分析交给专门的数据Agent。四级失败多个Agent都失败停住并升级给人工。用户会看到一个明确的需要帮助消息而不是无限循环。这里有个很关键的兜底机制——每个Agent都必须有退出条件。无论任务完成、失败还是遇到歧义都要在一个明确节点结束并上交回执坚决禁止Agent在没有总控指令的情况下自行发起后续动作。这是防止死循环的底线。4. 从零落一个最小可用版本代码级演示4.1 选型Python为主模型统一封装SQLite起步我实现这个项目时语言选的是Python理由很简单调度器和Agent定义需要频繁改动Python的迭代速度最合适生态里关于模型调用、工具封装的基础设施也最丰富。模型端我没有绑定某个特定厂商而是做了一层统一的模型接口。所有Agent只跟这层接口打交道底层模型换哪家、换成多大参数量都不影响上层逻辑。存储端一开始用的SQLite表结构清晰单机场景完全够用等以后任务量和日志量上来了再迁移到独立的数据库也不迟。4.2 核心数据表设计最小可用版本只需要三张表。CREATE TABLE agents ( id TEXT PRIMARY KEY, name TEXT NOT NULL, role_type TEXT NOT NULL, tool_list TEXT NOT NULL, prompt_template TEXT NOT NULL, is_active INTEGER DEFAULT 1 ); CREATE TABLE tasks ( task_id TEXT PRIMARY KEY, parent_task_id TEXT, intent TEXT NOT NULL, payload TEXT NOT NULL, assigned_agent TEXT, status TEXT NOT NULL, result_summary TEXT, retry_count INTEGER DEFAULT 0, created_at TEXT, finished_at TEXT ); CREATE TABLE task_logs ( log_id INTEGER PRIMARY KEY AUTOINCREMENT, task_id TEXT NOT NULL, agent_id TEXT NOT NULL, action TEXT NOT NULL, detail TEXT, created_at TEXT );tasks表里的parent_task_id用来表达任务树result_summary存前面说的摘要task_logs记录每一步动作是后面排查问题的关键依据。4.3 调度主循环一段看得懂的伪代码总控的核心调度逻辑非常直白主循环长这样while True: pending_tasks get_pending_tasks() idle_agents get_idle_agents() if not pending_tasks: maybe_wait_for_new_goal() continue for task in pending_tasks: agent match_agent(task, idle_agents) if agent is None: continue dispatch(task, agent) # 回收完成回执 for receipt in collect_receipts(): task get_task(receipt.task_id) if receipt.status done: validate_result(task, receipt) if passed: mark_finished(task) maybe_merge_result(task) else: requeue(task, reasonvalidation_failed) else: handle_failure(task, receipt)这段代码最重要的一行是match_agent——它根据任务的类型、工具需求和Agent的能力配置做匹配而不是随机派发。如果任务需要代码能力匹配到调研Agent后面大概率要出事。4.4 跑通第一个Demo资料搜集Agent报告撰写Agent我最早跑通的例子是社区健身角改造建议拆解逻辑很简单总控先派调研Agent去搜集典型健身角的配置和社区需求背景收到回执并校验通过后再派报告撰写Agent基于摘要和原始材料输出完整建议。两个Agent各干各的上下文完全不交叉。调研Agent的私有记忆里全是来源网页和摘录报告Agent只看到总控转交的公共摘要。最终报告出来之后我特意比对了一遍——没有串味引用点清晰结构完整。那一刻我才确定这套分层设计是可行的。5. 实测翻车现场五个高发问题与完整排查思路5.1 两个Agent互相打回任务死循环这是我第一次跑多Agent协作时遇到的最严重问题。审查Agent发现编码Agent代码里有问题打回重做编码Agent修改后再交审查Agent又换了一个角度打回。这个循环持续了十几轮直到我手动中断。排查过程我先看task_logs发现两个Agent的action交替出现revise_request和resubmit频率高得吓人。根因是审查标准里有一句确保代码质量这句描述太开放审查Agent永远能找出可以优化的点编码Agent永远要再改一遍。修复方案是给审查Agent增加明确的通过标准只检查是否符合功能要求、是否存在安全漏洞、是否满足性能基线不允许提风格类建议。同时给所有任务加上max_iterations超过三次打回必须升级给总控处理。没有这两个约束多Agent协作就是一场无休止的拉锯战。5.2 Agent上下文串味互相借用不该知道的信息有一次跑需求调研任务负责写需求文档的Agent在结果里引用了另一路调研Agent拿到的原始访谈记录。问题本身不致命但会带来严重的信息污染——因为调研Agent的访谈记录可能是未确认的原始素材直接当事实写进需求文档风险很大。排查时我发现公共黑板区里不仅放了摘要还放了部分貌似有用的原始段落。负责需求文档的Agent一看公共区有现成素材就顺手捡来用了。修复方案就是严格执行公共区只放结论摘要原始材料一律留在私有区同时给需求Agent的提示词里明确写了一句所有引用必须来自任务单附件未经验证的公共信息不得写入最终交付。从那以后这个问题再没出现过。5.3 任务描述太模糊Agent输出一堆等待更多信息有一次我给调研Agent派了分析用户对产品的满意度这个任务结果它交回来的回执写着需要更多信息才能继续。我点开日志一看它确实尝试做了几步然后自己停了下来。根因是任务描述里没给执行者任何约束——调查对象是谁、样本从哪里来、满意度的衡量维度是什么全都没有。Agent面对这种开放性任务最稳妥的做法反而是原地观望。修复办法是在拆任务时强制填充三个字段背景、输入源、输出要求。背景说明为什么做这件事输入源告诉Agent去哪里拿数据输出要求明确交付物的格式。任务单里的payload字段我会反复检查宁可多说几句不让Agent猜。5.4 结构化输出解析失败JSON里总有奇怪的东西多Agent协作必然涉及结构化输出我选的是JSON。实际跑下来模型的JSON输出里出现过中文引号、尾逗号、多余的注释、甚至把字段名翻译成中文的情况。最离谱的一次它在JSON里写了一段解释性文字解析器直接报错。我做了三层防护。第一层是容错解析遇到尾逗号和注释先自动清洗再解析。第二层是一次修复机会把解析错误和错误位置反馈给Agent让它重新生成。第三层是兜底校验如果两次修复仍然失败就把这条子任务标记为一级失败走重试逻辑。三层防护跑下来解析失败导致的空转基本消失了。5.5 并发限流与token预算失控系统里Agent一多对模型API的调用就会立刻遇到两个现实问题并发限流和token成本飙升。我第一次接8个Agent同时跑的时候不到一分钟就被限流后面的一堆任务全部排队排队等待。修复方案分两条腿走。一方面在总控里实现一个令牌桶限制同一时间的调用并发数低优先级的任务自动排到空闲时段。另一方面给每个Agent设置token预算任务启动前先估算大概消耗执行过程中如果超出预算就必须压缩上下文或者提前交回执。成本控制这件事等到账单出来才想起来一定已经超支了。6. 从能跑到稳定可用三个扩展方向最值得投入6.1 先建评测小实验再用数据判断值不值多Agent协作不是万灵药我见过一些简单任务用单体Agent更快更好。所以不要靠感觉优化先做一个小评测实验。我常用的评测设计是同一个任务跑5次单体Agent和5次多Agent版本记录三个指标——完成率是否正确交出了可用的最终结果、关键点遗漏率人工核对结果里是否漏掉原始需求中的重要信息、平均耗时和token消耗。表格整理出来方案值不值一目了然。指标单体Agentagency-agents完成率60%90%关键点遗漏率30%10%平均耗时2分钟4分钟token消耗8k15k这个表格来自我的一次实际测试。耗时和token确实翻了倍但完成率和信息完整度大幅提升对于复杂任务来说这笔开销是值得的。至于简单任务我依然会用单体Agent直接处理不硬上多Agent。6.2 把Agent沉淀成配置模板像搭积木一样复用一开始我把Agent写死在代码里每加一个角色都要改代码、重新测试。后来我把Agent定义全部配置化——提示词模板、工具列表、退出条件、token预算全部写进一个YAML或JSON文件里。agents: research_agent: role_type: researcher tools: [web_search, page_reader] max_iterations: 5 token_budget: 4000 output_schema: summary_v1这样一来新场景只需要组合已有Agent模板快速搭一个新流程。项目跑得越多沉淀下来的角色模板越丰富后续新需求的开发成本就越低。这跟前端工程沉淀组件库是同一个道理。6.3 全链路可观测性task_id串起所有动作没有日志的多Agent系统出问题等于连夜抓瞎。我在系统里跑得最多的排查动作就是对着task_logs表看某一条task_id的完整生命周期。一个典型的日志片段是这样的2024-05-21 09:30:01 T-001 dispatch - research_agent_01 2024-05-21 09:30:12 T-001 action web_search keywordcommunity_fitness_corner 2024-05-21 09:31:02 T-001 action page_reader urlhttps://... 2024-05-21 09:31:30 T-001 receipt done summary_len128 2024-05-21 09:31:31 T-001 validated pass 2024-05-21 09:31:32 T-002 dispatch - writer_agent_ok每个Agent的每次动作都记录在案出错时能精确回溯到谁在什么时间基于什么输入做了什么事。如果后续要做可视化用Task ID作为主键拉出整棵任务树比看代码日志高效太多。6.4 明确边界这些场景不要硬上多Agent最后说说什么场景不适合这个模式。单步骤简单任务比如一句话翻译、简单分类单体Agent一个请求就搞定了引入多Agent完全是浪费。强交互型任务比如需要和用户来回对话确认需求的场景总控拆解反而会把对话切得支离破碎。还有对响应时间极度敏感的场景多Agent的调度延迟可能让人无法接受。说到底架构是服务于场景的不要让方案反过来绑架需求。agency-agents这套思路最适合的是那些目标明确、步骤多、可以并行拆解、又需要高质量交付的复杂任务。做agency-agents这段时间我最大的体会是最难的不是让Agent干活而是想清楚让谁干什么、干到什么程度、怎么交接。总控的拆解逻辑、任务单的结构、上下文的边界每一样都比单个Agent本身的聪明程度更重要。如果你想试着搭一个类似的系统别急着写代码先用手写三张卡片——一张写总控的职责一张写每个Agent的边界一张写任务单的字段。这三张卡片理清了代码怎么写都不会乱。