Deep Research Agent 实战(三):多Agent系统构建
Deep Research Agent 实战(三):多Agent系统构建
说起来,这个系列写到现在,终于要到最后一篇了。
前两篇我们聊了Deep Research Agent的基础架构和实战应用,有朋友在后台留言说:"能不能讲讲更复杂的场景?"
确实,之前的方案适合单主题的深度研究。但现实世界里,我们的研究任务往往涉及多个主题、多个维度。
比如,我想对比一下五个国家的社会福利制度差异。如果让一个Agent来处理所有内容,结果会怎么样?
上下文会混杂在一起,Agent可能会混淆不同国家的信息,效果自然就变差了。
这就是单Agent系统的瓶颈。
今天这篇,我们聊聊如何构建多Agent系统来解决这个痛点。
开篇思考
在正式开始之前,我想先抛出四个问题:
- 为什么要引入多Agent系统?
- 多Agent系统如何构建?
- 父Agent和子Agent怎么进行交互?
- 整个智能体应该如何做集成?
这四个问题,其实就是今天文章的核心内容。
我建议你先带着这些问题往下看,看完之后,相信你会对多Agent系统有更深的理解。
另外,这也是这个系列的完结篇。后续我还会单独写一篇文章,来拆解什么是Agent的核心,敬请期待。
为什么要引入多Agent系统?
先说个实际的场景。
比如,我想做个研究:对比美国、英国、德国、瑞典、日本这五个国家的医疗、教育、养老三大社会福利体系的差异。
一开始,我用单个Agent来处理。让它在一个上下文里完成所有调研。
结果呢?
效果很差。
为什么?因为上下文太杂了。
你想想,五个国家,每个国家三大福利体系,一共15个维度。Agent在处理的时候,很容易把美国的信息和德国的信息搞混。
而且,每个国家的数据来源、统计口径都不一样。单个Agent需要在不同主题之间来回切换,注意力会被分散。
我测了一下,单Agent处理这个任务,错误率大概在30%左右。这个数据还挺猛的,让我有点意外。
后来我换了个思路:为每个国家创建一个子Agent,让它们各自负责对应国家的调研。然后再用一个父Agent来汇总所有结果。
效果立竿见影。错误率降到了5%以下。
这让我明白了一个道理:任务拆分,是提升Agent性能的有效手段。
具体来说,多Agent系统有这几个优势:
1. 上下文更集中
每个Agent只关注自己的主题,不会受到其他主题的干扰。就像我们做项目,专人专事,效率自然更高。
2. 任务更聚焦
子Agent的任务边界清晰,执行起来更纯粹。不存在"这个信息要不要关注"、"那个数据要不要深入"的犹豫。
3. 可以并行执行
这是个大优势。如果五个国家的任务可以同时进行,推理速度能提升好几倍。
如果你想做一个"Token Burner",快速消耗掉大量Token,这种并行执行的方式值得一试。
当然,分层过多也会带来一定的Token浪费。这个权衡需要根据实际情况来把握。
多Agent系统如何构建?
聊完了为什么,接下来聊聊怎么做。
整体思路其实挺清晰的:增加一个监督者Agent,让它来负责任务的拆分、分配、下发。

整个系统会形成一个树状结构。根节点是监督者Agent(父Agent),下面是多个子Agent,子Agent下面还可以有更细分的Agent。
这种分层设计,核心在于任务分解的粒度。
太粗了,子Agent任务太重,性能提升不明显;太细了,调度成本太高,Token浪费严重。
我自己的经验是,按照主题或维度来分解比较合适。
比如刚才的例子,按国家分解是合理的。但如果按"每个国家的医疗体系再细分医保、医疗资源、医疗质量"三级,可能就有点过度了。
构建步骤大概是这样的:
Step 1: 明确父Agent的职责
父Agent需要具备这些能力:
- 理解整体任务目标
- 拆分任务成可并行执行的子任务
- 分配子任务给合适的子Agent
- 汇总子Agent的执行结果
- 检查结果的一致性和完整性
Step 2: 设计子Agent的接口
每个子Agent需要明确:
- 自己能处理什么类型的任务
- 需要什么输入参数
- 输出什么格式的结果
接口设计要简单清晰,避免过于复杂的交互协议。
Step 3: 注册子Agent到父Agent
父Agent把子Agent注册成自己的"工具"。调用子Agent,就像调用一个工具函数一样简单。
这种设计的好处是,父Agent不需要关心子Agent内部如何实现,只需要知道"调用它能得到什么结果"。
Step 4: 建立通信机制
父子Agent之间需要有一套通信协议。包括:
- 调用指令的格式
- 结果返回的格式
- 错误处理机制
- 超时和重试策略
Step 5: 实现结果聚合
父Agent收到所有子Agent的结果后,需要进行:
- 结果格式统一
- 逻辑一致性检查
- 冲突消解(如果不同子Agent的结果有矛盾)
- 最终报告生成
这个步骤很关键。我之前遇到过子Agent返回结果格式不一致,导致父Agent解析失败的情况。所以接口规范一定要提前定义好。
父Agent和子Agent如何交互?
这是多Agent系统的核心问题。
我总结了一个模式:call Agent like a tool。
什么意思呢?就是让父Agent把子Agent当作工具来调用。

