一句话总结:LangGraph 是 LangChain 团队推出的 Agent 编排框架,它用有状态图来构建复杂 Agent 应用,解决了传统 Agent 循环在控制流、状态管理、持久化、人工介入等方面的工程难题。本文从"为什么需要 LangGraph"讲起,逐步深入核心架构,最后用一个完整代码示例带你上手。
一、为什么需要 LangGraph
1.1 从 LLM 到 Agent
最早的大模型应用非常简单,就是一个"输入 → Prompt → LLM → 输出"的单向流程:
用户输入
↓
Prompt
↓
LLM
↓
模型输出
这种模式适合文本生成、翻译、摘要、简单问答等场景。但它有一个明显限制:模型只能根据输入生成文本,不能主动完成复杂任务。
例如用户说:
帮我查一下订单状态,如果还没发货就提醒客服加急。
这件事不只是生成文本,它需要:
- 理解用户意图
- 查询订单系统
- 判断订单状态
- 必要时调用客服系统
- 生成回复
- 记录操作日志
这就进入了 Agent 场景。
1.2 Agent 是什么
Agent 可以理解为:能根据目标,自主决定下一步动作,并调用工具完成任务的智能程序。
一个典型 Agent 循环被称为 ReAct 范式(Reason + Act):
| 步骤 | 名称 | 说明 |
|---|---|---|
| 1 | Think(思考) | Agent 根据当前信息推理,决定接下来要做什么 |
| 2 | Act(行动) | 执行动作:调用工具、发起 API 查询、运行代码 |
| 3 | Observe(观察) | 拿到工具返回结果,写入全局状态 |
然后回到 Think,形成循环:Think → Act → Observe → Think → Act → Observe ...
1.3 传统 Agent 的工程难题
Agent 是多轮决策系统,这带来了很多工程问题:
| 问题 | 说明 |
|---|---|
| 状态管理 | 每一步执行结果要保存在哪里 |
| 流程控制 | 下一步执行哪个节点 |
| 工具调用 | 工具失败如何处理 |
| 循环控制 | 如何避免无限循环 |
| 人工介入 | 高风险操作是否需要审批 |
| 持久化 | 服务重启后能否恢复 |
| 可观测性 | 出错后如何定位问题 |
这些问题不是 prompt 能完全解决的。在 LangGraph 之前,很多人使用 AgentExecutor 或类似的循环结构,但在复杂项目中会遇到明显限制:
- 控制流不清晰——很难保证执行顺序,很难插入固定业务规则
- 状态管理混乱——所有东西都塞进 messages,token 成本越来越高
- 难以恢复和持久化——服务重启直接丢失上下文
- Human-in-the-Loop 不自然——人工介入经常是临时写 if/else
- 多 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 设计原则
- 字段语义清晰——不要所有东西都塞到 messages
- 只放流程需要的数据——不要把无关数据放进全局 state
- 中间结果结构化——
order_status比自然语言"订单还没发货"更容易判断 - 注意并行更新——多个节点写同一字段时,要定义 reducer
- 区分运行时 context 和 state——
user_id、trace_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
问自己几个问题:
- 是否有多个步骤?
- 是否有条件分支?
- 是否有循环?
- 是否需要保存状态?
- 是否需要人工审批?
- 是否需要任务恢复?
- 是否需要多个 Agent?
- 是否需要实时输出执行过程?
如果多个答案是"是",就适合 LangGraph。如果只是单次问答、简单 RAG、不需要持久化,直接用模型 API 或简单 LangChain 组件就够了。
七、常见误区
| 误区 | 正解 |
|---|---|
| LangGraph 就是画流程图 | 重点不是画图,而是让图可执行、可持久化、可恢复、可观测 |
| Node 必须是 LLM | Node 可以是普通函数、工具调用、数据库查询、规则判断、Agent 或子图 |
| State 就是 messages | messages 只是 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
参考资料:
如果这篇文章对你有帮助,欢迎点赞、收藏、关注!有问题可以在评论区留言讨论。

475

被折叠的 条评论
为什么被折叠?



