LangGraph学习笔记

1. LangGraph 核心概念

LangGraph 是基于 LangChain 的图编排框架,专门用来构建多轮 Agent、带循环、分支、人在回路的 LLM 应用,不是简单链式调用(LCEL)。

  • 核心思想:把 Agent 流程抽象成节点 (Node)边 (Edge) ,消息 / 数据保存在状态 State中,图按照边规则流转执行。
  • 与普通 LCEL 区别:LCEL 适合线性、无循环链路;LangGraph 天然支持循环、条件分支、中断暂停、状态持久化,是复杂 Agent 首选。
  • 基础术语
    1. Node 节点:图中的执行单元,可以是 LLM 调用、工具执行、自定义业务函数、子图。输入 state,返回 state 增量更新。
    2. Edge 边:节点之间的流转通路,分为普通边、条件边。
    3. State 状态:整个图的全局数据容器,图每一步都会读取 / 更新状态。
    4. Reducer(归约器):定义同一个字段多次更新时如何合并 ,而不是直接覆盖。最常用add_messages
    5. Checkpointer 检查点:保存每一步状态快照,实现持久化、暂停恢复。
    6. Thread 线程:会话隔离 ID,一个 thread 对应一轮独立对话任务。

✅笔记补充:LangGraph 本质是状态机;每一个节点返回的不是完整 state,是增量更新字典,由 reducer 合并进全局 state。

2. 状态 State 与 Reducer

2.1 State 定义(TypedDict / Pydantic)

两种定义 state 方式:

  1. TypedDict(推荐,轻量,笔记示例统一用这个):仅声明字段类型,不需要实例化,适合快速开发。

  2. Pydantic BaseModel:带校验、默认值,适合生产强校验场景。

    from typing import TypedDict, Annotated

    class State(TypedDict):
    # Annotated[类型, reducer函数]
    messages: Annotated[list, add_messages]
    query: str
    result: str

Annotated第二个参数就是 reducer,用来控制字段合并策略。

2.2 Reducer 归约器原理

  • 默认行为:不带 reducer,节点返回更新会直接覆盖原有字段
  • add_messages:LangGraph 内置 reducer,追加消息列表,不覆盖历史,Agent 消息字段标配
  • 自定义 reducer:自己写函数 func(old_val, new_val),接收旧值、新值,返回合并后的结果。

示例自定义 reducer:

复制代码
def append_list(old_list:list, new_list:list) -> list:
    return old_list + new_list

⚠️坑点:忘记加add_messages,节点返回消息会直接覆盖历史对话,上下文丢失。

2.3 状态更新规则

节点 return 字典,是增量更新,不需要返回完整 State

复制代码
def my_node(state:State):
    # 只需要返回要修改的字段
    return {"result": "计算结果xxx"}

3. StateGraph 图基础

3.1 StateGraph 核心组件

StateGraph是图构建器,用来注册节点、添加边、最后 compile 编译成可执行 graph 实例。

  • builder = StateGraph(State):传入状态类型,实例化图构建器
  • builder.add_node(name, func):注册节点,name 是节点标识字符串,func 是执行函数
  • builder.add_edge(from_node, to_node):普通静态边,A 执行完固定流向 B
  • builder.add_conditional_edges(from_node, route_func):条件边,路由函数根据 state 动态选择下一个节点
  • START:图入口,不是自定义节点;END:图终止标记,到达 END 图停止执行
  • graph = builder.compile():编译,生成可调用图对象,compile 时可以传入 checkpointer

3.2 基础线性图示例(无分支无循环)

复制代码
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END

class State(TypedDict):
    query:str
    reply:str

def preprocess(state:State):
    return {"query": state["query"].strip()}

def reply_node(state:State):
    return {"reply": f"收到你的问题:{state['query']}"}

builder = StateGraph(State)
builder.add_node("preprocess", preprocess)
builder.add_node("reply_node", reply_node)

builder.add_edge(START, "preprocess")
builder.add_edge("preprocess", "reply_node")
builder.add_edge("reply_node", END)

graph = builder.compile()
res = graph.invoke({"query":"   你好LangGraph  "})
print(res["reply"])

