Skip to content

概述

记忆(Memory)是一个能够记住先前交互信息的系统。对于 AI 智能体(agent)而言,记忆至关重要,因为它能让智能体记住之前的交互、从反馈中学习并适应用户偏好。随着智能体处理涉及大量用户交互的复杂任务,这种能力对于效率和用户满意度都变得至关重要。

短期记忆(Short-term memory)让你的应用程序能够记住单个线程(thread)或对话中的先前交互。

线程在一个会话中组织多次交互,类似于电子邮件将消息分组到单个对话中的方式。

对话历史(Conversation history)是最常见的短期记忆形式。长对话对当今的大语言模型(LLM)构成了挑战;完整的历史记录可能无法放入 LLM 的上下文窗口(context window),导致上下文丢失或错误。

即使你的模型支持完整的上下文长度,大多数 LLM 在处理长上下文时表现仍然不佳。它们会被过时或离题的内容“分散注意力”,同时还会遭受响应时间变慢和成本更高的困扰。

聊天模型使用消息(messages)来接受上下文,这些消息包括指令(系统消息)和输入(人类消息)。在聊天应用中,消息在人类输入和模型响应之间交替,导致消息列表随时间推移而变长。由于上下文窗口有限,许多应用可以通过使用技术来移除或“遗忘”过时信息而受益。

使用方法

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

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

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

状态通过检查点(checkpointer)持久化到数据库(或内存)中,因此线程可以在任何时候恢复。

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

python
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver  

agent = create_agent(
    "gpt-5",
    tools=[get_user_info],
    checkpointer=InMemorySaver(),  
)

agent.invoke(
    {"messages": [{"role": "user", "content": "Hi! My name is Bob."}]},
    {"configurable": {"thread_id": "1"}},  
)

在生产环境中

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

shell
pip install langgraph-checkpoint-postgres
python
from langchain.agents import create_agent

from langgraph.checkpoint.postgres import PostgresSaver  

DB_URI = "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
    checkpointer.setup() # auto create tables in PostgresSql
    agent = create_agent(
        "gpt-5",
        tools=[get_user_info],
        checkpointer=checkpointer,  
    )

自定义智能体记忆

默认情况下,智能体使用 AgentState 来管理短期记忆,特别是通过 messages 键管理对话历史。

你可以扩展 AgentState 来添加额外的字段。自定义状态模式(state schema)通过 state_schema 参数传递给 create_agent

python
from langchain.agents import create_agent, AgentState
from langgraph.checkpoint.memory import InMemorySaver

class CustomAgentState(AgentState):  
    user_id: str
    preferences: dict

agent = create_agent(
    "gpt-5",
    tools=[get_user_info],
    state_schema=CustomAgentState,  
    checkpointer=InMemorySaver(),
)

# 可以在调用时传入自定义状态
result = agent.invoke(
    {
        "messages": [{"role": "user", "content": "Hello"}],
        "user_id": "user_123",  
        "preferences": {"theme": "dark"}  
    },
    {"configurable": {"thread_id": "1"}})

常见模式

启用短期记忆后,长对话可能会超出 LLM 的上下文窗口。常见的解决方案有:

这使得智能体能够跟踪对话,而不会超出 LLM 的上下文窗口。

修剪消息

大多数 LLM 都有最大支持的上下文窗口(以令牌(tokens)为单位)。

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

要在智能体中修剪消息历史,请使用 @before_model 中间件装饰器:

python
from langchain.messages import RemoveMessage
from langgraph.graph.message import REMOVE_ALL_MESSAGES
from langgraph.checkpoint.memory import InMemorySaver
from langchain.agents import create_agent, AgentState
from langchain.agents.middleware import before_model
from langgraph.runtime import Runtime
from langchain_core.runnables import RunnableConfig
from typing import Any

