checkpoint 状态。这些检查点被保存在一个 thread 中,图执行后可以访问。因为 threads 允许在执行后访问图的状态,所以包括人机交互、记忆、时间旅行和容错在内的几个强大功能都是可能的。下面,我们将更详细地讨论这些概念。
LangGraph API 自动处理检查点
当使用 LangGraph API 时,您无需手动实现或配置检查点器。API 在幕后为您处理所有持久化基础设施。
线程
线程是检查点器为每个保存的检查点分配的唯一ID或线程标识符。它包含一系列运行的累积状态。当运行执行时,助手底层图的状态将被持久化到线程中。 在调用带有检查点的图时,您必须在配置的configurable 部分指定一个 thread_id。
检查点
线程在特定时间点的状态被称为检查点。检查点是每个超级步骤保存的图状态的快照,并由以下关键属性表示的StateSnapshot 对象表示:
config:与此检查点相关的配置。metadata:与此检查点相关的元数据。values:在此时间点状态通道的值。next:执行图中的下一个节点名称的元组。tasks:包含有关要执行的下个任务的信息的PregelTask对象的元组。如果步骤之前已尝试执行,它将包含错误信息。如果图在节点内部被中断 动态,任务将包含与中断相关的附加数据。
- 空检查点,以
START作为下一个要执行的节点 - 检查点,用户输入
{'foo': '', 'bar': []}和node_a作为下一个要执行的节点 - 检查点,以
node_a{'foo': 'a', 'bar': ['a']}和node_b的输出作为下一个要执行的节点 - 检查点,以
node_b{'foo': 'b', 'bar': ['a', 'b']}的输出作为下一个要执行的节点,没有后续节点要执行
bar 通道值包含来自两个节点的输出,因为我们有一个用于 bar 通道的reducer。
获取状态
当与保存的图状态交互时,您必须指定一个线程标识符。您可以通过调用graph.get_state(config) 来查看图的_最新_状态。这将返回一个 StateSnapshot 对象,该对象对应于配置中提供的线程ID关联的最新检查点,或者如果提供了检查点ID,则对应于线程的检查点。
get_state 的输出将如下所示:
获取状态历史
您可以通过调用graph.get_state_history(config) 获取给定线程的图执行完整历史记录。这将返回与配置中提供的线程 ID 相关的 StateSnapshot 对象列表。重要的是,检查点将按时间顺序排列,最新的检查点 / StateSnapshot 将列表中的第一个。
get_state_history 的输出将如下所示:
回放
它还可能播放回之前的图执行。如果我们invoke 一个包含 thread_id 和 checkpoint_id 的图,那么我们将 重放 在对应于 checkpoint_id 的检查点之前的先前执行的步骤,并且只执行检查点之后的步骤。
thread_id是线程的 ID。checkpoint_id是一个标识符,它指向线程中的特定检查点。
configurable 部分的一部分:
checkpoint_id 之前的步骤。所有在 checkpoint_id 之后的步骤都将被执行(即,一个新的分支),即使它们之前已经执行过。请参阅这个时间旅行指南,了解有关重放更多内容。
更新状态
除了重新播放特定checkpoints 中的图之外,我们还可以 编辑 图的状态。我们使用 graph.update_state() 来完成这项操作。此方法接受三个不同的参数:
config
配置应包含 thread_id 指定要更新的线程。当仅传递 thread_id 时,我们更新(或分叉)当前状态。可选地,如果我们包含 checkpoint_id 字段,那么我们将分叉所选的检查点。
values
这些值将用于更新状态。请注意,此更新与从节点接收的任何更新处理方式完全相同。这意味着如果图状态中的一些通道定义了reducer函数,这些值将被传递给这些函数。这意味着 update_state 不会自动覆盖每个通道的通道值,而只会覆盖没有reducers的通道。让我们通过一个例子来了解一下。
让我们假设您已经使用以下架构定义了您的图状态(参见上面的完整示例):
foo 键(通道)被完全更改(因为未指定该通道的reducer,所以 update_state 会覆盖它)。然而,已指定 bar 键的reducer,因此它将 "b" 追加到 bar 的状态中。
as_node
在调用 update_state 时,您可以可选地指定最后一个 as_node。如果您提供了它,更新将像来自节点 as_node 一样应用。如果没有提供 as_node,它将设置为最后更新状态的节点,如果不存在歧义。这之所以重要,是因为接下来要执行的步骤取决于最后提供更新的节点,因此这可以用来控制哪个节点接下来执行。有关分叉状态的更多了解,请参阅这个 时间旅行指南。
记忆存储
Store接口。作为一个例子,我们可以定义一个InMemoryStore来存储跨线程的用户信息。我们只需像以前一样使用检查点器编译我们的图,并使用我们新的in_memory_store变量。
LangGraph API 自动处理存储
当使用 LangGraph API 时,您无需手动实现或配置存储。API 在幕后为您处理所有存储基础设施。
基本用法
首先,让我们在不使用LangGraph的情况下单独展示这个功能。tuple 进行命名空间划分,在这个特定示例中将是 (<user_id>, "memories")。命名空间可以是任何长度,可以代表任何内容,不一定是用户特定的。
store.put 方法将记忆保存到存储中的命名空间。当我们这样做时,我们会指定命名空间,如上所述,以及一个记忆的键值对:键是记忆的简单唯一标识符 (memory_id),值(一个字典)则是记忆本身。
store.search 方法在我们的命名空间中读取记忆,该方法将返回给定用户的全部记忆作为一个列表。最新的记忆位于列表的末尾。
Item](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.Item)),具有某些属性。我们可以通过上述的`.dict`将其转换为字典来访问。
该智能体具有以下属性:
value:此内存的值(本身也是一个字典)key:在此命名空间中此内存的唯一键namespace:字符串列表,此内存类型的命名空间created_at:此内存创建时的戳记updated_at:此内存更新时的戳记
语义搜索
除了简单的检索之外,该存储还支持语义搜索,允许您根据意义而非精确匹配来查找记忆。要启用此功能,请使用嵌入模型配置存储:fields 参数或在存储记忆时指定 index 参数来控制哪些记忆部分被嵌入:
在 LangGraph 中使用
有了这一切,我们在LangGraph中使用in_memory_store。in_memory_store与检查点器协同工作:检查点器将状态保存到线程中,如上所述,而in_memory_store允许我们在线程之间存储任意信息。我们使用检查点器和in_memory_store编译图,如下所示。
thread_id 调用图,与之前一样,还使用 user_id,我们将用它来命名空间我们的记忆,以便为特定用户存储,正如上面所展示的。
store: BaseStore 和 config: RunnableConfig 作为节点参数,在 任何节点 中访问 in_memory_store 和 user_id。以下是我们在节点中使用语义搜索以查找相关记忆的方法:
store.search 方法来获取记忆。回忆一下,记忆是以对象列表的形式返回的,这些对象可以转换为字典。
user_id 相同,我们仍然可以访问相同的记忆。
langgraph.json文件中配置索引设置。例如:
检查点库
在底层,检查点功能由符合BaseCheckpointSaver接口的检查点对象提供支持。LangGraph提供了多个检查点实现,所有实现均通过独立的、可安装的库完成:langgraph-checkpoint:检查点保存器的基础接口([BaseCheckpointSaver](https://reference.langchain.com/python/langgraph/checkpoints/#langgraph.checkpoint.base.BaseCheckpointSaver))和序列化/反序列化接口([`SerializerProtocol`](https://reference.langchain.com/python/langgraph/checkpoints/#langgraph.checkpoint.serde.base.SerializerProtocol))。包括内存中的检查点实现([`InMemorySaver`](https://reference.langchain.com/python/langgraph/checkpoints/#langgraph.checkpoint.memory.InMemorySaver))用于实验。LangGraph自带`langgraph-checkpoint`。langgraph-checkpoint-sqlite:LangGraph检查点的实现,使用SQLite数据库(SqliteSaver/ [AsyncSqliteSaver](https://reference.langchain.com/python/langgraph/checkpoints/#langgraph.checkpoint.sqlite.aio.AsyncSqliteSaver))。适用于实验和本地工作流程。需要单独安装。langgraph-checkpoint-postgres:使用Postgres数据库的高级检查点(PostgresSaver/ [AsyncPostgresSaver](https://reference.langchain.com/python/langgraph/checkpoints/#langgraph.checkpoint.postgres.aio.AsyncPostgresSaver)),在LangSmith中使用。适用于生产环境。需要单独安装。
检查点接口
每个检查点保存器都遵循BaseCheckpointSaver接口,并实现了以下方法:.put- 存储带有其配置和元数据的检查点。.put_writes- 存储与检查点相关联的中间写入操作(即待写入操作)。.get_tuple- 使用给定的配置(thread_id和checkpoint_id)获取检查点元组。这用于在graph.get_state()中填充StateSnapshot。.list- 列出与给定配置和筛选标准匹配的检查点。这用于在graph.get_state_history()中填充状态历史记录。
.ainvoke、.astream、.abatch 执行图),将使用上述方法的异步版本(.aput、.aput_writes、.aget_tuple、.alist)。
为了异步运行您的图,您可以使用
InMemorySaver,或者 Sqlite/Postgres 检查点的异步版本 — AsyncSqliteSaver / AsyncPostgresSaver 检查点。langgraph_checkpoint 定义了 协议,用于实现序列化器,提供了一个默认实现 (JsonPlusSerializer),该实现可以处理多种类型,包括 LangChain 和 LangGraph 原语、日期时间、枚举等。
使用 pickle 进行序列化
默认序列化器 JsonPlusSerializer 在底层使用 ormsgpack 和 JSON,这并不适合所有类型的对象。
如果您想回退到pickle来处理我们msgpack编码器目前不支持的对象(例如Pandas数据框),您可以使用JsonPlusSerializer的pickle_fallback参数:
加密
检查点器可以选择加密所有持久化状态。要启用此功能,请将EncryptedSerializer 的实例传递给任何 BaseCheckpointSaver 实现的 serde 参数。创建加密序列化器的最简单方法是使用 from_pycryptodome_aes,它从 LANGGRAPH_AES_KEY 环境变量(或接受一个 key 参数)中读取 AES 密钥:
LANGGRAPH_AES_KEY,加密就会自动启用,因此您只需提供环境变量。可以通过实现 CipherProtocol 并将其提供给 EncryptedSerializer 来使用其他加密方案。