前言
在构建复杂的AI应用时,会话持久化是一个关键需求。想象一下,当用户与你的聊天机器人进行多轮对话时,或者当你的应用需要在多次调用之间保持状态时,如何确保这些状态不会丢失?LangGraph提供了一个强大的解决方案:Checkpoint功能。
什么是 Checkpoint?
Checkpoint 是 LangGraph 中的一个核心功能,用于保存和恢复图执行状态。它就像是程序执行过程中的一个"快照",可以让你:
- 在多次调用之间保存状态
- 在不同会话之间隔离状态
- 在程序重启后恢复之前的执行状态
- 实现复杂的会话管理逻辑
在这篇文章中,我们将通过一个简单但实用的示例,来学习如何使用 LangGraph 的 Checkpoint 功能实现基本的会话持久化。
示例功能概述
我们将创建一个简单的计数器应用,展示 Checkpoint 的基本用法:
- 每次调用图时,计数器的值会增加1
- 使用 Checkpoint 保存计数器的状态
- 通过不同的配置区分不同的会话
- 展示如何查看和管理保存的会话状态
完整代码实现
让我们先来看完整的代码实现:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
LangGraph Checkpoint 示例1: 基本会话持久化
这个示例展示了如何使用InMemorySaver来保存和恢复图执行状态,实现会话持久化。
应用功能:创建一个简单的计数器,每次调用增加计数,并通过checkpoint保存状态。
"""
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
# 定义状态类型
class CounterState(TypedDict):
count: int
# 定义节点函数
def increment_counter(state: CounterState) -> CounterState:
"""增加计数器的值"""
current_count = state.get("count", 0)
return {"count": current_count + 1}
# 创建StateGraph
counter_graph = StateGraph(CounterState)
# 添加节点
counter_graph.add_node("increment", increment_counter)
# 设置入口点和出口点
counter_graph.add_edge(START, "increment")
counter_graph.add_edge("increment", END)
# 创建checkpoint saver
checkpointer = InMemorySaver()
# 编译图并添加checkpointer
compiled_graph = counter_graph.compile(checkpointer=checkpointer)
# 创建两个不同的会话配置
session1_config = {"configurable": {"thread_id": "session-1"}}
session2_config = {"configurable": {"thread_id": "session-2"}}
# 执行多次调用,观察状态如何在会话间保持
print("--- 执行会话1的调用 ---")
# 第一次调用传入初始值
result = compiled_graph.invoke({"count": 0}, config=session1_config)
print(f"会话1 - 第1次调用: count = {result['count']}")
# 后续调用不传入初始值,使用保存的状态
for i in range(2):
result = compiled_graph.invoke({}, config=session1_config)
print(f"会话1 - 第{i+2}次调用: count = {result['count']}")
print("--- 执行会话2的调用 ---")
# 第一次调用传入初始值
result = compiled_graph.invoke({"count": 0}, config=session2_config)
print(f"会话2 - 第1次调用: count = {result['count']}")
# 后续调用不传入初始值,使用保存的状态
result = compiled_graph.invoke({}, config=session2_config)
print(f"会话2 - 第2次调用: count = {result['count']}")
print("--- 再次执行会话1的调用 ---")
result = compiled_graph.invoke({}, config=session1_config)
print(f"会话1 - 第4次调用: count = {result['count']}")
执行结果
--- 执行会话1的调用 ---
会话1 - 第1次调用: count = 1
会话1 - 第2次调用: count = 2
会话1 - 第3次调用: count = 3
--- 执行会话2的调用 ---
会话2 - 第1次调用: count = 1
会话2 - 第2次调用: count = 2
--- 再次执行会话1的调用 ---
会话1 - 第4次调用: count = 4
代码解析
1. 状态类型定义
class CounterState(TypedDict):
count: int
我们定义了一个简单的状态类型CounterState,只包含一个整数字段count,用于存储计数器的值。
2. 节点函数定义
def increment_counter(state: CounterState) -> CounterState:
"""增加计数器的值"""
current_count = state.get("count", 0)
return {"count": current_count + 1}
节点函数increment_counter接收当前状态,获取当前的计数值(如果不存在则默认为0),然后返回一个新的状态,其中计数器的值增加1。
3. 创建图结构
# 创建StateGraph
counter_graph = StateGraph(CounterState)
# 添加节点
counter_graph.add_node("increment", increment_counter)
# 设置入口点和出口点
counter_graph.add_edge(START, "increment")
counter_graph.add_edge("increment", END)
这部分代码创建了一个简单的图结构,包含一个increment节点,连接了START和END。
4. Checkpoint 配置
# 创建checkpoint saver
checkpointer = InMemorySaver()
# 编译图并添加checkpointer
compiled_graph = counter_graph.compile(checkpointer=checkpointer)
这是实现会话持久化的关键部分:
- 我们创建了一个
InMemorySaver实例,它会将状态保存在内存中 - 在编译图时,我们通过
checkpointer参数将这个saver传递给图
5. 会话配置
# 创建两个不同的会话配置
session1_config = {"configurable": {"thread_id": "session-1"}}
session2_config = {"configurable": {"thread_id": "session-2"}}
我们创建了两个不同的配置对象,用于标识不同的会话。每个配置对象都包含一个configurable字段,其中包含一个唯一的thread_id。
6. 执行图并观察结果
# 执行多次调用,观察状态如何在会话间保持
print("--- 执行会话1的调用 ---")
# 第一次调用传入初始值
result = compiled_graph.invoke({"count": 0}, config=session1_config)
print(f"会话1 - 第1次调用: count = {result['count']}")
# 后续调用不传入初始值,使用保存的状态
for i in range(2):
result = compiled_graph.invoke({}, config=session1_config)
print(f"会话1 - 第{i+2}次调用: count = {result['count']}")
我们通过invoke方法执行图,并通过config参数指定使用哪个会话配置:
- 第一次调用时传入初始值
{"count": 0} - 后续调用不传入值
{},这样LangGraph会使用之前保存的状态 - 由于Checkpoint的存在,计数器的值会在多次调用之间保持并递增
执行结果分析
执行上述代码,你会看到类似以下的输出:
--- 执行会话1的调用 ---
会话1 - 第1次调用: count = 1
会话1 - 第2次调用: count = 2
会话1 - 第3次调用: count = 3
--- 执行会话2的调用 ---
会话2 - 第1次调用: count = 1
会话2 - 第2次调用: count = 2
--- 再次执行会话1的调用 ---
会话1 - 第4次调用: count = 4
这个输出展示了Checkpoint的核心特性:
- 会话隔离:会话1和会话2的状态是完全独立的
- 状态持久化:在多次调用之间,每个会话的状态都被保存下来
- 状态恢复:当再次使用同一个会话配置调用图时,会从上次保存的状态继续执行
Checkpoint 的优势
通过上面的示例,我们可以看到Checkpoint的几个主要优势:
- 简化状态管理:不需要手动管理和传递状态,LangGraph会自动为你处理
- 会话隔离:不同会话之间的状态互不影响
- 灵活配置:通过
configurable参数可以灵活地标识和管理会话 - 可扩展性:支持多种存储后端,可以根据需要选择合适的存储方式
管理 Checkpoint
除了基本的保存和恢复功能,我们还可以通过一些方法来管理Checkpoint。需要注意的是,不同版本的LangGraph API可能有所不同:
# 从特定会话恢复状态
session1_state = checkpointer.get(session1_config)
print(f"会话1的当前状态: {session1_state}")
# 验证状态是否正确恢复
print("--- 验证会话状态独立性 ---")
# 即使会话1已执行多次,会话2的状态仍然独立
session2_latest = compiled_graph.invoke({}, config=session2_config)
print(f"会话2最新状态: count = {session2_latest['count']}")
# 注意:在不同版本的LangGraph中,API可能有所不同
在实际应用中,你可以通过这些方法来查看、获取和验证保存的会话状态,为会话管理提供更大的灵活性。
实际应用场景
Checkpoint功能在很多实际应用场景中都非常有用:
- 多轮对话应用:保存聊天历史,实现会话上下文管理
- 长期运行的任务:在任务执行过程中保存中间状态,支持断点续传
- 用户会话管理:为不同用户维护独立的应用状态
- 复杂工作流:在复杂工作流中保存执行进度,支持流程恢复
总结
Checkpoint是LangGraph中实现会话持久化的关键功能,它通过保存和恢复图执行状态,使得我们可以构建更加复杂和强大的AI应用。在本文中,我们通过一个简单的计数器示例,学习了如何使用InMemorySaver来实现基本的会话持久化。
在接下来的文章中,我们将深入探讨更多Checkpoint的高级用法,包括异步环境中的Checkpoint、多轮对话持久化以及自定义序列化等。
如果你觉得这篇文章对你有帮助,请点赞、收藏并关注我,以获取更多关于LangGraph的教程和技巧!