Skip to main content
在处理基于模型做出决策的非确定性系统(例如,由 LLM 驱动的代理)时,详细检查其决策过程可能会很有用:
  1. 理解推理:分析导致成功结果的步骤。
  2. 调试错误:确定错误发生的位置和原因。
  3. 探索替代方案:测试不同的路径以发现更好的解决方案。
LangGraph 提供时间旅行功能来支持这些用例。具体来说,您可以从之前的检查点恢复执行——既可以重放相同的状态,也可以修改状态以探索替代方案。在所有情况下,恢复过去的执行都会在历史记录中产生一个新的分支。 要在 LangGraph 中使用时间旅行:
  1. 使用 invokestream 方法以初始输入 运行图
  2. 在现有线程中识别检查点:使用 getStateHistory 方法检索特定 thread_id 的执行历史并找到所需的 checkpoint_id。 或者,在您希望暂停执行的节点之前设置 断点。然后,您可以找到该断点之前记录的最近检查点。
  3. 更新图状态(可选):使用 updateState 方法修改检查点处的图状态,并从替代状态恢复执行。
  4. 从检查点恢复执行:使用 invokestream 方法,输入为 null,配置包含适当的 thread_idcheckpoint_id

在工作流中

本示例构建了一个简单的 LangGraph 工作流,该工作流生成一个笑话主题并使用 LLM 编写笑话。它演示了如何运行图、检索过去的执行检查点、可选地修改状态,并从选定的检查点恢复执行以探索替代结果。

设置

要在本示例中构建此工作流,您需要设置 Anthropic LLM 并安装所需的依赖项:
  1. 安装依赖项
npm install @langchain/langgraph @langchain/core
  1. 初始化 LLM:
import { ChatAnthropic } from "@langchain/anthropic";

const llm = new ChatAnthropic({
  model: "claude-sonnet-4-6",
  apiKey: "<your_anthropic_key>"
});
注册 LangSmith 以快速发现问题并提高 LangGraph 项目的性能。LangSmith 允许您使用跟踪数据来调试、测试和监控使用 LangGraph 构建的 LLM 应用程序。您还可以 从 LangSmith 获取跟踪 以在本地重放和调试生产问题。
  1. 实现工作流 工作流的实现是一个简单的图,包含两个节点,一个用于生成笑话主题,另一个用于编写笑话本身,以及一个用于存储中间值的状态。
import { v4 as uuidv4 } from "uuid";
import * as z from "zod";
import { StateGraph, StateSchema, GraphNode, START, END, MemorySaver } from "@langchain/langgraph";
import { ChatAnthropic } from "@langchain/anthropic";

const State = new StateSchema({
  topic: z.string().optional(),
  joke: z.string().optional(),
});

const model = new ChatAnthropic({
  model: "claude-sonnet-4-6",
  temperature: 0,
});

const generateTopic: GraphNode<typeof State> = async (state) => {
  // LLM 调用以生成笑话的主题
  const msg = await model.invoke("Give me a funny topic for a joke");
  return { topic: msg.content };
};

const writeJoke: GraphNode<typeof State> = async (state) => {
  // LLM 调用以根据主题编写笑话
  const msg = await model.invoke(`Write a short joke about ${state.topic}`);
  return { joke: msg.content };
};

// 构建工作流
const workflow = new StateGraph(State)
  // 添加节点
  .addNode("generateTopic", generateTopic)
  .addNode("writeJoke", writeJoke)
  // 添加边以连接节点
  .addEdge(START, "generateTopic")
  .addEdge("generateTopic", "writeJoke")
  .addEdge("writeJoke", END);

// 编译
const checkpointer = new MemorySaver();
const graph = workflow.compile({ checkpointer });

1. 运行图

要启动工作流,在没有任何输入的情况下调用 invoke。请记下 thread_id 以便跟踪此执行并在以后检索其检查点。
const config = {
  configurable: {
    thread_id: uuidv4(),
  },
};

const state = await graph.invoke({}, config);

console.log(state.topic);
console.log();
console.log(state.joke);
输出:
How about "The Secret Life of Socks in the Dryer"? You know, exploring the mysterious phenomenon of how socks go into the laundry as pairs but come out as singles. Where do they go? Are they starting new lives elsewhere? Is there a sock paradise we don't know about? There's a lot of comedic potential in the everyday mystery that unites us all!

# The Secret Life of Socks in the Dryer

I finally discovered where all my missing socks go after the dryer. Turns out they're not missing at all—they've just eloped with someone else's socks from the laundromat to start new lives together.

My blue argyle is now living in Bermuda with a red polka dot, posting vacation photos on Sockstagram and sending me lint as alimony.

2. 识别检查点

要从图运行中的前一个点继续,请使用 get_state_history 检索所有状态,并选择您要从中恢复执行的状态。
// 状态按时间倒序返回。
const states = [];
for await (const state of graph.getStateHistory(config)) {
  states.push(state);
}

for (const state of states) {
  console.log(state.next);
  console.log(state.config.configurable?.checkpoint_id);
  console.log();
}
输出:
[]
1f02ac4a-ec9f-6524-8002-8f7b0bbeed0e

['writeJoke']
1f02ac4a-ce2a-6494-8001-cb2e2d651227

['generateTopic']
1f02ac4a-a4e0-630d-8000-b73c254ba748

['__start__']
1f02ac4a-a4dd-665e-bfff-e6c8c44315d9
// 这是倒数第二个状态(状态按时间顺序列出)
const selectedState = states[1];
console.log(selectedState.next);
console.log(selectedState.values);
输出:
['writeJoke']
{'topic': 'How about "The Secret Life of Socks in the Dryer"? You know, exploring the mysterious phenomenon of how socks go into the laundry as pairs but come out as singles. Where do they go? Are they starting new lives elsewhere? Is there a sock paradise we don\'t know about? There\'s a lot of comedic potential in the everyday mystery that unites us all!'}

3. 更新状态(可选)

updateState 将创建一个新的检查点。新的检查点将关联到同一个线程,但具有新的检查点 ID。
const newConfig = await graph.updateState(selectedState.config, {
  topic: "chickens",
});
console.log(newConfig);
输出:
{'configurable': {'thread_id': 'c62e2e03-c27b-4cb6-8cea-ea9bfedae006', 'checkpoint_ns': '', 'checkpoint_id': '1f02ac4a-ecee-600b-8002-a1d21df32e4c'}}

4. 从检查点恢复执行

要从选定的检查点恢复执行,请使用指向新检查点的配置调用 invoke
await graph.invoke(null, newConfig);
输出:
{
  'topic': 'chickens',
  'joke': 'Why did the chicken join a band?\n\nBecause it had excellent drumsticks!'
}