跳到主要内容

Open Deep Research — 总览

30 秒导读: 你给它一个问题(比如"对比 2025 年主流推理芯片的性价比"),它自己拆题、并行派多个"子研究员"上网查资料、逐个压缩、最后合成一份带引用的长报告。整个流程是一张用 LangGraph 搭的三层嵌套状态图,四个环节各自可以换用不同的大模型。这是 LangChain 官方开源、可跨模型 / 跨搜索 / 跨 MCP 配置的深度研究 agent。

本章只做全景与路由:讲清它是什么、顶层怎么转、三层图怎么嵌套、主线走一遍,以及各章去哪读。具体实现细节留给后面四章,本章不重复。


1. 这是什么(零基础也能懂)

一句话定义: Open Deep Research 是一个开源、可配置、跨模型 / 搜索工具 / MCP 服务器的深度研究 agent——输入一个问题,输出一份带引用的长报告(README.md:1-19)。

解决什么问题 / 给谁用。 "深度研究"(deep research)指的是那种你问一个宽泛问题、需要 agent 去网上查很多资料、交叉验证、再写成一篇结构化长文的任务。手动做要几小时,这个项目把它自动化。它的定位是简单、完全开源、可换任何模型/搜索后端,性能对标市面上流行的 deep research 产品(README.md:5)。

它能做什么:

  • 先判断你的问题够不够清楚,不清楚就反问一句再开工(可关闭)。
  • 把问题拆成若干子课题,并行派多个子研究员各查各的。
  • 每个子研究员是一个 ReAct 循环(搜索 → 反思 → 再搜索),查完把一大堆原始资料压缩成干净摘要。
  • 最后把所有摘要合成一份带引用的最终报告
  • 搜索后端可选 Tavily / OpenAI 原生联网 / Anthropic 原生联网 / 无(configuration.py:78-93),还能接 MCP 外部工具(configuration.py:214-233)。

用起来什么样。 它作为一张 LangGraph 图部署,入口在 langgraph.json 里登记(langgraph.json:3-5):

"graphs": {
"Deep Researcher": "./src/open_deep_research/deep_researcher.py:deep_researcher"
}

起本地服务后,在 LangGraph Studio 的 messages 输入框里问一个问题、点 Submit,就能看到它跑完整个研究流程(README.md:43-58)。

一句话直觉 / 类比。 把它想成一个研究团队:一位主管(supervisor)拿到选题后拆活、分派;若干研究员(researcher)各自上网查、各写一段;主管收齐后交给一位主笔(final report)统稿成文。这个"主管带一群并行研究员"的结构,就是整套系统的灵魂。


2. 顶层全景(它大概怎么转)

2.1 三段式主干

主图(main graph)是一条几乎笔直的流水线,只有开头一处会分叉(需要澄清就直接结束、等用户回话)。建图代码在 deep_researcher.py:699-719:

用户问题


┌────────────────────┐ need_clarification?
│ clarify_with_user │──── 是 ──▶ END(抛出一个澄清问题,等用户)
│ 判断要不要反问 │ deep_researcher.py:60-115
└─────────┬──────────┘
│ 否 / 关闭澄清

┌────────────────────┐
│ write_research_brief│ 把对话浓缩成一句"研究纲要"research_brief
│ 写研究纲要 │ 并初始化 supervisor 的消息上下文
└─────────┬──────────┘ deep_researcher.py:118-175

╔════════════════════╗
║ research_supervisor ║ ← 这是一个【子图】,内部是 supervisor 循环
║ (supervisor 子图) ║ 它会 fan-out 出多个并行 researcher 子图
╚═════════╤══════════╝ deep_researcher.py:710

┌────────────────────┐
│final_report_generation│ 把所有 notes 合成最终报告(含 token 超限重试)
│ 写终稿 │ deep_researcher.py:607-697
└─────────┬──────────┘

END(final_report)

四条主边非常直白(deep_researcher.py:714-716):START → clarify_with_user,research_supervisor → final_report_generation,final_report_generation → END;而 clarify_with_userwrite_research_brief 的去向用 Command(goto=...) 在节点内部动态决定(deep_researcher.py:77106-115163-164)。

2.2 supervisor 如何 fan-out 出并行 researcher

research_supervisor 不是一个普通节点,而是一个子图(deep_researcher.py:710supervisor_subgraph 当节点挂上去)。它内部是一个"想 → 干"的循环(deep_researcher.py:353-363 建图):

┌──────────────┐
START ─▶│ supervisor │ 调研究模型,决定调用哪些工具
└──────┬───────┘ deep_researcher.py:178-223

┌──────────────┐ ResearchComplete / 超迭代 / 无工具调用 ─▶ END
│supervisor_ │
│ tools │ ConductResearch × N ──┐
└──────┬───────┘ deep_researcher.py:225-349
│ │ asyncio.gather 并行
│ 还要继续研究 ▼
│ ┌───────────────────────┐
└────────回到 supervisor │ researcher 子图 × N │
│ (每个是独立 ReAct 循环)│
│ deep_researcher.py:589-605
└───────────────────────┘

