LangGraph 入门教程:从概念到实战

一句话总结:LangGraph 是 LangChain 团队推出的 Agent 编排框架,它用有状态图来构建复杂 Agent 应用,解决了传统 Agent 循环在控制流、状态管理、持久化、人工介入等方面的工程难题。本文从"为什么需要 LangGraph"讲起,逐步深入核心架构,最后用一个完整代码示例带你上手。


一、为什么需要 LangGraph

1.1 从 LLM 到 Agent

最早的大模型应用非常简单,就是一个"输入 → Prompt → LLM → 输出"的单向流程:

用户输入
    ↓
  Prompt
    ↓
   LLM
    ↓
 模型输出

这种模式适合文本生成、翻译、摘要、简单问答等场景。但它有一个明显限制:模型只能根据输入生成文本,不能主动完成复杂任务。

例如用户说:

帮我查一下订单状态,如果还没发货就提醒客服加急。

这件事不只是生成文本,它需要:

  1. 理解用户意图
  2. 查询订单系统
  3. 判断订单状态
  4. 必要时调用客服系统
  5. 生成回复
  6. 记录操作日志

这就进入了 Agent 场景。

1.2 Agent 是什么

Agent 可以理解为:能根据目标,自主决定下一步动作,并调用工具完成任务的智能程序。

一个典型 Agent 循环被称为 ReAct 范式(Reason + Act):

步骤名称说明
1Think(思考)Agent 根据当前信息推理,决定接下来要做什么
2Act(行动)执行动作:调用工具、发起 API 查询、运行代码
3Observe(观察)拿到工具返回结果,写入全局状态

然后回到 Think,形成循环:Think → Act → Observe → Think → Act → Observe ...

1.3 传统 Agent 的工程难题

Agent 是多轮决策系统,这带来了很多工程问题:

问题说明
状态管理每一步执行结果要保存在哪里
流程控制下一步执行哪个节点
工具调用工具失败如何处理
循环控制如何避免无限循环
人工介入高风险操作是否需要审批
持久化服务重启后能否恢复
可观测性出错后如何定位问题

这些问题不是 prompt 能完全解决的。在 LangGraph 之前,很多人使用 AgentExecutor 或类似的循环结构,但在复杂项目中会遇到明显限制:

  1. 控制流不清晰——很难保证执行顺序,很难插入固定业务规则
  2. 状态管理混乱——所有东西都塞进 messages,token 成本越来越高
  3. 难以恢复和持久化——服务重启直接丢失上下文
  4. Human-in-the-Loop 不自然——人工介入经常是临时写 if/else
  5. 多 Agent 协作困难——很难表达谁先执行、谁负责审查

二、LangGraph 核心思想

LangGraph 的名字里有两个关键词:Lang + Graph

它的核心思想是:把 Agent 应用建模成一个有状态的图。

2.1 图是什么

图由两部分组成:节点(Node)边(Edge)

在 LangGraph 中:

概念含义
Node执行单元,可以是函数、LLM 调用、工具调用、Agent、子图
Edge节点之间的连接,决定执行顺序
State图运行过程中的共享状态
Reducer多个节点更新同一字段时的合并规则
Checkpoint某一步执行后的状态快照

2.2 为什么用图表达 Agent

因为 Agent 天然不是一条直线。它经常有:分支、循环、条件判断、工具调用、人工审批、并行任务、子流程、多 Agent 协作。

这些结构用图表达非常自然。

2.3 LangGraph 带来的关键能力

能力说明
State显式状态管理,不再把所有东西塞进 messages
Control Flow可控流程,支持条件路由
Persistence通过 checkpointer 保存图状态,支持中断恢复
Human-in-the-Loop在关键节点暂停,等待人工审批后继续
Streaming实时输出执行进度和 token
Subgraph模块化,复杂系统拆成子图
Multi-Agent多 Agent 协作,每个 Agent 可以是节点或子图

三、LangGraph 核心架构