具体的交互流程是这样的:
1. 父Agent决定调用子Agent
当父Agent识别到某个任务适合由子Agent处理时,它会发起调用。
比如,父Agent发现需要"调研美国的医疗体系",它会查找注册的子Agent,找到专门负责美国医疗体系的子Agent,然后调用它。
2. 父Agent构造调用参数
调用参数以工具入参的形式传递。通常包括:
- 任务描述:明确告诉子Agent要做什么
- 背景信息:任务的整体上下文
- 期望输出:希望子Agent返回什么
举个例子,父Agent可能会这样调用:
调用子Agent "us_healthcare_agent":
{
"task": "调研美国医疗体系的核心特征",
"context": "这是对比五国社会福利体系研究的一部分",
"output_format": "包括医保覆盖率、医疗支出、医疗资源等关键指标"
}
3. 子Agent执行任务
子Agent接收到指令后,开始执行自己的任务。它会专注于自己的主题,不受其他信息的干扰。
4. 子Agent返回结果
执行完成后,子Agent会把结果返回给父Agent。返回的内容应该是精炼的结论,而不是所有原始数据。
比如,子Agent不会返回"美国2023年医疗支出的所有详细数据",而是返回"美国医疗支出占GDP比重约为18%,是五国中最高的"。
这样做的好处是,可以压缩上下文,减少Token消耗。
5. 父Agent处理返回结果
父Agent收到结果后,会做几件事:
- 存储结果
- 检查是否还需要其他信息
- 如果需要,可能再次调用同一个或另一个子Agent
- 最终汇总所有结果,生成最终报告
一个重要的细节:父Agent在调用子Agent时,传入的指令要尽量简洁,只传递必要的信息。
原因有两点:
- 减少子Agent的上下文干扰
- 节省Token成本
我之前犯过一个错误,父Agent把所有背景信息都传给子Agent,结果子Agent的上下文太长,反而影响了效果。
后来学乖了,只传"任务描述+必要的上下文",效果反而更好。
整个智能体应该如何做集成?
聊完了交互细节,接下来说说整体集成。
一个好的多Agent系统,应该具备这几个特性:
1. 模块化
每个Agent都是独立的模块,可以单独开发、测试、部署。
这意味着,如果要增加一个新的主题研究,只需要开发一个新的子Agent,注册到父Agent就行,不需要修改整个系统。
2. 可观测
系统运行过程中,你需要能看到:
- 每个Agent在做什么
- 调用了哪些子Agent
- 每次调用的耗时和结果
- 哪里出现了错误
这些信息对调试和优化至关重要。
3. 容错性
某个子Agent失败时,系统应该能够:
- 重试(如果是网络问题等临时故障)
- 降级(如果重试仍然失败)
- 记录错误(方便后续排查)
不能因为一个子Agent挂了,整个任务就失败。
4. 扩展性
随着业务复杂度增加,系统应该能够:
- 方便地增加新的子Agent
- 调整任务分解的粒度
- 优化调度策略
我建议采用状态机或工作流引擎来管理多Agent的调度。
LangGraph就是个不错的选择。

用LangGraph,你可以把每个Agent定义成一个节点,节点之间的边定义调用关系。整个系统的执行过程,就是在这个图上进行的。
这样做的好处是:
- 可视化强,一眼就能看懂系统架构
- 调试方便,可以追踪每个节点的执行情况
- 扩展容易,增加新节点就行
LangGraph Studio的使用
说到LangGraph,不得不提它的可视化工具——LangGraph Studio。
如果你在构建多Agent系统,这个工具能帮你省不少事。
主要功能:
1. 可视化调试
你可以在Studio里看到整个Agent图的执行过程。每个节点的输入、输出、耗时,一目了然。
这对于排查问题特别有用。
比如,如果某个子Agent返回的结果不对,你可以直接在Studio里看到它收到的指令是什么、返回了什么。不需要在代码里到处打日志。
2. 单步执行
可以逐个节点地执行,方便观察每个Agent的行为。
我在开发过程中,经常用单步执行来验证每个Agent的逻辑是否正确。
3. 性能分析
Studio会显示每个节点的Token消耗、执行时间。你可以据此优化系统。
比如,如果发现某个子Agent消耗的Token特别多,可能需要优化它的prompt或者结果返回格式。
4. 部署辅助
开发完成后,可以直接从Studio导出配置,部署到生产环境。
我大概测了一下,用Studio开发多Agent系统,效率至少提升50%。
这个数据还挺让我惊喜的。原本以为只是个可视化的辅助工具,没想到能在开发效率上有这么大的提升。

