Documentation Index
Fetch the complete documentation index at: https://nvd-54.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
持久执行是一种技术,其中进程或工作流在关键点保存其进度,允许它暂停并在稍后从中断处精确恢复。这在需要人机协作的场景中特别有用,用户可以在继续之前检查、验证或修改流程,以及在可能遇到中断或错误的长时间运行任务中(例如,调用 LLM 超时)。通过保存已完成的工作,持久执行使进程能够在不重新处理之前步骤的情况下恢复——即使在相当长的延迟之后(例如,一周后)。
LangGraph 内置的持久化层为工作流提供持久执行,确保每个执行步骤的状态都保存到持久存储中。此功能保证如果工作流被中断——无论是系统故障还是人机协作交互——都可以从最后记录的状态恢复。
要在 LangGraph 中利用持久执行,你需要:
-
通过指定将保存工作流进度的检查点来在工作流中启用持久化。
-
在执行工作流时指定线程标识符。这将跟踪工作流特定实例的执行历史。
-
将任何非确定性操作(例如,随机数生成)或具有副作用的操作(例如,文件写入、API 调用)包装在任务中,以确保当工作流恢复时,这些操作不会针对特定运行重复执行,而是从持久化层检索其结果。更多信息请参阅确定性和一致重放。
确定性和一致重放
当你恢复工作流运行时,代码不会从执行停止的同一行代码恢复;相反,它会确定一个合适的起始点来接续之前中断的地方。这意味着工作流将从起始点重放所有步骤,直到到达停止的位置。
因此,当你编写用于持久执行的工作流时,必须将任何非确定性操作(例如,随机数生成)和任何具有副作用的操作(例如,文件写入、API 调用)包装在任务或节点中。
为了确保你的工作流是确定性的并且可以一致重放,请遵循以下准则:
- 避免重复工作:如果一个节点包含多个具有副作用的操作(例如,日志记录、文件写入或网络调用),请将每个操作包装在单独的任务中。这确保当工作流恢复时,操作不会重复执行,其结果将从持久化层检索。
- 封装非确定性操作:将任何可能产生非确定性结果的代码(例如,随机数生成)包装在任务或节点中。这确保在恢复时,工作流以相同的结果遵循精确记录的步骤序列。
- 使用幂等操作:尽可能确保副作用(例如,API 调用、文件写入)是幂等的。这意味着如果在工作流失败后重试操作,它将与第一次执行时产生相同的效果。这对于导致数据写入的操作尤其重要。如果任务开始但未能成功完成,工作流的恢复将重新运行该任务,依赖记录的结果来维持一致性。使用幂等性键或验证现有结果以避免意外重复,确保工作流执行顺畅且可预测。
有关应避免的陷阱示例,请参阅函数式 API 中的常见陷阱部分,该部分展示了如何使用任务来构建代码以避免这些问题。同样的原则适用于 StateGraph(图 API)。
持久性模式
LangGraph 支持三种持久性模式,允许你根据应用需求平衡性能和数据一致性。更高的持久性模式会为工作流执行增加更多开销。你可以在调用任何图执行方法时指定持久性模式:
await graph.stream(
{ input: "test" },
{ durability: "sync" }
)
持久性模式从最低到最高持久性依次为:
"exit":LangGraph 仅在图执行成功退出、出错或由于人机协作中断退出时持久化更改。这为长时间运行的图提供了最佳性能,但意味着中间状态不会被保存,因此你无法从执行过程中发生的系统故障(如进程崩溃)中恢复。
"async":LangGraph 在下一步执行时异步持久化更改。这提供了良好的性能和持久性,但如果进程在执行过程中崩溃,存在 LangGraph 未写入检查点的小风险。
"sync":LangGraph 在下一步开始前同步持久化更改。这确保 LangGraph 在继续执行前写入每个检查点,以一些性能开销为代价提供高持久性。
在节点中使用任务
如果一个节点包含多个操作,你可能会发现将每个操作转换为任务比将操作重构为单独的节点更容易。
import { StateGraph, StateSchema, GraphNode, START, END, MemorySaver } from "@langchain/langgraph";
import { v7 as uuid7 } from "uuid";
import * as z from "zod";
// 定义 StateSchema 来表示状态
const State = new StateSchema({
url: z.string(),
result: z.string().optional(),
});
const callApi: GraphNode<typeof State> = async (state) => {
const response = await fetch(state.url);
const text = await response.text();
const result = text.slice(0, 100); // 副作用
return {
result,
};
};
// 创建 StateGraph 构建器并为 callApi 函数添加节点
const builder = new StateGraph(State)
.addNode("callApi", callApi)
.addEdge(START, "callApi")
.addEdge("callApi", END);
// 指定检查点
const checkpointer = new MemorySaver();
// 使用检查点编译图
const graph = builder.compile({ checkpointer });
// 定义带有线程 ID 的配置
const threadId = uuid7();
const config = { configurable: { thread_id: threadId } };
// 调用图
await graph.invoke({ url: "https://www.example.com" }, config);
import { StateGraph, StateSchema, GraphNode, START, END, MemorySaver, task } from "@langchain/langgraph";
import { v7 as uuid7 } from "uuid";
import * as z from "zod";
// 定义 StateSchema 来表示状态
const State = new StateSchema({
urls: z.array(z.string()),
results: z.array(z.string()).optional(),
});
const makeRequest = task("makeRequest", async (url: string) => {
const response = await fetch(url);
const text = await response.text();
return text.slice(0, 100);
});
const callApi: GraphNode<typeof State> = async (state) => {
const requests = state.urls.map((url) => makeRequest(url));
const results = await Promise.all(requests);
return {
results,
};
};
// 创建 StateGraph 构建器并为 callApi 函数添加节点
const builder = new StateGraph(State)
.addNode("callApi", callApi)
.addEdge(START, "callApi")
.addEdge("callApi", END);
// 指定检查点
const checkpointer = new MemorySaver();
// 使用检查点编译图
const graph = builder.compile({ checkpointer });
// 定义带有线程 ID 的配置
const threadId = uuid7();
const config = { configurable: { thread_id: threadId } };
// 调用图
await graph.invoke({ urls: ["https://www.example.com"] }, config);
恢复工作流
一旦你在工作流中启用了持久执行,你可以在以下场景中恢复执行:
- 暂停和恢复工作流:使用 interrupt 函数在特定点暂停工作流,使用
Command 原语以更新后的状态恢复它。更多详情请参阅中断。
- 从故障中恢复:在异常(例如,LLM 提供商中断)后从最后一个成功的检查点自动恢复工作流。这涉及通过提供
null 作为输入值,使用相同的线程标识符执行工作流(参见函数式 API 中的此示例)。
恢复工作流的起始点
- 如果你使用的是 StateGraph(图 API),起始点是执行停止处的节点的开始。
- 如果你在节点内进行子图调用,起始点将是调用被停止子图的父节点。
在子图内部,起始点将是执行停止处的特定节点。
- 如果你使用的是函数式 API,起始点是执行停止处的入口点的开始。
优雅关闭
将这些文档连接到 Claude、VSCode 等工具,通过 MCP 获取实时答案。