@before_model
def trim_messages(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
    """仅保留最后几条消息以适应上下文窗口。"""
    messages = state["messages"]

    if len(messages) <= 3:
        return None  # 无需更改

    first_msg = messages[0]
    recent_messages = messages[-3:] if len(messages) % 2 == 0 else messages[-4:]
    new_messages = [first_msg] + recent_messages

    return {
        "messages": [
            RemoveMessage(id=REMOVE_ALL_MESSAGES),
            *new_messages
        ]
    }

agent = create_agent(
    your_model_here,
    tools=your_tools_here,
    middleware=[trim_messages],
    checkpointer=InMemorySaver(),
)

config: RunnableConfig = {"configurable": {"thread_id": "1"}}

agent.invoke({"messages": "hi, my name is bob"}, config)
agent.invoke({"messages": "write a short poem about cats"}, config)
agent.invoke({"messages": "now do the same but for dogs"}, config)
final_response = agent.invoke({"messages": "what's my name?"}, config)

final_response["messages"][-1].pretty_print()
"""
================================== Ai Message ==================================

Your name is Bob. You told me that earlier.
If you'd like me to call you a nickname or use a different name, just say the word.
"""

删除消息

你可以从图状态中删除消息来管理消息历史。

当你想删除特定消息或清除整个消息历史时,这很有用。

要从图状态中删除消息,可以使用 RemoveMessage

要使 RemoveMessage 生效,你需要使用带有 add_messages 归约器(reducer)的状态键。

默认的 AgentState 提供了这个功能。

要删除特定消息:

python
from langchain.messages import RemoveMessage  

def delete_messages(state):
    messages = state["messages"]
    if len(messages) > 2:
        # 移除最早的两条消息
        return {"messages": [RemoveMessage(id=m.id) for m in messages[:2]]}  

要删除所有消息:

python
from langgraph.graph.message import REMOVE_ALL_MESSAGES

def delete_messages(state):
    return {"messages": [RemoveMessage(id=REMOVE_ALL_MESSAGES)]}  

删除消息时,务必确保生成的消息历史是有效的。请检查你使用的 LLM 提供商的限制。例如:

* 一些提供商期望消息历史以 `user` 消息开始
* 大多数提供商要求带有工具调用的 `assistant` 消息后面必须跟着相应的 `tool` 结果消息。
python
from langchain.messages import RemoveMessage
from langchain.agents import create_agent, AgentState
from langchain.agents.middleware import after_model
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.runtime import Runtime
from langchain_core.runnables import RunnableConfig

@after_model
def delete_old_messages(state: AgentState, runtime: Runtime) -> dict | None:
    """移除旧消息以保持对话可管理。"""
    messages = state["messages"]
    if len(messages) > 2:
        # 移除最早的两条消息
        return {"messages": [RemoveMessage(id=m.id) for m in messages[:2]]}
    return None

agent = create_agent(
    "gpt-5-nano",
    tools=[],
    system_prompt="Please be concise and to the point.",
    middleware=[delete_old_messages],
    checkpointer=InMemorySaver(),
)

config: RunnableConfig = {"configurable": {"thread_id": "1"}}

for event in agent.stream(
    {"messages": [{"role": "user", "content": "hi! I'm bob"}]},
    config,
    stream_mode="values",
):
    print([(message.type, message.content) for message in event["messages"]])

for event in agent.stream(
    {"messages": [{"role": "user", "content": "what's my name?"}]},
    config,
    stream_mode="values",
):
    print([(message.type, message.content) for message in event["messages"]])
[('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', "what's my name?")]
[('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', "what's my name?"), ('ai', 'Your name is Bob. How can I help you today, Bob?')]
[('human', "what's my name?"), ('ai', 'Your name is Bob. How can I help you today, Bob?')]

:::js

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-4o",
  tools: [],
  systemPrompt: "Please be concise and to the point.",
  middleware: [deleteOldMessages],
  checkpointer: new MemorySaver(),
});

const config = { configurable: { thread_id: "1" } };

const streamA = await agent.stream(
  { messages: [{ role: "user", content: "hi! I'm bob" }] },
  { ...config, streamMode: "values" }
);
for await (const event of streamA) {
  const messageDetails = event.messages.map((message) => [
    message.getType(),
    message.content,
  ]);
  console.log(messageDetails);
}

const streamB = await agent.stream(
  {
    messages: [{ role: "user", content: "what's my name?" }],
  },
  { ...config, streamMode: "values" }
);
for await (const event of streamB) {
  const messageDetails = event.messages.map((message) => [
    message.getType(),
    message.content,
  ]);
  console.log(messageDetails);
}
[[ "human", "hi! I'm bob" ]]
[[ "human", "hi! I'm bob" ], [ "ai", "Hello, Bob! How can I assist you today?" ]]
[[ "human", "hi! I'm bob" ], [ "ai", "Hello, Bob! How can I assist you today?" ]]
[[ "human", "hi! I'm bob" ], [ "ai", "Hello, Bob! How can I assist you today" ], ["human", "what's my name?" ]]
[[ "human", "hi! I'm bob" ], [ "ai",

LangChain 中文文档