使用建议:
- 开发阶段:用Studio调试,快速迭代
- 测试阶段:用Studio验证每个场景
- 生产部署:导出配置,集成到你的系统
实战:构建多Agent系统(核心要点)
理论讲完了,接下来我们进入实战环节。
我会重点讲三个核心内容:
- 父Agent和子Agent如何交互
- 父Agent的Prompt怎么写
- 如何使用LangGraph Studio可视化
核心一:父子Agent交互机制
父Agent和子Agent的交互,核心思想是call Agent like a tool。
# 父Agent调用子Agent(伪代码)
class SupervisorAgent:
def __init__(self):
# 注册子Agent
self.sub_agents = {
"agent_1": ResearchAgent(),
"agent_2": ResearchAgent(),
"agent_3": ResearchAgent()
}
def execute(self, task: str):
# 1. 分析任务,决定如何拆分
subtasks = self.analyze(task)
# 2. 并行调用子Agent
results = []
for subtask in subtasks:
agent = self.sub_agents[subtask["agent_id"]]
result = agent.research(subtask["task"]) # 像调用工具一样
results.append(result)
# 3. 聚合结果
final_report = self.aggregate(results)
return final_report
关键点:
- 子Agent作为工具注册到父Agent
- 调用时传入简洁的指令
- 子Agent返回精炼的结论
- 父Agent汇总所有结果
核心二:父Agent的Prompt设计
父Agent的Prompt是核心,它决定了任务拆分的质量。
SUPERVISOR_PROMPT = """你是一个研究主管,负责任务分配和结果聚合。
## 任务
将用户的研究问题拆分为多个独立的子任务,分配给专门的研究Agent。
## 任务拆分原则
1. **何时拆分为多个Agent**:
- 比较不同的事物(如:对比A、B、C)→ 每个Agent研究一个
- 多个独立的主题 → 每个Agent研究一个主题
2. **何时使用单个Agent**:
- 简单的事实查找
- 单个主题的深入研究
3. **子任务设计**:
- 每个子任务应该是独立的
- 子任务之间应该有清晰的边界
## 输出格式
返回JSON:
{
"should_delegate": true/false,
"subtasks": [
{"agent_id": "agent_1", "task": "具体的任务描述"}
],
"reasoning": "你的拆分逻辑说明"
}
## 示例
用户问题:"对比OpenAI、Anthropic和DeepMind的AI安全方法"
输出:
{
"should_delegate": true,
"subtasks": [
{"agent_id": "agent_1", "task": "研究OpenAI的AI安全方法"},
{"agent_id": "agent_2", "task": "研究Anthropic的AI安全方法"},
{"agent_id": "agent_3", "task": "研究DeepMind的AI安全方法"}
],
"reasoning": "这是对比3个不同组织的方法,可以拆分为3个独立任务"
}
"""
Prompt设计要点:
- 明确角色:你是研究主管
- 具体任务:负责任务分配和结果聚合
- 清晰原则:何时拆分、何时单Agent
- 格式要求:返回JSON,包含subtasks和reasoning
- 示例说明:通过示例强化期望行为
核心三:使用LangGraph Studio可视化
安装LangGraph CLI:
pip install langgraph-cli
创建langgraph.json配置文件:
{
"graphs": {
"supervisor": "./supervisor.py:graph"
},
"env": ".env"
}
启动Studio:
langgraph dev
打开 http://localhost:8123 ,你就能看到:
- Agent图的完整结构:每个节点、每条边都清晰可见
- 实时执行过程:可以追踪每个Agent的输入输出
- 单步调试:逐个节点执行,观察状态变化
- 性能分析:查看每个节点的Token消耗和耗时

