LangGraph知识点总结_进阶篇

LangGraph 知识点详细总结 --- 进阶篇

本篇为进阶篇,覆盖:持久化、人在回路、流式输出、记忆、时间旅行、子图、多Agent三大模式、DeepAgents、框架对比、API速查。

基础内容见 LangGraph知识点总结_基础篇.md


目录

  • [13. 持久化(Persistence)](#13. 持久化(Persistence))
  • [14. 人在回路(Human-in-the-Loop)](#14. 人在回路(Human-in-the-Loop))
  • [15. 流式输出(Streaming)](#15. 流式输出(Streaming))
  • [16. 记忆系统(Memory)](#16. 记忆系统(Memory))
  • [17. 时间旅行(Time Travel)](#17. 时间旅行(Time Travel))
  • [18. 子图(Subgraphs)](#18. 子图(Subgraphs))
  • [19. 多Agent架构 --- Agent-as-Tool](#19. 多Agent架构 — Agent-as-Tool)
  • [20. 多Agent架构 --- Handoff](#20. 多Agent架构 — Handoff)
  • [21. 多Agent架构 --- Supervisor](#21. 多Agent架构 — Supervisor)
  • [22. 三大多Agent模式对比](#22. 三大多Agent模式对比)
  • [23. DeepAgents --- 开箱即用的 Agent 框架](#23. DeepAgents — 开箱即用的 Agent 框架)
  • [24. LangGraph vs LangChain vs Dify 全面对比](#24. LangGraph vs LangChain vs Dify 全面对比)
  • [25. API 速查表](#25. API 速查表)

13. 持久化(Persistence)

13.1 为什么需要持久化

LangGraph 内置持久化层,将图状态保存为检查点(Checkpoint)。持久化是以下功能的基础:

功能 说明
人在回路 暂停执行,等待人类确认后恢复
对话记忆 同一会话的多轮对话保持上下文
时间旅行 回溯到之前的状态,重新执行
容错恢复 节点失败后从上一个成功的检查点恢复

13.2 核心概念

线程(Thread) :每个对话的唯一标识,通过 thread_id 区分:

python 复制代码
config = {"configurable": {"thread_id": "thread-1"}}
# thread_id 是检查点的主键
# 同一 thread_id = 同一对话
# 不同 thread_id = 不同对话

检查点(Checkpoint):每个超步后保存的状态快照:

复制代码
图执行 START → node_a → node_b → END 后,会产生 4 个检查点:
检查点 0: 空状态,下一个节点是 START
检查点 1: 用户输入,下一个节点是 node_a
检查点 2: node_a 的输出,下一个节点是 node_b
检查点 3: node_b 的输出,无下一个节点(完成)

超步(Super-step):检查点在每个超步边界保存:

复制代码
超步 0: 输入阶段
超步 1: node_a 执行
超步 2: node_b 执行
每个超步结束后 → 保存检查点

13.3 使用 Checkpointer

python 复制代码
from langgraph.checkpoint.memory import MemorySaver

# 内存检查点(开发/测试用)
checkpointer = MemorySaver()
graph = builder.compile(checkpointer=checkpointer)

# 执行时必须指定 thread_id
result = graph.invoke(
    {"messages": [HumanMessage(content="你好")]},
    config={"configurable": {"thread_id": "thread-1"}}
)

# 同一 thread_id 的后续调用会保留之前的上下文
result2 = graph.invoke(
    {"messages": [HumanMessage(content="我刚才说了什么?")]},
    config={"configurable": {"thread_id": "thread-1"}}
)
# Agent 能记住之前的对话!

伪代码理解 Checkpointer

复制代码
# 没有 Checkpointer:
graph.invoke(input1) → 结果1,不保存任何状态
graph.invoke(input2) → 结果2,完全独立,不记得 input1

# 有 Checkpointer:
graph.invoke(input1, thread_id="chat-1") 
  → 保存检查点:{thread: "chat-1", step: 1, state: ...}

graph.invoke(input2, thread_id="chat-1")
  → 读取检查点,恢复上下文 → Agent 知道之前的对话

13.4 获取和更新状态

python 复制代码
config = {"configurable": {"thread_id": "thread-1"}}

# 获取最新状态
state_snapshot = graph.get_state(config)

# 获取完整历史
history = list(graph.get_state_history(config))

# 手动更新状态(人在回路场景)
graph.update_state(config, {"approved": True})

StateSnapshot 字段详解

字段 类型 说明
values dict 当前检查点的状态值
next tuple[str, ...] 下一步要执行的节点,空 () 表示图已完成
config dict 包含 thread_idcheckpoint_nscheckpoint_id
metadata dict 执行元数据:source(来源)、writes(节点输出)、step(超步计数)
created_at str ISO 8601 时间戳
parent_config `dict None`
tasks tuple[PregelTask, ...] 当前步骤的任务列表

14. 人在回路(Human-in-the-Loop)

14.1 核心机制

使用 interrupt() 暂停执行,使用 Command(resume=...) 恢复执行。

伪代码理解

复制代码
# 人在回路 = 流程中的"审批站"
# 执行到审批站时暂停 → 等待人类决策 → 根据决策继续

def approval_node(state):
    # 暂停!等待人类审批
    approved = interrupt("是否批准此操作?")
    # 人类回复后,approved = 人类的回复值
    if approved:
        return Command(goto="proceed")
    else:
        return Command(goto="cancel")

14.2 使用 interrupt 暂停

python 复制代码
from langgraph.types import interrupt

def approval_node(state: State):
    # 暂停执行,等待人类输入
    # payload 会返回给调用者
    approved = interrupt({
        "question": "是否批准此操作?",
        "details": state["action_details"]
    })
    # 恢复后,approved = Command(resume=...) 传入的值
    return {"approved": approved}

interrupt 触发后的流程

复制代码
1. 图执行在 interrupt() 调用处暂停
2. 状态通过 checkpointer 保存
3. interrupt 的 payload 返回给调用者
4. 图无限期等待,直到恢复
5. 恢复时,Command(resume=...) 的值成为 interrupt() 的返回值
6. 节点从头重新执行(interrupt 之前的代码会重新运行)

14.3 使用 Command 恢复

python 复制代码
from langgraph.types import Command

config = {"configurable": {"thread_id": "thread-1"}}

# 第一次调用 → 触发 interrupt,暂停
result = graph.invoke({"input": "data"}, config=config)

# 恢复执行,传入人类的决策
graph.invoke(Command(resume=True), config=config)   # 批准
# 或
graph.invoke(Command(resume=False), config=config)  # 拒绝

14.4 常见模式

审批模式

python 复制代码
def approval_node(state: State) -> Command:
    is_approved = interrupt("是否批准此操作?")
    if is_approved:
        return Command(goto="proceed")
    else:
        return Command(goto="cancel")

审核编辑模式

python 复制代码
def review_node(state: State):
    # 让人类审核 LLM 的输出
    edited_content = interrupt({
        "message": "请审核以下内容",
        "content": state["draft"]
    })
    return {"final_content": edited_content}

处理多个 interrupt(并行分支中):

python 复制代码
# 当并行分支同时触发 interrupt 时
result = graph.invoke({"vals": []}, config)

# 恢复时,用字典映射每个 interrupt 的 ID 和回复
resume_map = {
    interrupt.id: f"answer for {interrupt.value}"
    for interrupt in result.interrupts
}
graph.invoke(Command(resume=resume_map), config)

14.5 interrupt 规则

规则 说明
不要用 try/except 包裹 interrupt 会导致中断机制失效
不要在节点内重排 interrupt 调用 恢复时节点从头执行,顺序必须一致
不要返回复杂值 interrupt 的值必须是 JSON 可序列化的
interrupt 前的副作用必须幂等 恢复时节点重新执行,副作用会重复

15. 流式输出(Streaming)

15.1 三种流式数据类别

类别 说明 stream_mode
工作流进度 每个节点执行后的状态更新 updates, values
LLM Token 模型生成的 token 流 messages
自定义更新 用户定义的进度信号 custom

15.2 流式模式详解

模式 类型 说明
values ValuesStreamPart 每步后的完整状态快照
updates UpdatesStreamPart 每步后的状态增量更新
messages MessagesStreamPart LLM token 流 + 元数据
custom CustomStreamPart 自定义数据(通过 get_stream_writer
checkpoints CheckpointStreamPart 检查点事件
tasks TasksStreamPart 任务开始/完成事件
debug DebugStreamPart 所有可用信息(checkpoints + tasks + 额外元数据)

15.3 基本用法

流式输出状态更新

python 复制代码
# updates 模式:只输出变化的字段
for chunk in graph.stream(
    {"topic": "ice cream"},
    stream_mode="updates",
    version="v2",
):
    if chunk["type"] == "updates":
        for node_name, state in chunk["data"].items():
            print(f"节点 {node_name} 更新: {state}")

# values 模式:输出完整状态
for chunk in graph.stream(
    {"topic": "ice cream"},
    stream_mode="values",
    version="v2",
):
    if chunk["type"] == "values":
        print(f"当前状态: {chunk['data']}")

流式输出 LLM Token

python 复制代码
for chunk in graph.stream(
    {"messages": [HumanMessage(content="写一首诗")]},
    stream_mode="messages",
    version="v2",
):
    if chunk["type"] == "messages":
        msg, metadata = chunk["data"]
        if isinstance(msg, AIMessageChunk) and msg.content:
            print(msg.content, end="", flush=True)

自定义流式数据

python 复制代码
from langgraph.config import get_stream_writer

def my_node(state: State):
    writer = get_stream_writer()
    writer({"status": "正在处理..."})  # 发出自定义信号
    result = do_work(state)
    return {"result": result}

for chunk in graph.stream(
    input,
    stream_mode=["updates", "custom"],
    version="v2",
):
    if chunk["type"] == "custom":
        print(f"进度: {chunk['data']}")

15.4 多模式同时使用

python 复制代码
for part in graph.stream(
    input,
    stream_mode=["values", "updates", "messages", "custom"],
    version="v2",
):
    if part["type"] == "values":
        print(f"State: {part['data']}")
    elif part["type"] == "updates":
        for node_name, state in part["data"].items():
            print(f"Node `{node_name}` updated: {state}")
    elif part["type"] == "messages":
        msg, metadata = part["data"]
        print(msg.content, end="", flush=True)
    elif part["type"] == "custom":
        print(f"Progress: {part['data']['progress']}%")

16. 记忆系统(Memory)

16.1 两种记忆类型

类型 范围 实现方式 比喻
短期记忆 单个会话(thread) State + Checkpointer 便签纸:记住当前对话
长期记忆 跨会话、跨线程 Store 笔记本:记住用户偏好

伪代码理解

复制代码
# 短期记忆 = 便签纸
# 只在当前对话中有效,对话结束就没了(除非用 checkpointer 保存)
thread_id = "chat-001"
graph.invoke(input, config={"thread_id": thread_id})  # 同一 thread 共享记忆

# 长期记忆 = 笔记本
# 跨对话、跨线程持久保存,随时可以查阅
store.put(("user", "alice"), "preferences", {"language": "zh"})
store.get(("user", "alice"), "preferences")  # 任何线程都能读到

16.2 短期记忆

通过 State + Checkpointer 实现:

python 复制代码
from langgraph.checkpoint.memory import MemorySaver

checkpointer = MemorySaver()
graph = builder.compile(checkpointer=checkpointer)

# 第一轮对话
graph.invoke(
    {"messages": [HumanMessage(content="我叫小明")]},
    config={"configurable": {"thread_id": "chat-1"}}
)

# 第二轮对话(同一 thread_id,Agent 记得之前的内容)
graph.invoke(
    {"messages": [HumanMessage(content="我叫什么名字?")]},
    config={"configurable": {"thread_id": "chat-1"}}
)
# Agent 回答:"你叫小明"

16.3 长期记忆

通过 Store 实现,跨会话、跨线程共享:

python 复制代码
from langgraph.store.memory import InMemoryStore

# 创建 Store(生产环境用数据库后端)
store = InMemoryStore(
    index={"embed": embed_function, "dims": 128}  # 支持语义搜索
)

# 写入记忆
user_id = "alice"
namespace = (user_id, "preferences")
store.put(namespace, "lang", {"language": "zh", "style": "简短"})

# 读取记忆
item = store.get(namespace, "lang")
print(item.value)  # {"language": "zh", "style": "简短"}

# 语义搜索
results = store.search(namespace, query="语言偏好")

16.4 记忆的三种类型

类型 存储内容 人类类比 Agent 示例
语义记忆 事实和知识 学校学到的知识 用户偏好、事实信息
情景记忆 过去的经历 旅行回忆 过去的 Agent 行动记录
程序记忆 规则和技能 骑自行车的技能 Agent 的系统提示词

语义记忆示例(用户画像)

python 复制代码
# 保存用户画像
store.put(("user", "alice"), "profile", {
    "name": "Alice",
    "language": "zh",
    "preferences": ["简短回答", "技术向"]
})

# 在节点中读取
def call_model(state, store: BaseStore):
    profile = store.get(("user", "alice"), "profile")
    # 根据用户偏好调整回答风格...

程序记忆示例(自我改进提示词)

python 复制代码
def update_instructions(state, store: BaseStore):
    # 读取当前指令
    current = store.search(("instructions",))[0]
    # 根据对话反馈改进指令
    new_instructions = llm.invoke(f"改进指令: {current}\n反馈: {state['feedback']}")
    store.put(("instructions",), "agent_a", {"instructions": new_instructions})

17. 时间旅行(Time Travel)

17.1 什么是时间旅行

回溯到之前的状态,从该点重新执行或探索不同路径。

伪代码理解

复制代码
# 时间旅行 = 游戏存档
# 每个超步后自动存档(checkpoint)
# 可以回到任意存档点,从那里重新开始

检查点 0: 初始状态
检查点 1: node_a 执行后
检查点 2: node_b 执行后  ← 回到这个点,尝试不同路径
检查点 3: node_c 执行后

17.2 回放(Replay)

python 复制代码
config = {"configurable": {"thread_id": "thread-1"}}

# 获取历史
history = list(graph.get_state_history(config))

# 回放到某个检查点
old_checkpoint = history[2]
graph.invoke(None, config={
    "configurable": {
        "thread_id": "thread-1",
        "checkpoint_id": old_checkpoint.config["configurable"]["checkpoint_id"]
    }
})

17.3 分叉(Fork)

从某个检查点开始,走不同的路径:

python 复制代码
# 在某个历史点上更新状态,然后继续执行
graph.update_state(
    config_with_checkpoint_id,
    {"decision": "different_choice"},  # 修改状态
    as_node="decision_node"            # 假装是某个节点写的
)

# 从修改后的状态继续执行
graph.invoke(None, config=config_with_checkpoint_id)

18. 子图(Subgraphs)

18.1 什么是子图

将复杂图拆分为多个子图,每个子图可以独立开发和测试,然后组合使用。

伪代码理解

复制代码
# 子图 = 部门
# 大公司(主图)包含多个部门(子图)
# 每个部门有自己的内部流程

主图:
  START → 路由 → 技术部门(子图) → END
                → 创意部门(子图) → END

技术部门(子图):
  接收任务 → 分析 → 编码 → 测试 → 返回结果

18.2 使用子图

python 复制代码
# 子图作为节点
parent_graph.add_node("sub_agent", sub_graph)

# 子图有自己独立的状态
# 父图和子图通过状态转换通信

18.3 检查点命名空间

每个检查点有 checkpoint_ns 字段标识所属图:

含义
"" (空字符串) 父图的检查点
"node_name:uuid" 子图的检查点
`"outer:uuid inner:uuid"`

19. 多Agent架构 --- Agent-as-Tool

19.1 核心概念

Agent-as-Tool:主 Agent 把子 Agent 当作可调用的工具。控制权始终在主 Agent 手里,子 Agent 执行完返回结果后,主 Agent 继续决策。

生活化比喻

复制代码
Agent-as-Tool = 前台打电话给某部门,等对方回复后继续
                前台始终掌控通话,没有把电话转出去

公司客服总台:
  员工来电 → 前台接待员(主 Agent)判断问题归属
    → IT 问题:前台打电话问 IT 专员(子 Agent),等回复后继续
    → HR 问题:前台打电话问 HR 专员(子 Agent),等回复后继续
    → 财务问题:前台打电话问财务专员(子 Agent),等回复后继续
  前台汇总回答 → END

19.2 架构图

复制代码
主 Agent (StateGraph)
  ├── main_llm: 判断需要哪个子 Agent
  └── main_tool_node: 调用子 Agent 工具
        │
        ├── call_it_support() ──→ it_graph.invoke(...) ──→ 返回回答
        ├── call_hr_support() ──→ hr_graph.invoke(...) ──→ 返回回答
        └── call_finance()    ──→ finance_graph.invoke(...) ──→ 返回回答
              │
              ▼
        回到 main_llm → 汇总回答 → END

19.3 关键实现步骤

1. 构建子 Agent 图 --- 每个子 Agent 是一个完整的 StateGraph:

python 复制代码
# 子 Agent 是完整的 ReAct Agent
it_graph = (
    StateGraph(ITState)
    .add_node("it_llm", it_llm)
    .add_node("it_tool", it_tool_node)
    .add_edge(START, "it_llm")
    .add_conditional_edges("it_llm", it_router, ["it_tool", END])
    .add_edge("it_tool", "it_llm")
    .compile()
)

2. 子 Agent 打包为 @tool --- 用装饰器把子图包装成普通工具:

python 复制代码
@tool
def call_it_support(query: str) -> str:
    """将 IT 相关问题转接给 IT 支持专员(密码、系统状态等)"""
    result = it_graph.invoke({"messages": [HumanMessage(content=query)]})
    return extract_final_answer(result)  # 提取最终 AI 回答

3. 主 Agent 绑定子 Agent 工具 --- 主 Agent 把子 Agent 当作普通工具调用:

python 复制代码
all_tools = [call_it_support, call_hr_support, call_finance]
main_model = model.bind_tools(all_tools)

# 主 Agent 的标准 ReAct 循环
main_graph = (
    StateGraph(MainState)
    .add_node("main_llm", main_llm)
    .add_node("main_tool", main_tool_node)
    .add_edge(START, "main_llm")
    .add_conditional_edges("main_llm", main_router, ["main_tool", END])
    .add_edge("main_tool", "main_llm")  # 子 Agent 返回后,主 Agent 继续决策
    .compile()
)

19.4 使用 build_react_agent 简化子 Agent 创建

python 复制代码
from utils.graph_helpers import build_react_agent

# 一行创建子 Agent(替代手写 StateGraph)
it_sub_agent = build_react_agent(
    model.bind_tools(it_tools),
    it_tools,
    system_prompt="你是 IT 支持专员,擅长解决密码、系统等技术问题。"
)

# 子 Agent 同样包装为 @tool
@tool
def call_it_support(query: str) -> str:
    """将 IT 相关问题转接给 IT 支持专员"""
    result = it_sub_agent.invoke({"messages": [HumanMessage(content=query)]})
    return extract_final_answer(result)

19.5 核心特征总结

特征 说明
✅ 子 Agent 是完整的 StateGraph 有自己的 LLM + 工具 + 循环
✅ 子 Agent 被 @tool 包装 对主 Agent 来说就是一个普通工具
✅ 控制权始终在主 Agent 子 Agent 执行完必须返回,主 Agent 汇总
✅ 消息流:主→子→主 同步调用,子 Agent 不感知主 Agent 的存在

20. 多Agent架构 --- Handoff

20.1 核心概念

Handoff(控制权交接):控制权从一个 Agent 完全转移到下一个 Agent,就像接力赛交棒,前一个 Agent 交出控制权后就不再参与。

生活化比喻

复制代码
Handoff = 接力赛交棒,接棒的人跑你就不跑了
          前台说"这个不归我管",把用户直接带到下一个科室

医院分诊系统:
  患者到院 → 分诊护士评估症状
    → 心脏相关 → 转给心内科(护士不再参与)
    → 心内科看后需拍片 → 转给放射科(心内科不再参与)
    → 放射科出报告 → END

20.2 核心 API:Command(goto=...)

python 复制代码
from langgraph.types import Command

# Command 同时完成两件事:
# 1. goto:指定下一个要执行的节点名(或 END)
# 2. update:更新状态(类似节点返回的 dict)

def triage_nurse(state: HandoffState):
    symptom = state["symptom"]
    if "心脏" in symptom:
        return Command(goto="cardiology", update={
            "path": ["分诊"],
            "diagnosis": "疑似心脏问题",
        })
    else:
        return Command(goto=END, update={
            "path": ["分诊"],
            "diagnosis": "常见症状,直接处理",
        })

20.3 架构图

复制代码
START → 分诊护士 →(症状判断)
            ├── 心脏相关 → 心内科 →(需拍片?)
            │                        ├── 需要放射科 → 放射科 → END
            │                        └── 不需要 → END
            ├── 神经相关 → 神经内科 → END
            └── 其他 → 全科医生 → END

关键:不需要 add_conditional_edges!
      每个节点用 Command(goto=...) 自己决定去哪

20.4 图构建方式

python 复制代码
# Handoff 模式:不需要条件边!只需连接起点
graph = (
    StateGraph(HandoffState)
    .add_node("triage", triage_nurse)
    .add_node("cardiology", cardiology)
    .add_node("neurology", neurology)
    .add_node("radiology", radiology)
    .add_node("general", general)
    .add_edge(START, "triage")  # 只需连起点!
    .compile()
)
# 后续所有跳转都由 Command(goto=...) 在运行时决定

与条件分支对比

python 复制代码
# 传统条件分支:需要在图构建时预设路由映射
.add_conditional_edges("triage", route, {
    "cardiology": "cardiology",
    "neurology": "neurology",
})

# Handoff 模式:运行时动态决定,无需预设映射
.add_edge(START, "triage")  # 只需连起点

20.5 LLM 版 Handoff

用结构化输出约束 LLM 的决策:

python 复制代码
from pydantic import BaseModel, Field
from typing import Literal

class TriageDecision(BaseModel):
    department: Literal["cardiology", "neurology", "general", "end"] = Field(
        description="应该转到的科室"
    )
    reason: str = Field(description="分诊理由")

triage_model = model.with_structured_output(TriageDecision)

def triage_nurse(state: HandoffState):
    decision = triage_model.invoke(f"患者症状:{state['symptom']}")
    # LLM 只能返回四个值之一,确保输出可控
    return Command(goto=decision.department, update={
        "path": ["分诊"],
        "diagnosis": f"分诊理由: {decision.reason}",
    })

20.6 核心特征总结

特征 说明
✅ 用 Command(goto=...) 代替 add_conditional_edges 运行时动态决定去向
✅ 控制权一旦交出就不回来 每个节点独立决策,前一个节点不参与后续
✅ 不需要在图构建时预设所有路由映射 灵活性更强
✅ 适合"接力式"流程 A → B → C → END(线性或分支)

21. 多Agent架构 --- Supervisor

21.1 核心概念

Supervisor(监督者):一个主管 Agent 管理多个工人 Agent 的工作流。工人完成后必须回到主管汇报,主管审查后再分配下一个任务,形成循环审查的工作流。

生活化比喻

复制代码
Supervisor = 装修队长
  队长负责:找工人、看进度、验收、决定下一步
  工人干完活必须回来找队长汇报
  队长审查后:满意 → 派下一个工人,不满意 → 让同一工人重做

软件开发团队:
  项目经理(Supervisor) → 分配研究员调研 → 研究员回来汇报
  → 审查后分配程序员编码 → 程序员回来汇报
  → 审查后分配评审员验收 → 评审员回来汇报
  → 项目经理认为完成 → END

21.2 架构图

复制代码
            ┌──────────────────────────────────┐
            │         supervisor(主管)          │
            │  唯一决策者:分配 + 审查 + 决定结束   │
            └────┬─────────┬──────────┬────────┘
                 │         │          │
        分配任务  │         │          │  汇报结果
                 ▼         ▼          ▼
            ┌────────┐┌────────┐┌────────┐
            │worker A││worker B││worker C│
            │研究员   ││程序员   ││评审员   │
            └───┬────┘└───┬────┘└───┬────┘
                │         │         │
                └─────────┴─────────┘
                          │
              Command(goto="supervisor")
              工人不能自行结束,必须回到主管

21.3 关键实现步骤

1. 状态设计 --- 迭代计数器防止无限循环:

python 复制代码
class SupervisorState(TypedDict):
    messages: Annotated[list, add_messages]
    task: str              # 项目需求
    next: str              # 下一个工人(由 supervisor 决定)
    work_product: str      # 当前工人的产出
    all_products: list     # 所有产出汇总
    iteration: int         # 迭代计数器(防无限循环)

2. Supervisor 节点 --- 用结构化输出决定分配:

python 复制代码
class SupervisorDecision(BaseModel):
    next_worker: Literal["researcher", "coder", "reviewer", "FINISH"] = Field(
        description="下一个要分配的工人,或 FINISH 表示完成"
    )
    reasoning: str = Field(description="为什么分配给这个工人")

supervisor_model = model.with_structured_output(SupervisorDecision)

def supervisor(state: SupervisorState):
    # 安全保护:超过迭代上限强制结束
    if state["iteration"] >= 5:
        return Command(goto=END, update={"next": "FINISH"})

    # LLM 智能决策下一个工人
    decision = supervisor_model.invoke(
        f"任务:{state['task']}。已有进展:{state['all_products']}。"
    )
    if decision.next_worker == "FINISH":
        return Command(goto=END, update={"next": "FINISH"})

    return Command(goto=decision.next_worker, update={
        "next": decision.next_worker,
    })

3. Worker 节点 --- 干完活必须回到 supervisor:

python 复制代码
def researcher(state: SupervisorState):
    # 执行调研工作...
    product = "调研报告:..."
    return Command(goto="supervisor", update={  # ★ 必须回到 supervisor
        "work_product": product,
        "all_products": state["all_products"] + [product],
        "iteration": state["iteration"] + 1,
    })

4. 图构建 --- 同样只需连接起点:

python 复制代码
graph = (
    StateGraph(SupervisorState)
    .add_node("supervisor", supervisor)
    .add_node("researcher", researcher)
    .add_node("coder", coder)
    .add_node("reviewer", reviewer)
    .add_edge(START, "supervisor")  # 只需连起点
    .compile()
)

21.4 无 LLM 版 vs LLM 版

维度 无 LLM 版 LLM 版
分配策略 固定顺序(iteration % 3 LLM 根据任务状态智能决定
结束判断 迭代次数到了强制结束 LLM 判断可交付则提前结束
工人产出 硬编码模板文本 LLM 扮演角色生成真实内容
审查能力 reviewer 用 LLM 审查并给出建议

21.5 核心特征总结

特征 说明
✅ Supervisor 是唯一做决策的 Agent 分配任务、审查结果、决定何时结束
✅ 所有 Worker 完成后必须回到 Supervisor Command(goto="supervisor")
✅ 可多次循环审查 不满意 → 再分配 → 再审查
✅ 用 iteration 防止无限循环 达到上限强制 END

22. 三大多Agent模式对比

22.1 一图对比

复制代码
┌──────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  Agent-as-Tool          Handoff              Supervisor              │
│  ┌──────────┐          ┌──────────┐        ┌──────────┐            │
│  │ 主 Agent  │          │  节点 A   │        │ 主管Agent │            │
│  │  ↓ 调用   │          │  ↓ 交出   │        │  ↓ 分配   │            │
│  │ 子Agent  │          │  节点 B   │        │ 工人Agent │            │
│  │  ↓ 返回   │          │  ↓ 交出   │        │  ↑ 汇报   │            │
│  │ 主Agent  │          │  节点 C   │        │ 主管审查   │            │
│  │  ↓ 继续   │          │  ↓ 结束   │        │  ↓ 再分配  │            │
│  │   END    │          │   END    │        │  ...循环   │            │
│  └──────────┘          └──────────┘        └──────────┘            │
│                                                                     │
│  控制:主Agent掌控        控制:交出不再回来   控制:主管循环管理        │
│  比喻:打电话问同事        比喻:接力赛交棒     比喻:包工头派活         │
│  API: @tool + bind_tools  API: Command(goto)  API: Command(goto)    │
│                                                                     │
└──────────────────────────────────────────────────────────────────────┘

22.2 详细对比表

维度 Agent-as-Tool Handoff Supervisor
控制权 始终在主 Agent 交出后不再回来 主管循环管理
子Agent关系 主 Agent 的"工具" 独立接力的"继任者" 主管的"下属"
子Agent能否自行结束 否,必须返回主 Agent 是,可以决定 END 否,必须回到主管
循环能力 无(调用一次返回) 否(线性接力) 是(审查→再分配)
路由方式 bind_tools + tool_calls Command(goto=...) Command(goto=...)
图构建 标准 ReAct 循环 只需 add_edge(START, ...) 只需 add_edge(START, ...)
适合场景 客服总台、专业咨询 分诊、任务转交 项目管理、迭代改进
迭代保护 不需要 不需要 必须有 iteration 上限
比喻 打电话问同事 接力赛交棒 包工头派活

22.3 选型决策

复制代码
你的多 Agent 需要什么?

1. 主 Agent 需要掌控全局,子 Agent 只是"咨询顾问"?
   → ✅ Agent-as-Tool
   (客服总台、专业咨询、工具式调用)

2. 任务需要"接力"完成,每个 Agent 独立决策后交出控制权?
   → ✅ Handoff
   (医院分诊、任务转交、线性流程)

3. 需要一个"主管"统一管理,循环审查直到满意?
   → ✅ Supervisor
   (项目管理、迭代开发、质量驱动的工作流)

4. 需要结合多种模式?
   → 可以!三种模式可以在同一个图中组合使用
   (例如:Supervisor 管理多个 Agent-as-Tool 的子 Agent)

23. DeepAgents --- 开箱即用的 Agent 框架

23.1 什么是 DeepAgents

DeepAgents 是 LangChain 团队在 LangGraph 之上封装的"batteries-included"Agent 框架。它用一行代码创建拥有完整能力的 Agent。

层级关系

复制代码
手动 StateGraph        ← 最底层,完全控制
      ↑
create_react_agent()   ← 封装了标准 ReAct 循环
      ↑
create_deep_agent()    ← 再封装:内置文件系统、任务规划、子代理、Skills 等

一句话:create_deep_agent() 返回的仍然是一个标准 LangGraph StateGraph,
只是它自动帮你装了所有常用工具,不用自己一个个加了。

生活化比喻:
  手动 StateGraph     = 自己买零件组装电脑
  create_react_agent() = 买品牌整机(标准配置)
  create_deep_agent()  = 买品牌整机 + 预装所有常用软件

23.2 六大核心内置能力

能力 说明 核心工具/API
1. 文件系统工具 Agent 自动拥有文件读写能力 ls / read_file / write_file / edit_file / glob / grep
2. 任务规划 把复杂任务拆解为 TODO 列表 write_todos
3. 子代理 主 Agent 可委派任务给独立子 Agent SubAgent(name, description, model, system_prompt)
4. Skills(技能文件) 通过 SKILL.md 定义可复用行为 skills=["./skills/"]
5. 上下文工程 管理长期会话的上下文 memory_paths
6. 模型灵活性 支持任何 LangChain 支持的模型 openai:gpt-5.5anthropic:claude-sonnet-4

23.3 架构一图览

复制代码
create_deep_agent(model, tools, ...)
  │
  ├── 内置工具
  │     ├── write_todos    (任务规划)
  │     ├── ls             (目录浏览)
  │     ├── read_file      (文件读取)
  │     ├── write_file     (文件写入)
  │     ├── edit_file      (文件编辑)
  │     ├── glob           (文件搜索)
  │     └── grep           (内容搜索)
  │
  ├── 用户自定义工具
  │     └── 通过 tools=[...] 传入
  │
  ├── 子代理
  │     └── 通过 subagents=[Subagent(...)] 传入
  │
  ├── Skills
  │     └── 通过 skills=["./skills/"] 传入 SKILL.md 目录
  │
  └── 文件系统后端
        └── 通过 backend=FilesystemBackend(root_dir="./workspace") 配置

所有组件组合后 → 返回标准 LangGraph StateGraph
  → 可以 invoke() / stream() / 接入 LangSmith

23.4 快速入门

最简 DeepAgent

python 复制代码
from deepagents import create_deep_agent
from langchain_core.tools import tool

@tool
def calculate(expression: str) -> str:
    """执行数学表达式计算"""
    return str(eval(expression))

# ★ 一行创建 DeepAgent!
agent = create_deep_agent(
    model=model,
    tools=[calculate],
    system_prompt="你是一个智能助手。",
)

result = agent.invoke({"messages": [HumanMessage(content="123乘456=?")]})

带文件系统的 DeepAgent

python 复制代码
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
import tempfile

# 配置文件系统后端(安全沙箱)
workspace = tempfile.mkdtemp(prefix="deepagent_workspace_")
backend = FilesystemBackend(root_dir=workspace, virtual_mode=False)

agent = create_deep_agent(
    model=model,
    backend=backend,  # Agent 自动拥有 ls/read_file/write_file/edit_file/glob/grep
    system_prompt="你是写作助手,可以读写文件。",
)

# Agent 可以自主创建文件!
result = agent.invoke({
    "messages": [HumanMessage(content="请创建 hello.txt,写一句问候语")]
})

23.5 子代理(SubAgents)

子代理是上下文隔离的独立 Agent:

python 复制代码
from deepagents import create_deep_agent, SubAgent

# 定义子代理
data_analyst = SubAgent(
    name="data_analyst",
    description="分析数据并生成统计报告",
    model=model,
    system_prompt="你是数据分析师,分析统计特征并返回报告。",
)

# 注册子代理
agent = create_deep_agent(
    model=model,
    subagents=[data_analyst],
    system_prompt="你是项目经理。遇到数据分析任务请委派给数据分析师。",
)

# 主 Agent 遇到数据分析任务时,自动委派给子代理
result = agent.invoke({
    "messages": [HumanMessage(content="分析这组数据:[12, 45, 23, 67, 34]")]
})

伪代码理解子代理

复制代码
# 子代理 = 专业外包
# 主 Agent = 项目经理
# 项目经理遇到专业任务时,委派给外包(子代理)
# 外包完成后交回结果,不污染主对话

主 Agent: "这组数据 [12,45,23,67] 需要分析"
  → 判断:这是数据分析任务
  → 委派给 data_analyst 子代理
  → 子代理独立执行(有自己的上下文、工具、LLM)
  → 返回分析报告
  → 主 Agent 继续对话

23.6 Skills(技能文件)

Skills 是通过 SKILL.md 文件定义的可复用行为模板:

python 复制代码
# 1. 创建 SKILL.md 文件
skill_content = """---
name: 代码审查
description: 审查代码并指出潜在问题、改进建议
triggers: 当用户请求审查代码、检查代码质量时
---

# 代码审查技能

你是一个资深代码审查员。审查代码时请:
1. 指出潜在 bug 和边界情况
2. 建议代码风格和改进方案
3. 评估时间/空间复杂度
"""

# 2. 加载技能
agent = create_deep_agent(
    model=model,
    backend=backend,
    skills=["./skills/"],  # Agent 自动扫描 SKILL.md 文件
    system_prompt="根据用户需求自动使用合适的技能。",
)

# 3. 触发技能
result = agent.invoke({
    "messages": [HumanMessage(content="请审查这段代码:def avg(n): return sum(n)/len(n)")]
})
# Agent 自动发现并加载"代码审查"技能

Skills 的优势

优势 说明
文件化 技能定义在文件中,版本控制友好
可共享 多个 Agent 可共享同一技能
即改即用 不用重新训练,改文件即改技能
自动发现 Agent 运行时自动扫描、按需加载

23.7 三种构建方式对比

方式 代码量 内置能力 自定义度 适合场景
手动 StateGraph ~50 行 ★★★★★ 需要完全控制每个节点和边
create_react_agent() 1 行 工具调用 ★★★ 标准 ReAct Agent,不需要文件操作
create_deep_agent() 1 行 全部内置 ★★★ 需要文件系统/子代理/Skills 的完整 Agent

23.8 何时用/不用 DeepAgents

复制代码
✅ 何时用 DeepAgents:
  - 需要 Agent 读写文件
  - 需要子代理委派任务
  - 需要 Skills 插件系统
  - 需要任务规划(TODO 列表)
  - 快速搭建生产级 Agent

❌ 何时不用:
  - 只需要简单工具调用(用 create_react_agent() 就够了)
  - 需要精细控制图结构(手动 StateGraph)
  - 教学理解底层原理(先学手动构建)

24. LangGraph vs LangChain vs Dify 全面对比

24.1 三者定位总览

复制代码
┌─────────────────────────────────────────────────────────────────────────┐
│                                                                        │
│  低代码/可视化 ◄──────────────────────────────────────────► 纯代码     │
│                                                                        │
│  Dify                  LangGraph              LangChain                │
│  ┌──────┐             ┌──────────┐          ┌──────────┐             │
│  │ 平台  │             │ 编排引擎  │          │ 组件框架  │             │
│  │      │             │          │          │          │             │
│  │可视化 │             │图结构编排  │          │链式组合   │             │
│  │拖拽式 │             │状态管理   │          │模型/工具  │             │
│  │全栈   │             │循环/分支  │          │数据连接   │             │
│  └──────┘             └──────────┘          └──────────┘             │
│                                                                        │
│  面向:产品经理/运营    面向:工程师          面向:工程师              │
│  门槛:低              门槛:中              门槛:高                  │
│  灵活度:★★            灵活度:★★★★★        灵活度:★★★★             │
│                                                                        │
└─────────────────────────────────────────────────────────────────────────┘

24.2 核心区别

维度 Dify LangGraph LangChain
本质 AI 应用开发平台(BaaS + LLMOps) 工作流编排引擎 LLM 应用组件框架
开发方式 可视化拖拽 + 低代码 纯代码(Python) 纯代码(Python/JS)
核心理念 "让 AI 应用开发像搭积木" "节点干活,边决定下一步" "组合(Chaining)"
工作流模型 DAG(有向无环图) 状态图(支持循环) 线性链(Chain)
是否支持循环 有限支持 原生支持 不支持
状态管理 节点间上下文变量传递 显式 State + Reducer 隐式 Memory
开源协议 Apache 2.0(部分功能企业版) MIT MIT
目标用户 产品经理、运营、低代码开发者 后端工程师、AI 工程师 全栈工程师、数据工程师

24.3 各自优缺点

Dify
优点 缺点
极速原型:几小时搭建可用 Demo 灵活度受限:复杂循环/动态分支难以可视化表达
低门槛:非技术人员也能参与开发 黑盒风险:可视化背后逻辑不透明,调试困难
内置 RAG:文档上传→分块→检索一站式 扩展性瓶颈:超出平台能力时需深度定制
多模型兼容:100+ LLM 统一接口切换 版本管理弱:工作流 JSON 不如代码方便 diff/review
全栈能力:Prompt 管理、日志、监控、API 一体化 循环支持有限:DAG 模型天然不支持复杂循环
国产化友好:深度适配通义千问、讯飞星火等 代码审查难:非代码资产难以纳入 CI/CD
LangGraph
优点 缺点
极致灵活:任意复杂的图结构 学习曲线陡:需理解 State/Reducer/Send 等概念
原生循环:ReAct Agent、迭代优化天然支持 无 UI:纯代码,非技术人员无法参与
持久化:Checkpoint + 人在回路 + 时间旅行 需自建基础设施:监控、部署、API 需自行搭建
流式输出:7 种流式模式,精细控制 生态依赖:依赖 LangChain 的模型/工具接口
生产级:容错恢复、状态管理、可扩展 样板代码多:简单 Agent 也需 20+ 行
LangChain
优点 缺点
生态最丰富:300+ 集成,社区最大 线性限制:Chain 是线性的,不支持循环
组件化:Model/Prompt/Tool/Parser 即插即用 抽象过重:多层抽象导致调试困难
快速上手:几行代码完成 LLM 调用 状态管理弱:Memory 机制不够灵活
多语言:Python + JavaScript/TypeScript 版本不稳定:API 频繁变动,迁移成本高

24.4 典型场景详解

场景一:企业知识库问答(RAG)
工具 适合度 说明
Dify ★★★★★ 内置文档处理→分块→向量化→检索→生成全流程,非技术人员 1 小时搞定
LangChain ★★★★ 丰富的文档加载器和检索器,但需自行组装
LangGraph ★★★ 需手动集成向量库,但可实现更复杂的检索策略
场景二:多步推理 Agent(ReAct)
工具 适合度 说明
LangGraph ★★★★★ 原生支持循环,create_react_agent() 一行搞定
LangChain ★★★ AgentExecutor,但黑盒、不可控、难调试
Dify ★★ Agent 功能有限,复杂工具链难以编排
场景三:审批流程(人在回路)
工具 适合度 说明
LangGraph ★★★★★ interrupt() + Command(resume=...) 原生支持
Dify ★★★ 人工节点支持,但灵活度不如代码
LangChain 无原生支持,需自行实现
场景四:内部工具快速上线
工具 适合度 说明
Dify ★★★★★ 拖拽→配置→发布 API→嵌入业务系统,全流程 1 天
LangChain ★★★ 需自行搭建 API 服务和前端
LangGraph ★★ 需自行搭建 API 服务、监控、运维
场景五:复杂多 Agent 协作
工具 适合度 说明
LangGraph ★★★★★ 子图 + Send + 状态管理,天生为多 Agent 设计
LangChain ★★ 需手动编排多 Agent 交互
Dify ★★ 单 Agent 场景足够,多 Agent 协作受限

24.5 组合使用策略

复制代码
策略一:Dify 做前端 + LangGraph 做后端
  Dify(可视化界面、RAG、用户管理)
    → API 调用 → LangGraph(复杂 Agent 逻辑、人在回路)

策略二:LangChain 做组件 + LangGraph 做编排
  LangChain(Model、Tool、Parser、Retriever)
    → LangGraph(StateGraph 编排、循环、持久化)

策略三:Dify 快速验证 → LangGraph 生产重构
  第一步:用 Dify 几小时验证想法
  第二步:验证通过后,用 LangGraph 重写为生产级代码

24.6 选型决策速查表

你的需求 推荐 理由
快速验证 AI 想法,非技术人员参与 Dify 可视化、低门槛、内置 RAG
简单的 LLM 调用链(翻译、摘要) LangChain 最简单,几行代码搞定
需要 Agent 循环(ReAct) LangGraph 原生循环支持
需要人在回路/审批 LangGraph interrupt() 原生支持
企业知识库问答 Dify 内置 RAG 全流程
复杂多 Agent 协作 LangGraph 子图 + Send + 状态管理
需要持久化/容错恢复 LangGraph Checkpointer 原生支持
内部工具快速上线 Dify 一键发布 API
需要精细控制 Agent 行为 LangGraph 完全可控,无黑盒
已有 LangChain 项目,需加循环 LangGraph 无缝集成 LangChain 组件

25. API 速查表

核心 API

python 复制代码
from langgraph.graph import StateGraph, START, END, MessagesState
from langgraph.graph.message import add_messages
from langgraph.types import Send, Command, interrupt
from langgraph.prebuilt import create_react_agent
from langgraph.config import get_stream_writer
from langgraph.checkpoint.memory import MemorySaver
from langgraph.store.memory import InMemoryStore

图构建

API 说明
StateGraph(state_schema) 创建图
.add_node(name, func) 添加节点
.add_edge(from, to) 添加普通边
.add_conditional_edges(source, router, mapping) 添加条件边
.compile(checkpointer=..., interrupt_before=..., interrupt_after=...) 编译图
.invoke(input, config) 执行图
.stream(input, stream_mode=..., version="v2") 流式执行

状态定义

API 说明
TypedDict 定义状态 Schema(最常用)
Annotated[type, reducer] 带 reducer 的状态字段
add_messages 消息列表的智能 reducer
operator.add 列表追加 reducer
MessagesState 内置消息状态
Overwrite 绕过 reducer 直接覆盖

工具相关

API 说明
@tool 装饰器,定义工具
model.bind_tools(tools) 绑定工具到模型
ToolMessage(content, tool_call_id) 工具执行结果消息
model.with_structured_output(Schema) 结构化输出

持久化与记忆

API 说明
MemorySaver() 内存 Checkpointer
graph.get_state(config) 获取当前状态
graph.get_state_history(config) 获取状态历史
graph.update_state(config, values) 手动更新状态
InMemoryStore() 内存 Store(长期记忆)
store.put(namespace, key, value) 写入长期记忆
store.get(namespace, key) 读取长期记忆
store.search(namespace, query=...) 搜索长期记忆

人在回路与 Command

API 说明
interrupt(value) 暂停等待人类输入
Command(resume=value) 恢复执行
Command(goto=node_name) 控制流程跳转(Handoff/Supervisor)
Command(update=state_dict) 更新状态并跳转
Command(goto=..., update=...) 同时跳转和更新

流式输出

API 说明
graph.stream(input, stream_mode="updates") 流式状态更新
graph.stream(input, stream_mode="messages") 流式 LLM Token
graph.stream(input, stream_mode="custom") 流式自定义数据
get_stream_writer() 在节点内发送自定义流式数据

多Agent与DeepAgents

API 说明
Send(node_name, state) 并行扇出
Command(goto=...) 控制权跳转(Handoff/Supervisor 核心)
create_deep_agent(model, tools, ...) 创建 DeepAgent
SubAgent(name, description, model, ...) 定义子代理
FilesystemBackend(root_dir=...) 文件系统后端
skills=["./skills/"] 加载 Skills 目录


进阶篇完 --- 基础内容(核心概念、状态、节点、边、构建图、ReAct Agent、工作流、工具、结构化输出、消息、Send API)见 LangGraph知识点总结_基础篇.md