零基础学AI大模型之LLM存储记忆功能之BaseChatMemory
零基础学AI大模型之LLM存储记忆功能之BaseChatMemory很多刚接触大模型工程的同学第一次做聊天机器人时都会遇到一个核心问题模型为什么“记不住”刚才说过的话答案通常不是模型变笨了而是你的程序没有把历史对话正确地传回去。围绕这个问题LangChain 早期提供过一套经典记忆抽象其中非常重要的一个基类就是BaseChatMemory。对工程师来说理解 BaseChatMemory 的价值不只是为了会用一个 API更重要的是搞清楚LLM 的“记忆”本质上是消息历史管理问题而不是模型内部真的长出永久记忆。不过也要注意官方文档已经明确说明BaseChatMemory 自 v1.0 起已弃用新项目不建议继续作为主方案使用。因此本文会采用“先学原理再看现代替代方案”的方式帮助你建立正确的工程认知。大家想学习更多AI知识可以看看这里GPTBUYS、ZeoAPI摘要摘要本文从零基础视角解释 BaseChatMemory 的定位、核心属性和方法、在链式调用中的工作方式以及它为什么被官方逐步迁移到 LangGraph 的状态持久化方案。你将学到以下内容BaseChatMemory 是什么它在 LangChain 旧架构中负责什么它如何通过chat_memory管理对话消息历史save_context、load_memory_variables、clear等方法怎么理解为什么官方说它已弃用问题出在哪里新项目应该优先选什么LangGraph checkpointer、store、RunnableWithMessageHistory 等如何从工程角度设计“短期记忆”和“长期记忆”BaseChatMemory 到底是什么摘要BaseChatMemory 是 LangChain 早期聊天记忆体系中的抽象基类职责是把对话消息历史接入链式调用过程。根据 LangChain 官方参考文档BaseChatMemory 是聊天记忆的抽象基类继承自BaseMemory和ABC。[1][2]它的设计目标非常直接把用户输入、模型输出整理成聊天消息历史并在下一轮调用时再注入回去。换句话说BaseChatMemory 并不是“数据库”也不是“向量知识库”它更像一个中间层接收本轮输入和输出转成消息历史存入chat_memory在后续调用中把这些历史内容作为上下文变量再交给链或模型这也是很多初学者容易混淆的地方BaseChatMemory 解决的是“会话上下文延续”不是“跨天、跨设备、跨用户画像”的长期记忆管理官方文档还特别指出这个抽象从 v1.0 开始已弃用并提示其不支持聊天模型原生 tool calling因此新代码不应继续基于它扩展。[1]这说明理解它仍然有学习价值但不能把它当成未来架构主线。BaseChatMemory 的核心属性与方法摘要掌握 BaseChatMemory 的四个关键属性和三类核心方法就能读懂大多数旧版记忆类实现。结合官方 API 文档[1][2] BaseChatMemory 的关键点主要有这些。1核心属性chat_memory类型是BaseChatMessageHistory。[2]这是最核心的属性真正存放消息历史的对象就是它。也就是说BaseChatMemory 自己不一定直接保存所有内容它更多是把历史管理委托给chat_memory。input_key用于指定“本轮用户输入”在上下文字典里的字段名。当链接收多个输入字段时这个参数就很重要否则框架可能不知道哪一个是用户消息。output_key用于指定“本轮模型输出”对应的字段名。如果链输出不止一个字段也需要靠它明确应该保存哪个结果进入对话历史。return_messages用于控制记忆变量返回时的格式。通常可理解为返回的是原始消息对象列表还是拼接好的文本形式。这直接影响后续 Prompt 组装方式。2核心方法load_memory_variables/aload_memory_variables作用是把历史记忆作为变量注入到链的输入中。[2]这是“读记忆”的过程。例如在一次链调用前框架会先问 memory“你这里有没有应该带上的历史上下文”save_context/asave_context作用是把本轮输入和输出保存进记忆。[1][2]这是“写记忆”的过程。当用户说了一句模型答了一句这一轮对话就会被整理后写入chat_memory。clear/aclear作用是清空当前记忆。[1][2]适用于用户主动要求“清空上下文”切换会话线程调试时重置状态避免历史过长导致上下文窗口超限从工程视角看这几个方法已经构成了一个很完整的生命周期加载记忆 → 调用模型 → 保存本轮 → 必要时清空。它在旧版 LangChain 里的工作流程摘要BaseChatMemory 的本质是把“消息历史注入”和“本轮对话保存”嵌入链调用生命周期中。很多人对记忆类的理解停留在“存个列表”但真正重要的是它如何与链协同工作。旧版 LangChain 的典型流程可以简化为下面 4 步第 1 步用户发起请求例如传入{input:帮我总结一下昨天讨论的接口设计}第 2 步链调用前加载记忆框架会调用load_memory_variables(...)或异步版本aload_memory_variables(...)此时 memory 会把已经保存的消息历史整理成某个字段比如history一起交给 Prompt 或 Chain 使用。第 3 步模型生成结果模型结合当前输入历史对话Prompt 模板生成新的回答。第 4 步调用后保存上下文框架再调用save_context(inputs, outputs)或asave_context(inputs, outputs)把本轮用户输入和模型输出写入chat_memory。这个机制说明BaseChatMemory 更偏向链式编排中的“会话状态适配器”而不是一个完整的记忆系统。它最大的价值是帮助开发者在不手写太多状态管理代码的前提下让聊天链“看起来有上下文”。为什么官方弃用了 BaseChatMemory摘要BaseChatMemory 被弃用不是因为它完全没用而是因为它难以支撑现代 Agent、工具调用和复杂持久化场景。这是本文最值得工程师重视的一部分。根据官方参考[1] BaseChatMemory 自 v1.0 起已经标记为deprecated并明确指出它不支持聊天模型原生 tool calling新代码不应使用。为什么会这样结合新版文档与迁移说明[3][4][5] 可以从几个工程原因理解。1旧式 memory 类偏“链时代”不适合现代 Agent 状态管理早期 LangChain 的抽象更偏向 Chain。但现在主流应用越来越多是多步骤 Agent工具调用多节点状态流转人机混合交互中断恢复与持久化执行这种场景下单纯“在链前后读写一段聊天历史”已经不够了。2短期记忆与长期记忆需要分开设计新版 LangGraph 文档明确区分了短期记忆线程内、会话内状态通常由 checkpointer 保存长期记忆跨线程、跨会话的用户或应用数据由 store 管理 [3][4]而 BaseChatMemory 更像是对“短期聊天历史”的一个轻量封装不适合承担长期记忆职责。3上下文窗口管理需要更强的策略官方新文档强调可以通过摘要或消息裁剪来避免超过模型上下文窗口。[3]旧式 Buffer 型记忆很容易无限增长最终导致token 成本上升响应变慢模型上下文超限关键信息被淹没4复杂工程更需要显式状态而不是隐式魔法迁移文档指出现代替代方式包括LangGraph persistenceLCEL 的RunnableWithMessageHistory[5]其中很多用户会发现LangGraph 持久化更适合复杂场景也更容易配置和维护。[5]因为它让状态变成系统设计的一部分而不是藏在 memory 对象里。新项目应该如何选择记忆方案摘要如果你是新项目不要把 BaseChatMemory 当主线应该按“线程内短期记忆”和“跨会话长期记忆”拆开选型。下面给出工程上更实用的选择思路。方案一只做简单单轮或少量多轮聊天如果只是一个很轻的聊天 Demo可以仍然理解旧式 memory 的思想但不建议继续深度绑定 BaseChatMemory。你至少要意识到它更适合教学理解消息历史机制阅读旧代码维护遗留项目方案二线程内短期记忆优先 LangGraph Checkpointer新版 LangGraph 文档推荐通过checkpointer保存线程内状态例如InMemorySaver。[3]它适用于一个会话线程内保留消息历史支持中断恢复更自然地与图状态融合这是当前官方主推方向。方案三跨会话长期记忆使用 Store 或专门记忆层如果你的需求是记住用户偏好记住历史工单摘要跨设备恢复用户画像多会话复用个人信息那就不是 BaseChatMemory 能解决的了。新版文档与生态工具更强调通过store或专门的长期记忆管理层实现。[3][6][8]例如 LangMem 的能力更偏向搜索记忆抽取记忆更新记忆结构化管理 [6]这与简单的聊天历史缓存完全不是一个层级。方案四中间态迁移可考虑 RunnableWithMessageHistory迁移指南指出LCEL 中可以使用RunnableWithMessageHistory结合对话历史处理。[5]这对从旧链式写法平滑升级的团队是一个折中方案。Key Comparison Table摘要下面这张表从工程落地角度比较 BaseChatMemory 与现代替代方案帮助你快速做技术选型。DimensionBaseChatMemoryRunnableWithMessageHistoryLangGraph CheckpointerStore / LangMem 类长期记忆官方定位旧版聊天记忆抽象基类已弃用[1]LCEL 体系下的消息历史包装方案[5]官方推荐的线程内状态持久化方案[3][4]跨会话、长期记忆管理方案[3][6]主要解决问题保存和注入聊天历史为 runnable 注入消息历史保存线程内对话状态、支持恢复保存用户偏好、长期事实、应用数据适合场景遗留项目、学习旧架构需要平滑迁移的中等复杂度项目新 Agent / 新对话系统主方案需要跨线程、跨会话记忆的系统对工具调用支持官方明确提示不支持原生 tool calling[1]相比旧 memory 更灵活但不是长期记忆更适合现代图式编排和工具调用流程[3][4]不负责线程流转但适合长期知识保留状态粒度主要是聊天消息列表主要是消息历史图状态/线程状态用户级、应用级长期数据持久化能力取决于底层 chat_memory实现较弱可接历史实现但需自行设计官方围绕 persistence 进行设计[3][5]通常具备更明确的持久化和检索能力[6][8]上下文控制旧式方案易无限增长需要自行结合裁剪策略可配合摘要、裁剪、线程状态管理[3]通常通过检索相关记忆而非全量塞进上下文新项目推荐度不推荐可作为过渡方案高高尤其适合个性化系统实战代码示例摘要下面先用一个“旧式理解示例”帮助你读懂 BaseChatMemory 的调用形态再给出一个更接近现代官方方向的 LangGraph 短期记忆示例。需要先强调下面第一个示例是为了帮助你理解 BaseChatMemory 思路不是推荐你在新项目中继续重度使用它。示例一理解 BaseChatMemory 风格的保存与读取# 目的演示旧式聊天记忆的基本调用思路# 关键点创建 memory对一轮输入输出执行 save_context再读取 memory variablesfromlangchain.memoryimportConversationBufferMemory# ConversationBufferMemory 是基于旧 memory 体系的常见实现# 本质上可看作 BaseChatMemory 思路的一个具体子类memoryConversationBufferMemory(memory_keyhistory,# 历史变量名供 Prompt 使用return_messagesFalse# 返回拼接文本而不是消息对象列表)# 模拟第一轮对话用户输入 模型输出memory.save_context({input:我叫小王是一名后端开发},{output:好的我记住了你叫小王是一名后端开发。})# 模拟第二轮开始前加载记忆memory_varsmemory.load_memory_variables({input:你还记得我是谁吗})print(注入给链的记忆变量)print(memory_vars)这个示例说明了两件事save_context 是写入动作load_memory_variables 是读取动作在旧式链里Prompt 通常会接收一个history变量然后把它和本轮用户输入一起发给模型。示例二LangGraph 风格的线程内短期记忆示意# 目的演示新版官方方向中的“线程内短期记忆”思路# 关键点使用 checkpointer 保存对话状态而不是依赖旧式 BaseChatMemoryfromlanggraph.checkpoint.memoryimportInMemorySaver# 创建一个内存型 checkpointer# 适合本地开发、Demo、单进程测试checkpointerInMemorySaver()# 实际项目里checkpointer 会挂接到图执行流程中# 每个 thread_id 对应一个会话线程状态thread_iduser-1001-session-01# 下面仅做概念示意状态通常包含 messages 等字段state{messages:[{role:user,content:我喜欢 Python 和分布式系统},{role:assistant,content:好的我会基于你的技术偏好继续交流。}]}# 在真实 LangGraph 中状态会随图节点推进而被持久化print(当前线程ID:,thread_id)print(当前状态:,state)print(checkpointer 已准备好可用于线程内状态保存。)这个示例虽然简化了图编排细节但能帮助你建立新版认知记忆不再只是一个 memory 对象而是整个图执行状态的一部分线程内记忆应由 persistence/checkpointer 负责 [3][4]示例三长期记忆设计思路示意# 目的说明“聊天历史”和“长期记忆”应分层设计# 关键点用户偏好不要直接无限堆进聊天上下文而应独立存储user_profile_store{user_1001:{name:小王,role:后端开发,preferences:[Python,分布式系统,性能优化]}}defget_user_profile(user_id:str):# 从长期存储中读取用户画像returnuser_profile_store.get(user_id,{})defbuild_runtime_context(user_id:str,recent_messages:list):# 运行时上下文 长期画像 最近消息# 这样避免把所有历史消息无脑塞给模型profileget_user_profile(user_id)return{profile:profile,recent_messages:recent_messages[-5:]# 只截取最近几轮控制上下文长度}contextbuild_runtime_context(user_1001,[{role:user,content:最近我在看 gRPC},{role:assistant,content:不错gRPC 很适合服务间通信。}])print(context)这个设计更符合现代工程实践最近对话放在线程内短期记忆稳定用户信息放在长期存储运行时按需拼装上下文而不是全量回灌这也是新版文档强调“短期记忆”和“长期记忆”分离的重要原因。[3][6]代码块注释规范摘要技术博客里的代码不怕短怕读者不知道你想证明什么所以注释应服务于“目的、关键步骤、边界条件”。写 CSDN 技术文章时我建议代码块注释遵循以下 4 条规则1开头先写“这段代码的目的”不要一上来就堆 API。先说明这段代码演示什么适用什么场景是推荐方案还是仅用于理解旧架构这样读者不会误学。2只注释关键步骤不要逐行翻译差的注释是x1# 定义x等于1好的注释是# 这里显式指定 memory_key避免 Prompt 模板拿不到历史字段memoryConversationBufferMemory(memory_keyhistory)注释应该解释“为什么这样写”而不是复述“写了什么”。3对旧方案要加风险提示像 BaseChatMemory 这类旧 API代码块里应明确标注这是旧式方案新项目不推荐用它只是为了理解机制或维护遗留代码这能避免读者直接复制到生产环境。4对省略部分要坦诚说明如果示例没有覆盖完整配置例如 LangGraph 图构建过程被简化了就要在注释中说明“这是概念示意”。工程博客最忌讳的是让读者以为“复制即运行”结果缺一堆上下文。常见问题与排错摘要BaseChatMemory 相关问题大多不是模型问题而是输入输出字段、历史注入方式和架构选型出了偏差。1为什么模型还是记不住上一轮内容先检查是否真的调用了save_context以及下一轮前是否调用了load_memory_variables。[2]如果链没有把历史变量拼进 Prompt模型当然“记不住”。2为什么保存失败或历史为空常见原因是input_key、output_key配错了。当输入输出是多字段字典时BaseChatMemory 不一定能自动判断该保存哪一个字段。[1][2]3为什么对话越聊越慢、token 越来越高因为旧式聊天记忆通常会不断累积历史。官方新版文档建议通过摘要或消息裁剪控制上下文长度。[3]4为什么工具调用场景表现很别扭因为官方已经明确说明 BaseChatMemory不支持聊天模型原生 tool calling。[1]如果你在做 Agent 或工具编排应该优先考虑 LangGraph 方案。5我是在维护老项目还能继续用吗可以维护但不建议继续围绕它扩展新架构。更稳妥的策略是旧功能维持运行新功能逐步迁移到 LangGraph persistence 或 RunnableWithMessageHistory。[5]结论学会 BaseChatMemory但别把它当终点摘要理解 BaseChatMemory 是为了建立记忆机制认知而真正的新项目落地应转向 LangGraph 的短期状态持久化与长期记忆分层设计。最后做个工程化总结你应该记住的 3 个核心事实BaseChatMemory 本质是聊天历史管理抽象它负责把输入输出转成消息历史并在后续调用时注入回去。[1][2]它已经被官方弃用新项目不应继续把它作为核心记忆架构尤其不适合现代 tool calling 场景。[1]现代记忆设计要分层线程内短期记忆优先 LangGraph checkpointer [3][4]跨会话长期记忆优先 store / LangMem / 外部持久化层 [3][6][8]给零基础同学的下一步建议如果你正在学习大模型工程我建议按这个顺序继续深入先理解消息历史是什么、为什么需要注入上下文再读懂 BaseChatMemory 这类旧式抽象建立基础概念接着学习 LangGraph 的 state、checkpointer、thread 概念最后再做长期记忆用户画像、偏好存储、事实抽取、召回拼装这样你的认知会从“会用一个类”升级为“会设计一套记忆系统”。参考资料BaseChatMemory | langchain_classic | LangChain Referencehttps://reference.langchain.com/python/langchain-classic/memory/chat_memory/BaseChatMemoryBaseChatMemory — LangChain documentationhttps://api.python.langchain.com/en/latest/langchain/memory/langchain.memory.chat_memory.BaseChatMemory.htmlMemory - Docs by LangChainPython / LangGraphhttps://docs.langchain.com/oss/python/langgraph/add-memoryMemory - Docs by LangChainJavaScript / LangGraphhttps://docs.langchain.com/oss/javascript/langgraph/add-memoryMigrating off ConversationBufferMemory or ConversationStringBufferMemoryhttps://langchain.cadn.net.cn/python/docs/versions/migrating_memory/conversation_buffer_memory/index.en.htmlMemory API ReferenceLangMemhttps://langchain-ai.lang.chat/langmem/reference/memory/LangChain memory is deprecated — what to use in 2026 (JavaScript)https://db0.ai/blog/langchain-memory-deprecatedLangGraphHoncho integrationhttps://docs.honcho.dev/v2/integrations/langgraph