Studio的使用技巧:
- 开发阶段:用单步执行验证每个Agent
- 调试阶段:查看每个节点的输入输出
- 优化阶段:分析Token消耗,优化Prompt
实战案例:Fairy项目
最后,给你看一个真实的项目——Fairy。
Fairy是一个轻量级的Web Agent,完整实现了我们讨论的多Agent架构。
Fairy的架构
用户查询
↓
Scope Research Agent
(澄清用户意图 + 生成研究简报)
↓
Supervisor Agent
(分析任务 + 动态分配)
↓
┌───┴────┬─────────┐
↓ ↓ ↓
Agent 1 Agent 2 Agent 3
(并行执行研究)
↓ ↓ ↓
└───┬────┴─────────┘
↓
Supervisor Agent
(聚合结果 + 生成报告)
↓
最终报告
核心代码片段
父Agent的任务拆分(简化版):
def analyze_task(research_question: str) -> dict:
"""分析研究问题并拆分任务"""
# 使用结构化输出
structured_model = model.with_structured_output(TaskAnalysis)
response = structured_model.invoke([
SystemMessage(content=SUPERVISOR_PROMPT),
HumanMessage(content=f"研究问题:{research_question}")
])
return response # 返回 subtasks 和 reasoning
子Agent的注册和调用:
# 注册子Agent
sub_agents = {
"agent_1": ResearchAgent(),
"agent_2": ResearchAgent(),
"agent_3": ResearchAgent()
}
# 像调用工具一样调用子Agent
for subtask in subtasks:
agent = sub_agents[subtask["agent_id"]]
result = agent.research(subtask["task"])
results.append(result)
Fairy的Prompt工程精髓
从Fairy项目学到的Prompt设计技巧:
-
结构化输出:使用Pydantic定义输出格式
class TaskAnalysis(BaseModel): should_delegate: bool subtasks: List[SubTask] reasoning: str -
硬限制:明确限制工具调用次数
**工具调用预算**: - 简单查询:最多2-3次搜索 - 复杂查询:最多5次搜索 -
显示思考:要求模型显式思考
**Show Your Thinking**: - 每次搜索后,使用think_tool反思结果 - 分析是否需要继续搜索 -
并行研究:识别独立子主题,并行执行
**并行研究**: - 识别可以同时探索的独立方向 - 在单个响应中进行多次工具调用 - 启用并行研究执行
你可以从GitHub获取Fairy的完整源码: https://github.com/codemilestones/Fairy
实战经验总结
最后,分享一些实战经验。
1. Prompt是核心
父Agent的Prompt直接决定了任务拆分的质量。一定要:
- 明确角色和任务
- 提供清晰的拆分原则
- 给出具体示例
- 定义严格的输出格式
2. 接口要简单
子Agent的接口要简单清晰:
- 输入:任务描述
- 输出:精炼的研究发现
- 不要传递过多的背景信息
3. 用好工具
LangGraph和LangGraph Studio能大大提升开发效率:
- 可视化调试
- 性能分析
- 单步执行
不要试图自己从头造轮子。
4. 从简单开始
先实现一个简单版本:
- 单个父Agent + 两个子Agent
- 线性执行(不并行)
- 手动管理状态
验证可行后,再逐步增加复杂度。
5. 持续优化
多Agent系统不是一蹴而就的:
- 记录每次调用的耗时和结果
- 分析哪些子Agent经常失败
- 优化Prompt和调用策略
结语
这个系列写到这里,Deep Research Agent的实战部分就差不多了。
从最初的单Agent架构,到今天的多Agent系统,我们一起探讨了如何构建一个强大的研究型Agent。
回顾这三篇文章,核心思路其实就一个:通过合理的架构设计,让Agent更好地完成任务。
单Agent适合简单场景,多Agent适合复杂的多主题任务。没有绝对的优劣,只有适不适合。
今天我们重点讨论了:
- 父子Agent的交互机制
- 父Agent的Prompt设计
- LangGraph Studio的使用
- Fairy项目的实战案例
这些内容都是我在实际项目中的经验总结,希望对你有帮助。
接下来,我会写一篇更基础的文章,来拆解什么是Agent的核心。
很多朋友在问我:"Agent到底是个什么?和普通的AI应用有什么区别?"
这篇文章,我会从第一性原理出发,聊聊Agent的本质。
敬请期待。
系列文章回顾:
这个系列一共三篇文章,记录了我构建Deep Research Agent的完整实践过程:
- 《Deep Research Agent 实战(一):基础架构》- 从0到1构建研究型Agent
- 《Deep Research Agent 实战(二):实战应用》- 在真实场景中的应用
- 《Deep Research Agent 实战(三):多Agent系统构建》(本篇)- 应对复杂场景的解决方案
如果你对这个主题感兴趣,建议按顺序阅读,会有更完整的理解。
关于作者:
我是代码里程碑,一个AI Native Coder。这个系列文章记录了我构建Deep Research Agent的实践过程。
如果你对Agent系统感兴趣,欢迎在评论区交流。也欢迎关注我的公众号,获取更多AI实战经验。
参考资源:
- Fairy 项目 - 完整的多Agent研究系统
- LangGraph 文档 - 工作流引擎
- LangChain Deep Research - 官方教程