执行链路:START → preprocess → reply_node → END

3.3 invoke /ainvoke/astream_events 调用方式

  1. graph.invoke(input, config={}):同步调用,阻塞直到图执行完成,返回最终完整 state。适合简单调试。
  2. await graph.ainvoke(input, config={}):异步调用,异步服务后端推荐。
  3. astream_events:流式事件,输出每一步节点启停、tool 调用事件,适合前端流式输出(后面 10.1 展开)

✅笔记补充:config用来传递thread_id、中断参数,不属于 state,不会存入状态快照。 ⚠️坑点:START不能 add_node 注册;END只是终止标记,不能作为节点添加。

4. 工具调用集成

4.1 ToolNode 内置工具节点

ToolNode 是 LangGraph 内置节点,专门用来解析 AIMessage 里的工具调用参数,自动执行对应函数,并返回 ToolMessage 写入状态,不用手动写工具解析逻辑。

适用场景:LLM 输出 tool_calls → ToolNode 自动调度工具。

  • 输入:状态中包含带 tool_callsAIMessage
  • 输出:追加一条或多条 ToolMessage 到 messages
  • 异常:工具执行报错时,错误信息会封装进 ToolMessage 回传给 LLM,方便 LLM 重试

4.2 tools_condition 内置路由

tools_condition 是 LangGraph 预置条件边函数,用来判断 LLM 输出是否包含工具调用:

  1. 如果消息存在 tool_calls → 返回 tools,路由到 ToolNode
  2. 没有 tool_calls → 返回 END,结束当前 Agent 轮次

不用自己手写判断工具调用的路由函数,单 Agent 模板标配。

4.3 工具绑定、ToolMessage 注入、工具异常处理

  1. 工具绑定 :使用 LangChain 的bind_tools把 python 函数绑定到大模型,让 LLM 知道可用工具的名称、参数 schema。
  2. ToolMessage 注入 :ToolNode 执行完工具,自动生成 ToolMessage,依靠add_messages reducer 追加进 state.messages。
  3. 工具异常处理
    • 默认:工具抛出异常,ToolNode 捕获异常,包装成 ToolMessage 返回,交给 LLM 感知错误,LLM 可以重新调用 / 修正参数。
    • 自定义:可以在工具函数内部 try-catch,自定义错误消息返回;也可以封装异常节点,走单独降级分支。

4.4 单 Agent 标准模板:LLM -> 工具路由 -> ToolNode

复制代码
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_openai import ChatOpenAI
from langchain.tools import tool

# 1. 定义状态
class State(TypedDict):
    messages: Annotated[list, add_messages]

# 2. 定义工具
@tool
def multiply(a:int, b:int) -> int:
    """计算两个数字相乘"""
    return a * b

tools = [multiply]
llm = ChatOpenAI(model="gpt-4o-mini").bind_tools(tools)

# 3. LLM节点
def llm_node(state:State):
    resp = llm.invoke(state["messages"])
    return {"messages": [resp]}

# 4. 构建图
builder = StateGraph(State)
builder.add_node("llm", llm_node)
builder.add_node("tools", ToolNode(tools))

# 边
builder.add_edge(START, "llm")
# 条件边:llm执行后交给tools_condition路由
builder.add_conditional_edges("llm", tools_condition)
builder.add_edge("tools", "llm") #工具执行完回到LLM继续判断

graph = builder.compile()

# 调用
res = graph.invoke({"messages": [("user", "3乘以7等于多少")]})
print(res["messages"])

执行流程:用户消息 → LLM 生成 tool_call → 路由到 ToolNode 执行 multiply → ToolMessage 写回 state → 回到 LLM 生成最终回答。

5. 持久化、线程与检查点

5.1 Checkpointer

为什么需要 checkpointer

默认 graph 执行是无状态的,每次 invoke 都是全新会话。Checkpointer 负责保存每一步执行后的状态快照、下一步路由信息,实现会话保存、暂停恢复、时间旅行。

