LangGraph 入门实战:用 StateGraph 搭建一条可视化状态流
LangGraph 适合用来描述"多个步骤按照既定关系流转,并在步骤之间共享状态"的程序。它经常被用于 AI Agent、工作流编排和多步骤任务,但理解 LangGraph 并不需要先接入大模型。
本文从一个完全确定、无需 API Key 的线性流程开始,使用 StateGraph 串联三个节点:
text
START -> step1 -> step2 -> step3 -> END
程序会依次修改共享状态、返回最终结果,并将图结构保存为 PNG 图片。
1. 运行环境
本文代码的实际运行环境如下:
text
Python 3.14.7
LangGraph 1.2.10
建议先为项目创建虚拟环境,避免不同项目的依赖互相影响:
bash
python -m venv .venv
source .venv/bin/activate
python -m pip install -U langgraph typing-extensions
在 Windows PowerShell 中,激活命令为:
powershell
.venv\Scripts\Activate.ps1
2. 认识 LangGraph 的核心概念
在编写代码之前,先了解示例中使用的几个概念。
| 概念 | 作用 |
|---|---|
| State | 整个流程共享的数据 |
| Node | 接收状态并返回状态更新的处理函数 |
| Edge | 指定节点之间的流转关系 |
| START | 图的固定入口 |
| END | 图的固定出口 |
| compile | 将图定义编译为可执行对象 |
| invoke | 使用初始状态执行一次完整流程 |
可以把 State 理解为一份在节点之间传递的数据表。每个节点读取需要的字段,并返回本次需要更新的字段。
3. 定义共享状态
示例使用 TypedDict 定义状态结构:
python
from typing_extensions import TypedDict
class State(TypedDict):
value1: str
value2: str
value3: str
这表示流程状态中包含三个字符串字段:value1、value2 和 value3。
TypedDict 能为编辑器和类型检查工具提供字段提示,但它不是运行时数据校验器。如果需要在运行时校验数据,还可以根据项目需求使用 Pydantic 模型定义状态。
4. 编写三个处理节点
LangGraph 节点可以是普通 Python 函数。函数接收当前状态,并返回需要写回状态的字典。
step1:更新 value1
python
def step1(state: State):
return {"value1": state["value1"] + "这是step1;"}
输入 value1 为 初始值 时,节点返回:
python
{"value1": "初始值这是step1;"}
节点只返回了 value1。LangGraph 会把它合并到当前状态中,未返回的 value2 和 value3 会继续保留。
step2:读取 step1 的结果
python
def step2(state: State):
v = state["value1"]
return {"value2": f"{v}; 这是step2"}
step2 读取的是 step1 更新后的 value1,然后生成 value2。这正是共享状态的价值:后续节点可以直接访问前面节点的执行结果。
step3:组合两个字段
python
def step3(state: State):
v1 = state["value1"]
v2 = state["value2"]
return {"value3": f"{v1} : {v2}"}
最后一个节点同时读取 value1 和 value2,将组合结果写入 value3。
5. 创建 StateGraph 并注册节点
python
from langgraph.graph import START, StateGraph
graph_builder = StateGraph(State)
graph_builder.add_node(step1).add_node(step2).add_node(step3)
StateGraph(State) 告诉 LangGraph,这张图中的所有节点都使用前面定义的 State 作为共享状态结构。
add_node 用于注册节点。没有显式传入名称时,LangGraph 会使用函数名作为节点名,因此这里得到的节点名分别是:
text
step1
step2
step3
add_node 会返回当前图构建器,所以可以像示例一样进行链式调用。
6. 使用边定义执行顺序
python
graph_builder.add_edge(START, "step1")
graph_builder.add_edge("step1", "step2")
graph_builder.add_edge("step2", "step3")
三条边定义了完整的执行顺序:
- 从
START进入step1。 step1完成后执行step2。step2完成后执行step3。
当前示例中,step3 没有后继节点,因此编译后的图会把它作为流程终点。为了让大型项目中的流程定义更加明确,也可以显式连接 END:
python
from langgraph.graph import END
graph_builder.add_edge("step3", END)
7. 编译并执行图
图的结构定义完成后,需要先调用 compile():
python
graph = graph_builder.compile()
编译会生成一个可执行图。随后通过 invoke() 传入初始状态:
python
res = graph.invoke(
{
"value1": "初始值",
"value2": "",
"value3": "",
}
)
print("执行结果:", res)
一次 invoke() 会从 START 开始运行,直到流程结束,并返回最终完整状态。
8. 状态是如何变化的
这次执行过程中,状态依次发生如下变化:
| 阶段 | value1 | value2 | value3 |
|---|---|---|---|
| 初始状态 | 初始值 |
空字符串 | 空字符串 |
| step1 后 | 初始值这是step1; |
空字符串 | 空字符串 |
| step2 后 | 初始值这是step1; |
初始值这是step1;; 这是step2 |
空字符串 |
| step3 后 | 初始值这是step1; |
初始值这是step1;; 这是step2 |
初始值这是step1; : 初始值这是step1;; 这是step2 |
这里的两个连续分号 ;; 来自原始代码:step1 的结果末尾已经有一个分号,step2 又在变量后追加了一个分号。这不是 LangGraph 重复执行造成的。如果希望输出更整洁,可以调整节点中的字符串拼接方式。
9. 将图结构导出为 PNG
可执行图可以转换为 Mermaid 图,再渲染为 PNG:
python
png_bytes = graph.get_graph().draw_mermaid_png()
with open("langgraph_chain.png", "wb") as f:
f.write(png_bytes)
代码执行后,会在当前目录生成 langgraph_chain.png:

