正如我们所知,LangGraph专注于构建智能体编排和运行时所需的底层核心能力。本文探讨这些核心能力以及它们的实现方式。
一、持久化能力(Persistence)
LangGraph 拥有一个内置的持久化层(persistence layer),该层通过检查点工具(checkpointers)实现。当你使用检查点工具编译图(graph)时,检查点工具会在每个 “超级步骤”(super-step)保存一份图状态的检查点(checkpoint)。这些检查点会被保存到一个线程(thread)中,在图执行完成后可对其进行访问。 由于通过线程能够在执行结束后访问图的状态,因此一系列强大的功能得以实现,包括人机协同(human-in-the-loop)、记忆(memory)、时间回溯(time travel)和容错(fault-tolerance)。
1.1 线程(Threads)
线程(Thread)是为检查点工具(checkpointer)保存的每个检查点(checkpoint)分配的唯一ID或线程标识符(thread identifier)。它包含了一系列运行实例(run)累积的状态。当某个运行实例执行时,助手(assistant)底层图(graph)的状态会被持久化到该线程中。 当调用带有检查点工具的图时,你必须在配置(config)的configurable中指定一个thread_id。像这样:
{"configurable": {"thread_id": "1"}}
1.2 检查点(Checkpoints)
线程在特定时间点的状态被称为检查点(Checkpoint)。检查点是在每个 “超级步骤”(super-step)保存的图(graph)状态快照,以 StateSnapshot(状态快照)对象形式存在,该对象包含以下关键属性:
- config(配置):与当前检查点关联的配置信息。
- metadata(元数据):与当前检查点关联的元数据。
- values(值):当前时间点下状态通道(state channels)的取值。
- next(下一执行节点):一个元组(tuple),包含图中接下来要执行的节点名称。
- tasks(任务列表):一个包含 PregelTask(Pregel 任务)对象的元组,存储待执行后续任务的相关信息。若该步骤此前曾尝试执行,此属性会包含错误信息;若图从某个节点内部被动态中断,此属性会包含与中断相关的额外数据。
检查点会被持久化存储,可用于在后续时间恢复线程的状态。
检查点工具库 在底层实现中,检查点(checkpointing)功能由符合 BaseCheckpointSaver 接口(BaseCheckpointSaver interface)的检查点工具对象(checkpointer objects)提供支持。LangGraph 提供了多种检查点工具实现,这些实现均通过可独立安装的库(standalone, installable libraries)提供,具体如下:
- langgraph-checkpoint:包含检查点工具保存器的基础接口(BaseCheckpointSaver)和序列化 / 反序列化接口(SerializerProtocol,SerializerProtocol interface)。该库还提供了用于实验场景的内存检查点工具实现(InMemorySaver,InMemorySaver)。LangGraph 框架默认已包含 langgraph-checkpoint 库。
- langgraph-checkpoint-sqlite:基于 SQLite 数据库实现的 LangGraph 检查点工具(对应实现类为 SqliteSaver / AsyncSqliteSaver)。该库非常适合实验场景和本地工作流使用,需单独安装。
- langgraph-checkpoint-postgres:基于 PostgreSQL 数据库的高级检查点工具(对应实现类为 PostgresSaver / AsyncPostgresSaver),已在 LangSmith 中使用。该库适用于生产环境,需单独安装。
1.3 存储(Store)
仅依靠检查点工具(checkpointer),我们无法在不同线程(thread)之间共享信息。这就催生了对存储接口(Store)的需求。存储(Store)提供跨线程(threads)和对话(conversations)持久化的长期记忆(long-term memory)。它支持分层命名空间(hierarchical namespaces)、键值存储(key-value storage),并可选支持向量搜索(vector search)功能。 举个例子,我们可以定义一个内存存储(InMemoryStore)来跨线程存储用户相关信息。我们只需用检查点工具编译图(graph),同时传入新定义的 in_memory_store 变量即可。
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.store.memory import InMemoryStore
# We need this because we want to enable threads (conversations)
checkpointer = InMemorySaver()
in_memory_store = InMemoryStore()
# ... Define the graph ...
# Compile the graph with the checkpointer and store
graph = graph.compile(checkpointer=checkpointer, store=in_memory_store)
注:langgraph.store未来可能会扩展多种存储模块,例如langgraph.store.file、langgraph.store.sqlite、langgraph.store.postgres、langgraph.store.mysql等。
二、持久化执行(Durable Execution)
持久化执行是一种技术,指流程或工作流会在关键节点保存自身进度,从而能够暂停,并在之后从暂停处精确恢复执行。该技术在以下场景中尤为实用:
- 需要人机协同(human-in-the-loop)的场景:用户可在流程继续前检查、验证或修改流程;
- 可能遇到中断或错误的长时间运行任务(例如,调用大语言模型(LLM)时出现超时)。
通过保留已完成的工作,持久化执行能让流程在恢复时无需重新处理之前的步骤 —— 即便间隔较长时间(例如,一周后)亦是如此。 LangGraph 的内置持久化层为工作流提供了持久化执行能力,确保每个执行步骤的状态都会保存到一个持久化存储中。这一能力可保证:无论工作流因系统故障中断,还是为进行人机协同交互而中断,都能从最后一次记录的状态恢复执行。
在 LangGraph 中利用持久化执行(Durable Execution),需执行以下操作:
- 为工作流启用持久化:通过指定一个检查点工具(checkpointer)来保存工作流进度,从而为工作流启用持久化功能。
- 执行工作流时指定线程标识符:执行工作流时,需指定一个线程标识符(thread identifier)。该标识符将跟踪特定工作流实例的执行历史。
- 将非确定性操作或有副作用的操作封装到任务中:将所有非确定性操作(例如生成随机数)或有副作用的操作(例如写入文件、调用API)封装到任务(task)内部。这样可确保:当工作流恢复执行时,对于该特定运行实例,这些操作不会重复执行,而是从持久化层中检索其结果。
三、流式传输(Streaming)
LangGraph 实现了一套流式传输系统,用于实时呈现更新内容。在基于大语言模型(LLM)构建的应用中,流式传输对于提升应用响应速度至关重要。通过逐步显示输出内容,流式传输能显著优化用户体验,尤其是在应对大语言模型延迟问题时,效果更为突出。 借助 LangGraph 流式传输可实现以下功能:
- 流式传输图状态(Stream graph state)—— 通过 updates 和 values 两种模式,获取状态更新内容或状态值。
- 流式传输子图输出(Stream subgraph outputs)—— 包含父图及所有嵌套子图的输出内容。
- 流式传输大语言模型令牌(Stream LLM tokens)—— 从任意位置捕获令牌流:节点(nodes)内部、子图中或工具(tools)里均可。
- 流式传输自定义数据(Stream custom data)—— 直接从工具函数中发送自定义更新内容或进度信号。
- 使用多种流式传输模式(Use multiple streaming modes)—— 可从以下模式中选择:值模式(values,完整状态)、更新模式(updates,状态增量)、消息模式(messages,大语言模型令牌 + 元数据)、自定义模式(custom,任意用户数据)或调试模式(debug,详细跟踪信息)。
支持的流模式:
| 模式(Mode) | 描述(Description) |
|---|---|
values | 流式传输图每一步执行后的完整状态值。 |
updates | 流式传输图每一步执行后的状态更新。若同一步骤中存在多个更新(例如运行多个节点),这些更新将被分别流式传输。 |
custom | 从图的节点内部流式传输自定义数据。 |
messages | 从调用大语言模型(LLM)的任意图节点中,流式传输二元组(2-tuples)(LLM 令牌, 元数据)。 |
debug | 在图的整个执行过程中,流式传输尽可能多的信息(用于调试)。 |
四、中断(Interrupts)
LangGraph支持开发者在图的节点(node)内任意位置调用 interrupt() 函数,以触发中断(Interrupts)。中断将暂停图(graph)的执行,并在继续执行前等待外部输入。 这一功能支持 “人机协同(human-in-the-loop)” 模式 —— 当流程需要外部输入才能推进时,可通过中断实现。当中断被触发时,LangGraph 会利用其持久化层(persistence layer)保存图的状态,并无限期等待,直至你恢复执行。
中断的工作原理是:在图的节点(node)内任意位置调用 interrupt() 函数。该函数可接收任何 JSON 可序列化的值,此值会传递给调用者。当你准备好继续执行时,通过 Command(命令)重新调用图即可恢复执行 —— 此时 Command 会成为节点内部 interrupt() 调用的返回值。
与静态断点(static breakpoints,仅在特定节点之前或之后暂停)不同,中断具有动态性:你可将其置于代码中的任意位置,且可根据应用逻辑设置触发条件。
检查点(Checkpointing)记录执行位置:检查点工具(checkpointer)会记录图的精确状态,因此即便处于错误状态,你也能在之后恢复执行。thread_id 作为定位标识:设置配置 config={"configurable": {"thread_id": ...}},告知检查点工具需加载哪个状态。
中断负载通过 interrupt 字段呈现:传递给 interrupt() 的值会通过 __interrupt__ 字段返回给调用者,便于你知晓图当前正在等待何种输入。 你选择的 thread_id 本质上是一个持久化的 “指针”:复用该 thread_id 会恢复到相同的检查点;使用新的 thread_id 则会启动一个全新的线程(thread),且该线程初始状态为空。
五、时间回溯(Time Travel)
在处理基于大模型做决策的非确定性系统时,详细检查其决策过程会很有帮助,具体包括:
- 理解推理过程(Understand reasoning):分析最终得出有效结果所经历的步骤。
- 调试错误(Debug mistakes):定位错误发生的位置及原因。
- 探索替代方案(Explore alternatives):测试不同路径以找到更优的解决方案。
LangGraph提供了时间回溯(time-travel)功能,以支持上述使用场景。具体而言,你可以从之前的检查点(checkpoint)恢复执行 —— 既可以重放相同的状态,也可以修改状态以探索替代方案。在所有情况下,从过往执行记录中恢复都会在历史记录里生成一个新的分支(fork)。
在 LangGraph 中使用时间回溯功能的步骤如下:
- 使用 invoke(调用)或 stream(流式传输)方法,传入初始输入来运行图(graph)。
- 在现有线程(thread)中定位检查点:通过 get_state_history 方法获取特定thread_id的执行历史,并找到目标 checkpoint_id(检查点标识)。此外,也可在希望暂停执行的节点(node)之前设置一个中断(interrupt),之后找到该中断发生前记录的最新检查点即可。
- 更新图状态(可选操作):使用 update_state 方法修改检查点处的图状态,从修改后的替代状态恢复执行。
- 从检查点恢复执行:使用 invoke 或 stream 方法,传入 None 作为输入,并在配置中包含对应的 thread_id 和 checkpoint_id,即可从该检查点恢复执行。
六、记忆(Memory)
人工智能应用需要借助记忆(Memory)在多次交互间共享上下文。在 LangGraph 中,你可以添加两种类型的记忆:
- 短期记忆(short-term memory):线程级持久化(thread-level persistence),可让智能体(agents)跟踪多轮对话(multi-turn conversations)。
- 长期记忆(long-term memory):用于跨会话(sessions)存储用户专属数据或应用级数据。
短期记忆管理 在启用短期记忆的情况下,较长的对话可能会超出大语言模型(LLM)的上下文窗口(context window)。常见的解决方案包括:
- 裁剪消息(Trim messages):移除开头或结尾的 N 条消息(在调用大语言模型之前执行)
- 从 LangGraph 状态中永久删除消息
- 总结消息(Summarize messages):对历史记录中较早的消息进行总结,并用总结内容替代原消息
- 管理检查点以存储和检索消息历史
- 自定义策略(Custom strategies):例如消息过滤等。 通过上述方式,智能体(agent)可在不超出大语言模型上下文窗口的前提下,持续跟踪对话进程。
长期记忆管理 LangMem是由LangChain维护的一个库,它提供了用于在智能体(agent)中管理长期记忆(long-term memories)的工具。
七、子图(Subgraphs)
子图指的是被用作另一张图中某个节点的图。 子图的实用场景包括:
- 构建多智能体系统(multi-agent systems)
- 在多张图中复用一组节点(re-using a set of nodes in multiple graphs)
- 分布式开发(Distributing development):当你希望不同团队独立开发图的不同部分时,可将每个部分定义为一个子图;只要子图的接口(即输入和输出模式,input and output schemas)符合规范,父图的构建就无需了解子图的任何细节。
添加子图时,需定义父图与子图的通信方式,主要有以下两种:
- 从节点中调用图(Invoke a graph from a node)—— 在父图的某个节点内部调用子图
- 将图作为节点添加(Add a graph as a node)—— 将子图直接作为父图的一个节点,且子图与父图共享状态键(state keys)