目录什么是对话记忆为什么需要对话记忆环境搭建与依赖安装完整代码实现代码逐行解析运行测试常见问题总结一、什么是对话记忆对话记忆Conversation Memory是 LangChain 中让大模型记住之前对话内容的能力。通俗理解· 无记忆 每次都是陌生人每次对话都从零开始· 有记忆 熟悉的老朋友记得你之前说过什么示例对比轮次 用户说 无记忆的AI 有记忆的AI第1轮 “我叫小明我喜欢吃披萨” “你好小明” “你好小明”第2轮 “我叫什么名字” “你没有告诉我你的名字” “你叫小明”第3轮 “我喜欢吃什么” “我不知道你喜欢吃什么” “你喜欢吃披萨”二、为什么需要对话记忆场景 无记忆的问题 有记忆的好处客服机器人 每轮都要重复身份信息 一次确认全程记住教育辅导 无法跟踪学习进度 记住学生薄弱环节医疗咨询 重复询问病史 持续跟踪病情游戏互动 每次都是新开始 保持游戏连续性三、环境搭建与依赖安装3.1 依赖库清单langchain langchain-openai langchain-community langchain-core python-dotenv3.2 一键安装pipinstalllangchain langchain-openai langchain-community langchain-core python-dotenv-ihttps://pypi.tuna.tsinghua.edu.cn/simple3.3 配置 API Key创建 .env 文件DASHSCOPE_API_KEY你的阿里云百炼API KeyDASHSCOPE_BASE_URLhttps://dashscope.aliyuncs.com/compatible-mode/v1四、完整代码实现# memory_demo.pyimportosfromdotenvimportload_dotenvfromlangchain_openaiimportChatOpenAIfromlangchain_core.promptsimportChatPromptTemplate,MessagesPlaceholderfromlangchain_core.output_parsersimportStrOutputParserfromlangchain_community.chat_message_historiesimportChatMessageHistoryfromlangchain_core.runnables.historyimportRunnableWithMessageHistory# 加载环境变量load_dotenv()# 初始化大模型llmChatOpenAI(modelqwen3-max,temperature0.7,openai_api_keyos.getenv(DASHSCOPE_API_KEY),openai_api_baseos.getenv(DASHSCOPE_BASE_URL),)print(*60)print(LangChain 对话记忆完整教程)print(*60)# # 第一部分无记忆对比实验# print(\n 第一部分无记忆模式每次都是新对话)print(-*40)# 创建不带记忆的提示词模板no_memory_promptChatPromptTemplate.from_messages([(human,用户说{input}\n请回答)])# 创建不带记忆的链no_memory_chainno_memory_prompt|llm|StrOutputParser()print(\n【第1轮对话】)print(用户我叫小明我喜欢吃披萨)result1no_memory_chain.invoke({input:我叫小明我喜欢吃披萨})print(fAI{result1})print(\n【第2轮对话】)print(用户我叫什么名字我喜欢吃什么)result2no_memory_chain.invoke({input:我叫什么名字我喜欢吃什么})print(fAI{result2})print(\n❌ 结论没有记忆AI完全忘记了用户之前说的话)# # 第二部分有记忆模式使用 RunnableWithMessageHistory# print(\n*60)print( 第二部分有记忆模式使用 RunnableWithMessageHistory)print(-*40)# 步骤1定义带历史占位符的提示词模板memory_promptChatPromptTemplate.from_messages([(system,你是一个友好的AI助手记住用户之前说过的话。),MessagesPlaceholder(variable_namehistory),# 历史消息会插入到这里(human,{input})])# 步骤2创建核心链提示词 模型 输出解析器core_chainmemory_prompt|llm|StrOutputParser()# 步骤3创建会话历史存储模拟数据库store{}defget_session_history(session_id:str):根据 session_id 获取或创建对话历史ifsession_idnotinstore:store[session_id]ChatMessageHistory()returnstore[session_id]# 步骤4用 RunnableWithMessageHistory 包装核心链使其具备记忆能力chain_with_memoryRunnableWithMessageHistory(core_chain,# 核心链get_session_history,# 获取历史的函数input_messages_keyinput,# 输入参数的 keyhistory_messages_keyhistory,# 历史占位符的变量名)# 使用固定的 session_id 来维持同一会话session_iduser_001print(\n【第1轮对话】)print(用户我叫小明我喜欢吃披萨)response1chain_with_memory.invoke({input:我叫小明我喜欢吃披萨},config{configurable:{session_id:session_id}})print(fAI{response1})print(\n【第2轮对话】)print(用户我叫什么名字我喜欢吃什么)response2chain_with_memory.invoke({input:我叫什么名字我喜欢吃什么},config{configurable:{session_id:session_id}})print(fAI{response2})print(\n✅ 结论有记忆AI正确记住了用户的信息)# # 第三部分查看和管理记忆# print(\n*60)print( 第三部分查看和管理记忆)print(-*40)# 获取当前会话的历史记录current_historyget_session_history(session_id)print(\n【查看记忆内容】)print(f对话历史中共有{len(current_history.messages)}条消息)fori,msginenumerate(current_history.messages,1):print(f{i}.{msg.type}:{msg.content[:50]}...)print(\n【清空记忆前】)print(f消息数量{len(current_history.messages)})# 清空记忆current_history.clear()print(\n【清空记忆后】)print(f消息数量{len(current_history.messages)})print(✅ 记忆已成功清空)# # 第四部分多会话管理同时服务多个用户# print(\n*60)print( 第四部分多会话管理同时服务多个用户)print(-*40)# 同一个核心链通过不同的 session_id 管理不同用户的对话users[{id:alice_001,name:Alice,question:我叫Alice我喜欢喝咖啡},{id:bob_001,name:Bob,question:我叫Bob我喜欢喝茶},]foruserinusers:print(f\n【用户{user[name]}】)print(f用户{user[question]})responsechain_with_memory.invoke({input:user[question]},config{configurable:{session_id:user[id]}})print(fAI{response})# 测试记忆print(f\n【测试记忆】用户我叫什么名字)test_responsechain_with_memory.invoke({input:我叫什么名字},config{configurable:{session_id:user[id]}})print(fAI{test_response})# # 第五部分实战 - 猜数字游戏# print(\n*60)print( 第五部分实战 - 猜数字游戏多轮对话)print(-*40)# 创建游戏专用的提示词game_promptChatPromptTemplate.from_messages([(system,你是一个猜数字游戏主持人。 游戏规则 1. 你想一个1-10之间的整数 2. 用户来猜这个数字 3. 每次用户猜完后告诉他猜大了、猜小了还是猜中了 4. 猜中后游戏结束并祝贺用户),MessagesPlaceholder(variable_namehistory),(human,{input})])# 创建游戏核心链game_coregame_prompt|llm|StrOutputParser()# 游戏会话存储game_store{}defget_game_history(session_id:str):ifsession_idnotingame_store:game_store[session_id]ChatMessageHistory()returngame_store[session_id]# 创建带记忆的游戏链game_chainRunnableWithMessageHistory(game_core,get_game_history,input_messages_keyinput,history_messages_keyhistory,)# 开始游戏game_sessiongame_001print(\n 猜数字游戏开始我心里想了一个1-10之间的数字...)conversations[我猜5,我猜7,我猜3,我猜8,]foruser_inputinconversations:print(f\n用户{user_input})responsegame_chain.invoke({input:user_input},config{configurable:{session_id:game_session}})print(fAI{response})print(\n*60)print( 教程完成)print(*60)五、代码逐行解析5.1 核心导入fromlangchain_core.promptsimportChatPromptTemplate,MessagesPlaceholderfromlangchain_community.chat_message_historiesimportChatMessageHistoryfromlangchain_core.runnables.historyimportRunnableWithMessageHistory导入 作用ChatPromptTemplate 创建提示词模板MessagesPlaceholder 在提示词中预留历史消息位置ChatMessageHistory 存储对话历史的容器RunnableWithMessageHistory 为核心链添加记忆能力的包装器5.2 无记忆 vs 有记忆的核心区别无记忆链chainprompt|llm|parser chain.invoke({input:你好})有记忆链# 1. 提示词中要预留 history 位置promptChatPromptTemplate.from_messages([MessagesPlaceholder(variable_namehistory),# 关键(human,{input})])# 2. 用 RunnableWithMessageHistory 包装chainRunnableWithMessageHistory(prompt|llm|parser,get_session_history,input_messages_keyinput,history_messages_keyhistory,)# 3. 调用时传入 session_idchain.invoke({input:你好},config{configurable:{session_id:user_001}})5.3 关键参数说明参数 作用 示例值input_messages_key 指定输入参数的字段名 “input”history_messages_key 指定历史占位符的变量名 “history”session_id 会话标识相同 ID 共享记忆 “user_001”5.4 记忆存储机制# 简单的内存存储演示用store{}defget_session_history(session_id:str):ifsession_idnotinstore:store[session_id]ChatMessageHistory()returnstore[session_id]生产环境可替换为· Redis· PostgreSQL· 文件存储六、运行测试6.1 执行脚本python memory_demo.py6.2 预期输出 LangChain 对话记忆完整教程 第一部分无记忆模式每次都是新对话 ---------------------------------------- 【第1轮对话】 用户我叫小明我喜欢吃披萨 AI你好小明披萨很好吃。 【第2轮对话】 用户我叫什么名字我喜欢吃什么 AI你没有告诉我你的名字和喜好。 ❌ 结论没有记忆AI完全忘记了用户之前说的话 第二部分有记忆模式使用 RunnableWithMessageHistory ---------------------------------------- 【第1轮对话】 用户我叫小明我喜欢吃披萨 AI你好小明很高兴认识你披萨确实很美味。 【第2轮对话】 用户我叫什么名字我喜欢吃什么 AI你叫小明喜欢吃披萨。 ✅ 结论有记忆AI正确记住了用户的信息七、常见问题Q1为什么要用 RunnableWithMessageHistory 而不是 ConversationBufferMemory对比 ConversationBufferMemory RunnableWithMessageHistory版本支持 旧版 API 新版推荐与 LCEL 兼容 ❌ 不兼容 ✅ 完美兼容多会话管理 麻烦 通过 session_id 轻松实现持久化扩展 困难 灵活可接入 Redis 等Q2如何让记忆持久化重启后还在# 将 store 替换为 Redisimportredisimportjson redis_clientredis.Redis(hostlocalhost,port6379)defget_session_history(session_id:str):dataredis_client.get(fchat:{session_id})historyChatMessageHistory()ifdata:formsginjson.loads(data):history.add_message(msg)returnhistoryQ3如何限制记忆长度fromlangchain_community.chat_message_historiesimportChatMessageHistory# 限制最多保留10条消息historyChatMessageHistory()MAX_MESSAGES10defadd_message_with_limit(msg):history.add_message(msg)iflen(history.messages)MAX_MESSAGES:history.messageshistory.messages[-MAX_MESSAGES:]八、总结本文实现了 LangChain 对话记忆的核心功能功能 实现方式 状态无记忆对话 普通 LCEL 链 ✅单会话记忆 RunnableWithMessageHistory ✅多会话管理 不同 session_id ✅记忆查看 ChatMessageHistory.messages ✅记忆清空 clear() 方法 ✅实战应用 猜数字游戏 ✅核心代码模板# 5 行代码实现对话记忆promptChatPromptTemplate.from_messages([MessagesPlaceholder(history),(human,{input})])chainprompt|llm|StrOutputParser()chain_with_memoryRunnableWithMessageHistory(chain,get_session_history,input_messages_keyinput,history_messages_keyhistory)responsechain_with_memory.invoke({input:你好},config{configurable:{session_id:user_001}})