记忆与对话历史
Agent 默认无状态:每次调用只看到这次输入的消息。记忆的职责是把跨调用的信息带回来。LangChain 1.0 的记忆体系与“记忆”分类(见 basic-concepts/memory)对齐:短期线程记忆 + 长期跨线程记忆。
两种记忆的对照
| 维度 | 短期记忆(对话历史) | 长期记忆(持久知识) |
|---|---|---|
| LangChain 机制 | checkpointer(线程状态) | store(跨线程键值) |
| 作用域 | 单个线程(thread_id) | 跨线程、跨会话 |
| 内容 | 消息历史、图状态 | 用户偏好、事实、共享知识 |
| 生命周期 | 线程内持续,随线程清理 | 长期保留 |
这个划分对应基本概念篇的“短期记忆 vs 长期记忆”:LangChain 用 checkpointer 实现短期、store 实现长期,面试时把两层对上说。
对话历史的接入
from langgraph.checkpoint.memory import InMemorySaver
checkpointer = InMemorySaver()
agent = create_agent(model=model, tools=[...], checkpointer=checkpointer)
# 每个会话一个 thread_id, 消息历史自动持久化
result = agent.invoke(
{"messages": "我叫小明"},
config={"configurable": {"thread_id": "thread-1"}},
)
result = agent.invoke(
{"messages": "我叫什么?"},
config={"configurable": {"thread_id": "thread-1"}}, # 记得
)要点:
- thread_id 是记忆的指针:同一 thread_id 的调用共享消息历史
- 换 thread_id 就是新会话(无记忆)
- 历史无限增长会撑爆上下文:需要裁剪(trim)、摘要(summarize)、或截断策略
历史管理:上下文工程
| 策略 | 做法 | 取舍 |
|---|---|---|
| 截断 | 只保留最近 N 条消息 | 简单,丢早期信息 |
| 摘要 | 把早期消息压缩成摘要 | 保留要点,有摘要成本 |
| 消息精简 | 合并/删除冗余消息(如连续工具结果) | 精细,规则复杂 |
这是上下文工程的实战形态(见 agent-engineering/context-engineering):记忆不是无限存,而是有策略地用。
长期记忆:store
from langgraph.store.memory import InMemoryStore
store = InMemoryStore()
agent = create_agent(model=model, tools=[...], store=store)
# 工具内读写 store: 跨线程共享
@tool
def remember_user_pref(user_id: str, pref: str):
store.put(("users", user_id), "pref", {"value": pref})- store 是键值存储:命名空间 + key + value
- 适合:用户偏好、事实记忆、跨会话知识(对应长期记忆条目)
- 生产用持久后端(Redis/Postgres),InMemory 只适合开发测试
面试追问
- agent 有记忆吗? 默认没有。消息只在单次调用内。记忆靠 checkpointer(线程内)和 store(跨线程)实现
- thread_id 是干什么的? 会话指针:同 id 共享历史,换 id 即新会话。生产用稳定 ID(用户会话)
- 对话历史无限增长怎么办? 裁剪/摘要/精简。这是上下文工程问题:记忆要有策略地用
- 短期和长期记忆怎么选? 会话连续性用 checkpointer,跨会话的用户偏好/事实用 store。两者可同用
- 和“记忆”分类什么关系? 分类讲的是记忆的理论框架(短期/长期、读写治理),LangChain 是具体实现:checkpointer 对应短期、store 对应长期