4-接入LangGraph与LangChain
4 - 深度研搜:接入 LangGraph 与 LangChain
本章课程目标:
- 理解为什么 DeepAgents 需要复用已有 LangGraph / LangChain 能力。
- 掌握
CompiledSubAgent的作用和基本配置方式。 - 理解 LangGraph 子智能体为什么必须包含可累加的
messages状态字段。 - 能判断复杂子任务应该交给 LangGraph,还是交给 LangChain Agent。
学习建议: 这一章学的是“复用现成能力”,不是重新学 LangGraph 或 LangChain。读的时候重点看 CompiledSubAgent 如何把已有图、Agent 或 Runnable 包成 DeepAgents 能调度的子智能体。如果 StateGraph、add_messages、条件边、@tool、create_agent() 还不熟,先回看对应章节,否则这里会像在看一层薄薄的适配胶水。
对应代码分支: 04-deepagents-langgraph-langchain
上一章我们学习了字典式子智能体。它适合定义简单、轻量、职责明确的助手,比如天气助手、数学助手、翻译助手。
但真实项目里,经常已经有一些现成能力:
- 前面用 LangGraph 写好的工作流;
- 前面用 LangChain
create_agent()创建的工具型 Agent; - 已经封装好的 Runnable;
- 需要多节点状态流转、条件分支或工具选择的复杂子任务。
这些能力如果全部重写成字典式子智能体,成本很高,也没有必要。DeepAgents 提供了 CompiledSubAgent,它的作用就是把已有图、Agent 或 Runnable 封装成 DeepAgents 可以调度的子智能体。
本章对应两个示例文件:
| 文件 | 主题 | 本章示例的业务含义 |
|---|---|---|
6-langgraph-subagent-wrapper.py |
兼容 LangGraph | 把“研究任务规划” LangGraph 工作流封装成子智能体 |
7-langchain-agent-subagent-wrapper.py |
兼容 LangChain | 把“资料检索” LangChain 工具型 Agent 封装成子智能体 |
1、先看整体关系:DeepAgents 负责调度
学习这一章时,先不要把 LangGraph、LangChain 和 DeepAgents 混在一起看。它们在本章里的分工很清楚:
1 | 用户问题 |
图 4-1 说明: DeepAgents 主智能体像一个调度层,它不需要关心 LangGraph 子图里有几个节点,也不需要知道 LangChain Agent 具体调用了哪个工具。它只要知道:某个子智能体能完成什么任务,以及应该在什么时候把任务交出去。
所以本章的主线其实只有一句话:
已经写好的 LangGraph 图或 LangChain Agent,不用推倒重写,可以通过
CompiledSubAgent接到 DeepAgents 主智能体下面。
理解了这一点,再看后面的两个例子就会轻松很多。
2、CompiledSubAgent 是什么
2.1 字典子智能体和 CompiledSubAgent 的区别
DeepAgents 中常见的子智能体配置方式有两种。
| 方式 | 适合场景 | 特点 |
|---|---|---|
| 字典配置 | 简单子智能体 | 写法简单,适合只配置提示词和工具 |
CompiledSubAgent |
复杂子智能体 | 可以接入已有 LangGraph / LangChain / Runnable |
字典方式更像“直接声明一个助手”:
1 | weather_agent = { |
CompiledSubAgent 更像“把已经写好的能力包一层,交给 DeepAgents 调度”:
1 | sub_agent = CompiledSubAgent( |
2.2 三个核心参数
CompiledSubAgent 最核心的是三个参数:
| 参数 | 说明 |
|---|---|
name |
子智能体名称,主智能体通过它识别子智能体 |
description |
子智能体能力说明,主智能体靠它判断什么时候调用 |
runnable |
真正执行任务的对象,可以是编译后的 LangGraph 图或 LangChain Agent |
也就是说,CompiledSubAgent 自己不负责写业务逻辑。业务逻辑在 runnable 里,它只负责把这个能力包装成 DeepAgents 能识别的子智能体。
3、接入 LangGraph:研究任务规划子图
3.1 这个例子解决什么问题
项目对应文件路径:deepsearch-agents/examples/6-langgraph-subagent-wrapper.py
这一节的 LangGraph 示例,是一个贴近「深度研搜」场景的子能力:研究任务规划器。
它不是简单返回一句“我处理完了”,而是模拟下面这条链路:
1 | 用户提出研究问题 |
这一节会用到你前面已经学过的 LangGraph 知识点:
| 本章用法 | 对应仓库案例 |
|---|---|
messages + add_messages |
案例与源码-3-LangGraph框架/03-state/reducers/StateReducer_AddMessages.py |
| 条件边 | 案例与源码-3-LangGraph框架/05-edge/Edge_Conditional.py |
| 多节点业务流 | 案例与源码-3-LangGraph框架/01-helloworld/LangGraphBiz.py |
3.2 State 为什么必须包含 messages
LangGraph 子图要接入 DeepAgents,最关键的要求是:State 里必须包含 messages 字段。
1 | class ResearchPlanState(TypedDict): |
这里有两个重点。
**第一,**DeepAgents 会通过 messages 把任务传给子图,也会从 messages 里读取子图返回结果。
第二,messages 要配合 add_messages 使用。这样节点返回的新消息会追加到原消息列表,而不是覆盖历史消息。
如果写成普通的:
1 | messages: list |
后面的节点很容易只看到最后一次状态,前面的用户问题、模型调度和子图中间结果都会丢失。
所以接入 DeepAgents 的 LangGraph 子图,一般要保留这种写法:
1 | messages: Annotated[list, add_messages] |
3.3 定义研究规划节点
第一个节点负责从任务中提取研究主题,并判断研究深度。
1 | def extract_topic(state: ResearchPlanState): |
这里故意没有调用大模型,而是用简单规则模拟主题提取和深度判断。这样你可以先看清楚 LangGraph 的状态流转,不会被模型输出的不确定性带偏。
3.4 使用条件边选择规划路径
如果任务是普通查询,就走 quick_plan;如果任务是深度研究,就走 deep_plan。
1 | def route_by_depth(state: ResearchPlanState): |
这部分对应前面 LangGraph 教程中的条件边:节点执行完成后,由路由函数读取当前 state,再决定下一步走向。
在「深度研搜」项目里,这类条件边很常见。例如:
- 资料是否足够,不够就继续搜索;
- 用户问题是否需要数据库查询;
- 检索结果是否需要交叉验证;
- 报告是否需要补充案例。
3.5 生成计划并汇总结果
深度研究计划节点会生成更完整的执行步骤:
1 | def build_deep_plan(state: ResearchPlanState): |
最后由 finalize_plan() 把状态里的 topic、depth、plan 汇总成一条 AIMessage,返回给 DeepAgents 主智能体。
3.6 封装成 CompiledSubAgent
LangGraph 图编译完成后,就可以作为 runnable 传给 CompiledSubAgent。
1 | compiled_graph = workflow.compile() |
最后注册到 DeepAgents 主智能体:
1 | main_agent = create_deep_agent( |
3.7 执行输出
执行文件验证,成功:
主智能体生成的 tool_calls、LangGraph 子图内部的打印、子图返回的 ToolMessage,以及主智能体最后整理出的 AIMessage。
1 | tools@ToolsMacBook-Pro deepsearch-agents % uv run examples/6-langgraph-subagent-wrapper.py |
上面这段输出省略了 token_usage、id 等调试字段,保留的是最适合读者理解执行链路的部分。开头的 LangChainPendingDeprecationWarning 是依赖库提示,不影响本例运行。
这段输出可以按四个位置来看:
| 输出片段 | 说明 |
|---|---|
tool_calls -> research_planner_graph |
主智能体决定通过 task 调用 LangGraph 子智能体 |
【LangGraph】提取研究主题... |
子图内部节点正在执行,这是 LangGraph 自己的业务流程 |
ToolMessage(content='研究主题...') |
子图执行完成,结果作为 task 工具返回给主智能体 |
最后一条 AIMessage(content=...) |
主智能体基于子图结果整理最终回复 |
这里最重要的是:DeepAgents 主智能体不需要知道 LangGraph 子图内部有几个节点,它只需要知道有一个叫 research_planner_graph 的子智能体可以完成“研究规划”。
4、接入 LangChain:资料检索 Agent
4.1 这个例子解决什么问题
项目对应文件路径:deepsearch-agents/examples/7-langchain-agent-subagent-wrapper.py
这一节的 LangChain Agent 示例,是一个贴近「深度研搜」场景的子能力:资料检索助手。
它负责在自己的工具集中选择合适工具:
1 | 用户问题 |
这一节会用到你前面已经学过的 LangChain 知识点:
| 本章用法 | 对应仓库案例 |
|---|---|
@tool 工具封装 |
案例与源码-2-LangChain框架/08-tools/QueryWeatherTool.py |
create_agent() |
案例与源码-2-LangChain框架/12-agent/AgentSmartSelectV1.0.py |
| 结构化结果意识 | 案例与源码-2-LangChain框架/05_parser/StructuredOutput_TypedDict.py |
4.2 定义资料检索工具
示例里没有直接调用真实搜索引擎或企业知识库,而是先用两个模拟工具表达真实项目中的两类资料来源。
1 |
|
另一个工具模拟企业内部知识库:
1 |
|
这里的关键不是模拟数据本身,而是工具边界:
- 公开资料工具负责外部趋势;
- 内部知识库工具负责企业经验;
- LangChain Agent 负责判断什么时候调用哪个工具。
4.3 创建 LangChain Agent
接下来用 create_agent() 创建一个普通 LangChain Agent。
1 | research_retriever_agent = create_agent( |
这个 Agent 本身已经能根据用户问题调用工具。DeepAgents 需要做的,不是重写它,而是把它包成子智能体。
4.4 封装成 DeepAgents 子智能体
1 | research_retriever_subagent = CompiledSubAgent( |
然后注册到 DeepAgents 主智能体:
1 | deep_agent = create_deep_agent( |
注意这里主智能体的 tools=[]。也就是说,公开搜索工具和内部知识库工具没有直接挂在 DeepAgents 主智能体上,而是挂在 LangChain 子 Agent 上。
这就形成了两层职责:
1 | DeepAgents 主智能体:决定是否需要资料检索 |
4.5 执行输出
执行文件验证,成功:
这段输出比 LangGraph 示例更能体现“工具型 Agent”的特点:主智能体一次派出两个 task,LangChain Agent 再分别调用公开资料工具和内部知识库工具。
1 | tools@ToolsMacBook-Pro deepsearch-agents % uv run examples/7-langchain-agent-subagent-wrapper.py |
这段输出对应的含义是:
| 输出片段 | 说明 |
|---|---|
task -> research_retriever_agent |
DeepAgents 主智能体把检索任务交给 LangChain Agent |
【LangChain Tool】检索公开资料... |
LangChain Agent 调用了公开资料工具 |
【LangChain Tool】检索内部知识库... |
LangChain Agent 调用了内部知识库工具 |
ToolMessage(content=...) |
LangChain Agent 的最终结果返回给 DeepAgents 主智能体 |
最后一条 AIMessage(content=...) |
主智能体整理最终答复 |
这个例子里,主智能体一次生成了两个 task 调用:一个检索公开趋势,一个检索内部经验。真正选择并执行 search_public_web、search_internal_knowledge_base 的,是被封装进去的 LangChain Agent。
5、两种兼容方式怎么选
5.1 LangGraph 适合什么场景
如果子任务内部有明确的状态流转、多节点处理、条件判断,推荐用 LangGraph。
例如:
- 研究问题 -> 提取主题 -> 判断研究深度 -> 生成计划 -> 汇总;
- 数据库查询 -> SQL 生成 -> SQL 校验 -> 执行 -> 返回结果;
- 多路召回 -> 合并 -> 排序 -> 过滤;
- 报告生成 -> 反思缺口 -> 补充资料 -> 重新生成。
这类任务本身就是一张图,封装成 LangGraph 子智能体更自然。
5.2 LangChain Agent 适合什么场景
如果子任务更像“一个会自己选工具的 Agent”,可以用 LangChain Agent。
例如:
- 根据问题选择公开搜索工具或内部知识库工具;
- 根据用户输入选择不同 API;
- 已经写好的 ReAct / Tool Calling Agent 需要复用;
- 希望把工具选择细节隔离在子智能体内部。
它不一定需要复杂图结构,只要能作为 Runnable 执行,就可以通过 CompiledSubAgent 接入。
5.3 选择建议
可以用下面这张表快速判断。
| 问题 | 推荐方式 |
|---|---|
| 子任务流程固定、有多个步骤 | LangGraph + CompiledSubAgent |
| 子任务需要条件分支和状态流转 | LangGraph |
| 子任务是已有 LangChain Agent | LangChain Agent + CompiledSubAgent |
| 子任务核心是工具选择 | LangChain Agent |
| 子任务只是简单提示词和工具 | 字典式子智能体 |
1 | flowchart TD |
放到「深度研搜」项目里,可以这样分工:
1 | DeepAgents:总调度,决定下一步交给谁 |
5.4 容易踩坑的地方
| 问题 | 建议 |
|---|---|
LangGraph 子图没有 messages 字段 |
接入 DeepAgents 时必须保留 messages: Annotated[list, add_messages] |
| 主智能体和子智能体都挂同一批工具 | 尽量让工具归属于真正执行任务的那一层,主智能体保持调度职责 |
description 写得太泛 |
写清楚“适合什么任务”,主智能体才更容易选对子智能体 |
| 子任务只是一次确定函数调用 | 优先做成普通工具,不必为了拆分而拆分 |
| 已有 LangGraph / LangChain 能力能复用 | 优先用 CompiledSubAgent 包装,不要重复造一套 |
本章小结:
这一章讲的是 DeepAgents 和已有生态的连接方式。
字典式子智能体适合简单任务,但如果你已经有 LangGraph 图或 LangChain Agent,就不应该重写一遍,而是用 CompiledSubAgent 封装。
兼容 LangGraph 时,要特别注意 State 中必须包含 messages,并且这个字段要能累加消息。因为 DeepAgents 通过 messages 给子智能体传任务,也通过它读取结果。
兼容 LangChain 时,思路更像“把已有工具型 Agent 接进来”:工具仍然挂在 LangChain Agent 上,DeepAgents 主智能体只负责通过 task 分派任务。
请记住: CompiledSubAgent 是 DeepAgents 连接外部 Agent 能力的适配器,它让已有 LangGraph / LangChain 能力可以继续复用。