Skip to content

短期记忆 (Short-term memory) ​

概述 ​

记忆是用于记住以往交互信息的系统。对于 AI 智能体而言,记忆至关重要,因为它能让智能体记住先前的交互、从反馈中学习,并适应用户偏好。随着智能体在涉及大量用户交互的场景中处理愈发复杂的任务,这项能力对于提升效率和用户满意度变得必不可少。

短期记忆使你的应用程序能够在单个线程(thread)或会话中记住以往的交互。

NOTE

线程(thread)用于组织会话中的多次交互,类似于电子邮件将消息归组在单封对话中的方式。

对话历史是短期记忆最常见的形式。长对话对当今的大语言模型(LLM)构成了挑战;完整的历史记录可能无法容纳在 LLM 的上下文窗口内,从而导致上下文丢失或报错。

即使你的模型支持完整的上下文长度,大多数 LLM 在超长上下文下的表现仍然不佳。它们会被陈旧或偏离主题的内容“分散注意力”,同时还会导致响应时间变长和成本增加。

聊天模型使用消息(messages)接收上下文,其中包括指令(系统消息)和输入(人类消息)。在聊天应用程序中,消息在人类输入和模型响应之间交替出现,导致消息列表随着时间的推移不断增长。由于上下文窗口是有限的,许多应用程序都可以从移除或“遗忘”过时信息的技术中受益。

TIP

需要跨对话记住信息?请使用长期记忆在不同的线程和会话之间存储和回忆特定于用户或应用程序级别的数据。

使用方法 ​

要为智能体添加短期记忆(线程级持久化),你需要在创建智能体时指定一个 checkpointer(检查点保存器)。

NOTE

LangChain 的智能体将短期记忆作为智能体状态(state)的一部分进行管理。

通过将这些数据存储在图的状态中,智能体可以访问给定对话的完整上下文,同时保持不同线程之间的隔离。

状态通过检查点保存器持久化到数据库(或内存)中,以便线程可以随时恢复。

当智能体被调用或完成一个步骤(如工具调用)时,短期记忆会进行更新,并且在每个步骤开始时都会读取状态。

ts
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph"; 
import * as z from "zod";

const getUserInfo = tool(() => "No user profile on file.", {
  name: "get_user_info",
  description: "Look up information about the current user.",
  schema: z.object({}),
});

const checkpointer = new MemorySaver(); 

const agent = createAgent({
  model: "google:gemini-3.6-flash",
  tools: [getUserInfo],
  checkpointer,
});