关键在 supervisor_tools:主管每轮可以发出多个 ConductResearch 工具调用,代码把它们截到 max_concurrent_research_units 个(deep_researcher.py:291),然后用 asyncio.gather 同时 invoke 多份 researcher_subgraph(deep_researcher.py:295-305)——这就是并行 fan-out。每个子研究员返回压缩后的研究结果,汇回主管消息里(deep_researcher.py:308-313),循环继续直到主管喊停。

具体的委派逻辑、ReAct 循环、并行细节见 02-supervisor-researcher-loop.md;收尾的压缩与终稿容错见 03-compression-token-limits.md

2.3 部件一句话职责

部件干什么在哪(文件:符号)
clarify_with_user判断问题是否需要反问澄清,不需要就放行deep_researcher.py:60
write_research_brief把对话浓缩成结构化研究纲要,初始化 supervisor 上下文deep_researcher.py:118
supervisor主管:读纲要,决定用 think_tool 规划、还是 ConductResearch 派活deep_researcher.py:178
supervisor_tools执行主管的工具调用;并行 fan-out researcher 子图;判断收尾deep_researcher.py:225
researcher单个研究员:带工具跑一轮,产生工具调用deep_researcher.py:365
researcher_tools执行研究员的搜索/MCP 工具调用,决定继续还是去压缩deep_researcher.py:435
compress_research把一个研究员的所有原始资料压成干净摘要deep_researcher.py:511
final_report_generation汇总所有 notes 写最终报告,带 token 超限重试deep_researcher.py:607

3. 三层 StateGraph 嵌套 + 四个 LLM 角色

3.1 三层图是"图套图"

整个系统由三张 StateGraph 嵌套而成——外层图把内层图当作一个节点直接挂进去。这是理解全局最关键的一张结构图:

┌─────────────────────────────────────────────────────────┐
│ 主图 deep_researcher (state: AgentState) │
│ deep_researcher.py:701-719 │
│ clarify → write_brief → [research_supervisor] → report │
│ │ │
│ 把子图当一个节点挂进来(第 710 行) │
│ ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ supervisor 子图 (state: SupervisorState) │ │
│ │ deep_researcher.py:353-363 │ │
│ │ supervisor ⇄ supervisor_tools │ │
│ │ │ gather 并行 invoke 多份 │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ researcher 子图 (state: ResearcherState) │ │ │
│ │ │ deep_researcher.py:589-605 │ │ │
│ │ │ researcher ⇄ researcher_tools → compress │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

每层图有自己独立的 state 类型,互不污染(定义在 state.py):

State 类型关键字段定义处
主图AgentStatemessages / research_brief / notes / final_reportstate.py:65-72
supervisor 子图SupervisorStatesupervisor_messages / research_iterations / notesstate.py:74-81
researcher 子图ResearcherStateresearcher_messages / tool_call_iterations / compressed_researchstate.py:83-90

层与层之间靠 Command(update=...) 显式传值:比如 write_research_briefresearch_brief 塞进 supervisor 上下文(deep_researcher.py:163-174),supervisor_tools 又给每个 researcher 子图喂一个 research_topic(deep_researcher.py:296-301)。多个并行分支往同一字段(如 notesraw_notes)写值时,用 override_reducer 决定是覆盖还是累加(state.py:55-60)。这套状态流转是下一章 01-orchestration-graph.md 的主题。

3.2 四个可独立配置的 LLM 角色

系统不是"一个模型干到底",而是把工作切成四个角色,每个角色都能在配置里单独换模型(configuration.py),让你把便宜模型用在量大的活、把强模型用在关键的活:

角色配置字段默认模型用途定义处
摘要summarization_modelopenai:gpt-4.1-mini压缩单条搜索结果网页configuration.py:121
研究research_modelopenai:gpt-4.1驱动 supervisor 和 researcher(拆题、搜索决策)configuration.py:153
压缩compression_modelopenai:gpt-4.1把一个研究员的全部发现压成摘要configuration.py:173
终稿final_report_modelopenai:gpt-4.1合成最终报告configuration.py:193

所有节点都通过同一个 configurable_model(一个 configurable_fields 可运行时改的 init_chat_model,deep_researcher.py:56-58)+ 各自的 .with_config({"model": ...}) 来切换到对应角色的模型。除了模型,还有并发上限 max_concurrent_research_units(默认 5,configuration.py:64)、主管迭代上限 max_researcher_iterations(默认 6,configuration.py:94)、单研究员工具调用上限 max_react_tool_calls(默认 10,configuration.py:107)等关键旋钮。


4. 主线走一遍(高层,不进代码)