三类常用 Checkpointer
  1. MemorySaver:内存存储,仅进程有效,重启丢失;适合本地调试。
  2. SqliteSaver:本地 sqlite 文件持久化,单实例,适合小流量、demo。
  3. PostgresSaver:生产推荐,支持多进程、多服务并发,高可用。

检查点保存内容:state 快照、下一跳节点信息、中断标记。不是只存 messages,包含完整状态 + 执行指针。

5.2 Thread 与多会话

thread_id 是会话隔离标识。同一个 thread_id 代表同一个会话,不同 thread_id 状态完全隔离。

  • 调用 graph.invoke 的时候,传入 config={"configurable":{"thread_id":"xxx"}}

  • 同一线程:可以中断、恢复,读取历史状态

  • 多用户最佳实践:每个用户 / 每个独立对话分配唯一 thread_id;生产不要复用 thread_id 跨不同任务。

    from langgraph.checkpoint.memory import MemorySaver

    checkpointer = MemorySaver()
    graph = builder.compile(checkpointer=checkpointer)
    config = {"configurable":{"thread_id":"session_001"}}
    graph.invoke({"messages":[("user","你好")]}, config=config)

5.3 时间旅行

  • get_state(config) 获取当前最新状态快照
  • get_state_history(config) 获取该 thread 全部历史检查点列表
  • update_state(config, state_updates):手动修改当前线程的状态快照
  • 回放:拿到历史检查点 id,在 config 指定checkpoint_id,从历史快照重新执行,用于分支调试、回滚。

时间旅行底层就是读取历史 checkpoint,重置图执行指针到该快照。适合调试、人工修改历史消息。

6. 控制流:条件边、循环、并行与子图

6.1 条件边与分支

add_conditional_edges 支持多分支路由。条件函数接收 state,返回目标节点名称。

复制代码
def route(state):
    query = state["query"]
    if "计算" in query:
        return "calc_node"
    elif "搜索" in query:
        return "search_node"
    else:
        return "reply_node"

builder.add_conditional_edges("preprocess", route)

6.2 循环与递归

循环就是条件边路由回前面节点,形成闭环。必须设计终止条件,否则死循环。 LangGraph 有递归深度限制 ,防止无限循环。编译时可以配置recursion_limit,默认一般 100。

典型场景:Agent 多轮调用工具,直到满足退出条件。

6.3 并行与扇出 / 扇入

Send API:运行时动态创建多个独立任务,并行执行同一个节点。

  • Send 接收目标节点名称 + 该任务独立子 state

  • 并行节点共享主图 state,依靠 reducer 汇总结果

  • 扇出:一次分发多个 Send 任务;扇入:全部并行任务完成后汇总。

    from langgraph.graph import Send

    def dispatch(state):
    tasks = []
    for item in state["task_list"]:
    tasks.append(Send("sub_task_node", {"item": item}))
    return tasks
    builder.add_conditional_edges("dispatch", dispatch)

6.4 子图

子图就是把一段完整 StateGraph 封装成一个节点,实现逻辑复用。

  • 子图有自己的状态定义,也可以复用主图 state

  • 主图调用子图,子图执行完成后,将更新合并入主图状态

  • 优势:模块化,多个业务流程复用同一套子 Agent 逻辑

    子图构建

    sub_builder = StateGraph(SubState)

    ...子图节点和边定义

    subgraph = sub_builder.compile()

    主图添加子图作为节点

    builder.add_node("subgraph_node", subgraph)

6.5 复杂流程图设计示例

典型多轮 Agent 流程图结构: START → 预处理节点 → LLM 节点 →【条件路由】

  • 需要工具 → ToolNode → 回到 LLM(循环)
  • 需要人工审批 → interrupt 暂停(HITL)
  • 任务完成 → END

7. 人在回路 Human-in-the-Loop

7.1 interrupt 两种暂停方式

  1. interrupt_before=["nodeA"]:在执行 nodeA之前暂停,执行节点前触发中断
  2. interrupt_after=["nodeB"]:nodeB 执行完成后暂停
  3. 节点内部interrupt():节点函数内部主动抛出中断,适合动态判断何时暂停。

中断后:图执行停止,可以读取 state、修改 state,再调用invoke恢复继续跑。