LangGraph 最重要的三个概念是:State + Node + Edge

Node 负责做事,Edge 负责决定下一步,State 负责保存整个过程中的数据。

核心组件一览

组件作用
StateGraph构建图的 builder
State图运行时的共享状态
Node执行逻辑的函数或 Runnable
Edge节点之间的连接
Reducer多个更新写入同一字段时的合并规则
START图的开始位置
END图的结束位置
CompiledGraph.compile() 后得到的可执行图

注意StateGraph 只是构建器,不能直接执行。必须调用 .compile() 编译后才能使用 invoke() / stream() 等方法。

3.1 State:图的共享状态

State 是 LangGraph 的中心。每个节点都会接收当前 state,执行后返回 state 的局部更新

使用 TypedDict 定义 State
from typing import TypedDict, Annotated
from langgraph.graph import MessagesState

class State(TypedDict):
    messages: list       # 聊天消息列表
    intent: str          # 用户意图
    tool_result: dict    # 工具调用结果
Node 如何读写 State
def chatbot(state: State) -> State:
    # 读取 state
    user_message = state["messages"][-1]
    
    # 执行逻辑(如调用 LLM)
    response = call_llm(user_message)
    
    # 返回 state 更新(只返回需要更新的字段)
    return {"messages": [response]}

关键点:节点通常不返回完整 state,而是返回局部更新。LangGraph 会自动合并。

State 不等于 messages

很多初学者会把 state 理解成聊天消息,这是不完整的。messages 只是 state 里的一个字段:

class CustomerServiceState(TypedDict):
    messages: list           # 聊天消息
    intent: str              # 用户意图(如"查询订单")
    order_id: str            # 订单号
    order_status: str        # 订单状态
    needs_human: bool        # 是否需要转人工

真实项目中,state 应该保存结构化业务数据,而不只是聊天历史。

State 设计原则
  1. 字段语义清晰——不要所有东西都塞到 messages
  2. 只放流程需要的数据——不要把无关数据放进全局 state
  3. 中间结果结构化——order_status 比自然语言"订单还没发货"更容易判断
  4. 注意并行更新——多个节点写同一字段时,要定义 reducer
  5. 区分运行时 context 和 state——user_idtrace_id 这类上下文用 runtime context

3.2 Node:图中的执行单元

Node 是 LangGraph 中真正做事的地方。Node 可以是:

  • 普通 Python 函数
  • 异步函数
  • LLM 调用
  • Tool 调用
  • Agent
  • Subgraph(子图)
  • Runnable
最简单的 Node
def greet(state: State) -> State:
    name = state.get("name", "用户")
    return {"messages": [f"你好,{name}!"]}

节点函数签名通常是 def node(state: State) -> dict,接收当前 state,返回 state 更新。

添加 Node
from langgraph.graph import StateGraph

builder = StateGraph(State)

# 方式一:显式命名(推荐)
builder.add_node("greet", greet)

# 方式二:省略名称,使用函数名作为节点名
# builder.add_node(greet)

建议:生产项目显式命名,图结构更清晰,日志和 tracing 也更容易看。

Node 中调用 LLM

LangGraph 不要求每个 node 都调用 LLM。有些 node 只是普通业务逻辑:

from langchain_openai import ChatOpenAI

# 初始化 LLM
llm = ChatOpenAI(model="gpt-4o-mini")

def chatbot(state: State) -> State:
    # 调用 LLM 生成回复
    response = llm.invoke(state["messages"])
    
    # 返回 state 更新
    return {"messages": [response]}
Node 设计原则
  • 职责单一——一个节点只做一件事
  • 输入输出清晰
  • 不偷偷修改外部全局状态
  • 返回结构化更新
  • 出错时容易定位

3.3 Edge:节点之间的连接

Edge 决定图如何流动。LangGraph 中常见两类 edge:

类型说明
普通 edge固定从 A 到 B
conditional edge根据 state 动态选择下一步
普通 edge
# START → greet → chatbot → END
builder.add_edge(START, "greet")
builder.add_edge("greet", "chatbot")
builder.add_edge("chatbot", END)
Conditional Edge(条件边)

条件边用于动态路由,是 LangGraph 的核心能力之一:

def route_intent(state: State) -> str:
    """根据用户意图路由到不同节点"""
    intent = state.get("intent", "")
    
    if intent == "查询订单":
        return "query_order"
    elif intent == "查询物流":
        return "query_logistics"
    else:
        return "human_handoff"

# 添加条件边
builder.add_conditional_edges(
    "classify_intent",   # 源节点
    route_intent,        # 路由函数
    {
        "query_order": "query_order",
        "query_logistics": "query_logistics",
        "human_handoff": "human_handoff",
    }
)

Edge 设计原则:Edge 应该表达流程关系,不要把大量业务逻辑藏在 route 函数里。复杂逻辑应该放到 node 中,route 函数只做轻量判断。

3.4 compile:从 Builder 到可执行图

StateGraph 是 builder,添加完 nodes 和 edges 后,必须调用 compile() 编译:

# 编译图
graph = builder.compile()

# 执行
result = graph.invoke({"messages": [HumanMessage(content="你好")]})
compile 做了什么

compile() 会把图变成可执行对象,并做结构检查:

  • 是否有入口
  • 节点名是否存在
  • 是否有孤立节点
  • 图结构是否合法
  • 是否配置 checkpointer、store、cache 等运行能力
compile 时配置 checkpointer
from langgraph.checkpoint.memory import InMemorySaver

# 内存检查点(生产环境替换为 Redis/Postgres)
checkpointer = InMemorySaver()

# 编译时加入 checkpointer
graph = builder.compile(checkpointer=checkpointer)

InMemorySaver 是内存检查点,用来开启会话记忆、多轮对话,重启程序数据就丢失。生产环境需替换为 Redis / Postgres 持久化存储。

compile 后的常用方法
方法说明
invoke()同步执行,返回最终 state
ainvoke()异步执行,返回最终 state
stream()同步流式执行
astream()异步流式执行
get_state()获取某个 thread 的状态
get_state_history()获取某个 thread 的状态历史

四、完整实战:构建一个客服 Agent

下面实现一个简单的客服图。

目标

  • 输入用户问题
  • 识别意图
  • 根据意图路由(查询订单 / 查询物流 / 转人工)
  • 生成最终回答

4.1 定义 State

from typing import TypedDict

class ServiceState(TypedDict):
    messages: list       # 消息列表
    intent: str          # 识别出的意图
    query_result: str    # 查询结果
    final_answer: str    # 最终回答

4.2 定义节点

def classify_intent(state: ServiceState) -> ServiceState:
    """识别用户意图"""
    user_msg = state["messages"][-1] if state.get("messages") else ""
    
    if "订单" in str(user_msg):
        intent = "查询订单"
    elif "物流" in str(user_msg) or "快递" in str(user_msg):
        intent = "查询物流"
    else:
        intent = "转人工"
    
    return {"intent": intent}


def query_order(state: ServiceState) -> ServiceState:
    """查询订单"""
    return {"query_result": "您的订单已发货,预计明天送达。"}


def query_logistics(state: ServiceState) -> ServiceState:
    """查询物流"""
    return {"query_result": "快递正在运输中,当前位置:北京转运中心。"}


def human_handoff(state: ServiceState) -> ServiceState:
    """转人工"""
    return {"query_result": "已为您转接人工客服,请稍候。"}


def final_answer(state: ServiceState) -> ServiceState:
    """生成最终回答"""
    answer = state.get("query_result", "暂无相关信息。")
    return {"final_answer": answer}

4.3 定义路由函数

def route_intent(state: ServiceState) -> str:
    """根据意图路由到不同节点"""
    intent = state.get("intent", "")
    
    if intent == "查询订单":
        return "query_order"
    elif intent == "查询物流":
        return "query_logistics"
    else:
        return "human_handoff"

