LangGraph和LangChain

CompiledSubAgent:

  作用就是把已有图、Agent 或 Runnable 封装成 DeepAgents 可以调度的子智能体

已经写好的 LangGraph 图或 LangChain Agent,不用推倒重写,可以通过 CompiledSubAgent 接到 DeepAgents 主智能体下面。

字典子智能体和CompiledSubAgent的区别

方式 适合场景 特点
字典配置 简单子智能体 写法简单,适合只配置提示词和工具
CompiledSubAgent 复杂子智能体 可以接入已有 LangGraph / LangChain / Runnable

字典方式更像“直接声明一个助手”:

weather_agent = {
    "name": "weather_helper",
    "description": "用于查询天气信息。",
    "system_prompt": "你是一个天气助手。",
    "tools": [],
}

CompiledSubAgent 更像“把已经写好的能力包一层,交给 DeepAgents 调度”:

sub_agent = CompiledSubAgent(
    name="research_planner_graph",
    description="用于把开放研究问题拆解成可执行的研究计划。",
    runnable=compiled_graph,
)

三个核心参数:

参数 说明
name 子智能体名称,主智能体通过它识别子智能体
description 子智能体能力说明,主智能体靠它判断什么时候调用
runnable 真正执行任务的对象,可以是编译后的 LangGraph 图或 LangChain Agent

也就是说,CompiledSubAgent 自己不负责写业务逻辑。业务逻辑在 runnable 里,它只负责把这个能力包装成 DeepAgents 能识别的子智能体

State要包含Messages

LangGraph 子图要接入 DeepAgents,最关键的要求是:State 里必须包含 messages 字段

class ResearchPlanState(TypedDict):
    messages: Annotated[list, add_messages]
    topic: str
    depth: Literal["quick", "deep"]
    plan: list[str]

第一,DeepAgents 会通过 messages 把任务传给子图,也会从 messages 里读取子图返回结果。

第二,messages 要配合 add_messages 使用。这样节点返回的新消息会追加到原消息列表,而不是覆盖历史消息

写成普通的:messages: list,后面的节点很容易只看到最后一次状态,前面的用户问题、模型调度和子图中间结果都会丢失.所以接入 DeepAgents 的 LangGraph 子图,一般要保留这种写法: messages: Annotated[list, add_messages]

定义研究规划节点

第一个节点负责从任务中提取研究主题,并判断研究深度

def extract_topic(state: ResearchPlanState):
    """从用户任务中提取研究主题,并判断是否需要深度研究。"""
    task = state["messages"][-1].content
    depth = "deep" if any(word in task for word in ["深度", "报告", "系统", "趋势"]) else "quick"
    topic = (
        task.replace("请", "")
        .replace("帮我", "")
        .replace("生成", "")
        .replace("一份", "")
        .strip("。 ")
    )
    print(f"【LangGraph】提取研究主题:{topic}")
    print(f"【LangGraph】判断研究深度:{depth}")
    return {
        "topic": topic,
        "depth": depth,
        "messages": [AIMessage(content=f"已识别研究主题:{topic};研究深度:{depth}")],
    }
# 这里故意没有调用大模型,而是用简单规则模拟主题提取和深度判断。这样你可以先看清楚 LangGraph 的状态流转,不会被模型输出的不确定性带偏

 使用条件边选择规划路径

  1. 普通查询,就走 quick_plan
  2. 深度研究,就走 deep_plan
def route_by_depth(state: ResearchPlanState):
    """条件边:根据研究深度选择普通规划或深度规划。"""
    return state["depth"]


workflow.add_conditional_edges(
    "extract_topic",
    route_by_depth,
    {
        "quick": "quick_plan",
        "deep": "deep_plan",
    },
)

 生成计划并汇总结果

def build_deep_plan(state: ResearchPlanState):
    """深度研究规划:适合报告、趋势分析、行业调研等长链路任务。"""
    topic = state["topic"]
    plan = [
        f"明确「{topic}」的研究范围、时间窗口和核心问题",
        "搜索公开资料,优先收集权威媒体、机构报告和官方信息",
        "拆分技术、产业、公司案例、风险四个方向分别整理证据",
        "对不同来源的信息做交叉验证,记录冲突和缺口",
        "输出结构化研究报告:背景、现状、趋势、案例、风险、结论",
    ]
    print("【LangGraph】进入 deep_plan 节点")
    return {
        "plan": plan,
        "messages": [AIMessage(content="已生成深度研究计划。")],
    }
# 最后由 finalize_plan() 把状态里的 topic、depth、plan 汇总成一条 AIMessage,返回给 DeepAgents 主智能体

封装成CompiledSubAgent 

 LangGraph 图编译完成后,就可以作为 runnable 传给 CompiledSubAgent

compiled_graph = workflow.compile()

research_planner_graph = CompiledSubAgent(
    name="research_planner_graph",
    description="用于把开放研究问题拆解成可执行的研究计划,适合行业趋势、技术调研、报告规划等任务。",
    runnable=compiled_graph,
)

 注册到 DeepAgents 主智能体

main_agent = create_deep_agent(
    model=llm,
    tools=[],
    subagents=[research_planner_graph],
    system_prompt="""
    你是深度研搜系统的主智能体。
    当用户需要研究规划、报告大纲、趋势调研步骤时,必须调用 research_planner_graph。
    你不自己编造研究计划,而是根据子智能体返回的计划整理最终回复。
    """,
)

 

