跳到内容

如何缓存高开销节点

先决条件

本指南假定您熟悉以下内容

节点缓存对于避免重复操作非常有用,例如当您执行一些高开销(无论是时间还是成本)的操作时。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,这些 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