LangChain - 短期记忆(Short-term Memory)
一、短期记忆是什么?
▎ 短期记忆让您的应用程序能够记住单个线程或对话中的先前交互。
打个比方:短期记忆就是"这一通电话里的记性"——你跟智能体在这一个对话中聊过的所有内容,它都记得。但换一个对话(挂断重拨)
,就从头开始。
关键概念:线程(thread)
▎ 线程将一次会话中的多次交互组织起来,类似于电子邮件将消息归入单个对话中的方式。
一句话:一个线程 = 一次连续对话。短期记忆就是"按线程保存的记忆"。
二、为什么需要专门管理短期记忆?
几个痛点:
- 上下文窗口有限:完整对话历史可能塞不进 LLM 的上下文窗口,导致"上下文丢失或错误"。
- 长上下文表现变差:即使模型支持长上下文,也会因陈旧/无关内容"分心",且响应变慢、成本增加。
- 消息会不断增长:聊天中人类输入和模型回复交替,消息列表越来越长。
所以需要"移除或遗忘陈旧信息"的技术来管理记忆。
▎ 区分:要记住跨对话的信息,请用长期记忆(store),本文讲的是短期记忆(单线程内)。
三、怎么启用短期记忆?—— checkpointer
核心一句话:
▎ 要为智能体添加短期记忆,需要在创建智能体时指定一个 checkpointer(检查点器)。
checkpointer 就是负责"把状态存下来、下次能恢复"的组件。最简单的内存版:
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent(
"gpt-5.4",
tools=[get_user_info],
checkpointer=InMemorySaver(), # 关键:加 checkpointer
)
# 调用时用 thread_id 标识"这是哪个对话"
agent.invoke(
{"messages": [{"role": "user", "content": "Hi! My name is Bob."}]},
{"configurable": {"thread_id": "1"}}, # 线程 ID
)
两个关键点:
- checkpointer:决定记忆存哪(内存/数据库)
- thread_id:标识哪个对话线程,相同 thread_id = 同一个对话
生产环境:用数据库版 checkpointer
InMemorySaver 重启就丢,生产要用持久化的,比如 Postgres:
pip install langgraph-checkpoint-postgres
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() # 自动建表
agent = create_agent(
"gpt-5.4",
tools=[get_user_info],
checkpointer=checkpointer,
)
短期记忆的工作机制
- 短期记忆作为智能体状态的一部分管理,存在图状态里
- 通过 checkpointer 持久化到数据库(或内存),线程可随时恢复
- 更新时机:调用智能体或完成一步(如工具调用)时更新
- 读取时机:每一步开始时读取状态
四、自定义智能体记忆
默认智能体用 AgentState 管理短期记忆(通过 messages 键存对话历史)。你可以扩展 AgentState 添加额外字段:
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.4",
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"}}
)
这样除了对话历史,还能在这个线程里记住 user_id、preferences 等额外信息。
五、长对话的管理策略(核心!)
启用短期记忆后,长对话可能超出上下文窗口。页面给出四种常见策略:
| 策略 | 做法 | 适用场景 |
|---|---|---|
| 截断消息 | 调用 LLM 前移除部分消息(保留最近的) | 防止超出 token 上限 |
| 删除消息 | 永久删除 LangGraph 状态中的消息 | 移除特定消息或清空历史 |
| 总结消息 | 总结早期消息,用摘要替换它们 | 既省空间又尽量不丢信息 |
| 自定义策略 | 如消息过滤等 | 特殊需求 |
策略 1:截断消息(@before_model 中间件)
用 @before_model 中间件,在调用模型之前修剪消息历史。思路是:保留第一条消息 + 最近几条,中间的丢掉:
from langchain.messages import RemoveMessage
from langgraph.graph.message import REMOVE_ALL_MESSAGES
from langchain.agents import create_agent, AgentState
from langchain.agents.middleware import before_model
from langgraph.runtime import Runtime
from typing import Any
@before_model
def trim_messages(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
"""Keep only the last few messages to fit context window."""
messages = state["messages"]
if len(messages) <= 3: # 消息不多就不用动
return None
first_msg = messages[0] # 保留第一条
recent_messages = messages[-3:] # 保留最近几条
new_messages = [first_msg] + recent_messages
return {
"messages": [
RemoveMessage(id=REMOVE_ALL_MESSAGES), # 先清空
*new_messages # 再放回保留的
]
}
agent = create_agent(
"gpt-5-nano",
tools=[],
middleware=[trim_messages], # 挂上中间件
checkpointer=InMemorySaver(),
)
效果:即使中间聊了很多轮,模型也只看到"开头 + 最近几条"
策略 2:删除消息(RemoveMessage)
用 RemoveMessage 删除特定消息或全部消息。需要状态键带 add_messages reducer(默认 AgentState 已提供)。
删除特定消息(最早的 2 条):
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]]}
删除所有消息:
from langgraph.graph.message import REMOVE_ALL_MESSAGES
def delete_messages(state):
return {"messages": [RemoveMessage(id=REMOVE_ALL_MESSAGES)]}
⚠️ 重要提醒:删除消息后要确保消息历史仍然有效,否则模型会报错:
- 某些厂商要求消息历史以 user 消息开头
- 大多数厂商要求带工具调用的 assistant 消息后必须跟对应的 tool 结果消息(不能拆散)
实战中常用 @after_model 中间件在模型回复后删除旧消息:
from langchain.agents.middleware import after_model
@after_model
def delete_old_messages(state: AgentState, runtime: Runtime) -> dict | None:
"""Remove old messages to keep conversation manageable."""
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(),
)
策略 3:总结消息(SummarizationMiddleware)
截断/删除的问题:会丢失信息。更聪明的方法是用模型把早期消息总结成摘要,再用摘要替换它们。
LangChain 提供内置的 SummarizationMiddleware:
from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware
agent = create_agent(
model="gpt-5.4",
tools=[],
middleware=[
SummarizationMiddleware(
model="gpt-5.4-mini", # 用小模型做总结,省钱
trigger=("tokens", 4000), # 触发条件:token 达到 4000
keep=("messages", 20) # 保留最近 20 条消息不总结
)
],
checkpointer=InMemorySaver(),
)
三个参数:
- model:用哪个模型做总结(推荐用便宜的小模型)
- trigger:什么时候触发总结(这里设为 token 数到 4000)
- keep:保留多少条最近消息不被总结
效果:长对话中,早期内容被压缩成摘要,模型既能看到历史梗概,又不会超出窗口
六、访问短期记忆的三种途径
短期记忆(状态)可以通过三个地方访问和修改:
途径 1:工具里访问(runtime.state)
用 ToolRuntime 参数在工具中读写状态(对模型隐藏):
from langchain.tools import tool, ToolRuntime
from langchain.agents import create_agent, AgentState
class CustomState(AgentState):
user_id: str
@tool
def get_user_info(runtime: ToolRuntime) -> str:
"""Look up user info."""
user_id = runtime.state["user_id"] # 读短期记忆
return "User is John Smith" if user_id == "user_123" else "Unknown user"
工具还能写入短期记忆——直接返回 Command(update={…}):
from langgraph.types import Command
from langchain.messages import ToolMessage
@tool
def update_user_info(runtime: ToolRuntime[CustomContext, CustomState]) -> Command:
"""Look up and update user info."""
user_id = runtime.context.user_id
name = "John Smith" if user_id == "user_123" else "Unknown user"
return Command(update={
"user_name": name, # 写入自定义状态字段
"messages": [
ToolMessage(
"Successfully looked up user information",
tool_call_id=runtime.tool_call_id
)
]
})
途径 2:提示里访问(@dynamic_prompt 中间件)
根据状态/上下文生成动态系统提示:
from langchain.agents.middleware import dynamic_prompt, ModelRequest
class CustomContext(TypedDict):
user_name: str
@dynamic_prompt
def dynamic_system_prompt(request: ModelRequest) -> str:
user_name = request.runtime.context["user_name"]
return f"You are a helpful assistant. Address the user as {user_name}."
agent = create_agent(
model="gpt-5-nano",
tools=[get_weather],
middleware=[dynamic_system_prompt],
context_schema=CustomContext,
)
效果:系统提示会变成 “Address the user as John Smith”,模型回复时就会称呼用户名字。
途径 3:模型执行前/后访问(@before_model / @after_model)
- @before_model:调用模型前处理消息(如截断历史)
- @after_model:调用模型后处理消息(如删除含敏感词的回复)
from langchain.agents.middleware import after_model
@after_model
def validate_response(state: AgentState, runtime: Runtime) -> dict | None:
"""Remove messages containing sensitive words."""
STOP_WORDS = ["password", "secret"]
last_message = state["messages"][-1]
if any(word in last_message.content for word in STOP_WORDS):
return {"messages": [RemoveMessage(id=last_message.id)]}
return None
七、访问记忆途径总览
| 途径 | 装饰器/方式 | 时机 | 典型用途 |
|---|---|---|---|
| 工具 | runtime: ToolRuntime | 工具执行时 | 在工具中读写状态 |
| 提示 | @dynamic_prompt | 生成系统提示时 | 根据状态/上下文定制提示 |
| 模型执行前 | @before_model | 调用模型前 | 截断/修剪消息历史 |
| 模型执行后 | @after_model | 调用模型后 | 删除敏感词、清理消息 |
八、一句话总结
▎ 短期记忆 = 单个线程(对话)内的记忆,靠 checkpointer + thread_id 实现"同一对话记得住、能恢复"。
▎ 长对话靠四种策略管理:截断 / 删除 / 总结 /自定义。
▎ 访问记忆有四个切入点:工具(runtime.state)、动态提示(@dynamic_prompt)、模型执行前(@before_model)、模型执行后(@after_model)。
九、和前几讲的关系
把这几讲串起来:
- 短期记忆:线程内对话历史,存 state[“messages”],靠 checkpointer 持久化
- 长期记忆(store):跨线程永久保存,靠 runtime.store
- 工具访问上下文:工具用 runtime.state / runtime.context / runtime.store 读写这些记忆
- 消息(messages):短期记忆的载体,四种类型组成对话历史
一句话:短期记忆是"这次通话的草稿纸",checkpointer 是"草稿纸的存档",thread_id 是"通话编号"。
更多推荐


所有评论(0)