1. 项目全貌:先弄清“agency-agents”到底在解决什么
做Agent开发这一年多,我踩过最大的坑不是模型选型,而是单Agent扛不住复杂任务。你给它一个“做个市场调研并输出报告”的需求,它要么在前两步就钻进细节出不来,要么生成一份结构完整但内容全靠编的漂亮文档。后来我和团队搭了一套叫“agency-agents”的多智能体协作系统,核心思路不复杂——把一组大模型驱动的Agent组织得像一家微型代理事务所那样运转:有人接单拆解,有人分头干活,有人专门挑毛病返工。这篇文章就把这套项目的设计思路、踩坑实录和可直接抄的配置方式完整写出来。
先解释下名字。“agency”在英文里有双重含义:一是“代理”,对应Agent本身;二是“代理机构”“事务所”,对应这套系统的组织形态。所以agency-agents翻译过来就是“让Agent们像事务所一样协作”的编排框架,而不是单个智能体的增强。它解决的核心问题是:当任务的复杂度超出单一模型上下文窗口和推理能力时,如何通过多个Agent的分工、流转、互检,把整体输出质量拉回可用水平。
什么人适合参考这套方案?
- 已经在用LangChain、CrewAI或自研框架做Agent应用,但发现单个Agent处理端到端任务时质量不稳定的开发者;
- 想给团队搭建“AI员工小组”的产品和技术负责人,需要一套角色分工、任务流转、质量验收的工程化思路;
- 被“多Agent协作”这个概念打动,但不知道从哪下手、担心做成玩具项目的初学者。
这套体系跑通以后,最直观的变化是:**同样一份行业分析报告任务,之前单个Agent产出的东西只能当草稿,现在这个“虚拟事务所”交出来的东西基本能直接进评审流程。**下面从设计思路开始拆。
1.1 单Agent的瓶颈:为什么单独一个大模型智能体不够用
先说个扎心的事实:大模型Agent的失败模式是高度可预测的。
我做过一个对比实验,用同一个模型分别以“单个Agent”和“多Agent协作”两种方式完成一个竞品分析任务。单Agent模式暴露的问题非常稳定:
- 任务分解靠“感觉”:模型拿到“分析竞品”这种开放性指令,自己拆出来的子任务经常是“研究市场”“分析功能”“写报告”这种维度混杂的列表,而不是真正可执行的工作流。
- 上下文单向膨胀:单Agent要把所有中间结果都塞在自己的上下文里,跑完三步以后,前面两步的有用信息往往被后面的内容挤掉,或者被模型自己遗忘。
- 没有质检回路:模型生成完直接交付,错别字、数据编造、逻辑断层没人拦。你让模型自己“检查一下”,它大概率回你一句“整体良好,无需修改”。
- 单点故障:一旦某一步工具调用失败或者返回格式异常,整个任务链直接崩掉,前面的工作全白费。
这四点单拎出来都能绕过去,但叠在一起就很致命。尤其当你把Agent从“玩一玩”推向“真正干活”的时候,出错的代价不再是一次对话的尴尬,而是业务方的信任流失。
1.2 “agency”模式的核心:把智能体组织成一个微型机构
我参考了现实中小型咨询公司的运作方式,把Agent分成了三类角色:
- 主管Agent(Dispatcher):负责接需求、拆任务、派活、汇总。它不直接产出内容,只做决策和调度。
- 执行Agent(Worker):按主管分配的子任务干活,比如文献检索、数据分析、初稿撰写。一个任务可以对应多个Worker并发执行。
- 质检Agent(Inspector):对Worker的产出做验收,从事实准确性、格式规范性、逻辑完整性三个维度打分。不达标就打回重做,并附上具体修改意见。
这套结构对应到代码层面,就是三条核心原则:
- 角色隔离:每个Agent有自己的系统提示词、工具白名单和输出格式约束,不允许越权。
- 任务作为一等公民:任务不再是一段自然语言指令,而是结构化的JSON对象,包含任务ID、目标、约束、依赖关系和验收标准。
- 强制质检节点:任何任务在进入下一环节之前,必须经过质检Agent的验收,验收不通过不允许流转。
你可能会问:多一次质检不就多一次模型调用、多花一份token钱吗?确实是,但质检Agent发现的问题,远比它多花的钱值钱。后面实测部分我会给出具体成本数据。
1.3 项目边界与目标:什么样的任务适合这种架构
做了这版以后,我总结了三类“天生适合agency-agents模式”的任务:
- 研究分析类:行业调研、竞品拆解、政策解读。这类任务天然可以拆成“资料收集→信息整理→观点提炼→报告输出”四段,而且每段之间都有明确的验收点。
- 内容生产类:长文写作、SEO文案矩阵、课程大纲设计。单个Agent写八千字的长文,写到后面经常忘了前面的论点;拆成“提纲→分章节撰写→统稿润色”以后,每一章的质量都能被单独评审。
- 代码任务类:功能模块开发、Bug修复分析。主管拆需求,Worker写代码,质检Agent先静态审查再决定要不要进入测试环节。
不适合的也有三类:实时聊天对话(多一跳调度就有延迟)、简单确定性操作(查个天气还要走一遍拆单派单纯属浪费)、强依赖隐性上下文的场景(比如客服对话里客户只说了半句话,剩下全靠脑补)。
2. 方案选型与核心设计思路
2.1 编排框架的取舍:自研还是用现成框架
项目启动的时候,团队内部先吵了一轮:是直接用CrewAI、AutoGen这些现成框架,还是自己写编排层?
我的结论是:**框架可以拿来当参考,但核心调度必须自己掌控。**原因有三:
- 现成框架的角色模型(比如CrewAI的Agent+Roles)确实好用,但它们的抽象层次偏高,遇到“需要给不同Agent配置不同模型”“需要精确控制任务依赖关系”这类需求时,反而要绕开框架做一堆hack。
- 多Agent系统的瓶颈从来不在“能不能对话”,而在“状态怎么管”。现成框架大多把状态存在内存会话里,一旦进程重启,任务跑到一半就丢了。我们需要的是可持久化的任务状态机。
- 质检回路的控制逻辑非常个性化。你希望质检Agent看到什么东西、按照什么标准打分、打回时携带什么反馈信息,这些细节框架很难覆盖周全。
最终我们选择了自研一个轻量调度层 + 复用成熟组件的组合:调度层用Python写,任务状态存SQLite,Agent调用层直接封装各家大模型API,数据插桩用现成的向量库。这套组合的好处是灵活性和可控性都在自己手里,坏处是前期开发量会多出两三天——但这两三天省下来的,是后面调试协作逻辑时无数的弯路。
2.2 角色设计:主管、执行者、质检者的分工逻辑
三大角色的提示词设计是整个项目里性价比最高的一步。我见过太多项目把Agent提示词写得像使用说明书,结果模型根本不按套路出牌。我们的做法是把每个角色的系统提示词压缩成“身份+职责边界+输出格式+禁区”四个模块。
以主管Agent为例,它的系统提示词核心长这样(简化版):
你是一个项目经理型Agent,负责接收任务、拆解子任务、分配给执行Agent,并在最终环节汇总产出。 职责边界: - 你可以调用“任务拆解工具”和“任务分配工具”。 - 你无权直接调用任何数据检索、内容生成相关的工具。 - 你必须为每个子任务指定验收标准,字段为acceptance_criteria。 输出格式: - 所有决策必须以JSON格式输出,包含task_id, subtasks, assignments三个字段。 禁区: - 禁止输出自然语言格式的任务描述,所有任务描述必须是结构化字段。 - 如果用户需求不明确,输出status: "need_clarification",禁止擅自猜测需求。这里最关键的三个设计是:
- 不让主管Agent干活——它一旦能调用数据分析或内容生成工具,马上就会忍不住自己动手,整个协作结构就会退化成单Agent。
- 强制结构化输出——任务描述必须带验收标准,这一步把“模型含糊其辞”的空间压缩到最小。
- 明确“不清楚就说不知道”的选项——很多Agent系统翻车是因为模型在需求不明确时强行开工,这一条从Prompt层面做了硬约束。
执行Agent的提示词设计则更侧重“查询参数规范化”和“中间结果结构化”。比如它被要求:所有查询请求必须拆分出source、time_range、keywords三个字段,缺一不可;所有检索结果必须按summary + raw_data_links的格式返回。质检Agent的提示词则是一张打分卡,后面会专门展开。
2.3 通信与状态:任务看板与消息协议
多Agent系统最容易被忽视的就是通信协议。如果让两个Agent直接用自然语言对话,你很快就会遇到三个鬼故事:模型开始寒暄、模型开始重复对方的话、模型开始自作主张替对方做决定。
我们为系统定义了一套“任务看板协议”,所有Agent之间的通信都通过结构化消息完成,消息格式长这样:
{ "message_id": "msg_20250112_001", "from": "dispatcher", "to": "worker_web_research", "type": "task_assignment", "payload": { "task_id": "task_20250112_001", "objective": "收集过去一年内行业TOP10公司的融资信息", "constraints": { "source_type": ["news", "official_announcement"], "time_range": "2024-01-01_2024-12-31", "output_format": "json_array" }, "acceptance_criteria": [ "每家公司须包含公司名称、融资金额、融资轮次、投资方列表", "融资信息必须附带原始链接" ], "depends_on": [] } }这套协议的核心价值不在于格式有多漂亮,而在于:
- 消息类型只有四种:
task_assignment(分配任务)、task_result(提交结果)、task_review(质检反馈)、task_update(状态变更)。Agent之间不允许传其他类型的消息,从机制上杜绝了“聊着聊着跑题”的问题。 - 所有消息进入持久化存储:也就是所谓“任务看板”,每个消息带时间戳和状态字段,方便回溯。
- 依赖关系显式声明:如果任务B依赖任务A的结果,B的消息里会带上
depends_on: [task_A],调度器据此决定执行顺序。
我在项目文档里给这套模式起了个外号,叫**“哑巴通信”**——Agent之间不做任何闲聊式对话,只交换结构化工件。实践下来,这个设计对最终系统稳定性的贡献,比任何一个精心调过的Prompt都大。
3. 核心实现拆解:配置、提示词与调度逻辑
3.1 角色配置与质检打分卡
前面提到质检Agent是整个项目质量保障的枢纽,我把它的输出设计成“打分卡”形式。质检Agent每次验收一个任务结果,必须输出如下JSON:
{ "review_id": "review_20250112_001", "task_id": "task_20250112_001", "decision": "pass|rework", "scores": { "factual_accuracy": 85, "format_compliance": 90, "logical_coherence": 70 }, "issues": [ { "type": "missing_citation", "severity": "major", "description": "融资信息中提及某公司完成2亿美元融资,但未附原始新闻链接", "suggestion": "补充官方新闻源或工商变更记录链接" } ], "feedback_summary": "有两条融资信息无法溯源,且结论段存在前后矛盾,建议返工。" }这个打分卡有几个细节值得讲:
decision字段只有两个值:pass或rework,没有“勉强通过”这个中间态。实际运行中,一旦允许“勉强通过”,质检Agent很快会变成和稀泥的角色,所有结果都会以低质量糊弄过去。issues必须结构化:每条问题要有类型、严重程度和修改建议。质检Agent只输出“此处不完整”这种模糊反馈没有任何价值,我们的经验是:如果质检反馈里没有给修改建议,这个任务90%会进入“返工-提交-再返工”的死循环。- 严重程度分级:
major级别的问题强制返工,minor级别的可以带问题流转,但进入最终汇总阶段时由主管Agent统一决定是否修复。这个分级避免了质检环节陷入无休止的细节打磨。
3.2 任务拆分与分配策略
主管Agent的任务拆解能力直接决定了整个系统的上限。最初版本里,我让主管Agent自由发挥拆分逻辑,结果它经常拆出“维度重叠”的子任务——比如同时拆出“分析市场规模”和“评估用户群体规模”,两个Worker找到的数据一半重叠。
后来我引入了一个“任务分解检查器”,本质上是一个辅助Agent,专门审查主管Agent拆分出来的任务列表是否符合三个约束:
- 互斥性:任意两个子任务的目标不能有超过20%的重叠。
- 穷尽性:所有子任务的并集必须覆盖原始任务的目标。
- 可验证性:每个子任务必须能被独立的验收标准量化衡量。
有了这个检查器,任务拆解质量明显提升。另外还有一个实用小技巧:分配任务时,不要只给Worker一个目标,要同时给它“已经有谁做过什么”的共享背景。这能大幅减少两份子任务重复检索同一批数据的情况。
3.3 上下文管理与记忆截断
多Agent系统的上下文管理比单Agent要复杂一个量级,因为每个Agent都有自己的上下文窗口,而且它们还会共享同一个“记忆库”。我们踩过最大的坑是共享记忆库无限制增长——跑了一天以后,每个Agent的上下文窗口里都塞满了和当前任务无关的历史记录,推理速度肉眼可见地变慢。
最终采用的方案是“三层记忆截断”:
- 工作记忆:只保留当前任务相关的消息,任务完成后清空,但消息会归档到持久化存储。
- 共享记忆:存任务级产物、关键结论和引用来源,每条记录打上时间戳和任务编号,满100条自动按时间淘汰最老的20条。
- 长期记忆:只有系统最终交付的产出会写入这里,比如最终报告全文、关键数据摘要。
这个设计的好处是:主管Agent从长期记忆里拿“大图”,Worker从工作记忆里拿“细节”,质检Agent在必要时可以回溯共享记忆做溯源核对。各取所需,互不干扰。
3.4 工具注册与权限控制
最后讲一个容易被新人忽略的点——工具权限。在多Agent架构里,给所有Agent都暴露全部工具是大忌。我们遇到过的一次事故是:一个负责写文案的Worker顺手调用了“发送邮件”工具,差点把不成熟的初稿发给客户。
现在的工具注册机制是一个白名单字典,每个角色对应一组可用工具:
| 角色 | 可用工具列表 | 不可用操作 |
|---|---|---|
| 主管Agent | 任务拆解、任务分配、最终汇总、状态查询 | 数据检索、内容生成、外部通信 |
| 执行Agent(检索类) | 网页检索、向量库查询、数据抓取 | 内容生成、任务拆分、外部通信 |
| 执行Agent(写作类) | 大纲生成、章节撰写、格式转换 | 数据抓取、任务拆分 |
| 质检Agent | 文本审阅、格式检查、引用核对 | 内容生成、数据检索、外部通信 |
代码层面就是一个简单的装饰器路由:
TOOL_WHITELIST = { "dispatcher": ["split_task", "assign_worker", "merge_result"], "worker_researcher": ["search_web", "query_db", "scrape_page"], "worker_writer": ["generate_outline", "write_section", "convert_format"], "inspector": ["review_text", "check_format", "verify_citation"] } def check_tool_access(agent_role, tool_name): if tool_name not in TOOL_WHITELIST.get(agent_role, []): raise PermissionError( f"Agent {agent_role} 无权调用 {tool_name}," f"当前角色仅允许调用 {TOOL_WHITELIST[agent_role]}" )别小看这个简单的白名单,它阻止的问题比任何Prompt调优都多——当系统里有五个Agent、每个Agent能调用十个工具的时候,组合出来的权限空间已经非常巨大,不掐死这个口子,安全性和稳定性都是纸上谈兵。
4. 实操过程:从零搭一个最小可用体系
4.1 项目骨架与依赖
以下是我们跑通完整流程时的最小依赖清单。所有关键逻辑都在core目录下,约600行Python代码,外加几个JSON配置文件。
agency_agents/ ├── core/ │ ├── message_bus.py # 消息总线,所有Agent通信走这里 │ ├── task_store.py # 任务看板(SQLite持久化) │ ├── dispatcher.py # 主管Agent入口 │ ├── worker.py # 执行Agent基类 │ ├── inspector.py # 质检Agent入口 │ └── llm_client.py # 大模型API统一封装 ├── config/ │ ├── agents_config.json # 角色与工具白名单配置 │ └── prompts/ │ ├── dispatcher_prompt.txt │ ├── worker_prompt.txt │ └── inspector_prompt.txt └── main.py # 系统入口依赖库就三个:openai(大模型调用)、sqlite3(标准库,任务状态存储)、requests(网页检索调用)。不用消息队列、不用Redis、不用Docker——第一版跑通之前,任何多余的组件都是负担。
4.2 关键代码片段:消息总线的实现
消息总线是整个系统的“血管”。我实现它的策略是:先写一个send_message函数处理格式校验和持久化,再让所有Agent通过这个函数通信。
import json import sqlite3 from datetime import datetime DB_PATH = "task_store.db" class MessageBus: def __init__(self, db_path=DB_PATH): self.conn = sqlite3.connect(db_path, check_same_thread=False) self._init_table() def _init_table(self): self.conn.execute(""" CREATE TABLE IF NOT EXISTS messages ( message_id TEXT PRIMARY KEY, from_role TEXT NOT NULL, to_role TEXT NOT NULL, type TEXT NOT NULL, payload TEXT NOT NULL, status TEXT DEFAULT 'pending', created_at TEXT NOT NULL ) """) self.conn.commit() def send_message(self, from_role, to_role, msg_type, payload): # 核心校验:只允许四种消息类型 assert msg_type in ("task_assignment", "task_result", "task_review", "task_update") message_id = f"msg_{int(datetime.now().timestamp() * 1000)}" record = { "message_id": message_id, "from_role": from_role, "to_role": to_role, "type": msg_type, "payload": json.dumps(payload, ensure_ascii=False), "status": "pending", "created_at": datetime.now().isoformat(), } self.conn.execute( "INSERT INTO messages VALUES (?, ?, ?, ?, ?, ?, ?)", tuple(record.values()) ) self.conn.commit() return message_id def get_pending_tasks(self, to_role): cur = self.conn.execute( "SELECT * FROM messages WHERE to_role=? AND status='pending'", (to_role,) ) return [(row[0], row[2], row[3], json.loads(row[4])) for row in cur.fetchall()]这段代码里的assert不是摆设,它是防止Agent通信“跑味”的最后一道防线。如果哪天某个Agent试图传出casual_chat类型的消息,系统会直接抛异常,这种问题暴露得越早越好。
4.3 主管Agent的调度逻辑
主管Agent不直接调用大模型生成内容,它的核心是一个“决策循环”:读取待处理任务,调用大模型拆解,校验拆分结果,然后分发。我这里用一个简化版的调度循环展示核心思路:
def dispatcher_loop(msg_bus: MessageBus, llm_client): pending = msg_bus.get_pending_tasks("dispatcher") for msg_id, from_role, msg_type, payload in pending: if msg_type == "task_update" and payload.get("status") == "all_completed": # 所有子任务完成,进入最终汇总 final_report = llm_client.generate_report( system_prompt=DISPTACHER_REPORT_PROMPT, sub_results=payload.get("sub_results", []) ) # 汇总报告也要过质检 msg_bus.send_message( "dispatcher", "inspector", "task_review", {"report": final_report, "task_id": payload["task_id"]} ) continue if msg_type == "task_assignment" and from_role == "user": # 拆解任务 split_result = llm_client.split_task( system_prompt=DISPTACHER_SPLIT_PROMPT, user_request=payload["objective"] ) # 校验拆分结果是否满足互斥性/穷尽性/可验证性 check_result = llm_client.check_split( system_prompt=SPLIT_CHECKER_PROMPT, split_result=split_result, original_request=payload["objective"] ) if not check_result["is_valid"]: # 拆分不合格,重新拆,最多重试两次 continue # 分发子任务给合适的Worker for sub in split_result["subtasks"]: worker_role = route_to_worker(sub["type"]) msg_bus.send_message( "dispatcher", worker_role, "task_assignment", sub )主线逻辑不复杂,复杂的是边界条件:任务拆解校验失败怎么处理、某个Worker迟迟不返回怎么超时、子任务间存在依赖时需要等哪个先完成。这些细节我在后面“常见问题”章节里展开。
4.4 一次完整任务流转的现场记录
拿一个测试任务举例:“调研2024年国内AI写作工具市场的竞争格局,输出一份3000字分析报告”。
系统实际跑完一次任务,消息流转是这样:
- 用户提交需求→ 消息类型
task_update,状态new,发送给主管Agent。 - 主管拆解→ 输出4个子任务:
- 子任务A:检索市场规模与增长数据;
- 子任务B:梳理头部产品的功能对比;
- 子任务C:整理用户评价与口碑数据;
- 子任务D:分析行业趋势与潜在机会。
- 分发→ 主管将A和C发给“检索Worker”,B发给“对比分析Worker”,D发给“趋势研判Worker”。
- 三个Worker并行执行→ 各自返回结构化中间结果。
- 主管汇总初稿→ 生成一份约3500字的报告初稿,发送给质检Agent。
- 质检→ 打分卡给出:事实准确性78分(有两个数据缺引用)、格式合规90分、逻辑连贯85分。
decision为rework,附带3条修改建议。 - 打回重做→ 主管根据质检建议定位到对应段落,让检索Worker重新补数据和链接,再进行二次汇总。
- 二次质检→ 打分提升到事实准确性92、格式合规95、逻辑连贯90,
decision为pass。 - 交付→ 主管将最终报告和质检记录一起提交。
整个流程耗时约4分半钟(模型API调用为主),token消耗约12万。换成单Agent直接写同样任务,耗时约2分钟、token消耗约6万,但产出质量基本被质检Agent按在及格线上下摩擦。
5. 实测数据与效果评估
5.1 质量和效率的权衡:不是所有任务都值得“走流程”
项目上线后,我专门对20个不同类型的任务做了对比实验,用同一组模型,分别走“单Agent直出”和“agency-agents协作流”,请了团队里三位同事按1-10分盲评打分:
| 任务类型 | 单Agent平均分 | 多Agent协作平均分 | 耗时增幅 | 成本增幅 |
|---|---|---|---|---|
| 行业分析报告 | 5.2 | 8.1 | 125% | 108% |
| 长文写作(3000+字) | 5.8 | 7.9 | 86% | 95% |
| 代码Bug分析 | 6.5 | 8.3 | 70% | 82% |
| 短视频文案脚本 | 7.1 | 7.4 | 32% | 40% |
| 简单问答 | 8.2 | 6.9 | 55% | 65% |
数据说明一个重要事实:复杂度越低的任务,协作流的“质检-返工”环节带来的损耗越明显。短视频文案这种本身比较模式化的内容,单Agent已经能良好完成,硬走协作流反而会因为过度审校让它失去灵气。所以我们的决策是:只有预估复杂度在“需要处理外部信息且需要多角度交叉验证”级别的任务,才推荐走agency-agents模式。用一张路由表来判断:
- 任务目标中有“分析、研究、对比、综述、调研”等动词,且预期产出在800字以上 → 上协作流。
- 任务目标是明确的模板化产出(如周报、会议纪要、文案改写)→ 单Agent直出,必要时加一个轻量质检。
5.2 成本控制:质检Agent是花小钱省大钱的典型
很多人一看“多Agent”就觉得token成本高不可攀,但实测下来,情况没那么吓人。以行业分析报告为例,单Agent直出的6万token里,大概有20%是模型“自己推翻自己”产生的无效推理。协作流的12万token里,质检Agent只占约1.5万token,但它拦截下来的问题(数据编造、逻辑断层、格式错乱),如果直接交付,团队的返工成本可能是一个工作日。
事实上,我在另一个项目里尝试过“加大质检Agent的权重”来进一步提升质量,比如给它更高昂的“返工指令”,质检Agent会变得异常挑剔,一份报告返工四五次,质量和第一次相比只提升了0.3分。质检不是越严越好,严到边际收益趋近于零时,纯属烧钱。我们最终的策略是:质检Agent打回一级修改后,二稿直接进入盲审,盲审不过再考虑人工介入。
5.3 在哪些场景收益最大
结合半年多的生产环境使用,我给这套系统打出的“最佳适配场景画像”是:
- 跨领域信息综合:单Agent的知识范围有限,但多Agent可以通过“检索Worker”补足外部信息,再让“研判Worker”做分析,比单模型靠内部知识硬答靠谱得多。
- 长流程任务:任务链条越长,单Agent“忘记前文”的概率越大。协作流把整个链条拆成了独立的上下文窗口,每个环节只载入自己需要的信息。
- 需要可审计性的交付物:质检记录天然形成了交付物的审计日志。老板问“为什么你觉得这个数据可靠”,你可以直接调出质检Agent当年的核验记录。
6. 常见问题与排查技巧实录
6.1 Agent之间陷入死循环:返工卡死
现象:质检Agent打回任务后,Worker改了一轮再交上来,质检Agent又因为别的问题打回,改了再交,又被打回。循环往复,任务永远无法通过。
根因:质检Agent的“验收标准”和Worker的“修改能力”不匹配。常见原因是验收标准写得太抽象,比如“请确保内容深度足够”,Worker根本无法判断“深度够”是什么样,只能瞎改。
排查思路:第一步看质检记录里的issues字段,如果连续两次打回的issues存在重叠,说明质检标准没被执行者理解。第二步检查验收标准是否可量化。我们后来把所有验收标准改成“包含/不包含”句式:
- 错误示范:“报告应该有足够的数据支撑。”
- 正确示范:“报告必须包含至少5组来源可溯的统计数据,每组数据须在文末参考文献中给出对应编号。”
解决方法:在调度循环里加一个“返工次数上限”,默认3次。超过上限后,任务不再打回,而是带着质检意见直接进入“人工复核队列”,由人工决定是放行还是终止。
6.2 上下文爆炸:每个Agent都塞了一肚子历史
现象:系统跑久了,Agent的响应速度明显变慢,而且后期任务的产出质量忽高忽低。
根因:虽然设计了记忆截断,但实践中发现,当Worker处理完第一个任务后,它的上下文窗口里还残留着上一个任务的中间结果。它再处理新任务时,会把旧任务的信息混进新任务。
排查思路:给每个消息加上trace_id(链路追踪号),在日志里检索同一个trace_id下Agent调用大模型的输入是否包含无关历史内容。
解决方法:Worker处理完一个任务后强制执行“上下文重置”——清掉工作记忆,只保留共享记忆里与其强相关的关键结论。类似于一个员工换新项目工位时,把上一项目的草稿纸全部扔掉,只留下脑中的经验。
6.3 Agent“假协作”:看似在接力,其实在各自编故事
现象:质检打回的情况很少,但最终交付物里多处数据对不上。比如甲Worker说市场规模50亿,乙Worker引用了针锋相对的40亿数据,两份数据竟然都被质检放行了。
根因:质检Agent的职责边界没设好,它默认“每份子任务结果独立检查”,而不对比不同子任务之间的数据一致性。这是质检环节最容易漏掉的一环。
排查思路:在质检Agent的打分卡里加一个cross_consistency维度,让它对同一任务不同子结果之间的关键数据进行交叉比对,出现矛盾直接判rework。
解决方法:改为“两阶段质检”,第一阶段是单任务质检,第二阶段是汇总前的一致性核验,由主管Agent在汇总阶段触发。这个改动上线后,跨模块数据冲突的事故率降低了约70%。
6.4 工具调用失败后Agent“装成功”
现象:Worker调外部数据接口失败了,接口返回超时或空值,但Worker在提交结果时一切照常,完全没提失败的事。
根因:大模型在“完成任务”和“汇报失败”的取舍里,天然倾向于前者。它宁可编一个看似合理的数据结构,也不愿意输出一个“status: failed”的空结果。
排查思路:所有工具调用必须走统一封装,封装层捕获异常后直接返回结构化错误对象,而不是把异常吞掉让模型自由发挥。
def call_with_error_tracking(tool_func, *args, **kwargs): try: result = tool_func(*args, **kwargs) # 结果必须带状态字段,空结果也不能例外 return {"status": "ok", "data": result} except Exception as e: # 禁止把原始异常发给模型自由发挥 return { "status": "failed", "error_code": type(e).__name__, "error_message": str(e)[:500] }解决方法:在Worker的系统提示词里明确写一条硬规则:“如果你收到status=failed的返回,必须立即中止任务并汇报失败,禁止尝试自行伪造补齐数据。”这条规则加进去以后,模型伪装成功的行为几乎绝迹。
6.5 排查技巧速查表
| 症状 | 优先排查方向 | 常用手段 |
|---|---|---|
| 返工死循环 | 验收标准是否可量化 | 查看issues是否重复、检查acceptance_criteria字段 |
| 响应越来越慢 | 上下文膨胀 | 检查记忆截断策略、清理工作记忆 |
| 数据前后矛盾 | 跨模块一致性缺失 | 启用两阶段质检、增加cross_consistency评分 |
| 工具失败但继续输出 | 异常处理被吞 | 统一工具封装、强制状态字段 |
| 任务拆解重叠 | 拆分检查器未生效 | 检查互斥性校验逻辑 |
| Agent擅自动用无关工具 | 权限白名单缺失 | 检查TOOL_WHITELIST配置 |
最后再分享一个个人体会
做完agency-agents这个项目,我最大的感受是:多Agent系统真正的难点不在Agent本身,而在Agent之间那套“看不见的秩序”。你的模型能力再强,如果角色边界不清、通信协议混乱、质检标准模糊,系统跑起来就是一群聪明人在互相添乱。
如果你也准备搭类似的东西,我的建议是先别急着上复杂架构。把主管、一个Worker、一个质检这三个角色先跑通——哪怕用最简单的JSON消息在本地进程间传递——然后把“返工循环”“上下文膨胀”“假协作”这几个坑全部踩一遍,你自然就知道下一步该往哪个方向投入。
这套体系在更长远的维度还有一个可以扩展的方向:把质检Agent沉淀下来的问题库做成一个反馈回路,定期用真实案例反向微调各角色的提示词。简单说就是让系统自己“吃一堑长一智”,那个方向做下去,项目会越来越像一个真正成熟的事务所,而不是三个固定工位的临时团队。