短期记忆 = 让应用在单个 thread(会话)内记住先前的交互。官方实现路径只有一句话:创建 agent 时指定一个 checkpointer,再通过 thread_id 区分会话。本节先讲基于内存的 InMemorySaver。
五条官方关键事实(务必记牢):
- 必须显式指定
checkpointer,否则没有 thread 级持久化;- 短期记忆是 agent state 的一部分,存在图的 state 里;
- 同一 thread 能访问完整上下文,不同 thread 之间保持隔离;
- state 通过 checkpointer 持久化,thread 可以随时恢复;
- 记忆更新时机:agent 被调用时、或某个 step(如工具调用)完成时;读取时机:每个 step 开始时。
2.1 基于内存的持久化器(InMemorySaver)
InMemorySaver 把 state 存在进程的内存字典里。官方定位很明确:适用于开发与示例,生产请用数据库支持的 checkpointer。
2.1.1 举例 1:没有记忆
对照组——不传 checkpointer,即使传了 thread_id 也没用:
from langchain.agents import create_agent
def get_user_info() -> str:
"""Look up information about the current user."""
return "No user profile on file."
agent = create_agent(
model="google_genai:gemini-3.6-flash",
tools=[get_user_info],
# ← 没有 checkpointer,短期记忆未启用
)
thread_config = {"configurable": {"thread_id": "1"}} # 传了也没用
r1 = agent.invoke(
{"messages": [{"role": "user", "content": "Hi! My name is Bob."}]},
thread_config,
)
print(r1["messages"][-1].content)
# Hi Bob! Nice to see you here. How are you doing?
r2 = agent.invoke(
{"messages": [{"role": "user", "content": "What's my name?"}]},
thread_config,
)
print(r2["messages"][-1].content)
# I'm not sure what your name is — you haven't told me yet! ← 忘了
2.1.2 举例 2:拥有记忆
实验组——只多传一个 checkpointer,其余代码一模一样:
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
def get_user_info() -> str:
"""Look up information about the current user."""
return "No user profile on file."
agent = create_agent(
model="google_genai:gemini-3.6-flash",
tools=[get_user_info],
checkpointer=InMemorySaver(), # ← 唯一的变化:启用短期记忆
)
thread_config = {"configurable": {"thread_id": "1"}}
r1 = agent.invoke(
{"messages": [{"role": "user", "content": "Hi! My name is Bob."}]},
thread_config,
)
print(r1["messages"][-1].content)
# Hi Bob! Nice to see you here. How are you doing?
r2 = agent.invoke(
{"messages": [{"role": "user", "content": "What's my name?"}]},
thread_config,
)
print(r2["messages"][-1].content)
# You are Bob! ← 记住了
结论:两个例子的差别只有一行——checkpointer=InMemorySaver()。这就是短期记忆的全部开关。
2.1.3 关键步骤说明

# 用户 A 的会话
cfg_a = {"configurable": {"thread_id": "user-A"}}
agent.invoke({"messages": "我叫小明,喜欢极简风格"}, cfg_a)
# 用户 B 的会话(完全独立的记忆空间)
cfg_b = {"configurable": {"thread_id": "user-B"}}
agent.invoke({"messages": "我叫小红,喜欢详细解释"}, cfg_b)
agent.invoke({"messages": "我叫什么?"}, cfg_a) # → 小明
agent.invoke({"messages": "我叫什么?"}, cfg_b) # → 小红
# 永不串台:thread 之间天然隔离
2.1.4 工作原理

为什么要"逐步落盘"?因为这样才有断点恢复能力:如果第 3 个 step 崩了,重启后从 checkpoint#2 继续即可,前面已经花掉的 token 不浪费。这也是 HumanInTheLoop 中断、时间旅行(回到某个历史 state)能工作的底层原因——它们复用的都是同一套 checkpoint 机制。
2.1.5 常见问题
| 现象 | 根因 | 解决 |
|---|---|---|
| 传了 thread_id 却还是记不住 | 创建 agent 时没传 checkpointer | 两个条件必须同时满足:checkpointer= + thread_id |
| 程序重启后记忆全没了 | 用了 InMemorySaver,数据在内存里,进程退出即销毁 |
换 SqliteSaver / PostgresSaver(见 2.2) |
| 两个用户互相看到对方对话 | 所有用户共用了同一个 thread_id |
thread_id 用 user_id + session_id 组合,保证唯一 |
| 想把 user_id 存进 state 让工具读 | 默认 AgentState 只有 messages |
继承 AgentState 加字段 + state_schema=(见 2.5) |
| RemoveMessage 删不掉消息 | 对应 state key 没有 add_messages reducer |
默认 AgentState 已提供;自定义 key 要显式加 Annotated[list, add_messages]</ |

126

被折叠的 条评论
为什么被折叠?



