🌐 English version: 03-memory.en.md
🧭 导航 · 🏠 首页 · 📚 学习路线 · 上一章:02 链(LCEL) · 下一章:04 检索与 RAG · 相关:02 链(LCEL) · 04 检索与 RAG
🏷️ 难度:初级 · 时长:约 25 分钟 · 前置:02 链(LCEL)
真实对话需要上下文。本章学习如何让链"记住"历史消息,实现多轮连贯对话。
- 理解对话历史在 LangChain 中的表示方式(
messages列表)。 - 掌握
RunnableWithMessageHistory给链挂载记忆。 - 了解不同记忆存储后端(内存、文件、数据库)。
LangChain 用消息对象(System / Human / AI)记录对话。记忆的本质,就是在每次调用时把历史消息拼进提示词。
早期做法是用 RunnableWithMessageHistory 包裹你的 LCEL 链,并提供一个"根据 session_id 取历史"的函数。这样:
- 每次调用自动带上该会话的历史;
- 模型回复后自动写回历史。
⚠️ RunnableWithMessageHistory在 langchain 1.x 已被标记为 deprecated(仍可用、会告警)。 新项目推荐改用 LangGraph 的StateGraph+MemorySaver(见第 5 节 /examples/03_memory_graph.py)。 老写法仅作对照保留在examples/03_memory_runnable.py。
- 内存(
InMemoryChatMessageHistory):最简单,重启即丢失,适合练习。 - 文件 / SQLite / Redis:持久化,适合真实应用。
- 关键点是"按
session_id隔离不同用户的对话"。
- 历史过长会超出模型上下文窗口,需要考虑"截断"或"摘要压缩"。
- 可只保留最近 N 条,或定期把旧对话总结成一段摘要。
- 用
InMemoryChatMessageHistory实现多轮对话,验证"后面轮次能引用前面信息"。 - 用不同
session_id验证会话相互隔离。 - 实现一个"只保留最近 3 轮"的截断策略。
- 忘记在链里传入/读取
session_id,导致所有用户共享同一段历史。 - 历史无限增长导致 token 超限或费用暴涨——务必做截断/摘要。
- 把敏感对话明文落盘,注意数据安全与合规。
RunnableWithMessageHistory 方便但已废弃。LangGraph 用「状态图」统一管理多轮消息,是 langchain 1.x 推荐的记忆方案:
- 用
MessagesState承载messages列表,节点返回的新消息会自动追加进历史; - 用
MemorySaver(内存)或SqliteSaver等 checkpointer 按thread_id隔离会话; - 把你的 LCEL 链放进一个节点即可,无需手写"取/写历史"函数。
对应示例:
examples/03_memory_graph.py—— LangGraph 推荐写法(默认make run-03演示此版本)。examples/03_memory_runnable.py—— 老 API 对照(仅用于理解差异)。
- LangChain 官方 "Memory" / "How to add message history" 文档。
- 向量化长期记忆(把旧对话存入向量库按需检索)进阶玩法。