news 2026/6/13 18:08:51

模型没失忆,是你没给它记忆——速通 LangChain Memory最短路径

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
模型没失忆,是你没给它记忆——速通 LangChain Memory最短路径

DeepSeek 官网能记住我叫大煊、刚才聊啥;同样的问题搬到本地invoke(调模型),第二轮它直接回「我不知道你刚才提到的名字」。模型没坏,是我没给记忆

对 Javaer 来说,LangChain 的 Memory(记忆)就像HttpSession 里塞对话记录,或者 ThreadLocal 里挂的上下文——不是大模型自带记忆,是你在每次请求前后维护一份「聊天记录副本」,再塞给模型看。

我从对照实验到RunnableWithMessageHistory(带历史消息的可运行链)跑通的最短路径:内存版 → 多 session 隔离 → 可选 Redis 落盘。


真相:失忆发生在应用层,不在模型里

模型本身不会自动记住上一轮。是应用层在读历史、写历史、拼 Prompt(提示词)。

实现记忆就三件事:

  1. :从记忆组件取出历史对话
  2. :历史 + 当前用户输入塞进 Prompt,传给模型
  3. :本轮问答写回记忆组件

可以记一句:Memory 不是让模型变聪明,是让每次请求变「完整」

用户输入

Prompt: history + question

Memory

Model

回答


RunnableWithMessageHistory

LangChain 早期有个ConversationChain,几行代码就能聊,但提示模板和内存逻辑绑死,跟 LCEL (链式调用)的Runnable体系不太合拍。

LangChain 0.3.x 起推荐RunnableWithMessageHistory:你的链还是prompt | llm | parser,外面套一层壳,通过get_session_history回调决定历史存哪儿。跟 Spring 从「一个大而全的 Facade」往「小接口 + 组合」演进的路数有点像——换存储后端不用动链本身。

自己往history.messages里塞消息再llm.invoke也能拼,但 Runnable替你自动化了读 → 拼 → 写那一圈。

存储后端怎么选

实现特性
InMemoryChatMessageHistory内存,进程退出即丢
FileChatMessageHistory文件,单机持久化
RedisChatMessageHistoryRedis,多实例共享、可持久化
ElasticsearchChatMessageHistoryES,适合检索型场景

BaseChatMessageHistory是用来保存聊天消息历史的抽象基类,其中最重要就是清空所有消息和添加消息

点开抽象基类可以看到Langchain可选的记忆存储组件还是有很多选型

对照实验:没 Memory 的「我不知道」

跑下面这段,两轮invoke彼此独立——上一轮的内容根本没传进去:

importosfromdotenvimportload_dotenvfromlangchain_core.output_parsersimportStrOutputParserfromlangchain_core.promptsimportPromptTemplatefromlangchain.chat_modelsimportinit_chat_model load_dotenv()llm=init_chat_model(model=os.getenv("DEEPSEEK_MODEL","deepseek-chat"),model_provider="openai",api_key=os.getenv("DEEPSEEK_API_KEY"),temperature=0.0,base_url=os.getenv("DEEPSEEK_BASE_URL","https://api.deepseek.com"),)prompt=PromptTemplate.from_template("请根据用户问题作答:{question}")chain=prompt|llm|StrOutputParser()print(chain.invoke({"question":"你好,我是大煊,你怎么称呼?"}))print(chain.invoke({"question":"你还记得我刚才自我介绍的名字吗?"}))


自动挡:多 session + RunnableWithMessageHistory

真实项目里不同用户得隔离历史。用一个dictsession_id(会话标识)存各自的InMemoryChatMessageHistory——跟 Java 里Map<String, HttpSession>按 userId 取 Session 是一个路数。

importosfromdotenvimportload_dotenvfromlangchain.chat_modelsimportinit_chat_modelfromlangchain_core.chat_historyimportInMemoryChatMessageHistoryfromlangchain_core.runnables.historyimportRunnableWithMessageHistoryfromlangchain_core.promptsimportChatPromptTemplate,MessagesPlaceholderfromlangchain_core.output_parsersimportStrOutputParser load_dotenv()llm=init_chat_model(model=os.getenv("DEEPSEEK_MODEL","deepseek-chat"),model_provider="openai",api_key=os.getenv("DEEPSEEK_API_KEY"),base_url=os.getenv("DEEPSEEK_BASE_URL","https://api.deepseek.com"),)store={}defget_session_history(session_id:str):ifsession_idnotinstore:store[session_id]=InMemoryChatMessageHistory()returnstore[session_id]prompt=ChatPromptTemplate.from_messages([("system","你是一个耐心的中文助理,会结合上文连贯作答。"),MessagesPlaceholder("history"),("human","{question}"),])memory_chain=prompt|llm|StrOutputParser()with_history=RunnableWithMessageHistory(memory_chain,get_session_history,input_messages_key="question",history_messages_key="history",)cfg={"configurable":{"session_id":"user-001"}}print("AI:",with_history.invoke({"question":"我是大煊,Javaer 一枚。"},cfg))print("AI:",with_history.invoke({"question":"你记得我叫啥不?"},cfg))