4.4 构建 StateGraph

from langgraph.graph import StateGraph, START, END

# 创建图的 builder
builder = StateGraph(ServiceState)

# 添加节点
builder.add_node("classify_intent", classify_intent)
builder.add_node("query_order", query_order)
builder.add_node("query_logistics", query_logistics)
builder.add_node("human_handoff", human_handoff)
builder.add_node("final_answer", final_answer)

# 添加边
builder.add_edge(START, "classify_intent")

# 添加条件边(根据意图路由)
builder.add_conditional_edges(
    "classify_intent",
    route_intent,
    {
        "query_order": "query_order",
        "query_logistics": "query_logistics",
        "human_handoff": "human_handoff",
    }
)

# 所有查询节点都汇入 final_answer
builder.add_edge("query_order", "final_answer")
builder.add_edge("query_logistics", "final_answer")
builder.add_edge("human_handoff", "final_answer")
builder.add_edge("final_answer", END)

# 编译图
graph = builder.compile()

4.5 调用图

# 调用图,传入初始 state
result = graph.invoke({
    "messages": ["我的订单到哪了?"]
})

print(f"识别意图: {result['intent']}")
print(f"查询结果: {result['query_result']}")
print(f"最终回答: {result['final_answer']}")

执行流程

START
  ↓
classify_intent  →  识别到"订单"  →  intent = "查询订单"
  ↓ (条件路由)
query_order
  ↓
final_answer
  ↓
END

4.6 加入 LLM 的完整示例

上面的例子中,意图识别用的是简单关键词匹配。实际项目中通常用 LLM 来做意图识别:

from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage

# 初始化 LLM
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)


class State(TypedDict):
    messages: list
    intent: str
    query_result: str
    final_answer: str


def classify_intent(state: State) -> State:
    """使用 LLM 识别用户意图"""
    user_msg = state["messages"][-1].content if state.get("messages") else ""
    
    prompt = f"""请判断以下用户消息的意图,只返回以下三个选项之一:
    - 查询订单
    - 查询物流
    - 转人工
    
    用户消息:{user_msg}
    
    意图:"""
    
    response = llm.invoke([HumanMessage(content=prompt)])
    intent = response.content.strip()
    
    return {"intent": intent}


def chatbot(state: State) -> State:
    """LLM 节点:根据查询结果生成自然语言回复"""
    query_result = state.get("query_result", "")
    user_msg = state["messages"][-1].content if state.get("messages") else ""
    
    prompt = f"""根据以下信息回复用户:
    
    用户问题:{user_msg}
    查询到的信息:{query_result}
    
    请用友好的语气回复用户:"""
    
    response = llm.invoke([HumanMessage(content=prompt)])
    
    return {"final_answer": response.content, "messages": [response]}


# 构建图
builder = StateGraph(State)
builder.add_node("classify_intent", classify_intent)
builder.add_node("query_order", query_order)
builder.add_node("query_logistics", query_logistics)
builder.add_node("human_handoff", human_handoff)
builder.add_node("chatbot", chatbot)

builder.add_edge(START, "classify_intent")
builder.add_conditional_edges(
    "classify_intent",
    route_intent,
    {
        "查询订单": "query_order",
        "查询物流": "query_logistics",
        "转人工": "human_handoff",
    }
)
builder.add_edge("query_order", "chatbot")
builder.add_edge("query_logistics", "chatbot")
builder.add_edge("human_handoff", "chatbot")
builder.add_edge("chatbot", END)

# 编译并执行
graph = builder.compile()

result = graph.invoke({
    "messages": [HumanMessage(content="我的订单发了吗?")]
})
print(result["final_answer"])

五、常见图结构

5.1 线性图

START → Node A → Node B → Node C → END

适合固定流程:文档处理、固定审批、固定流水线。