7.2 人工审批与状态修改

中断后人工可以:

  1. 查看当前 state,阅读 Agent 生成的计划
  2. 审批通过:直接继续执行
  3. 拒绝 / 修改:update_state 修改 state(修改消息、修改参数),恢复执行后使用修改后的状态。

7.3 外部系统对接模式

  1. 前端轮询:后端保存 thread_id,前端轮询查询任务状态(是否中断、是否完成)
  2. Webhook:图执行结束 / 触发中断回调外部服务通知
  3. 任务队列:LangGraph 放到后台任务队列,异步执行,适合长耗时任务。

7.4 人工兜底与错误恢复

当 LLM 自动流程无法完成任务,自动触发 interrupt 交给人工兜底;人工修正后继续。 可搭配异常分支:节点报错后,路由到人工审批节点。

8. 异常处理、重试、超时

8.1 节点异常捕获

节点函数内部使用 try/except 捕获业务异常;未捕获异常会终止图执行。 也可以捕获异常,返回状态更新写入错误字段,交给条件边路由降级分支。

8.2 节点重试策略

可以使用@retry装饰器(tenacity)包裹节点函数,对网络波动、LLM 限流自动重试。

注意:重试会重复执行节点逻辑,有副作用的操作(调用外部 API、写数据库)要考虑幂等。

8.3 异常路由:失败降级分支

节点捕获异常,将异常信息写入 state "error";条件边读取 error 字段,如果存在错误,路由到降级 / 通知节点,而直接走到 END。

8.4 超时控制

两层超时:

  1. 节点内:LLM 调用、工具调用单独设置 http 超时
  2. 图级别:外部封装 invoke 调用,增加全局超时,防止任务卡死。

9. 多 Agent 架构

9.1 多 Agent 常见模式

  1. Supervisor 主管模式:主管 Agent 接收总任务,拆分任务调度多个子 Agent,汇总子 Agent 结果。最常用。
  2. Hierarchical 层级:多层级主管,一级主管拆大任务,二级主管再细分。
  3. Peer-to-peer 对等网络:Agent 之间互相 handoff 交接任务,无中心主管。

9.2 Supervisor 主管 Agent 实现

主管节点负责:

  1. 读取全局任务状态
  2. LLM 判断当前该交给哪个子 Agent 执行
  3. 返回 Command 完成任务交接
  4. 收集子 Agent 输出,判断任务是否全部完成。 子 Agent 拥有独立业务逻辑;可以共享主图 state,也可以隔离状态。

9.3 Agent 通信与状态共享

  • 共享图状态:所有 Agent 读写同一个 state,数据互通,适合强协作场景;缺点:容易出现消息污染。
  • 独立消息传递:Agent 之间只传递消息,子 Agent 状态隔离,完成任务后返回结果合并入主状态。
  • Command /handoff:handoff 是高层封装,底层基于 Command,用于 Agent 之间任务交接。

9.4 多 Agent 实战坑点

  1. 循环控制:防止 Agent 之间来回无限互相调用,设置最大轮次。
  2. 消息去重:防止重复消息不断追加,消耗 token。
  3. 上下文窗口管理:消息过长时做摘要、截断,避免超出模型上下文上限。

10. 调试、可视化与工程上线

10.1 流式事件 astream_events

astream_events 可以拿到全链路事件:节点开始、节点结束、tool 调用事件、断点中断事件。适合前端流式渲染、日志埋点。

复制代码
async for event in graph.astream_events(inputs, config=config, version="v2"):
    print(event["event"], event["name"])

10.2 图可视化

复制代码
# 输出mermaid文本
print(graph.get_graph().draw_mermaid())
# 输出图片(需要额外依赖)
graph.get_graph().draw_png("graph.png")

LangGraph Studio:官方 IDE,可以加载 checkpoint,可视化每一步状态、断点调试,本地开发强烈推荐。

10.3 生产优化

  1. 状态大小控制:messages 不断累积,及时消息摘要、截断,防止 state 过大,序列化开销高。
  2. Checkpointer 选型:Sqlite 只适合单实例本地;生产环境 PostgresSaver。
  3. 并发注意:多进程并发读写同一个 thread 的 checkpoint 要注意锁;避免多实例同时操作同一个 thread_id。

