跳到内容

如何缓存耗时节点

先决条件

本指南假设您熟悉以下内容:

节点缓存适用于您希望避免重复操作(例如执行耗时或耗费成本的操作)的情况。LangGraph 允许您为图中的节点添加个性化的缓存策略。

要配置缓存策略,请将 cachePolicy 参数传递给 addNode 方法。在下面的示例中,我们指定了一个生命周期(TTL)为 120 秒的缓存策略和默认的键序列化函数。然后,要为图启用节点级缓存,请在编译图时设置 cache 参数。以下示例使用 InMemoryCache 设置带有内存缓存的图。

import { StateGraph, Annotation, START } from "@langchain/langgraph";
import { InMemoryCache } from "@langchain/langgraph-checkpoint";

const StateAnnotation = Annotation.Root({
  items: Annotation<string[]>({
    default: () => [],
    reducer: (acc, item) => [...acc, ...item],
  }),
});

const cache = new InMemoryCache();

const graph = new StateGraph(StateAnnotation)
  .addNode(
    "node",
    async () => {
      // Simulate an expensive operation
      await new Promise((resolve) => setTimeout(resolve, 3000));
      return { items: ["Hello, how are you?"] };
    },
    { cachePolicy: { ttl: 120 } }
  )
  .addEdge(START, "node")
  .compile({ cache });

由于缓存为空,首次运行将需要 3 秒。后续使用相同输入的运行将被缓存并立即产出结果。

console.time("First run");
await graph.invoke({ items: ["Hello!"] });
console.timeEnd("First run");

console.time("Second run");
await graph.invoke({ items: ["Hello!"] });
console.timeEnd("Second run");
First run: 3.006s
Second run: 4.148ms
您还可以将自定义键序列化函数传递给 cachePolicy 参数。这可用于跳过序列化中的某些字段,例如消息 ID,它们在每次运行中可能是随机的。

import { StateGraph, MessagesAnnotation, START } from "@langchain/langgraph";
import { InMemoryCache } from "@langchain/langgraph-checkpoint";
import { BaseMessage } from "@langchain/core/messages";

const cache = new InMemoryCache();
const graph = new StateGraph(MessagesAnnotation)
  .addNode(
    "node",
    async () => {
      await new Promise((resolve) => setTimeout(resolve, 3000));
      return { messages: [{ type: "ai", content: "Hello, how are you?" }] };
    },
    {
      cachePolicy: {
        ttl: 120,
        keyFunc([{ messages }]: [{ messages: BaseMessage[] }]) {
          // Cache based on the content and relative position of the messages
          return JSON.stringify(messages.map((m, idx) => [idx, m.content]));
        },
      },
    }
  )
  .addEdge(START, "node")
  .compile({ cache });

// First run will take 3 seconds
console.time("First run");
await graph.invoke({ messages: [{ type: "human", content: "Hello!" }] });
console.timeEnd("First run");

// Second run will be cached and yield immediately
console.time("Second run");
await graph.invoke({ messages: [{ type: "human", content: "Hello!" }] });
console.timeEnd("Second run");
First run: 3.004s
Second run: 2.012ms