const threadConfig = { configurable: { thread_id: "1" } };
let result = await agent.invoke(
  { messages: [{ role: "user", content: "Hi! My name is Bob." }] },
  threadConfig, 
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,Bob!很高兴在这里见到你。你最近怎么样?"

result = await agent.invoke(
  { messages: [{ role: "user", content: "What's my name?" }] },
  threadConfig, 
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是 Bob!"
ts
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph"; 
import * as z from "zod";

const getUserInfo = tool(() => "No user profile on file.", {
  name: "get_user_info",
  description: "Look up information about the current user.",
  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: "Hi! My name is Bob." }] },
  threadConfig, 
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,Bob!很高兴在这里见到你。你最近怎么样?"

result = await agent.invoke(
  { messages: [{ role: "user", content: "What's my name?" }] },
  threadConfig, 
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是 Bob!"
ts
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph"; 
import * as z from "zod";

const getUserInfo = tool(() => "No user profile on file.", {
  name: "get_user_info",
  description: "Look up information about the current user.",
  schema: z.object({}),
});

const checkpointer = new MemorySaver(); 

const agent = createAgent({
  model: "anthropic:claude-sonnet-5",
  tools: [getUserInfo],
  checkpointer,
});

const threadConfig = { configurable: { thread_id: "1" } };
let result = await agent.invoke(
  { messages: [{ role: "user", content: "Hi! My name is Bob." }] },
  threadConfig, 
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,Bob!很高兴在这里见到你。你最近怎么样?"

result = await agent.invoke(
  { messages: [{ role: "user", content: "What's my name?" }] },
  threadConfig, 
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是 Bob!"
ts
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph"; 
import * as z from "zod";

const getUserInfo = tool(() => "No user profile on file.", {
  name: "get_user_info",
  description: "Look up information about the current user.",
  schema: z.object({}),
});

const checkpointer = new MemorySaver(); 

const agent = createAgent({
  model: "openrouter:z-ai/glm-5.2",
  tools: [getUserInfo],
  checkpointer,
});

const threadConfig = { configurable: { thread_id: "1" } };
let result = await agent.invoke(
  { messages: [{ role: "user", content: "Hi! My name is Bob." }] },
  threadConfig, 
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,Bob!很高兴在这里见到你。你最近怎么样?"

result = await agent.invoke(
  { messages: [{ role: "user", content: "What's my name?" }] },
  threadConfig, 
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是 Bob!"
ts
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph"; 
import * as z from "zod";

const getUserInfo = tool(() => "No user profile on file.", {
  name: "get_user_info",
  description: "Look up information about the current user.",
  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: "Hi! My name is Bob." }] },
  threadConfig, 
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,Bob!很高兴在这里见到你。你最近怎么样?"

result = await agent.invoke(
  { messages: [{ role: "user", content: "What's my name?" }] },
  threadConfig, 
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是 Bob!"
ts
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph"; 
import * as z from "zod";

const getUserInfo = tool(() => "No user profile on file.", {
  name: "get_user_info",
  description: "Look up information about the current user.",
  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: "Hi! My name is Bob." }] },
  threadConfig, 
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,Bob!很高兴在这里见到你。你最近怎么样?"

result = await agent.invoke(
  { messages: [{ role: "user", content: "What's my name?" }] },
  threadConfig, 
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是 Bob!"
ts
import { createAgent, tool } from "langchain";
import { MemorySaver } from "@langchain/langgraph"; 
import * as z from "zod";

const getUserInfo = tool(() => "No user profile on file.", {
  name: "get_user_info",
  description: "Look up information about the current user.",
  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: "Hi! My name is Bob." }] },
  threadConfig, 
);
let response = result.messages.at(-1)?.content;
console.log(response); // "嗨,Bob!很高兴在这里见到你。你最近怎么样?"

result = await agent.invoke(
  { messages: [{ role: "user", content: "What's my name?" }] },
  threadConfig, 
);
response = result.messages.at(-1)?.content;
console.log(response); // "你是 Bob!"

查看示例追踪

打开该示例的公开 LangSmith 运行记录。

生产环境中使用 ​

在生产环境中,请使用由数据库支持的检查点保存器:

ts
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";

const DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable";
const checkpointer = PostgresSaver.fromConnString(DB_URI);

NOTE

有关包括 SQLite、Postgres 和 Azure Cosmos DB 在内的更多检查点保存器选项,请参阅持久化文档中的检查点库列表。

自定义智能体记忆 ​

你可以通过创建带有状态模式(state schema)的自定义中间件来扩展智能体状态。可以在中间件中使用 stateSchema 参数传递自定义状态模式。建议优先使用 StateSchema 类进行状态定义(同时也支持纯 Zod 对象)。

typescript
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 条或后 N 条消息(在调用 LLM 之前)

删除消息

从 LangGraph 状态中永久删除消息

摘要消息

总结历史记录中较早的消息并用摘要替换它们

自定义策略

自定义策略(例如:消息过滤等)

这使得智能体能够在不超过 LLM 上下文窗口的情况下跟踪对话。

修剪消息 ​

大多数 LLM 都有一个最大支持的上下文窗口(以 Token 为单位计算)。

决定何时截断消息的一种方法是计算消息历史中的 Token 数量,并在其接近该限制时进行截断。如果你正在使用 LangChain,可以使用修剪消息工具函数,并指定列表中要保留的 Token 数量,以及用于处理边界的 strategy(例如:保留最后的 maxTokens)。

要在智能体中修剪消息历史,请使用带有 beforeModel 钩子的 createMiddleware:

typescript
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 reducer 的状态键,例如 MessagesValue。

移除特定消息:

typescript
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 结果消息。
typescript
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: "Please be concise and to the point.",
  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", "Hi Bob! Nice to meet you. How can I help you today? I can answer questions, brainstorm ideas, draft text, explain things, or help with code."]]
[["human", "hi! I'm bob"], ["ai", "Hi Bob! Nice to meet you. How can I help you today? I can answer questions, brainstorm ideas, draft text, explain things, or help with code."], ["human", "write a short poem about cats"]]
[["human", "hi! I'm bob"], ["ai", "Hi Bob! Nice to meet you. How can I help you today? I can answer questions, brainstorm ideas, draft text, explain things, or help with code."], ["human", "write a short poem about cats"], ["ai", "There once was a cat on a wall, Who barely moved at all..."]]
[["human", "write a short poem about cats"], ["ai", "There once was a cat on a wall, Who barely moved at all..."]]
[["human", "write a short poem about cats"], ["ai", "There once was a cat on a wall, Who barely moved at all..."], ["human", "what's my name?"]]
[["human", "write a short poem about cats"], ["ai", "There once was a cat on a wall, Who barely moved at all..."], ["human", "what's my name?"], ["ai", "I don't know your name - you haven't told me!"]]
[["human", "what's my name?"], ["ai", "I don't know your name - you haven't told me!"]]

摘要消息 ​

如上所述,修剪或删除消息的问题在于,你可能会因为剔除消息队列而丢失信息。 正因如此,一些应用程序可以从使用聊天模型对消息历史进行摘要这一更精细的方法中获益。

Summary

要在智能体中对消息历史进行摘要,请使用内置的 summarizationMiddleware:

typescript
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);
// 你的名字是 Bob!

有关更多配置选项,请参阅 summarizationMiddleware。

访问记忆 ​

你可以通过几种方式访问和修改智能体的短期记忆(状态):

工具 ​

在工具中读取短期记忆 ​

在工具中使用 runtime 参数(类型为 ToolRuntime)访问短期记忆(状态)。

runtime 参数在工具签名中是隐藏的(因此模型看不到它),但工具可以通过它访问状态。

typescript
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" : "Unknown User";
  },
  {
    name: "get_user_info",
    description: "Get user info",
    schema: z.object({}),
  }
);

const agent = createAgent({
  model: "gpt-5-nano",
  tools: [getUserInfo],
  stateSchema: CustomState,
});

const result = await agent.invoke(
  {
    messages: [{ role: "user", content: "what's my name?" }],
    userId: "user_123",
  },
  {
    context: {},
  }
);

console.log(result.messages.at(-1)?.content);
// 输出: "Your name is John Doe."

从工具中写入短期记忆 ​

要在执行期间修改智能体的短期记忆(状态),你可以直接从工具返回状态更新。

这对于持久化中间结果或使后续工具或提示词能够访问这些信息非常有用。

typescript
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" : "Unknown user";
    return new Command({
      update: {
        userName: name,
        // 更新消息历史
        messages: [
          new ToolMessage({
            content: "Successfully looked up user information",
            tool_call_id: config.toolCall?.id ?? "",
          }),
        ],
      },
    });
  },
  {
    name: "update_user_info",
    description: "Look up and update user info.",
    schema: z.object({}),
  }
);

