目录
[1. 编码前的版本说明](#1. 编码前的版本说明)
[2. 【案例一】智能快递配送系统](#2. 【案例一】智能快递配送系统)
[2.1 Graph API 编码思路](#2.1 Graph API 编码思路)
[2.2 代码实现](#2.2 代码实现)
[2.2.1 步骤1:定义 State,设置快递的"包裹信息"](#2.2.1 步骤1:定义 State,设置快递的"包裹信息")
[2.2.1.1 【知识点】State 更新机制:Reducers](#2.2.1.1 【知识点】State 更新机制:Reducers)
[2.2.2 步骤2:定义 StateGraph 图,成立快递公司](#2.2.2 步骤2:定义 StateGraph 图,成立快递公司)
[2.2.3 步骤3:定义 Nodes,创建配送站点](#2.2.3 步骤3:定义 Nodes,创建配送站点)
[2.2.4 步骤4:添加 Nodes,建设配送站点](#2.2.4 步骤4:添加 Nodes,建设配送站点)
[2.2.5 步骤5:添加 Edges,规划运输路线](#2.2.5 步骤5:添加 Edges,规划运输路线)
1. 编码前的版本说明
LangGraph 放弃了对 Python 3.9 的支持,所有 LangChain 包现在都需要 Python 3.10 或更高版本。. 使用 python --version 查看 Python 版本。如低于3.9,需要重装。
2. 【案例一】智能快递配送系统
2.1 Graph API 编码思路
构建 Graph 图,首先需要【定义状态】,然后【定义并添加节点和边】,最后【编译】它。编译提供 了对图形结构的一些基本检查(没有孤立节点等)
LangGraph 所谓的"编译" 与 传统意义上的语言编译完全不同,LangGraph 编译本质是在运行时动 态构建和验证一个复杂的图,而非翻译代码。
C++的编译是"完整编译"或"静态编译"的典范。 它追求在程序运行之前,就将所有代码"解 决"完毕,生成一个独立、高效、可直接被操作系统调用的"成品"。它比 Java 的编译更彻底(直接 到机器码,而非中间码),也更底层(紧密绑定操作系统和CPU架构)。 "编译"对比表格:
| 特性 | LangGraph 的编译 | Java 的编译 | C++ 的编译 |
|---|---|---|---|
| 本质 | 配置组装与验证 | 语言翻译与转换(到中间码/字节码) | 语言翻译与转换(到本地机器码) |
| 发生阶段 | 应用程序运行时(初始化阶段) | 应用程序开发时(构建阶段) | 应用程序开发时(构建阶段) |
| 主要目的 | 1. 构建可执行的工作流对象 2. 进行结构验证 | 1. 将高级语言转为低级语言(字节码) 2. 进行语法和深度语义检查 | 1. 将高级语言转为特定平台的本地机器码 2. 进行彻底的语法、语义检查与优化 3. 链接所有模块,生成完整的可执行文件 |
2.2 代码实现
先明确流程,敲定好状态,节点,边


2.2.1 步骤1:定义 State,设置快递的"包裹信息"
定义图时要做的第一件事是定义图的 状态 。**状态 将是图中所有 节点 和 边 的输入,**可以是 TypedDict 或 Pydantic 模型( Pydantic 的性能不如 TypedDict )。如下所示:
python
import operator
from typing import TypedDict, Annotated
from langgraph.constants import END
from langgraph.constants import START
from langgraph.graph import StateGraph
#1、状态定义(贯穿整个图)
class PackageState(TypedDict):
#input:str
#output:str
#包裹状态:
#包裹id
package_id:str
#包裹始发站
origin:str
#包裹终点站
destination:str
#next
#配送状态:
#状态("待揽收","已揽收","运输中","派送中","已签收")
status:str
#流转历史 list
#知识点:
#history:list[str] #覆盖耿欣
history:Annotated[list[str],operator.add] #最追加更新
#总历程
total_distance:Annotated[int,operator.add]
#配送详情:
#("普通","加急")
priority:str
2.2.1.1 【知识点】State 更新机制:Reducers
状态 支持更新的关键是 Reducers。在示例中,我们使用 Annotated 类型为 history 和 total_distance 指定了一个 reducer 函数 ( operator.add )。这定义了如何更新包裹信 息,特别是当多个站点都要记录信息时。当状态更新时,新的值会追加到现有值中,而不是替换。如 下所示:
python
#配送状态:
#状态("待揽收","已揽收","运输中","派送中","已签收")
status:str
#流转历史 list
#知识点:
#history:list[str] #覆盖耿欣
history:Annotated[list[str],operator.add] #最追加更新
#总历程
total_distance:Annotated[int,operator.add]
2.2.2 步骤2:定义 StateGraph 图,成立快递公司
StateGraph 是一个有状态图计算框架,它基于有向图(Directed Graph) 模型构建,专门设计用于处 理多步骤、有状态的工作流程。 **StateGraph 用来将复杂的工作流程可视化、模块化,让开发者能够像设计快递配送网络一样设计软件 系统。**通过这种思维方式,即使是复杂的多步骤 AI 应用也变得清晰可控。 我们需要使用 langgraph.graph.state.StateGraph 来定义。 StateGraph 仅是一个构建 器类,可以使用 State 来构建。如下所示:
python
#2、定义图(依赖状态)
delivery = StateGraph(PackageState)
注意,这里仅是构建出 StateGraph ,还无法直接用于执行。
2.2.3 步骤3:定义 Nodes,创建配送站点
接着我们可以定义各个配送站点(节点)。在 LangGraph 中, 节点 就是一个 Python 函数(同步或 异步)。注意
• 节点 接收 状态 作为参数。
• 节点 不需要返回整个 状态 模式,只需一个更新。 如下所示:
python
#3、节点定义(函数)
#输入:状态
#输出:状态的更新
#节点之间使用state进行通信
# def test(state:PackageState):
# return {
# "history":["揽收站"],
# "total_distance":500
# }
#揽收站节点
def receive_package(state:PackageState):
"""揽收站"""
# 进行状态扭转
# 状态值获取
print("---执行到揽收站节点")
origin=state["origin"]
return {
"status":"已揽收",
"history":[f"在{origin}揽收"]
}
#分拣中心节点:根据目的地进行分拣
def sort_package(state:PackageState):
"""分拣中心"""
print("---执行到分拣中心节点")
destination=state["destination"]
if"北京" in destination:
next="北京分拣中心"
elif"上海" in destination:
next="上海分拣中心"
else:
next="其他分拣中心"
return {
"status": "已分拣",
"history": [f"将分拣至{next}"]
}
#派送站节点
def final_delivery(state:PackageState):
"""派送站"""
print("---执行到派送站节点")
return {
"status":"已签收",
"history":[f"已送达{state['destination']}"]
}
#标准配送节点
def standard_delivery(state:PackageState):
"""标准配送"""
print("---执行到标准配送节点")
return{
"status": "运输中",
"history": ["标准陆运"],
"total_distance": 500
}
#加急配送节点
def express_delivery(state:PackageState):
"""加急配送"""
print("---执行到加急配送节点")
return {
"status": "加急运输",
"history": ["空运加急"],
"total_distance": 800
}
2.2.4 步骤4:添加 Nodes,建设配送站点
接下来,我们需要将各节点,组织进图中,即添加节点到 StateGraph 中。可以使用 add_node() 将新节点添加到 StateGraph 。 add_node() 方法常用参数说明:
| 参数名 | 类型 | 描述 |
|---|---|---|
| node | 字符串 或 StateNode 对象 | 作用 :指定节点要运行的函数或可执行对象。 使用方式 : - 如果传入字符串,该字符串将作为节点名称,此时会使用 action 参数作为实际的执行函数。 - 如果传入 StateNode 对象,则直接使用该对象定义节点。 |
| action | StateNode 对象 或 None(默认值) | 作用 :定义与节点关联的动作(执行逻辑)。 使用方式 : - 当 node 参数是字符串时,action 会作为该节点的实际执行函数。 - 当 node 参数已经是 StateNode 对象时,action 通常为 None。 |
示例:
python
#4、添加节点
delivery.add_node("揽收站",receive_package)
delivery.add_node("分拣中心",sort_package)
delivery.add_node("派送站",final_delivery)
delivery.add_node("标准配送",standard_delivery)
delivery.add_node("加急配送",express_delivery)
2.2.5 步骤5:添加 Edges,规划运输路线
各节点(站点)准备好后,则需要为快递运输规划路线。如:

实际上,这就是为 图 定义 边 。边有几种关键类型:
• 普通边/固定边(Normal Edges):直接从一个节点转到下一个节点。
• 条件边(Conditional Edges):调用函数来确定下一步要转到哪个节点。 例如,设置最简单运输路线:快递由揽收站接收,下一站固定为分拣中心,最后到派送中进行派送。 这就是固定边,如下图所示:

再例如,我们可以根据以下条件,判断快递如何运输:
• 包裹是加急件 → 走空运线路
• 包裹不是加急件 → 走标准线路 这就是条件边,如下图所示:

要说明的是,在 LangGraph 中:
• START 节点:是一个特殊节点,表示将用户输入发送到图形的节点。引用此节点的主要目的是确 定应该首先调用哪些节点。
• END 节点:是一个表示终端节点的特殊节点。当想要指示哪些边在完成后没有后续动作时,将引 用此节点。
• 条件入口点(Conditional Entry Point):调用一个函数来确定在用户输入到达时首先调用哪个节 点。
1)使用 add_edge() 向图中添加从开始节点(或起始节点列表)到结束节点的固定边。 add_edge() 方法常用参数说明:

2)使用 add_conditional_edges() 向图中添加 从起始节点到任意数量的目标节点 的条件边。 add_conditional_edges() 方法常用参数说明:


开始实现代码:
这里的路由方法有两种写法哈:
cpp
#5、添加边
delivery.add_edge(START,"揽收站") #固定边
delivery.add_edge("揽收站","分拣中心")
#路由方法
# def select_delivery(state:PackageState):
# priority=state["priority"]
# if priority=="加急":
# return "加急配送" #返回的是字符串,不是节点
# else:
# return "标准配送" #返回的是字符串,不是节点
#
# #添加条边(需要我们先写一个方法,来对后续节点进行映射)
# delivery.add_conditional_edges(
# "分拣中心", #条件的起始节点
# select_delivery, #path:确定下一个节点可调节对象
# ["加急配送","标准配送"] #path_map:节点名称(这里的这种写法要求路由字符串要与节点名称一致)
# )
def select_delivery(state:PackageState):
priority=state["priority"]
if priority=="加急":
return "备注加急" #返回的是字符串,不是节点
else: #这里的两个字符串可以随便给,此时需要在后面的添加边里面给出一个map映射,映射后续的节点名称
return "无备注" #返回的是字符串,不是节点
#添加条边(需要我们先写一个方法,来对后续节点进行映射)
delivery.add_conditional_edges(
"分拣中心", #条件的起始节点
select_delivery, #path:确定下一个节点可调节对象
{
"备注加急":"加急配送",
"无备注":"标准配送"
} #path_map:节点名称(对后续节点的映射)
)
delivery.add_edge("加急配送","派送站")
delivery.add_edge("标准配送","派送站")
delivery.add_edge("派送站",END)
2.2.6 步骤6:StateGraph 图编译,从公司创建到运行
在步骤2中,我们仅是构建出 StateGraph ,还无法直接用于执行。LangGraph 要求:必须先编译 图,然后才能使用它。编译提供了对图结构的一些基本检查,这会验证:
• 从 START 到所有节点的可达性
• 从所有节点到 END 的可达性
• 没有孤立节点或死循环
使用compile() 方法即可编译图。 该方法将StateGraph 编译为 CompiledStateGraph 对 象 。**编译后的图实现了 Runnable 接口,可以异步调用、流式传输、批处理和运行。**代码如下:
python
#6、编译图(这一步是系统来编译检查,光是人工检查还不够)
delivery_system = delivery.compile()
#7、执行图(输入初始状态,输出最终状态)
test_packages = [
{
"package_id": "P001",
"origin": "北京",
"destination": "上海",
"priority": "普通",
"history": [],
"total_distance": 0
},
{
"package_id": "P002",
"origin": "广州",
"destination": "乌鲁木齐",
"priority": "加急",
"history": [],
"total_distance": 0
}
]
for package in test_packages:
print(f"\n配送包裹: {package['package_id']}")
#执行图,发一次快递
result = delivery_system.invoke(package)
print("最终状态:", result["status"])
print("配送历史:", result["history"])
print("总里程:", result["total_distance"])
运行:

到此,我们已经构建出了一个图式的智能快递配送系统,来理解 LangGraph 图的基本能力与用法!
回顾一下:
-
State = 包裹信息卡(记录所有状态)
-
Nodes = 配送站点(执行具体操作)
-
Edges = 运输路线(控制流转顺序)
-
Reducers = 信息更新规则(如何记录变更)
-
编译 = 从路线图到运营系统的转换
代码(总):
python
import operator
from typing import TypedDict, Annotated
from langgraph.constants import END
from langgraph.constants import START
from langgraph.graph import StateGraph
#1、状态定义(贯穿整个图)
class PackageState(TypedDict):
#input:str
#output:str
#包裹状态:
#包裹id
package_id:str
#包裹始发站
origin:str
#包裹终点站
destination:str
#next
#配送状态:
#状态("待揽收","已揽收","运输中","派送中","已签收")
status:str
#流转历史 list
#知识点:
#history:list[str] #覆盖耿欣
history:Annotated[list[str],operator.add] #最追加更新
#总历程
total_distance:Annotated[int,operator.add]
#配送详情:
#("普通","加急")
priority:str
#2、定义图(依赖状态)
delivery = StateGraph(PackageState)
#3、节点定义(函数)
#输入:状态
#输出:状态的更新
#节点之间使用state进行通信
# def test(state:PackageState):
# return {
# "history":["揽收站"],
# "total_distance":500
# }
#揽收站节点
def receive_package(state:PackageState):
"""揽收站"""
# 进行状态扭转
# 状态值获取
print("---执行到揽收站节点")
origin=state["origin"]
return {
"status":"已揽收",
"history":[f"在{origin}揽收"]
}
#分拣中心节点:根据目的地进行分拣
def sort_package(state:PackageState):
"""分拣中心"""
print("---执行到分拣中心节点")
destination=state["destination"]
if"北京" in destination:
next="北京分拣中心"
elif"上海" in destination:
next="上海分拣中心"
else:
next="其他分拣中心"
return {
"status": "已分拣",
"history": [f"将分拣至{next}"]
}
#派送站节点
def final_delivery(state:PackageState):
"""派送站"""
print("---执行到派送站节点")
return {
"status":"已签收",
"history":[f"已送达{state['destination']}"]
}
#标准配送节点
def standard_delivery(state:PackageState):
"""标准配送"""
print("---执行到标准配送节点")
return{
"status": "运输中",
"history": ["标准陆运"],
"total_distance": 500
}
#加急配送节点
def express_delivery(state:PackageState):
"""加急配送"""
print("---执行到加急配送节点")
return {
"status": "加急运输",
"history": ["空运加急"],
"total_distance": 800
}
#4、添加节点
delivery.add_node("揽收站",receive_package)
delivery.add_node("分拣中心",sort_package)
delivery.add_node("派送站",final_delivery)
delivery.add_node("标准配送",standard_delivery)
delivery.add_node("加急配送",express_delivery)
#5、添加边
delivery.add_edge(START,"揽收站") #固定边
delivery.add_edge("揽收站","分拣中心")
#路由方法
# def select_delivery(state:PackageState):
# priority=state["priority"]
# if priority=="加急":
# return "加急配送" #返回的是字符串,不是节点
# else:
# return "标准配送" #返回的是字符串,不是节点
#
# #添加条边(需要我们先写一个方法,来对后续节点进行映射)
# delivery.add_conditional_edges(
# "分拣中心", #条件的起始节点
# select_delivery, #path:确定下一个节点可调节对象
# ["加急配送","标准配送"] #path_map:节点名称(这里的这种写法要求路由字符串要与节点名称一致)
# )
def select_delivery(state:PackageState):
priority=state["priority"]
if priority=="加急":
return "备注加急" #返回的是字符串,不是节点
else: #这里的两个字符串可以随便给,此时需要在后面的添加边里面给出一个map映射,映射后续的节点名称
return "无备注" #返回的是字符串,不是节点
#添加边(需要我们先写一个方法,来对后续节点进行映射)
delivery.add_conditional_edges(
"分拣中心", #条件的起始节点
select_delivery, #path:确定下一个节点可调节对象
{
"备注加急":"加急配送",
"无备注":"标准配送"
} #path_map:节点名称(对后续节点的映射)
)
delivery.add_edge("加急配送","派送站")
delivery.add_edge("标准配送","派送站")
delivery.add_edge("派送站",END)
#6、编译图(这一步是系统来编译检查,光是人工检查还不够)
delivery_system = delivery.compile()
#7、执行图(输入初始状态,输出最终状态)
test_packages = [
{
"package_id": "P001",
"origin": "北京",
"destination": "上海",
"priority": "普通",
"history": [],
"total_distance": 0
},
{
"package_id": "P002",
"origin": "广州",
"destination": "乌鲁木齐",
"priority": "加急",
"history": [],
"total_distance": 0
}
]
for package in test_packages:
print(f"\n配送包裹: {package['package_id']}")
#执行图,发一次快递
result = delivery_system.invoke(package)
print("最终状态:", result["status"])
print("配送历史:", result["history"])
print("总里程:", result["total_distance"])