记忆(Memory)
AI 应用程序需要记忆来跨多次交互共享上下文。在 LangGraph 中,你可以添加两种类型的记忆:
添加短期记忆
短期记忆(线程级持久化)使智能体能够跟踪多轮对话。要添加短期记忆:
import { MemorySaver, StateGraph } from "@langchain/langgraph";
const checkpointer = new MemorySaver();
const builder = new StateGraph(...);
const graph = builder.compile({ checkpointer });
await graph.invoke(
{ messages: [{ role: "user", content: "hi! i am Bob" }] },
{ configurable: { thread_id: "1" } }
);在生产中使用
在生产中,使用由数据库支持的检查点保存器:
Postgres
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
const DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable";
const checkpointer = PostgresSaver.fromConnString(DB_URI);
const builder = new StateGraph(...);
const graph = builder.compile({ checkpointer });MongoDB
import { MongoClient } from "mongodb";
import { MongoDBSaver } from "@langchain/langgraph-checkpoint-mongodb";
const client = new MongoClient("mongodb://user:password@localhost:27017");
const checkpointer = new MongoDBSaver({ client });
const builder = new StateGraph(...);
const graph = builder.compile({ checkpointer });示例:使用 Postgres 检查点保存器
npm install @langchain/langgraph-checkpoint-postgresTIP
首次使用 Postgres 检查点保存器时,需要调用 checkpointer.setup()
import { ChatAnthropic } from "@langchain/anthropic";
import { StateGraph, StateSchema, MessagesValue, GraphNode, START } from "@langchain/langgraph";
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
const State = new StateSchema({
messages: MessagesValue,
});
const model = new ChatAnthropic({ model: "claude-haiku-4-5-20251001" });
const DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable";
const checkpointer = PostgresSaver.fromConnString(DB_URI);
// await checkpointer.setup();
const callModel: GraphNode<typeof State> = async (state) => {
const response = await model.invoke(state.messages);
return { messages: [response] };
};
const builder = new StateGraph(State)
.addNode("call_model", callModel)
.addEdge(START, "call_model");
const graph = builder.compile({ checkpointer });
const config = {
configurable: {
thread_id: "1"
}
};
const stream1 = await graph.streamEvents(
{ messages: [{ role: "user", content: "hi! I'm bob" }] },
{ ...config, version: "v3" }
);
for await (const snapshot of stream1.values) {
console.log(snapshot);
}
const stream2 = await graph.streamEvents(
{ messages: [{ role: "user", content: "what's my name?" }] },
{ ...config, version: "v3" }
);
for await (const snapshot of stream2.values) {
console.log(snapshot);
}:::
示例:使用 MongoDB 检查点保存器
npm install @langchain/langgraph-checkpoint-mongodbimport { ChatAnthropic } from "@langchain/anthropic";
import { StateGraph, StateSchema, MessagesValue, GraphNode, START } from "@langchain/langgraph";
import { MongoDBSaver } from "@langchain/langgraph-checkpoint-mongodb";
import { MongoClient } from "mongodb";
const State = new StateSchema({
messages: MessagesValue,
});
const model = new ChatAnthropic({ model: "claude-haiku-4-5-20251001" });
const client = new MongoClient("mongodb://user:password@localhost:27017");
const checkpointer = new MongoDBSaver({ client, dbName: "langgraph" });
const callModel: GraphNode<typeof State> = async (state) => {
const response = await model.invoke(state.messages);
return { messages: [response] };
};
const builder = new StateGraph(State)
.addNode("call_model", callModel)
.addEdge(START, "call_model");
const graph = builder.compile({ checkpointer });
const config = { configurable: { thread_id: "1" } };
const stream1 = await graph.streamEvents(
{ messages: [{ role: "user", content: "hi! I'm bob" }] },
{ ...config, version: "v3" }
);
for await (const snapshot of stream1.values) {
console.log(snapshot);
}
const stream2 = await graph.streamEvents(
{ messages: [{ role: "user", content: "what's my name?" }] },
{ ...config, version: "v3" }
);
for await (const snapshot of stream2.values) {
console.log(snapshot);
}在子图中使用
如果你的图包含子图,你只需在编译父图时提供检查点保存器即可。LangGraph 会自动将检查点保存器传播到子图。
import { StateGraph, StateSchema, START, MemorySaver } from "@langchain/langgraph";
import { z } from "zod/v4";
const State = new StateSchema({ foo: z.string() });
const subgraphBuilder = new StateGraph(State)
.addNode("subgraph_node_1", (state) => {
return { foo: state.foo + "bar" };
})
.addEdge(START, "subgraph_node_1");
const subgraph = subgraphBuilder.compile();
const builder = new StateGraph(State)
.addNode("node_1", subgraph)
.addEdge(START, "node_1");
const checkpointer = new MemorySaver();
const graph = builder.compile({ checkpointer });你可以配置特定于子图的检查点行为。有关包括中断支持和有状态继续执行在内的持久化级别的详细信息,请参阅子图持久化。
const subgraphBuilder = new StateGraph(...);
const subgraph = subgraphBuilder.compile({ checkpointer: true });添加长期记忆
使用长期记忆来跨会话存储特定于用户或特定于应用程序的数据。
import { InMemoryStore, StateGraph } from "@langchain/langgraph";
const store = new InMemoryStore();
const builder = new StateGraph(...);
const graph = builder.compile({ store });在节点内部访问存储
使用存储编译图后,LangGraph 会自动将存储注入到你的节点函数中。推荐的访问存储的方式是通过 Runtime 对象。
import { StateGraph, StateSchema, MessagesValue, GraphNode, START } from "@langchain/langgraph";
import { v7 as uuid7 } from "uuid";
const State = new StateSchema({
messages: MessagesValue,
});
const callModel: GraphNode<typeof State> = async (state, runtime) => {
const userId = runtime.context?.userId;
const namespace = [userId, "memories"];
// 搜索相关记忆
const memories = await runtime.store?.search(namespace, {
query: state.messages.at(-1)?.content,
limit: 3,
});
const info = memories?.map((d) => d.value.data).join("\n") || "";
// ... 在模型调用中使用记忆
// 存储新记忆
await runtime.store?.put(namespace, uuid7(), { data: "User prefers dark mode" });
};
const builder = new StateGraph(State)
.addNode("call_model", callModel)
.addEdge(START, "call_model");
const graph = builder.compile({ store });
// 在调用时传递上下文
await graph.invoke(
{ messages: [{ role: "user", content: "hi" }] },
{ configurable: { thread_id: "1" }, context: { userId: "1" } }
);在生产中使用
在生产中,使用由数据库支持的存储:
import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store";
const DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable";
const store = PostgresStore.fromConnString(DB_URI);
const builder = new StateGraph(...);
const graph = builder.compile({ store });import { MongoDBStore } from "@langchain/langgraph-checkpoint-mongodb";
const MONGODB_URI = "mongodb://user:password@localhost:27017";
const store = await MongoDBStore.fromConnString(MONGODB_URI, {
dbName: "langgraph",
collectionName: "store",
});
const builder = new StateGraph(...);
const graph = builder.compile({ store });示例:使用 Postgres 存储
npm install @langchain/langgraph-checkpoint-postgresTIP
首次使用 Postgres 存储时需要调用 store.setup()
import { ChatAnthropic } from "@langchain/anthropic";
import { StateGraph, StateSchema, MessagesValue, GraphNode, START } from "@langchain/langgraph";
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store";
import { v7 as uuid7 } from "uuid";
const State = new StateSchema({
messages: MessagesValue,
});
const model = new ChatAnthropic({ model: "claude-haiku-4-5-20251001" });
const callModel: GraphNode<typeof State> = async (state, runtime) => {
const userId = runtime.context?.userId;
const namespace = ["memories", userId];
const memories = await runtime.store?.search(namespace, { query: state.messages.at(-1)?.content });
const info = memories?.map(d => d.value.data).join("\n") || "";
const systemMsg = `You are a helpful assistant talking to the user. User info: ${info}`;
// 如果用户要求模型记住,则存储新记忆
const lastMessage = state.messages.at(-1);
if (lastMessage?.content?.toLowerCase().includes("remember")) {
const memory = "User name is Bob";
await runtime.store?.put(namespace, uuid7(), { data: memory });
}
const response = await model.invoke([
{ role: "system", content: systemMsg },
...state.messages
]);
return { messages: [response] };
};
const DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable";
const store = PostgresStore.fromConnString(DB_URI);
const checkpointer = PostgresSaver.fromConnString(DB_URI);
// await store.setup();
// await checkpointer.setup();
const builder = new StateGraph(State)
.addNode("call_model", callModel)
.addEdge(START, "call_model");
const graph = builder.compile({
checkpointer,
store,
});
const stream1 = await graph.streamEvents(
{ messages: [{ role: "user", content: "Hi! Remember: my name is Bob" }] },
{ configurable: { thread_id: "1" }, context: { userId: "1" }, version: "v3" }
);
for await (const snapshot of stream1.values) {
console.log(snapshot);
}
const stream2 = await graph.streamEvents(
{ messages: [{ role: "user", content: "what is my name?" }] },
{ configurable: { thread_id: "2" }, context: { userId: "1" }, version: "v3" }
);
for await (const snapshot of stream2.values) {
console.log(snapshot);
}:::
管理短期记忆
在启用了短期记忆的情况下,长对话可能会超出 LLM 的上下文窗口。常见的解决方案有:
- 修剪消息(Trim messages):移除前 N 条或后 N 条消息(在调用 LLM 之前)
- 从 LangGraph 状态中永久删除消息(Delete messages)
- 总结消息(Summarize messages):总结历史记录中较早的消息并将其替换为摘要
- 管理检查点(Manage checkpoints)以存储和检索消息历史记录
- 自定义策略(例如消息过滤等)
这允许智能体跟踪对话而不超出 LLM 的上下文窗口。
修剪消息
大多数 LLM 都有最大支持的上下文窗口(以 Token 为单位)。确定何时截断消息的一种方法是计算消息历史记录中的 Token 数,并在接近该限制时进行截断。如果你使用的是 LangChain,可以使用修剪消息工具并指定要从列表中保留的 Token 数,以及用于处理边界的 strategy(例如保留最后的 maxTokens)。
要修剪消息历史记录,请使用 trimMessages 函数:
import { trimMessages } from "@langchain/core/messages";
import { StateSchema, MessagesValue, GraphNode } from "@langchain/langgraph";
const State = new StateSchema({
messages: MessagesValue,
});
const callModel: GraphNode<typeof State> = async (state) => {
const messages = trimMessages(state.messages, {
strategy: "last",
maxTokens: 128,
startOn: "human",
endOn: ["human", "tool"],
});
const response = await model.invoke(messages);
return { messages: [response] };
};
const builder = new StateGraph(State)
.addNode("call_model", callModel);删除消息
你可以从图状态中删除消息以管理消息历史记录。当你想要删除特定消息或清除整个消息历史记录时,这很有用。
要从图状态中删除消息,可以使用 RemoveMessage。为了使 RemoveMessage 起作用,你需要使用带有 messagesStateReducer reducer 的状态键,如 MessagesValue。
若要删除特定消息:
import { RemoveMessage } from "@langchain/core/messages";
const deleteMessages = (state) => {
const messages = state.messages;
if (messages.length > 2) {
// 移除最早的两条消息
return {
messages: messages
.slice(0, 2)
.map((m) => new RemoveMessage({ id: m.id })),
};
}
};WARNING
删除消息时,请确保产生的消息历史记录是有效的。请检查你所使用的 LLM 提供商的限制。例如:
- 某些提供商希望消息历史记录以
user消息开头 - 大多数提供商要求包含工具调用的
assistant消息后面紧跟相应的tool结果消息。
总结消息
管理长会话的另一种常见方法是生成消息历史记录的摘要,并在调用 LLM 之前将较早的消息替换为该摘要。通过引入一个专门的 summarization 节点,当消息长度超出预设阈值时自动压缩历史。
管理检查点
使用 graph.getStateHistory() 可以查看历史检查点,并配合检查点修剪机制定期清理过期的陈旧快照,防止长周期数据库无界膨胀。