draw_mermaid_png() 默认可能需要访问 Mermaid 渲染服务。如果图片生成阶段出现网络错误,可以先检查网络连接;在对外网络受限的环境中,也可以改用本地 Mermaid 渲染方案。
10. 完整代码
下面是本文实际运行的完整代码:
python
from langgraph.graph import START, StateGraph
from typing_extensions import TypedDict
class State(TypedDict):
value1: str
value2: str
value3: str
def step1(state: State):
return {"value1": state["value1"] + "这是step1;"}
def step2(state: State):
v = state["value1"]
return {"value2": f"{v}; 这是step2"}
def step3(state: State):
v1 = state["value1"]
v2 = state["value2"]
return {"value3": f"{v1} : {v2}"}
# 构建流程图
graph_builder = StateGraph(State)
graph_builder.add_node(step1).add_node(step2).add_node(step3)
# 线性边流转
graph_builder.add_edge(START, "step1")
graph_builder.add_edge("step1", "step2")
graph_builder.add_edge("step2", "step3")
# 编译图
graph = graph_builder.compile()
# 绘制结构图
png_bytes = graph.get_graph().draw_mermaid_png()
with open("langgraph_chain.png", "wb") as f:
f.write(png_bytes)
# 执行流程
res = graph.invoke({"value1": "初始值", "value2": "", "value3": ""})
print("执行结果:", res)
说明:项目当前的 index.py 不需要 IPython.display,因此实际源文件已经移除了对应导入。如果准备在 Jupyter Notebook 中直接展示图片,则可以使用:
python
from IPython.display import Image, display
display(Image(png_bytes))
11. 运行代码和真实输出
进入代码所在目录后执行:
bash
python index.py
本文在本地实际运行得到的控制台输出为:
text
执行结果:
{'value1': '初始值这是step1;',
'value2': '初始值这是step1;; 这是step2',
'value3': '初始值这是step1; : 初始值这是step1;; 这是step2'}
同时,目录中会生成:
text
langgraph_chain.png
12. 常见问题
12.1 Pylance 提示无法解析 langgraph.graph
通常是 VS Code 选择的 Python 解释器与安装依赖时使用的解释器不一致。先在终端确认:
bash
python -c "import sys; print(sys.executable)"
python -m pip show langgraph
然后在 VS Code 中执行 Python: Select Interpreter,选择当前项目的 .venv/bin/python。
12.2 为什么节点只返回一个字段,最终结果却包含全部字段
节点返回的是状态更新,不是用新字典替换整个状态。LangGraph 会按照 State 中每个字段对应的更新规则合并结果。在本例中,普通字符串字段会被节点返回的新值覆盖,其他未返回字段保持不变。
12.3 为什么需要 compile
构建阶段只是在描述节点和边。compile() 会检查图结构并生成真正可调用的运行对象。只有编译后的 graph 才能执行 invoke()、生成图结构或接入检查点等运行能力。
12.4 invoke() 和直接调用三个函数有什么区别
对于三个固定步骤,直接依次调用函数当然也能实现相同结果。LangGraph 的价值在流程变复杂后更加明显,例如:
- 根据状态进行条件分支;
- 某些步骤循环执行;
- 并行执行多个节点;
- 保存检查点并恢复流程;
- 在人工确认后继续运行;
- 将大模型、工具调用和业务节点组合成 Agent。
当前这个线性示例建立了最重要的基础:状态、节点、边、编译和执行。掌握这五个概念后,就可以继续学习条件边、持久化和人工介入等能力。
总结
使用 LangGraph 创建一个基础工作流,可以归纳为五步:
- 使用
TypedDict等类型定义共享状态。 - 编写接收状态、返回状态更新的节点函数。
- 使用
StateGraph注册节点。 - 使用边描述节点之间的流转关系。
- 编译图并通过
invoke()执行。
这个示例虽然简单,但已经完整展示了 LangGraph 的核心运行模型。后续无论是增加条件判断、接入大模型,还是实现可恢复的 Agent,本质上都是在这套"状态 + 节点 + 边"的结构上继续扩展。