const greet = tool(
  async (_, config: ToolRuntime<typeof CustomState.State>) => {
    const userName = config.state.userName;
    return `Hello ${userName}!`;
  },
  {
    name: "greet",
    description: "Use this to greet the user once you found their info.",
    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: "greet the user" }],
  userId: "user_123",
});

console.log(result.messages.at(-1)?.content);
// 输出: "Hello John Smith! It's great to meet you. How can I help you today?"

提示词 ​

在中间件中访问短期记忆(状态),以基于对话历史或自定义状态字段创建动态提示词。

typescript
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 `The weather in ${city} is always sunny!`;
  },
  {
    name: "get_weather",
    description: "Get user info",
    schema: z.object({
      city: z.string(),
    }),
  }
);

const agent = createAgent({
  model: "gpt-5-nano",
  tools: [getWeather],
  contextSchema,
  middleware: [
    dynamicSystemPromptMiddleware<ContextSchema>((_, config) => {
      return `You are a helpful assistant. Address the user as ${config.context?.userName}.`;
    }),
  ],
});

const result = await agent.invoke(
  {
    messages: [{ role: "user", content: "What is the weather in SF?" }],
  },
  {
    context: {
      userName: "John Smith",
    },
  }
);

for (const message of result.messages) {
  console.log(message);
}
/**
 * HumanMessage {
 *   "content": "What is the weather in SF?",
 *   // ...
 * }
 * AIMessage {
 *   // ...
 *   "tool_calls": [
 *     {
 *       "name": "get_weather",
 *       "args": {
 *         "city": "San Francisco"
 *       },
 *       "type": "tool_call",
 *       "id": "call_tCidbv0apTpQpEWb3O2zQ4Yx"
 *     }
 *   ],
 *   // ...
 * }
 * ToolMessage {
 *   "content": "The weather in San Francisco is always sunny!",
 *   "tool_call_id": "call_tCidbv0apTpQpEWb3O2zQ4Yx"
 *   // ...
 * }
 * AIMessage {
 *   "content": "John Smith, here's the latest: The weather in San Francisco is always sunny!\n\nIf you'd like more details (temperature, wind, humidity) or a forecast for the next few days, I can pull that up. What would you like?",
 *   // ...
 * }
 */

模型调用前(Before model) ​

typescript
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,
});

模型调用后(After model) ​

typescript
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("confidential")
    ) {
      return {
        messages: [
          new RemoveMessage({ id: REMOVE_ALL_MESSAGES }),
        ],
      };
    }
    return;
  },
});

const agent = createAgent({
  model: "gpt-5-nano",
  tools: [],
  middleware: [validateResponse],
});