10.4 自定义 Checkpointer(进阶可选)

实现自定义 Checkpointer 抽象基类,对接 Redis、MongoDB 等存储,实现企业自有存储持久化。需要实现 put/get/list 等检查点读写接口。


Command:节点内部直接控制跳转完整示例

Command 优先级高于边定义的路由规则。节点 return Command,可以同时做两件事:更新 state,并且直接指定下一跳节点。非常适合 Supervisor 多 Agent 任务交接。

复制代码
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END, Command
from langgraph.graph.message import add_messages

class State(TypedDict):
    messages: Annotated[list, add_messages]
    task: str

# 子节点A
def agent_a(state: State):
    print("=== Agent A 执行 ===")
    return {
        "messages": [("ai", "A完成初步分析")]
    }

# 子节点B
def agent_b(state: State):
    print("=== Agent B 执行 ===")
    return {
        "messages": [("ai", "B完成详细计算")]
    }

# Supervisor主管节点:使用Command动态跳转
def supervisor(state: State):
    task = state["task"]
    if "分析" in task:
        # 更新state,并且指定下一步跳 agent_a
        return Command(
            goto="agent_a",
            update={"messages": [("ai", "主管:交给AgentA分析任务")]}
        )
    elif "计算" in task:
        return Command(
            goto="agent_b",
            update={"messages": [("ai", "主管:交给AgentB计算任务")]}
        )
    else:
        # 直接跳到END结束
        return Command(goto=END, update={"messages":[("ai","任务无法处理,结束")]})


# 构建图
builder = StateGraph(State)
builder.add_node("supervisor", supervisor)
builder.add_node("agent_a", agent_a)
builder.add_node("agent_b", agent_b)

builder.add_edge(START, "supervisor")
# 不需要额外条件边!由Command控制跳转
builder.add_edge("agent_a", END)
builder.add_edge("agent_b", END)

graph = builder.compile()

# 测试1:路由agent_a
res1 = graph.invoke({"task":"业务分析需求"})
print("\n结果1消息:", res1["messages"])

# 测试2:路由agent_b
res2 = graph.invoke({"task":"数据计算需求"})
print("\n结果2消息:", res2["messages"])

Command 关键参数说明

  1. goto:目标节点名称,可以是节点名字符串,也可以是END;支持列表,一次性多 Send 并行。
  2. update:状态更新字典,和普通节点返回的增量更新语法一致,会经过 reducer 合并进 state。
  3. 适用场景:主管 Agent 动态分配任务、跨子图跳转、人工中断恢复后定向路由。
  4. 注意:Command是节点返回值;不能在条件边函数返回 Command,条件边只能返回节点名称字符串。
相关推荐
AIGC大时代1 天前
LangGraph 生产级笔记:Checkpointer + interrupt(),把人审做成可恢复状态机
python·agent·状态机·langgraph·hitl
m0_579146652 天前
Agent会话持久化:事件日志重放与状态快照Checkpoint深度解析
claude·langgraph·会话记忆
.唉5 天前
05. LangGraph 深度解析:从基础概念到高级应用全指南
agent·langgraph
阿图灵5 天前
LangGraph 实战 03:Workflows 与 Agents——六种工作流模式与智能体实战(附 6 个可运行示例)
java·前端·javascript·工作流·ai agent·智能体·langgraph
虎虎(_ _)。゜zzZ10 天前
LangGraph-State架构深度解构
langgraph·大模型工程化·状态机设计·ai agent架构·llm开发·python高级编程
虎虎(_ _)。゜zzZ10 天前
重构 Agent 思维链:LangGraph 核心架构的深度解构与实战
大模型应用·langgraph·agent框架·ai工程化·状态机 python
rising start14 天前
LangGraph 中断、工具调用与部署
langgraph
闲猫16 天前
LangGraph / Capabilities / Fault tolerance
python·agent·langgraph
闲猫16 天前
LangGraph / Capabilities / Stores
python·agent·langgraph