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