三个名字必须对齐:模板里MessagesPlaceholder("history")history_messages_key="history"input_messages_key="question"与 Prompt 里 human 的{question}一致——我对过一次,少一个历史就是空的,模型每轮都像第一次见面。


可选进阶:Redis Stack

内存版进程一挂,历史就没了。要上生产或本地重启还想接着聊,把get_session_history里的实现换成RedisChatMessageHistory就行——链和 Prompt 不用动,只改回调。开发阶段注意REDIS_URL端口和 reids端口映射对齐;有ttl的话历史可能被自动清掉。

Redis Stack是redis的扩展版,也是近年来redis的主推版本

功能维度原生 RedisRedis Stack 增强功能
数据结构字符串、列表、集合、哈希等增加 JSON、图、时间序列、概率结构等高级类型
查询能力仅限键值查询支持全文搜索、向量搜索、图查询、JSON 查询
使用场景缓存、消息队列、计数器等实时推荐、时序分析、知识图谱、文档数据库、AI 向量检索
开发体验命令行操作,需手动拼装逻辑提供 RedisInsight 和对象映射库,开发效率更高

复杂多 Agent 编排以后可以换 LangGraph 的 Checkpointer


这是「AI Agent 实战踩坑」系列的第 5 篇。欢迎大家关注,后续还会继续分享知识片段

参考:LangChain Short-term memory

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/13 18:06:54

DRG存档编辑器:5分钟学会修改《深岩银河》游戏数据

DRG存档编辑器&#xff1a;5分钟学会修改《深岩银河》游戏数据 【免费下载链接】DRG-Save-Editor Rock and stone! 项目地址: https://gitcode.com/gh_mirrors/dr/DRG-Save-Editor 还在为《深岩银河》中稀有的Enor Pearl矿物而烦恼吗&#xff1f;是否觉得职业升级太慢&a…

作者头像 李华
网站建设 2026/6/13 18:02:55

MySQL InnoDB锁信息解析

一、字段含义总览表格字段含义lock_type锁的类型&#xff0c;分为表级锁&#xff08;TABLE&#xff09;和行级锁&#xff08;RECORD&#xff09;lock_mode锁的模式&#xff0c;包含共享 / 排他、间隙锁 / 记录锁等组合lock_data被锁定的数据&#xff08;主键 / 索引值&#xff…

作者头像 李华
网站建设 2026/6/13 17:55:31

MC68377 QADC64模块:队列式ADC原理、配置与多通道采集实战

1. 项目概述与核心价值在嵌入式系统开发&#xff0c;尤其是涉及工业控制、汽车电子或精密仪器仪表时&#xff0c;模拟信号的采集与处理是绕不开的核心任务。传统的ADC&#xff08;模数转换器&#xff09;使用方式&#xff0c;通常是CPU轮询或响应中断&#xff0c;然后配置寄存器…

作者头像 李华
网站建设 2026/6/13 17:52:22

Path of Building PoE2:流放之路2角色构建的精准模拟器

Path of Building PoE2&#xff1a;流放之路2角色构建的精准模拟器 【免费下载链接】PathOfBuilding-PoE2 项目地址: https://gitcode.com/GitHub_Trending/pa/PathOfBuilding-PoE2 Path of Building PoE2是一款专为《流放之路2》设计的开源角色构建模拟器&#xff0c;…

作者头像 李华
网站建设 2026/6/13 17:49:52

ncmdump终极指南:三步解锁网易云音乐NCM格式的完整解决方案

ncmdump终极指南&#xff1a;三步解锁网易云音乐NCM格式的完整解决方案 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 你是否曾经购买过网易云音乐的付费歌曲&#xff0c;却发现下载的NCM格式文件只能在特定客户端播放&#xff1f;…

作者头像 李华