最近我把手里一个摸索了很久的多智能体项目正式命名为agency-agents,简单说就是一个“AI 代理事务所”——用多个有明确分工的 LLM Agent 去模拟一个小型团队,有人负责搜索、有人负责推理、有人负责写稿,最后把协作结果汇总成一份可用交付物。这个项目解决的核心问题,不是“怎么让单个 Agent 更聪明”,而是“怎么让多个 Agent 像真实团队一样配合,不在传递信息、重复劳动和幻觉放大上翻车”。如果你正在纠结单智能体和多智能体怎么选,或者已经在做 Agent 编排但被上下文污染和工具循环折磨得焦头烂额,这篇笔记应该能给你一些能直接抄作业的思路。我会把架构选型、调度逻辑、提示词设计、工具调用协议、以及调试时踩过的坑全部摊开讲。
1. 项目定位:agency-agents 到底在解决什么问题
1.1 单智能体与多智能体的边界判断
很多人上来就问:“项目该用单 Agent 还是多 Agent?”我的判断标准其实很朴素:如果一件事让一个 Agent 干,效果已经够用,就别上多 Agent。
单个 Agent 的优势是天然具备完整上下文。比如让它把一段录音整理成会议纪要,或者把一份需求文档改写成 PRD,它从头到尾都清楚自己做过什么。这种情况下硬拆多个 Agent,反而会出现“信息传递损耗”——Agent A 的理解和 Agent B 的理解之间隔了一层摘要,消息来回传几次,关键信息就丢了。
我在项目里做过一个对照实验:同样的“分析某公司财报并输出投资备忘录”任务,单 Agent 直接跑,和多 Agent 分三岗协作跑,结果多 Agent 版本在财务指标提取的准确率上反而低了 12%。原因很简单,拆分后的“数据提取 Agent”把毛利率保留两位小数还是三位小数这种细节理解错了,后面的分析 Agent 又不愿意重新翻原始数据,直接在错误基础上继续发挥。
那什么时候必须上多 Agent?我的经验是三类场景:
- 任务需要多种差异极大的能力。比如既要写代码,又要查文档,还要生成图片,单个模型在切换工具和思维模式时容易变得“不伦不类”。
- 需要隔离上下文。某些数据只允许被特定角色读取,比如安全审核 Agent 不能看到业务侧的原始日志,拆开比在单会话里硬控更干净。
- 需要质量对抗。让一个 Agent 写方案,另一个 Agent 专门挑毛病,比让它自写自审更有效。
1.2 真正有价值的多智能体不只是“分角色”
“分角色”只是表面。我在 agency-agents 里真正关注的,是角色之间怎么交接半成品。
打个比方:真实公司里,产品经理把需求文档交给开发,开发不会要求 PM 把接口字段都定义好;但 PM 必须明确说清楚“这个页面要展示哪些数据”。Agent 之间也一样,下游 Agent 能不能高效工作,取决于上游 Agent 给它的东西是否足够结构化。
我把每个 Agent 的输出都强制设计成固定格式,比如:
- 调研 Agent 输出“事实清单”,每条事实必须带来源 URL 和置信度。
- 分析 Agent 输出“结论列表”,每条结论必须标注依据的是哪几条事实。
- 写作 Agent 只消费“结论列表”,不看原始资料。
这么做初期会付出一些工程成本:每个角色都要写一堆输出约束、写校验函数。但跑通之后收益极大——任何一个环节出问题,我都能快速定位是“事实错了”还是“结论推错了”还是“表达错了”,而不是面对一坨分不清来路的文字。
1.3 警惕“为了架构而架构”
这个项目最开始我也走过弯路,想着多 Agent 就该像微服务一样越拆越细,于是设计了 7 个 Agent:意图识别、关键词提取、搜索、筛选、总结、推理、写作。结果跑一次任务要调用 30 多次模型接口,耗时拉满,质量并没有提升。
后来我砍到 3 个核心 Agent:调研 Worker、分析 Supervisor、写作 Specialist。中间的那些“识别”“提取”“筛选”全部降级成 Prompt 里的结构化指令,不再单独成 Agent。砍完之后系统的稳定性和速度都上来了。
所以 agency-agents 这个项目给我的第一个教训是:多智能体的价值是减少认知复杂度,而不是制造工程复杂度。角色能合并就合并,只有必须隔离上下文或需要独立工具集时才拆开。
2. 多智能体协作系统的四种常见编排模式
编排方式决定了 Agent 之间是“同事关系”还是“上下级关系”。不同模式适合不同任务类型,我整理出来通常就这四种。
2.1 主管-工人模式(Supervisor / Workers)
这是我最常用也最推荐的模式。由一个“主管 Agent”负责拆解任务、分配任务、检查结果、决定是否返工,下面的“工人 Agent”只负责执行单一职责。
它的关键设计在于:工人 Agent 不互相通信,只和主管通信。这就避免了多 Agent 之间相互“传染”错误,也大幅降低了上下文串扰。Worker 把自己那部分结果交回来,Supervisor 判断质量是否合格,不合格就打回重做。
这种模式适合任务粒度天然分层的场景,比如“市场调研报告 = 多个细分市场调研 + 汇总分析”。我通常在 Supervisor 的提示词里强调“你是一个项目经理,你不亲自执行,你只派活和验收”。
2.2 顺序流水线模式(Pipeline)
Pipeline 就是上一个 Agent 的输出直接作为下一个 Agent 的输入,形成一条固定流水线。比如“抓取 → 清洗 → 分析 → 可视化”。
优点是可以针对每一级单独优化提示词和模型参数,比如抓取阶段用便宜且支持长上下文的模型,分析阶段用更强推理性模型。缺点是错误会沿流水线累积,第一级的小偏差可能在后面被放大成完全错误的结论。
所以我通常只在任务足够线性、每级输出结构又非常确定时才用。如果中间有分支判断,建议还是回到主管-工人模式。
2.3 对抗/辩论模式(Debate)
这种模式让多个 Agent 扮演不同立场,比如“合规审查 Agent”和“业务发展 Agent”针对一个方案互相辩论,最后由仲裁 Agent 拍板。
我在写产品文档或者做技术选型时试过这种模式,效果比单一“自我批判”好很多。因为真实模型在自我批判时经常只是象征性地挑刺,但两个独立 Agent 看过同一批资料后提出反对意见,冲突度要高得多。
对抗模式最大的坑是跑偏。两个 Agent 各说各话、反复车轱辘,既消耗 Token,又不收敛。我的解决方案是给辩论设置硬性轮数上限,比如最多三轮,并且要求双方每轮必须引用新事实,不能重复同一论点。
2.4 市场/自由协作模式(Marketplace)
这是我目前还在实验的模式,思路是让多个 Agent 像自由职业者一样在“市场上”自发认领任务、互相评价产出。
坦白讲,现在的模型能力还撑不起这种全自由协作。它的主要内容:任务描述推送到消息队列,Worker Agent 根据自己的 skill 描述决定是否认领,产出提交后由 Reviewer Agent 打分,分数影响后续派单优先级。
这个模式适合研究性探索,生产环境风险太高。如果你要试,重点做好防死锁——比如任务无人认领 5 分钟后,强制回退给默认主管 Agent。
2.5 四种模式对比速查表
| 模式 | 适用场景 | 主要风险 | 维护成本 |
|---|---|---|---|
| 主管-工人 | 任务可拆、结果需验收 | 主管成为瓶颈 | 中 |
| 顺序流水线 | 流程线性、输出稳定 | 错误累积 | 低 |
| 对抗辩论 | 需要决策风控、质量审查 | 不收敛、浪费 Token | 中 |
| 市场协作 | 研究探索、任务动态变化 | 难以控制、易死锁 | 高 |
我建议大部分项目从主管-工人模式起步,跑通之后再根据痛点引入流水线或辩论机制。agency-agents 现在的主架构就是 Supervisor 加三个 Worker,局部任务用了 Pipeline。
3. 从零搭一个轻量级 agency-agents 框架
3.1 第 1 步:定义角色和交接协议
开工前我会做一张简单的表格,写清楚每个角色的:职责范围、可调用工具、输入要求、输出格式、质量验收标准。
以最常见的“信息调研”流程为例:
| 角色 | 职责 | 工具 | 输入 | 输出 |
|---|---|---|---|---|
| 调研 Worker | 搜索并提取原始事实 | 搜索 API、网页抓取 | 调研主题标签 | JSON 事实清单数组,含原文摘要、来源、置信度 |
| 分析 Supervisor | 整合事实、给出结论 | 无(只读事实清单) | 事实清单数组 | 结论列表,每条含依据事实索引 |
| 写作 Specialist | 生成最终报告 | 无(只读结论列表) | 结论列表 | Markdown 格式的完整报告 |
定义好这张表,后面写代码基本不会乱。交接协议其实就是输出的 JSON schema,我建议直接手写一个简单的 Pydantic 模型或 JSON Schema 文件,让模型输出之前先看到 schema 示例。
3.2 第 2 步:写一个极简的调度器
在 agency-agents 项目里,我没有用那些重量级编排框架,第一版只用了 80 行 Python 写了个最小调度器。核心逻辑就三个方法:run_worker、collect_results、judge_and_route。
from dataclasses import dataclass, field from typing import Any, Callable @dataclass class AgentContext: role: str system_prompt: str tools: dict[str, Callable] = field(default_factory=dict) class AgentSupervisor: def __init__(self, agents: dict[str, AgentContext], llm_call: Callable): self.agents = agents # llm_call 是一个统一封装,负责把 prompt + messages 发到模型并解析返回 self.llm = llm_call self.session_messages = [] def run_worker(self, role: str, task: str, history: list | None = None) -> str: """调用某个角色 Agent,允许注入额外历史消息""" worker = self.agents[role] messages = [ {"role": "system", "content": worker.system_prompt}, *history or [], {"role": "user", "content": task}, ] return self.llm(messages, tools=worker.tools) def handoff(self, data: Any, to_role: str, as_task: str) -> str: """把结构化结果转成下一个角色的输入任务""" # 这里把上一个 Agent 的输出序列化成 JSON,再拼接上预设指令 payload = f"根据以下结构化数据完成你的工作:\n{data}\n\n你的任务:{as_task}" return self.run_worker(to_role, payload)实际项目里,llm_call会统一处理 token 截断、重试、JSON 解析失败恢复等逻辑。调度器本身不要写太多业务规则,越薄越好。真正的业务判断应该放在模型提示词里,否则你会陷入“规则比 Agent 还复杂”的窘境。
3.3 第 3 步:让 Agent 真正调用外部工具
调度器写好后,第二步就是把工具接进来。工具的本质是:把外部的确定性计算能力,暴露给非确定性的模型。
我封装每个工具都坚持同一个模式:函数名是动词,参数是严格 JSON,返回是字符串,所有异常都在内部捕获,绝不让异常堆栈漏给模型。
def search_web(self, query: str, max_results: int = 5) -> str: try: results = api.real_search(query, count=max_results) return json.dumps(results, ensure_ascii=False) except Exception as e: return f"[搜索失败,请尝试更换关键词] {e}"工具数量不是越多越好。我观察到一个规律:每接入一个工具,模型调用它的频率和对它的理解准确度都会下降。与其给 20 个不常用的工具,不如只保留 8 个核心工具。多智能体系统里,通常主管 Agent 不挂工具,只做派活;工人 Agent 才挂具体工具。
3.4 第 4 步:可观测性设计
多智能体系统比单 Agent 难调试十倍,主要原因是链路变长了。我的习惯是从第一版就插入三个日志点:
- 调度日志:记录哪个 Agent 被调用、输入的任务是什么、耗时多少。
- 工具日志:记录调用了哪些工具、参数是什么、返回结果摘要是什么。
- 质量日志:记录 Supervisor 是否判定结果通过,是否打回重做。
def llm_call(messages, tools=None): print(f"--- LLM CALL role_guess={messages[0]['role']} tokens≈{estimate_tokens(messages)}") ...这一行一行的日志,在跑真实任务时能救你命。有一次我排查某客户反馈“调研报告里数据全是编的”,查日志发现是调研 Worker 拿到的搜索工具返回了空字符串,模型为了完成任务,就开始自己编数据。如果没日志,我可能要瞎猜几天。
4. 提示词与上下文的工程化设计
很多做 Agent 项目的人把重心放在模型选型上,却忽略了两件真正决定成败的事:提示词的系统约束和上下文管理。这两块在单 Agent 下只是“写得好不好”,在多 Agent 协同下直接决定“系统能不能跑”。
4.1 角色提示词里必须写清楚的三类东西
我在每个 Agent 的 system prompt 里固定安排三个板块:角色定位、行为红线、输出规格。
角色定位不用文艺,直接说人话:你是一个行业研究员,你只负责从网页中提取事实,你不需要总结观点,不需要提出建议。行为红线是防止“帮手变杀手”的关键,例如“不要编造数据,如果搜索结果为空,你必须返回空数组并说明原因”。输出规格则给出你期望的 JSON 示例,最好附上一个完整的 few-shot 样例。
我踩过一个典型坑:调研 Worker 的输出规格只写了“返回事实列表”,没写“必须包含来源 URL 字段”,结果模型有时给来源有时不给,导致下游分析 Agent 找不到引用来源,只能瞎编一个。后来我把输出规格改成“缺少来源 URL 的条目视为无效,必须重写”,问题才解决。
4.2 消息传递的格式约定
多 Agent 之间的消息,不能直接传大段散文,必须走结构化数据。我的约定是:
- 每个 Agent 的输出先过一轮
format_validator,不合法就自动退回让模型重新生成。 - 传递的数据固定用 JSON,且顶级字段名称保持统一,例如
items、task_status、error。 - 各 Agent 之间不传原始大文档,只传摘要、指标、结论。原始数据统一放外部存储,谁想复核谁自己读。
这样做有一个额外好处:我可以随时在中间环节手动接入人工审核。比如客服场景里,面向用户回复之前,可以让一个“安全审查 Agent”先过一遍敏感信息,而且因为它接收的是结构化字段,漏检率远低于直接读整段对话。
4.3 上下文长度控制与压缩策略
多 Agent 系统中 token 消耗几乎是“爆炸式”的。我在 agency-agents 里做了三个层面的控制:
| 策略 | 做法 | 适用位置 |
|---|---|---|
| 裁剪历史 | 只保留最近 N 轮对话,且 N 由任务复杂度决定 | 主管-工人交接时 |
| 摘要历史 | 把多轮历史先总结成一段 300 字内摘要,再拼接到 system prompt | 长期会话、持续交互 |
| 结构化记忆 | 把关键决策点单独写入一个 KV 存储,查询时再把相关内容注入 | 需要跨任务回忆的场景 |
我建议给每个 Agent 单独设一个 token 预算,而不是全系统共享一个窗口。比如调研 Worker 的上下文窗口上限是 8000 token,因为它要看长网页;分析 Supervisor 上限 4000 就够了,因为它只读结构化结论。按角色分预算,能让调度器在 Agent 输出超长时及时截断,而不是让整个链路的请求都跟着膨胀。
4.4 模型选型可以不统一
不同角色用不同模型,是 agency-agents 项目里让我最省成本的一个决策。调研 Worker 任务量大但逻辑简单,我就用便宜、响应快的轻量模型;分析 Supervisor 需要严谨推理,我就用参数更大、推理更强的模型;写作 Specialist 需要语言润色,又优先选文风自然的模型。
但有个前提:工具调用能力必须稳定。如果某个 Worker 需要调用工具,哪怕任务简单,模型也要具备可靠的 function calling 能力。否则你会看到模型在“假装调用工具”,返回一大堆前言不搭后语的伪 JSON。
5. 实战:做一个“行业信息调研”智能体小团队
光讲概念容易飘,我用一个具体场景完整走一遍流程。这个任务来自当时一个模拟项目:某公司想知道“智能家居在东南亚市场近半年的新产品趋势”,要求输出一份可汇报的市场简报。
5.1 场景设定和 Agent 分工
我明确拆成三个角色:
- 搜索 Worker:负责调用搜索 API,按国家/地区拆分查询,返回“标题 + 摘要 + 来源 + 发布时间”字段的事实清单。
- 分析 Supervisor:接收事实清单,做归因、去重、趋势判断,输出“3 条关键结论 + 2 条风险提示”。
- 写作 Specialist:把结论和提示整理成 Markdown 报告,包含摘要、分地区表现、竞争格局、未来预判。
这个分工绕开了“所有 AI 内容一个 Agent 生成”的常见问题——分析和表达被拆开之后,既不会出现逻辑混乱的长篇大论,也不会出现只有结论没有依据的空洞报告。
5.2 每个 Agent 的核心提示词示例
搜索 Worker 的 prompt 我写了这样一段:
你是一个资深搜索专员。你的任务是根据用户给定的查询词,生成至少 5 组不同的搜索关键词,并用工具逐一搜索。对每条搜索结果,提取标题、来源、发布时间、正文摘要,按 JSON 数组返回。注意:如果某组关键词没有结果,请修改措辞后重试一次;如果仍然没有结果,跳过。严禁使用推测性内容,所有字段必须来自搜索结果原文。
分析 Supervisor 的 prompt 则强调“先聚合再判断”:
你是一个行业分析师。你会收到一批结构化的搜索事实,你需要先去除互相矛盾的重复信息,再按地区、品类、趋势三个维度聚类。输出格式:
trends.json,包含conclusion字段、evidence_ids引用字段、confidence字段。如果某条结论没有足够的证据支持,请你将它移入risks字段,而不是强行写入结论。
写作 Specialist 的 prompt 则更偏表达:
你是一个资深商业撰稿人。你收到的是分析师产出的结论文件。不要新增任何数据,不要修改任何结论,只负责把结论组织成结构清晰、适合管理层阅读的 Markdown 简报。第一段要有 Executive Summary,之后各节按重点展开。
5.3 整个执行链路怎么流转
调度器运行过程大致长这样:
- 收到任务后,Supervisor 先把原始任务拆成 3 个地区子任务。
- 搜索 Worker 依次执行 3 个子任务,各返回 5 到 8 条事实。
- 事实清单合并到一个临时字段,交给分析 Supervisor。
- 分析 Supervisor 输出结论 JSON,里面每条都带上
evidence_id,对应事实清单里的某条记录。 - 写作 Specialist 读取结论 JSON 和事实清单,纯靠这些结构化数据生成报告。
整个流程大约 4 分钟,调用约 15 次模型接口,token 消耗主要花在搜索 Worker 的网页摘要上。
5.4 结果质量怎么把控
第一版跑完后,我拿报告给几个真实用户看,他们反馈“数据太散、没有重点”。问题出现在分析 Supervisor 的confidence字段没被写作 Specialist 利用。写作时根本不看置信度,把高置信和低置信的结论写到同一层级,自然显得没重点。
后来我在交接协议里加了个规则:低置信度结论必须单独标为“待验证”,并放在文末附录。这一改,报告的可读性立刻提升。质量把控不是写在最终审核环节,而是写在各角色交接的合同条款里。这是多智能体项目最实用的经验。
6. 踩坑实录:多智能体项目中 5 个让人抓狂的问题
6.1 上下文污染导致幻觉
现象:后置 Agent 输出明明基于前置结果,却突然出现前置结果里不存在的数据。
排查:多半是前置 Agent 的输出格式变来变去,把本该隔离的数据混进了备注字段,后置 Agent 把备注当事实读了。
对策:每个 Agent 的输出必须过 JSON Schema 校验。我在项目里用了一个很土的办法:解析失败就自动重试一次,并在重试 prompt 里附带你上一次的错误信息。这比让用户手动纠错效率高很多。
6.2 工具循环调用
现象:Agent 反复调用同一个工具,参数基本没变,却一直说“我再确认一下”。
排查:这是模型在“安全地拖时间”——它不知道目标是否达成,所以不断重复工具调用。
对策:在工具返回中嵌入“是否满足任务要求”字段,并设置最大工具调用轮数。比如搜索任务最多调用 3 轮,超过后强制转入总结阶段。代码里加个计数器即可,成本极低。
6.3 Token 爆炸
现象:多跑几次任务,日志显示每次请求都在往上下文里塞历史对话,token 总量成倍增长。
排查:调度器把多轮会话的历史消息无脑传递下去了。
对策:按角色做上下文隔离,只传“结构化中间产物”,不传原始聊天记录。我给 schedule 加了context_policy参数,分别支持drop_all、summary、pass_through三种模式,默认全部走pass_through就是灾难。
6.4 角色碰撞
现象:两个 Agent 互相把对方的职责扛下来。例如分析 Agent 在输出里直接改写了别人的事实,而不只是给出结论。
排查:说明系统 prompt 对职责边界写得不够硬。
对策:在角色红线里加一条“你只能输出本角色职责范围内的内容,禁止修改上游数据。”同时最好在数据层限定权限——如果分析 Supervisor 根本接触不到搜索工具 API,它就没机会越权。
6.5 工作流难调试
现象:某一环节报错,日志显示一大堆调用,但分不清是哪个 Agent 哪次调用出了问题。
对策:我给每个 Agent 的请求 ID 都加上了标记,例如worker-search-2025-07-01-001,并在所有工具日志、模型日志里输出同一个 trace_id。这虽然不是新东西,但很多 Agent 项目根本没做。等你想排查问题时,就会发现没有 trace_id 的日志完全是灾难。
6.6 排查思路速查表
| 问题现象 | 第一检查点 | 第二检查点 | 兜底方案 |
|---|---|---|---|
| 输出数据造假 | 搜索工具返回了什么 | 工具结果是否被截断 | 强制要求工具返回原文引用 |
| 任务不结束 | 最大轮次计数器是否生效 | Supervisor 的验收标准是否过严 | 放宽验收,或直接降级为单 Agent |
| 结论质量低 | 分析 Supervisor 得到的事实是否完整 | 事实清单是否被摘要压缩过度 | 提高调研 Worker 的输出 token 上限 |
| 成本飙升 | 看看是哪个 Agent 额外调用了模型 | 是否存在重试死循环 | 限制最大重试次数为 1 或 2 |
| 格式频繁报错 | 模型输出是否符合 schema | 冷启动时是否给足 few-shot 样例 | 解析失败时用上次成功输出做兜底 |
6.7 我自己的调试心法
多智能体调试和写业务代码完全不同。写业务代码是看堆栈,看断点;多智能体这边,异常很难稳定复现,必须把它当成“分布式系统”来调。所以我的顺序永远是:先看日志链路,再量化 token 消耗,最后才去改提示词。很多问题表面上是“模型不听话”,底层其实是“输入信息不全”或者“上下文被污染”。
另外,别指望一次跑通。第一次上线时把期望值调到“能跑是完全靠运气,跑不通才是正常”。多智能体系统本质上是个概率系统,同样的输入可能输出不一样的结果。我给自己定了条规矩:某个流程连续跑 3 次都稳定成功,才敢写进自动执行链路;连续失败 2 次就退出自动流程,转人工处理。
7. 一点个人体会
agency-agents 做到现在,我最深的体会是:多智能体的本质不是“多个模型分工”,而是“一套可管理的协作协议”。模型本身并没有变得更聪明,只是通过协议把不同模型的优势放在了正确的位置上。
如果你准备做类似项目,我建议从最小闭环开始,三个 Agent 就足够了,多了只会让问题指数增加。先把交接格式定死、把 trace 日志铺好、把角色红线写进提示词,再谈更复杂的编排模式。
最后再分享一个小技巧:第一次让多 Agent 系统跑通的瞬间,别急着做优化,先把当时的系统提示词、示例数据、调度参数原样存档。后续无论怎么改,你都需要一个“绝对稳定版本”作为对照基线。没有基线的多智能体项目,最后一定会被玄学问题拖垮。