大模型天生就是个“金鱼脑”,聊完就忘。
上下文工程,就是让它在干当前这事的时候,能把前面聊的、工具查到的东西“摊在桌面上”。不然它干到第三步就忘了第一步说了啥,直接瞎指挥。
记忆,就是让它下次还能认出你——你上周跟它说“我过敏不吃香菜”,这周它还能想起来。
一句话总结:上下文工程管“眼前这摊事别断片”,记忆管“别每次都像第一次见面”。
一、概述
1.1 什么是短期记忆?
在 AI Agent 的世界里,**记忆(Memory)**是一套用于留存历史交互信息的机制。
对于 Agent 而言,记忆至关重要:它使 Agent 能够记住过往对话、从反馈中学习、适配用户偏好
短期记忆:让应用在单条会话线程内记住历史交互。
1.2 langchain 三类上下文
官方文档划分:静态运行上下文、动态运行上下文(短期记忆)、跨会话动态上下文(长期记忆)
1.3 使用方法
要为 Agent 开启短期记忆(线程级持久化),创建 Agent 的时候必须指定检查点器 (checkpointer)。
LangChain Agent 将短期记忆作为 Agent 状态的一部分进行管理。
把对话上下文保存在图状态中,Agent 既可以拿到单条对话的完整上下文,又可以做到不同线程之间数据相互隔离
状态通过 checkpointer 持久化到数据库(或内存),因此任意时刻都可以恢复这条线程继续执行。
Agent 被调用、或是完成一步(例如工具调用)时,短期记忆就会更新;每一步执行开始时都会读取状态。
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
def get_user_info() -> str:
"""查看用户信息"""
return "现在还没有用户"
agent = create_agent(
model="openai:gpt-5.5",
tools=[get_user_info],
checkpointer=InMemorySaver(),
)
1.4 存储
不管是长期记忆还是短期记忆,都有两种存储记忆的方式:
- 基于内存
- 基于外部存储
二、使用方式
2.1 通过内存
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
def get_user_info() -> str:
"""查看用户信息."""
return "空空如也."
agent = create_agent(
model=model,
tools=[get_user_info],
checkpointer=InMemorySaver(),
)
thread_config = {"configurable": {"thread_id": "1"}}
response = agent.invoke(
{"messages": [{"role": "user", "content": "哈喽,我是张三."}]},
thread_config,
)["messages"][-1].content
rprint(response)
response = agent.invoke(
{"messages": [{"role": "user", "content": "我是谁?"}]},
thread_config,
)["messages"][-1].content
rprint(response)
2.2 使用pgsql
在生产环境中,使用由数据库支持的checkpointer
安装pgsql包
pip install -U langgraph-checkpoint-postgres "psycopg[binary]"
默认情况下,langgraph-checkpoint-postgres 会安装 psycopg(Psycopg 3),但不包含额外组件。
上述安装方式会添加 psycopg[binary],这是大多数用户的推荐选择。
from langchain.agents import create_agent
from langgraph.checkpoint.postgres import PostgresSaver
def get_user_info() -> str:
"""查看用户信息."""
return "空空如也."
DB_URI = "postgresql://admin:Pw%40123456@133.133.133.133:5432/mydatabase?sslmode=disable"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
checkpointer.setup() # 会在pgsql中自动创建表
agent = create_agent(
model=model,
tools=[get_user_info],
checkpointer=checkpointer,
)
看一下,自动创建的表结构
这四张表都是 setup() 函数初始化时创建的。
checkpoints:这是主表,存每个 thread 在某个时刻的 checkpoint 快照。checkpoint_blobs:这张表专门存不适合直接内联进 checkpoints.checkpoint 的较复杂 channel 值。checkpoint_writes:这张表存的是中间写入 / pending writes,不是最终完整 checkpoint。checkpoint_migrations:这张表不是业务数据表,而是迁移版本表。
2.3 自定义 AgentState
默认情况下,agent 使用 AgentState 来管理短期内存
可以扩展 AgentState 以添加额外的字段。自定义状态 schema 通过 state_schema 参数传递给 create_agent。
class CustomAgentState(AgentState):
user_id: str
preferences: dict
agent = create_agent(
model = model,
tools = [get_user_info],
state_schema = CustomAgentState,
checkpointer = InMemorySaver(),
)
thread_config = {"configurable": {"thread_id": "1"}}
response = agent.invoke(
{
"messages": [{"role": "user", "content": "哈喽,我是张三."}],
"user_id": "user_123",
"preferences": {"theme": "dark"}
},
thread_config,
)["messages"][-1].content
rprint(response)
state = agent.get_state(thread_config)
for key, value in state.values.items():
print(f"{key}: {value}")
三、常用模式
3.1 裁剪消息
绝大多数大模型都存在最大支持上下文窗口(以 token 为计量单位)
一种消息截断策略:统计消息历史的 token 数量,当总量接近上限时执行截断
调用修剪消息工具函数,指定需要保留的 token 数量,同时配置边界处理策略(例如:保留最后的 max_tokens 条 token)
想要在 Agent 内部对消息历史做修剪,可以使用 before_model 中间件装饰器。
@before_model
def trim_messages(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
"""只保留最近5条消息到上下文中"""
messages = state["messages"]
# 消息<=3什么也不干
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),
# 解包后放入messages里
*new_messages
]
}
agent = create_agent(
model=model,
middleware=[trim_messages],
checkpointer=InMemorySaver(),
)
config: RunnableConfig = {"configurable": {"thread_id": "1"}}
agent.invoke({"messages": "哈喽,我是 bob"}, config)
agent.invoke({"messages": "你家是不是有个加菲猫"}, config)
agent.invoke({"messages": "不是,我家现在是一个 dog 吴"}, config)
final_response = agent.invoke({"messages": "我叫什么?"}, config)
for e in final_response["messages"]:
e.pretty_print()
打印结果如下:
提示:@before_model
3.2 截断 (truncate) vs 摘要 (summarize)
区别
- truncate /trim(修剪截断):直接删除旧消息,原始内容丢失;速度快,无额外模型调用开销。
- summarization(摘要):调用模型把旧消息浓缩成一段摘要文本,保留信息;会消耗额外 token,有调用成本。
before_model是通用钩子:除了 trim 修剪,你也可以在这里自己实现摘要、过滤、增删消息。而SummarizationMiddleware是封装好的成品中间件;before_model是底层自定义扩展点。
执行时机
before_model:组装完消息,还没发给大模型那一刻执行。 只修改即将送入模型的消息,不会自动改写持久化在 checkpointer 里的 state 历史消息。
3.3 删除消息
从图状态(graph state)中删除消息,以此管理消息历史。适合场景:移除指定消息,或者清空全部对话历史。
在图状态里删除消息,使用
RemoveMessage对象。想要RemoveMessage生效,状态字段必须使用add_messages归约器(reducer)
LangGraph 默认的 AgentState 已经内置该归约器,开箱可用
- 删除所有消息
from langgraph.graph.message import REMOVE_ALL_MESSAGES
def delete_messages(state):
return {"messages": [RemoveMessage(id=REMOVE_ALL_MESSAGES)]}
- 删除指定的消息
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]]}
输出详情:
Agent 删除消息处理时序图
3.4 摘要
消息截断或删除消息存在一个问题:清理消息队列时会直接丢失信息。
如此,部分应用会采用更完善的方案:借助对话大模型对消息历史做摘要处理。
详见:《Langchain实战:常用中间件Summarization》
四、总结
- 短期记忆:解决大模型“金鱼脑”问题,保证单次会话(Thread)不“断片”。
- 核心机制:靠 Checkpointer(检查点器) 持久化状态,用 thread_id 隔离会话。
- 存储选型:开发调试用 内存,生产环境上 PostgreSQL。
- 防爆窗三招:消息太多时,要么 裁剪(Trim) 丢旧消息,要么 删除(Remove) 清缓存,要么 摘要(Summarize) 保核心。
上下文工程管眼前,记忆管长远。短期记忆只是上半场,搞定它,你的 Agent 才算真正“在线”。
觉得有用的话,点个赞或在看支持一下吧~