5.2 条件分支图

          START
            ↓
      classify_intent
            ↓ (条件路由)
     ┌──────┼──────┐
     ↓      ↓      ↓
  查询订单 查询物流 转人工
     └──────┼──────┘
            ↓
       final_answer
            ↓
           END

适合:意图识别、分类路由、不同业务路径。

5.3 循环图

START → Think → Act → Observe → (继续?) → Think ...
                                     ↓ 否
                                    END

适合:Tool Calling Agent、代码生成后 review、反复检索直到足够。

注意:循环图要设置终止条件,避免无限循环。

5.4 并行图

START → ┬→ Node A ─→┐
        ├→ Node B ─→┤→ Merge → END
        └→ Node C ─→┘

适合:多路检索、多文档处理、多 Agent 并行。并行图经常需要 reducer。

5.5 子图

适合大型项目模块化,复杂系统拆成多个子图,提升可维护性。


六、LangGraph 适合什么场景

LangGraph 适合构建复杂、有状态、需要可靠执行的 Agent 应用。

场景为什么适合
多轮对话 Agent需要 thread、checkpoint、memory
工具调用 Agent需要控制工具调用流程
企业客服 Agent需要意图识别、工具查询、人工转接
RAG Agent需要检索、重排、生成、引用检查
Deep Research Agent需要搜索、总结、报告生成、多步骤流程
Multi-Agent 系统需要多个 Agent 协作
审批型 Agent需要 Human-in-the-Loop
长任务 Agent需要中断恢复和 durable execution

判断是否需要 LangGraph

问自己几个问题:

  1. 是否有多个步骤?
  2. 是否有条件分支?
  3. 是否有循环?
  4. 是否需要保存状态?
  5. 是否需要人工审批?
  6. 是否需要任务恢复?
  7. 是否需要多个 Agent?
  8. 是否需要实时输出执行过程?

如果多个答案是"是",就适合 LangGraph。如果只是单次问答、简单 RAG、不需要持久化,直接用模型 API 或简单 LangChain 组件就够了。


七、常见误区

误区正解
LangGraph 就是画流程图重点不是画图,而是让图可执行、可持久化、可恢复、可观测
Node 必须是 LLMNode 可以是普通函数、工具调用、数据库查询、规则判断、Agent 或子图
State 就是 messagesmessages 只是 state 的一个字段,真实项目应该使用结构化 state
Edge 只能固定连接支持 conditional edge、Command、Send 等动态控制方式
compile 后图结构还能改应该修改 builder 后重新 compile
Reducer 是可选的只要有并行更新、Map Reduce、多 Agent 汇总,就必须理解 reducer

八、总结

LangGraph 出现的根本原因,是 Agent 应用进入生产环境后,单纯依赖 prompt 和简单 Agent 循环已经不够。

LangGraph 的核心架构可以浓缩成一句话

定义 State → 写 Node → 用 Edge 连接 → compile → invoke/stream

LangGraph 让 Agent 从"模型自己循环调用工具",升级为"可编排、可恢复、可调试、可扩展的生产级系统"。

核心 API 速查

from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
from typing import TypedDict

# 1. 定义 State
class State(TypedDict):
    messages: list
    intent: str

# 2. 定义 Node
def my_node(state: State) -> State:
    return {"intent": "hello"}

# 3. 构建 Graph
builder = StateGraph(State)
builder.add_node("my_node", my_node)
builder.add_edge(START, "my_node")
builder.add_edge("my_node", END)

# 4. 编译(可选 checkpointer)
graph = builder.compile(checkpointer=InMemorySaver())

# 5. 执行
result = graph.invoke({"messages": ["hi"]})

# 6. 流式执行
for chunk in graph.stream({"messages": ["hi"]}):
    print(chunk)

安装

pip install langgraph langgraph-checkpoint

参考资料

如果这篇文章对你有帮助,欢迎点赞、收藏、关注!有问题可以在评论区留言讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值