概述
记忆是一个记录先前交互信息的系统。对于 AI 代理而言,记忆至关重要,因为它能让代理记住之前的交互,从反馈中学习,并适应用户的偏好。随着代理处理更复杂的任务并进行大量用户交互,这种能力对于效率和用户满意度都变得必不可少。 短期记忆使您的应用程序能够记住单个线程或会话中的先前交互。一个线程组织会话中的多次交互,类似于电子邮件将消息分组在一次对话中的方式。
需要记住 跨 对话的信息?使用 长期记忆 来存储和召回特定于用户或应用程序级别的数据,跨越不同的线程和会话。
用法
要向代理添加短期记忆(线程级别持久化),您需要在创建代理时指定一个checkpointer。
LangChain 的代理将短期记忆作为代理状态的一部分进行管理。通过将这些存储在图的状态中,代理可以访问给定对话的完整上下文,同时保持不同线程之间的分离。状态使用检查点持久化到数据库(或内存),以便可以随时恢复线程。当代理被调用或一个步骤(如工具调用)完成时,短期记忆会更新,并且在每个步骤开始时会读取状态。
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
import * as z from "zod";
const getUserInfo = tool(() => "文件中没有用户资料。", {
name: "get_user_info",
description: "查找关于当前用户的信息。",
schema: z.object({}),
});
const checkpointer = new MemorySaver();
const agent = createAgent({
model: "google-genai:gemini-3.6-flash",
tools: [getUserInfo],
checkpointer,
});
const threadConfig = { configurable: { thread_id: "1" } };
let result = await agent.invoke(
{ messages: [{ role: "user", content: "嗨!我的名字是鲍勃。" }] },
threadConfig,
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,鲍勃!很高兴在这里见到你。你最近怎么样?"
result = await agent.invoke(
{ messages: [{ role: "user", content: "我的名字是什么?" }] },
threadConfig,
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是鲍勃!"
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
import * as z from "zod";
const getUserInfo = tool(() => "文件中没有用户资料。", {
name: "get_user_info",
description: "查找关于当前用户的信息。",
schema: z.object({}),
});
const checkpointer = new MemorySaver();
const agent = createAgent({
model: "openai:gpt-5.5",
tools: [getUserInfo],
checkpointer,
});
const threadConfig = { configurable: { thread_id: "1" } };
let result = await agent.invoke(
{ messages: [{ role: "user", content: "嗨!我的名字是鲍勃。" }] },
threadConfig,
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,鲍勃!很高兴在这里见到你。你最近怎么样?"
result = await agent.invoke(
{ messages: [{ role: "user", content: "我的名字是什么?" }] },
threadConfig,
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是鲍勃!"
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
import * as z from "zod";
const getUserInfo = tool(() => "文件中没有用户资料。", {
name: "get_user_info",
description: "查找关于当前用户的信息。",
schema: z.object({}),
});
const checkpointer = new MemorySaver();
const agent = createAgent({
model: "anthropic:claude-sonnet-4-6",
tools: [getUserInfo],
checkpointer,
});
const threadConfig = { configurable: { thread_id: "1" } };
let result = await agent.invoke(
{ messages: [{ role: "user", content: "嗨!我的名字是鲍勃。" }] },
threadConfig,
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,鲍勃!很高兴在这里见到你。你最近怎么样?"
result = await agent.invoke(
{ messages: [{ role: "user", content: "我的名字是什么?" }] },
threadConfig,
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是鲍勃!"
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
import * as z from "zod";
const getUserInfo = tool(() => "文件中没有用户资料。", {
name: "get_user_info",
description: "查找关于当前用户的信息。",
schema: z.object({}),
});
const checkpointer = new MemorySaver();
const agent = createAgent({
model: "openrouter:openrouter:z-ai/glm-5.2",
tools: [getUserInfo],
checkpointer,
});
const threadConfig = { configurable: { thread_id: "1" } };
let result = await agent.invoke(
{ messages: [{ role: "user", content: "嗨!我的名字是鲍勃。" }] },
threadConfig,
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,鲍勃!很高兴在这里见到你。你最近怎么样?"
result = await agent.invoke(
{ messages: [{ role: "user", content: "我的名字是什么?" }] },
threadConfig,
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是鲍勃!"
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
import * as z from "zod";
const getUserInfo = tool(() => "文件中没有用户资料。", {
name: "get_user_info",
description: "查找关于当前用户的信息。",
schema: z.object({}),
});
const checkpointer = new MemorySaver();
const agent = createAgent({
model: "fireworks:accounts/fireworks/models/glm-5p2",
tools: [getUserInfo],
checkpointer,
});
const threadConfig = { configurable: { thread_id: "1" } };
let result = await agent.invoke(
{ messages: [{ role: "user", content: "嗨!我的名字是鲍勃。" }] },
threadConfig,
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,鲍勃!很高兴在这里见到你。你最近怎么样?"
result = await agent.invoke(
{ messages: [{ role: "user", content: "我的名字是什么?" }] },
threadConfig,
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是鲍勃!"
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
import * as z from "zod";
const getUserInfo = tool(() => "文件中没有用户资料。", {
name: "get_user_info",
description: "查找关于当前用户的信息。",
schema: z.object({}),
});
const checkpointer = new MemorySaver();
const agent = createAgent({
model: "baseten:zai-org/GLM-5.2",
tools: [getUserInfo],
checkpointer,
});
const threadConfig = { configurable: { thread_id: "1" } };
let result = await agent.invoke(
{ messages: [{ role: "user", content: "嗨!我的名字是鲍勃。" }] },
threadConfig,
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,鲍勃!很高兴在这里见到你。你最近怎么样?"
result = await agent.invoke(
{ messages: [{ role: "user", content: "我的名字是什么?" }] },
threadConfig,
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是鲍勃!"
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
import * as z from "zod";
const getUserInfo = tool(() => "文件中没有用户资料。", {
name: "get_user_info",
description: "查找关于当前用户的信息。",
schema: z.object({}),
});
const checkpointer = new MemorySaver();
const agent = createAgent({
model: "ollama:north-mini-code-1.0",
tools: [getUserInfo],
checkpointer,
});
const threadConfig = { configurable: { thread_id: "1" } };
let result = await agent.invoke(
{ messages: [{ role: "user", content: "嗨!我的名字是鲍勃。" }] },
threadConfig,
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,鲍勃!很高兴在这里见到你。你最近怎么样?"
result = await agent.invoke(
{ messages: [{ role: "user", content: "我的名字是什么?" }] },
threadConfig,
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是鲍勃!"
生产环境
在生产环境中,请使用数据库支持的检查点:import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
const DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable";
const checkpointer = PostgresSaver.fromConnString(DB_URI);
关于更多检查点选项,包括 SQLite、Postgres 和 Azure Cosmos DB,请参阅持久化文档中的 检查点库列表。
自定义代理记忆
您可以通过创建带有状态模式的自定义中间件来扩展代理状态。自定义状态模式可以使用中间件中的stateSchema 参数传递。建议使用 StateSchema 类进行状态定义(也支持纯 Zod 对象)。
import { createAgent, createMiddleware } from "langchain";
import { StateSchema, MemorySaver } from "@langchain/langgraph";
import * as z from "zod";
const CustomState = new StateSchema({
userId: z.string(),
preferences: z.record(z.string(), z.any()),
});
const stateExtensionMiddleware = createMiddleware({
name: "StateExtension",
stateSchema: CustomState,
});
const checkpointer = new MemorySaver();
const agent = createAgent({
model: "gpt-5.5",
tools: [],
middleware: [stateExtensionMiddleware],
checkpointer,
});
// 自定义状态可以在 invoke 时传入
const result = await agent.invoke({
messages: [{ role: "user", content: "Hello" }],
userId: "user_123",
preferences: { theme: "dark" },
});
常见模式
启用了 短期记忆 后,长对话可能会超出 LLM 的上下文窗口。常见的解决方案有:裁剪消息
移除最前或最后 N 条消息(在调用 LLM 之前)
删除消息
从 LangGraph 状态中永久删除消息
总结消息
总结历史中的早期消息并用摘要替换它们
自定义策略
自定义策略(例如,消息过滤等)
裁剪消息
大多数 LLM 都有最大支持的上下文窗口(以 token 计量)。 决定何时截断消息的一种方法是计算消息历史中的 token 数,并在接近该限制时进行截断。如果您正在使用 LangChain,可以使用裁剪消息工具,并指定要从列表中保留的 token 数量,以及用于处理边界的strategy(例如,保留最后的 maxTokens)。
要在代理中裁剪消息历史,请使用带有 beforeModel 钩子的 createMiddleware:
import { RemoveMessage } from "@langchain/core/messages";
import { createAgent, createMiddleware } from "langchain";
import { MemorySaver, REMOVE_ALL_MESSAGES } from "@langchain/langgraph";
const trimMessages = createMiddleware({
name: "TrimMessages",
beforeModel: (state) => {
const messages = state.messages;
if (messages.length <= 3) {
return; // 不需要更改
}
const firstMsg = messages[0];
const recentMessages =
messages.length % 2 === 0 ? messages.slice(-3) : messages.slice(-4);
const newMessages = [firstMsg, ...recentMessages];
return {
messages: [
new RemoveMessage({ id: REMOVE_ALL_MESSAGES }),
...newMessages,
],
};
},
});
const checkpointer = new MemorySaver();
const agent = createAgent({
model: "gpt-5.5",
tools: [...],
middleware: [trimMessages],
checkpointer,
});
删除消息
您可以从图状态中删除消息以管理消息历史。 当您想要删除特定消息或清除整个消息历史时,这非常有用。 要从图状态中删除消息,您可以使用RemoveMessage。为了让 RemoveMessage 生效,您需要使用带有 messagesStateReducer 归约器 的状态键,例如 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 })),
};
}
};
删除消息时,请 确保 结果消息历史记录是有效的。请检查您正在使用的 LLM 提供商的限制。例如:
- 一些提供商期望消息历史以
user消息开头 - 大多数提供商要求带有工具调用的
assistant消息后面必须跟相应的tool结果消息。
import { RemoveMessage } from "@langchain/core/messages";
import { createAgent, createMiddleware } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
const deleteOldMessages = createMiddleware({
name: "DeleteOldMessages",
afterModel: (state) => {
const messages = state.messages;
if (messages.length > 2) {
// 删除最早的两条消息
return {
messages: messages
.slice(0, 2)
.map((m) => new RemoveMessage({ id: m.id! })),
};
}
return;
},
});
const agent = createAgent({
model: "gpt-5.5",
tools: [],
systemPrompt: "请简洁扼要。",
middleware: [deleteOldMessages],
checkpointer: new MemorySaver(),
});
const config = { configurable: { thread_id: "1" } };
const streamA = await agent.streamEvents(
{ messages: [{ role: "user", content: "hi! I'm bob" }] },
{ ...config, version: "v3" }
);
for await (const snapshot of streamA.values) {
const messageDetails = snapshot.messages.map((message) => [
message.getType(),
message.content,
]);
console.log(messageDetails);
}
const streamB = await agent.streamEvents(
{ messages: [{ role: "user", content: "write a short poem about cats" }] },
{ ...config, version: "v3" }
);
for await (const snapshot of streamB.values) {
const messageDetails = snapshot.messages.map((message) => [
message.getType(),
message.content,
]);
console.log(messageDetails);
}
const streamC = await agent.streamEvents(
{ messages: [{ role: "user", content: "what's my name?" }] },
{ ...config, version: "v3" }
);
for await (const snapshot of streamC.values) {
const messageDetails = snapshot.messages.map((message) => [
message.getType(),
message.content,
]);
console.log(messageDetails);
}
[["human", "hi! I'm bob"]]
[["human", "hi! I'm bob"], ["ai", "嗨,鲍勃!很高兴见到你。今天我能帮你什么?我可以回答问题、头脑风暴想法、起草文本、解释事情或帮助编写代码。"]]
[["human", "hi! I'm bob"], ["ai", "嗨,鲍勃!很高兴见到你。今天我能帮你什么?我可以回答问题、头脑风暴想法、起草文本、解释事情或帮助编写代码。"], ["human", "write a short poem about cats"]]
[["human", "hi! I'm bob"], ["ai", "嗨,鲍勃!很高兴见到你。今天我能帮你什么?我可以回答问题、头脑风暴想法、起草文本、解释事情或帮助编写代码。"], ["human", "write a short poem about cats"], ["ai", "从前有只猫在墙上,它几乎一动不动..."]]
[["human", "write a short poem about cats"], ["ai", "从前有只猫在墙上,它几乎一动不动..."]]
[["human", "write a short poem about cats"], ["ai", "从前有只猫在墙上,它几乎一动不动..."], ["human", "what's my name?"]]
[["human", "write a short poem about cats"], ["ai", "从前有只猫在墙上,它几乎一动不动..."], ["human", "what's my name?"], ["ai", "我不知道你的名字 - 你还没告诉我!"]]
[["human", "what's my name?"], ["ai", "我不知道你的名字 - 你还没告诉我!"]]
总结消息
如上所示,裁剪或删除消息的问题在于,您可能会因裁剪消息队列而丢失信息。 因此,一些应用程序受益于使用聊天模型来总结消息历史的更复杂方法。
要在代理中总结消息历史,请使用内置的 summarizationMiddleware:
import { createAgent, summarizationMiddleware } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
const checkpointer = new MemorySaver();
const agent = createAgent({
model: "gpt-5.5",
tools: [],
middleware: [
summarizationMiddleware({
model: "gpt-5.4-mini",
trigger: { tokens: 4000 },
keep: { messages: 20 },
}),
],
checkpointer,
});
const config = { configurable: { thread_id: "1" } };
await agent.invoke({ messages: "hi, my name is bob" }, config);
await agent.invoke({ messages: "write a short poem about cats" }, config);
await agent.invoke({ messages: "now do the same but for dogs" }, config);
const finalResponse = await agent.invoke({ messages: "what's my name?" }, config);
console.log(finalResponse.messages.at(-1)?.content);
// 你的名字是鲍勃!
summarizationMiddleware。
访问记忆
您可以通过几种方式访问和修改代理的短期记忆(状态):工具
在工具中读取短期记忆
使用runtime 参数(类型为 ToolRuntime)在工具中访问短期记忆(状态)。
runtime 参数在工具签名中是隐藏的(因此模型看不到它),但工具可以通过它访问状态。
import { createAgent, tool, type ToolRuntime } from "langchain";
import { StateSchema } from "@langchain/langgraph";
import * as z from "zod";
const CustomState = new StateSchema({
userId: z.string(),
});
const getUserInfo = tool(
async (_, config: ToolRuntime<typeof CustomState.State>) => {
const userId = config.state.userId;
return userId === "user_123" ? "John Doe" : "未知用户";
},
{
name: "get_user_info",
description: "获取用户信息",
schema: z.object({}),
}
);
const agent = createAgent({
model: "gpt-5-nano",
tools: [getUserInfo],
stateSchema: CustomState,
});
const result = await agent.invoke(
{
messages: [{ role: "user", content: "我的名字是什么?" }],
userId: "user_123",
},
{
context: {},
}
);
console.log(result.messages.at(-1)?.content);
// 输出: "你的名字是 John Doe。"
从工具写入短期记忆
要在执行期间修改代理的短期记忆(状态),您可以直接从工具返回状态更新。 这对于持久化中间结果或使信息可被后续工具或提示访问非常有用。import { tool, createAgent, ToolMessage, type ToolRuntime } from "langchain";
import { Command, StateSchema } from "@langchain/langgraph";
import * as z from "zod";
const CustomState = new StateSchema({
userId: z.string().optional(),
userName: z.string().optional(),
});
const updateUserInfo = tool(
async (_, config: ToolRuntime<typeof CustomState.State>) => {
const userId = config.state.userId;
const name = userId === "user_123" ? "John Smith" : "未知用户";
return new Command({
update: {
userName: name,
// 更新消息历史
messages: [
new ToolMessage({
content: "成功查询到用户信息",
tool_call_id: config.toolCall?.id ?? "",
}),
],
},
});
},
{
name: "update_user_info",
description: "查询并更新用户信息。",
schema: z.object({}),
}
);
const greet = tool(
async (_, config: ToolRuntime<typeof CustomState.State>) => {
const userName = config.state.userName;
return `你好 ${userName}!`;
},
{
name: "greet",
description: "在你找到用户信息后使用这个工具来问候用户。",
schema: z.object({}),
}
);
const agent = createAgent({
model: "openai:gpt-5-mini",
tools: [updateUserInfo, greet],
stateSchema: CustomState,
});
const result = await agent.invoke({
messages: [{ role: "user", content: "问候用户" }],
userId: "user_123",
});
console.log(result.messages.at(-1)?.content);
// 输出: "你好 John Smith!很高兴见到你。今天我能帮你什么?"
提示
在中间件中访问短期记忆(状态),以创建基于对话历史或自定义状态字段的动态提示。import * as z from "zod";
import { createAgent, tool, dynamicSystemPromptMiddleware } from "langchain";
const contextSchema = z.object({
userName: z.string(),
});
type ContextSchema = z.infer<typeof contextSchema>;
const getWeather = tool(
async ({ city }) => {
return `${city} 的天气总是晴朗!`;
},
{
name: "get_weather",
description: "获取用户信息",
schema: z.object({
city: z.string(),
}),
}
);
const agent = createAgent({
model: "gpt-5-nano",
tools: [getWeather],
contextSchema,
middleware: [
dynamicSystemPromptMiddleware<ContextSchema>((_, config) => {
return `你是一个乐于助人的助手。称呼用户为 ${config.context?.userName}。`;
}),
],
});
const result = await agent.invoke(
{
messages: [{ role: "user", content: "旧金山天气怎么样?" }],
},
{
context: {
userName: "John Smith",
},
}
);
for (const message of result.messages) {
console.log(message);
}
/**
* HumanMessage {
* "content": "旧金山天气怎么样?",
* // ...
* }
* AIMessage {
* // ...
* "tool_calls": [
* {
* "name": "get_weather",
* "args": {
* "city": "旧金山"
* },
* "type": "tool_call",
* "id": "call_tCidbv0apTpQpEWb3O2zQ4Yx"
* }
* ],
* // ...
* }
* ToolMessage {
* "content": "旧金山的天气总是晴朗!",
* "tool_call_id": "call_tCidbv0apTpQpEWb3O2zQ4Yx"
* // ...
* }
* AIMessage {
* "content": "John Smith,最新情况是:旧金山的天气总是晴朗!\n\n如果你需要更多细节(温度、风力、湿度)或未来几天的天气预报,我可以帮你查一下。你需要什么?",
* // ...
* }
*/
模型调用前
import { RemoveMessage } from "@langchain/core/messages";
import { createAgent, createMiddleware, trimMessages } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
import { REMOVE_ALL_MESSAGES } from "@langchain/langgraph";
const trimMessageHistory = createMiddleware({
name: "TrimMessages",
beforeModel: async (state) => {
const trimmed = await trimMessages(state.messages, {
maxTokens: 384,
strategy: "last",
startOn: "human",
endOn: ["human", "tool"],
tokenCounter: (msgs) => msgs.length,
});
return {
messages: [new RemoveMessage({ id: REMOVE_ALL_MESSAGES }), ...trimmed],
};
},
});
const checkpointer = new MemorySaver();
const agent = createAgent({
model: "gpt-5-nano",
tools: [],
middleware: [trimMessageHistory],
checkpointer,
});
模型调用后
import { RemoveMessage } from "@langchain/core/messages";
import { createAgent, createMiddleware } from "langchain";
import { REMOVE_ALL_MESSAGES } from "@langchain/langgraph";
const validateResponse = createMiddleware({
name: "ValidateResponse",
afterModel: (state) => {
const lastMessage = state.messages.at(-1)?.content;
if (
typeof lastMessage === "string" &&
lastMessage.toLowerCase().includes("机密")
) {
return {
messages: [
new RemoveMessage({ id: REMOVE_ALL_MESSAGES }),
],
};
}
return;
},
});
const agent = createAgent({
model: "gpt-5-nano",
tools: [],
middleware: [validateResponse],
});
Connect these docs to Claude, VSCode, and more via MCP for real-time answers.