跟着一个问题走完全程,只讲"经过谁、传了什么",细节留给后续章节:

  1. 进澄清关。 用户问题进 clarify_with_user。若配置关了澄清、或模型判断问题够清楚,直接放行到写纲要;否则抛出一个反问、结束本轮等用户补充(deep_researcher.py:75-115)。

  2. 写研究纲要。 write_research_brief 用研究模型把整段对话浓缩成一句聚焦的 research_brief,并把它作为 HumanMessage、连同主管系统提示一起塞进 supervisor_messages,交棒给 supervisor 子图(deep_researcher.py:149-175)。

  3. 主管拆题派活。 supervisor 读纲要,决定调用哪些工具:think_tool 做规划反思、ConductResearch 派子课题、ResearchComplete 收工(deep_researcher.py:202)。

  4. 并行研究。 supervisor_tools 把本轮所有 ConductResearch 截到并发上限,用 asyncio.gather 并行跑多个 researcher 子图(deep_researcher.py:291-305)。每个研究员在自己的 ReAct 循环里搜索、反思,查完由 compress_research 压成摘要返回。

  5. 回主管,循环或收尾。 主管把各研究员的压缩结果收进消息继续下一轮;当喊 ResearchComplete、超过迭代上限、或没有工具调用时,子图结束,把汇总的 notes 交回主图(deep_researcher.py:255-262)。

  6. 写终稿。 final_report_generation 把所有 notes 拼起来,用终稿模型生成最终报告;若 token 超限就按模型上限渐进截断 findings 重试(deep_researcher.py:634-697)。产出 final_report,主图结束。


5. 巧妙之处(可带走的精华)

  • "图套图"的三层嵌套 + 每层独立 state。 子图直接被当节点挂进外层(deep_researcher.py:710),LangGraph 负责跨层传值,让"主管—研究员"这种层级团队天然映射成代码结构,而每层只关心自己的字段(state.py:65-90)。

  • fan-out 用 asyncio.gather,一句话实现并行子研究员。 不需要专门的并发框架,列表推导出多份子图 invoke、gather 收齐即可(deep_researcher.py:295-305),并用切片做并发上限保护(deep_researcher.py:291-292)。

  • 一份 configurable_model,四个角色随处切换。init_chat_model(configurable_fields=...)(deep_researcher.py:56-58)+ 各节点 .with_config 就能给不同环节配不同模型,兼顾成本与质量(configuration.py:121-212)。

  • override_reducer:同一字段既能累加又能覆盖。 通过在写入值里带 {"type": "override"} 标记,同一个 reducer 支持两种语义——重置 supervisor 上下文时覆盖、并行分支汇总 notes 时累加(state.py:55-60,用例见 deep_researcher.py:167-172622)。

  • "够清楚就别问"的前置澄清。 用结构化输出 ClarifyWithUser 让模型自己判断要不要反问,可一键关闭(deep_researcher.py:75state.py:30-41),在自动化程度和交互质量之间留了开关。

  • 压缩与终稿两处都内建 token 超限重试。 研究员压缩时按需丢弃旧消息重试(deep_researcher.py:565-574),终稿生成时按模型上限渐进截断 findings(deep_researcher.py:663-683)——把 LLM 最常见的失败模式(上下文爆掉)当一等公民处理。详见 03-compression-token-limits.md


6. 阅读地图

建议顺序(由浅入深):

顺序章节讲什么什么时候读
0index.md(本章)全景 / 主线 / 路由先读,建立心智模型
101-orchestration-graph.md三层 LangGraph 怎么建、state 与 reducer 怎么流转想搞懂"图套图"和状态传递
202-supervisor-researcher-loop.mdsupervisor 委派 + researcher ReAct + 并行 fan-out想搞懂核心研究引擎
303-compression-token-limits.md研究压缩、token 超限重试、终稿生成想搞懂容错与收尾
404-tools-search-mcp.md搜索抽象、Tavily 摘要管线、MCP 集成与鉴权想搞懂工具装配与外部集成

7. 代码地图(导航索引)

主题文件符号
图入口登记langgraph.jsongraphs["Deep Researcher"]deep_researcher
主图建图src/open_deep_research/deep_researcher.pydeep_researcher_builder / deep_researcher
澄清节点src/open_deep_research/deep_researcher.pyclarify_with_user
写研究纲要src/open_deep_research/deep_researcher.pywrite_research_brief
supervisor 子图src/open_deep_research/deep_researcher.pysupervisor_builder / supervisor_subgraph
主管逻辑 / 派活src/open_deep_research/deep_researcher.pysupervisor / supervisor_tools
researcher 子图src/open_deep_research/deep_researcher.pyresearcher_builder / researcher_subgraph
研究员 ReActsrc/open_deep_research/deep_researcher.pyresearcher / researcher_tools
研究压缩src/open_deep_research/deep_researcher.pycompress_research
终稿生成src/open_deep_research/deep_researcher.pyfinal_report_generation
可配置模型句柄src/open_deep_research/deep_researcher.pyconfigurable_model
四个 LLM 角色 + 旋钮src/open_deep_research/configuration.pyConfiguration(research_model / summarization_model / compression_model / final_report_model)
三层 statesrc/open_deep_research/state.pyAgentState / SupervisorState / ResearcherState
覆盖/累加 reducersrc/open_deep_research/state.pyoverride_reducer
结构化输出模型src/open_deep_research/state.pyClarifyWithUser / ResearchQuestion / ConductResearch / ResearchComplete