这里最重要的是:DeepAgents 主智能体不需要知道 LangGraph 子图内部有几个节点,它只需要知道有一个叫 research_planner_graph 的子智能体可以完成“研究规划

 

接入langChain: 资料搜索Agent

定义资料检索工具

示例里没有直接调用真实搜索引擎或企业知识库,而是先用两个模拟工具表达真实项目中的两类资料来源

@tool
def search_public_web(query: str) -> str:
    """
    检索公开网络资料。

    参数:
        query: 用户关注的研究主题或关键词。

    返回:
        模拟的公开资料摘要。真实项目中可以替换为 Tavily、搜索引擎 API 或爬虫服务。
    """
    print(f"【LangChain Tool】检索公开资料:{query}")
    return (
        "公开资料检索结果:\n"
        "1. 多家机构认为具身智能正在推动机器人从单点自动化走向通用任务执行。\n"
        "2. 机器人产业热点集中在大模型控制、灵巧手、低成本传感器和仿真训练。"
    )

另一个工具模拟企业内部知识库:

@tool
def search_internal_knowledge_base(query: str) -> str:
    """检索企业内部知识库。"""
    print(f"【LangChain Tool】检索内部知识库:{query}")
    return (
        "内部知识库检索结果:\n"
        "1. 历史项目复盘显示,客户最关注机器人方案的稳定性、部署周期和维护成本。\n"
        "2. 销售材料中高频卖点包括:多模态感知、自动任务分解、远程运维和持续学习。"
    )

创建LangChain Agent

# 用 create_agent() 创建一个普通 LangChain Agent
research_retriever_agent = create_agent(
    model=llm,
    tools=[search_public_web, search_internal_knowledge_base],
    system_prompt="""
    你是资料检索助手,负责为深度研究任务收集资料。
    当用户需要行业公开信息时,调用 search_public_web。
    当用户需要企业内部经验、项目复盘或知识库内容时,调用 search_internal_knowledge_base。
    如果问题同时涉及公开趋势和内部经验,可以两个工具都调用。
    最后请用中文输出:资料来源、关键发现、后续建议。
    """,
)

封装成子智能体 

research_retriever_subagent = CompiledSubAgent(
    name="research_retriever_agent",
    description="用于检索公开资料和企业内部知识库,适合为深度研究报告收集证据和背景信息。",
    runnable=research_retriever_agent,
)

注册到 DeepAgents 主智能体:

deep_agent = create_deep_agent(
    model=llm,
    tools=[],
    system_prompt="""
    你是深度研搜系统的主智能体。
    当用户需要查找资料、收集证据、检索公开信息或内部知识库时,必须调用 research_retriever_agent。
    你不直接检索资料,只负责分派任务并整理子智能体返回的结果。
    """,
    subagents=[research_retriever_subagent],
)
# 主智能体的 tools=[]。也就是说,公开搜索工具和内部知识库工具没有直接挂在 DeepAgents 主智能体上,而是挂在 LangChain 子 Agent 上
  1.  DeepAgents 主智能体:决定是否需要资料检索
  2. LangChain 子智能体:决定调用公开资料工具还是内部知识库工具

两种方式选择

  1. LangGraph : 如果子任务内部有明确的状态流转、多节点处理、条件判断
    1. 研究问题 -> 提取主题 -> 判断研究深度 -> 生成计划 -> 汇总
    2. 数据库查询 -> SQL 生成 -> SQL 校验 -> 执行 -> 返回结果
    3. 多路召回 -> 合并 -> 排序 -> 过滤
    4. 报告生成 -> 反思缺口 -> 补充资料 -> 重新生成
  2. LangChain Agent : 子任务更像“一个会自己选工具的 Agent”
    1. 根据问题选择公开搜索工具或内部知识库工具
    2. 根据用户输入选择不同 API
    3. 已经写好的 ReAct / Tool Calling Agent 需要复用
    4. 希望把工具选择细节隔离在子智能体内部
  3. CompiledSubAgent: 不一定需要复杂图结构,只要能作为 Runnable 执行.

选择建议:

问题 推荐方式
子任务流程固定、有多个步骤 LangGraph + CompiledSubAgent
子任务需要条件分支和状态流转 LangGraph
子任务是已有 LangChain Agent LangChain Agent + CompiledSubAgent
子任务核心是工具选择 LangChain Agent
子任务只是简单提示词和工具 字典式子智能体

image

注意踩坑

问题 建议
LangGraph 子图没有 messages 字段 接入 DeepAgents 时必须保留 messages: Annotated[list, add_messages]
主智能体和子智能体都挂同一批工具 尽量让工具归属于真正执行任务的那一层,主智能体保持调度职责
description 写得太泛 写清楚“适合什么任务”,主智能体才更容易选对子智能体
子任务只是一次确定函数调用 优先做成普通工具,不必为了拆分而拆分
已有 LangGraph / LangChain 能力能复用 优先用 CompiledSubAgent 包装,不要重复造一套

 

posted @ 2026-06-22 09:53  幻影之舞  阅读(2)  评论(0)    收藏  举报