目录
- 什么是对话记忆
- 为什么需要对话记忆
- 环境搭建与依赖安装
- 完整代码实现
- 代码逐行解析
- 运行测试
- 常见问题
- 总结
一、什么是对话记忆
对话记忆(Conversation Memory)是 LangChain 中让大模型记住之前对话内容的能力。
通俗理解:
· 无记忆 = 每次都是陌生人(每次对话都从零开始)
· 有记忆 = 熟悉的老朋友(记得你之前说过什么)
示例对比:
轮次 用户说 无记忆的AI 有记忆的AI
第1轮 "我叫小明,我喜欢吃披萨" "你好小明!" "你好小明!"
第2轮 "我叫什么名字?" "你没有告诉我你的名字" "你叫小明"
第3轮 "我喜欢吃什么?" "我不知道你喜欢吃什么" "你喜欢吃披萨"
二、为什么需要对话记忆
场景 无记忆的问题 有记忆的好处
客服机器人 每轮都要重复身份信息 一次确认,全程记住
教育辅导 无法跟踪学习进度 记住学生薄弱环节
医疗咨询 重复询问病史 持续跟踪病情
游戏互动 每次都是新开始 保持游戏连续性
三、环境搭建与依赖安装
3.1 依赖库清单
text
langchain
langchain-openai
langchain-community
langchain-core
python-dotenv
3.2 一键安装
bash
pip install langchain langchain-openai langchain-community langchain-core python-dotenv -i https://pypi.tuna.tsinghua.edu.cn/simple
3.3 配置 API Key
创建 .env 文件:
bash
DASHSCOPE_API_KEY="你的阿里云百炼API Key"
DASHSCOPE_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
四、完整代码实现
python
# memory_demo.py
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
# 加载环境变量
load_dotenv()
# 初始化大模型
llm = ChatOpenAI(
model="qwen3-max",
temperature=0.7,
openai_api_key=os.getenv("DASHSCOPE_API_KEY"),
openai_api_base=os.getenv("DASHSCOPE_BASE_URL"),
)
print("=" * 60)
print("LangChain 对话记忆完整教程")
print("=" * 60)
# ============================================================
# 第一部分:无记忆对比实验
# ============================================================
print("\n📌 第一部分:无记忆模式(每次都是新对话)")
print("-" * 40)
# 创建不带记忆的提示词模板
no_memory_prompt = ChatPromptTemplate.from_messages([
("human", "用户说:{input}\n请回答:")
])
# 创建不带记忆的链
no_memory_chain = no_memory_prompt | llm | StrOutputParser()
print("\n【第1轮对话】")
print("用户:我叫小明,我喜欢吃披萨")
result1 = no_memory_chain.invoke({"input": "我叫小明,我喜欢吃披萨"})
print(f"AI:{result1}")
print("\n【第2轮对话】")
print("用户:我叫什么名字?我喜欢吃什么?")
result2 = no_memory_chain.invoke({"input": "我叫什么名字?我喜欢吃什么?"})
print(f"AI:{result2}")
print("\n❌ 结论:没有记忆,AI完全忘记了用户之前说的话")
# ============================================================
# 第二部分:有记忆模式(使用 RunnableWithMessageHistory)
# ============================================================
print("\n" + "=" * 60)
print("📌 第二部分:有记忆模式(使用 RunnableWithMessageHistory)")
print("-" * 40)
# 步骤1:定义带历史占位符的提示词模板
memory_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个友好的AI助手,记住用户之前说过的话。"),
MessagesPlaceholder(variable_name="history"), # 历史消息会插入到这里
("human", "{input}")
])
# 步骤2:创建核心链(提示词 + 模型 + 输出解析器)
core_chain = memory_prompt | llm | StrOutputParser()
# 步骤3:创建会话历史存储(模拟数据库)
store = {}
def get_session_history(session_id: str):
"""根据 session_id 获取或创建对话历史"""
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
# 步骤4:用 RunnableWithMessageHistory 包装核心链,使其具备记忆能力
chain_with_memory = RunnableWithMessageHistory(
core_chain, # 核心链
get_session_history, # 获取历史的函数
input_messages_key="input", # 输入参数的 key
history_messages_key="history", # 历史占位符的变量名
)
# 使用固定的 session_id 来维持同一会话
session_id = "user_001"
print("\n【第1轮对话】")
print("用户:我叫小明,我喜欢吃披萨")
response1 = chain_with_memory.invoke(
{"input": "我叫小明,我喜欢吃披萨"},
config={"configurable": {"session_id": session_id}}
)
print(f"AI:{response1}")
print("\n【第2轮对话】")
print("用户:我叫什么名字?我喜欢吃什么?")
response2 = chain_with_memory.invoke(
{"input": "我叫什么名字?我喜欢吃什么?"},
config={"configurable": {"session_id": session_id}}
)
print(f"AI:{response2}")
print("\n✅ 结论:有记忆,AI正确记住了用户的信息!")
# ============================================================
# 第三部分:查看和管理记忆
# ============================================================
print("\n" + "=" * 60)
print("📌 第三部分:查看和管理记忆")
print("-" * 40)
# 获取当前会话的历史记录
current_history = get_session_history(session_id)
print("\n【查看记忆内容】")
print(f"对话历史中共有 {len(current_history.messages)} 条消息")
for i, msg in enumerate(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,我喜欢喝茶"},
]
for user in users:
print(f"\n【用户:{user['name']}】")
print(f"用户:{user['question']}")
response = chain_with_memory.invoke(
{"input": user['question']},
config={"configurable": {"session_id": user['id']}}
)
print(f"AI:{response}")
# 测试记忆
print(f"\n【测试记忆】用户:我叫什么名字?")
test_response = chain_with_memory.invoke(
{"input": "我叫什么名字?"},
config={"configurable": {"session_id": user['id']}}
)
print(f"AI:{test_response}")
# ============================================================
# 第五部分:实战 - 猜数字游戏
# ============================================================
print("\n" + "=" * 60)
print("📌 第五部分:实战 - 猜数字游戏(多轮对话)")
print("-" * 40)
# 创建游戏专用的提示词
game_prompt = ChatPromptTemplate.from_messages([
("system", """你是一个猜数字游戏主持人。
游戏规则:
1. 你想一个1-10之间的整数
2. 用户来猜这个数字
3. 每次用户猜完后,告诉他猜大了、猜小了还是猜中了
4. 猜中后游戏结束,并祝贺用户"""),
MessagesPlaceholder(variable_name="history"),
("human", "{input}")
])
# 创建游戏核心链
game_core = game_prompt | llm | StrOutputParser()
# 游戏会话存储
game_store = {}
def get_game_history(session_id: str):
if session_id not in game_store:
game_store[session_id] = ChatMessageHistory()
return game_store[session_id]
# 创建带记忆的游戏链
game_chain = RunnableWithMessageHistory(
game_core,
get_game_history,
input_messages_key="input",
history_messages_key="history",
)
# 开始游戏
game_session = "game_001"
print("\n🎮 猜数字游戏开始!我心里想了一个1-10之间的数字...")
conversations = [
"我猜5",
"我猜7",
"我猜3",
"我猜8",
]
for user_input in conversations:
print(f"\n用户:{user_input}")
response = game_chain.invoke(
{"input": user_input},
config={"configurable": {"session_id": game_session}}
)
print(f"AI:{response}")
print("\n" + "=" * 60)
print("🎉 教程完成!")
print("=" * 60)
五、代码逐行解析
5.1 核心导入
python
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
导入 作用
ChatPromptTemplate 创建提示词模板
MessagesPlaceholder 在提示词中预留历史消息位置
ChatMessageHistory 存储对话历史的容器
RunnableWithMessageHistory 为核心链添加记忆能力的包装器
5.2 无记忆 vs 有记忆的核心区别
无记忆链:
python
chain = prompt | llm | parser
chain.invoke({"input": "你好"})
有记忆链:
python
# 1. 提示词中要预留 history 位置
prompt = ChatPromptTemplate.from_messages([
MessagesPlaceholder(variable_name="history"), # 关键!
("human", "{input}")
])
# 2. 用 RunnableWithMessageHistory 包装
chain = RunnableWithMessageHistory(
prompt | llm | parser,
get_session_history,
input_messages_key="input",
history_messages_key="history",
)
# 3. 调用时传入 session_id
chain.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 记忆存储机制
python
# 简单的内存存储(演示用)
store = {}
def get_session_history(session_id: str):
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
生产环境可替换为:
· Redis
· PostgreSQL
· 文件存储
六、运行测试
6.1 执行脚本
bash
python memory_demo.py
6.2 预期输出
============================================================
LangChain 对话记忆完整教程
============================================================
📌 第一部分:无记忆模式(每次都是新对话)
----------------------------------------
【第1轮对话】
用户:我叫小明,我喜欢吃披萨
AI:你好小明!披萨很好吃。
【第2轮对话】
用户:我叫什么名字?我喜欢吃什么?
AI:你没有告诉我你的名字和喜好。
❌ 结论:没有记忆,AI完全忘记了用户之前说的话
============================================================
📌 第二部分:有记忆模式(使用 RunnableWithMessageHistory)
----------------------------------------
【第1轮对话】
用户:我叫小明,我喜欢吃披萨
AI:你好小明!很高兴认识你,披萨确实很美味。
【第2轮对话】
用户:我叫什么名字?我喜欢吃什么?
AI:你叫小明,喜欢吃披萨。
✅ 结论:有记忆,AI正确记住了用户的信息!
七、常见问题
Q1:为什么要用 RunnableWithMessageHistory 而不是 ConversationBufferMemory?
对比 ConversationBufferMemory RunnableWithMessageHistory
版本支持 旧版 API 新版推荐
与 LCEL 兼容 ❌ 不兼容 ✅ 完美兼容
多会话管理 麻烦 通过 session_id 轻松实现
持久化扩展 困难 灵活(可接入 Redis 等)
Q2:如何让记忆持久化(重启后还在)?
python
# 将 store 替换为 Redis
import redis
import json
redis_client = redis.Redis(host='localhost', port=6379)
def get_session_history(session_id: str):
data = redis_client.get(f"chat:{session_id}")
history = ChatMessageHistory()
if data:
for msg in json.loads(data):
history.add_message(msg)
return history
Q3:如何限制记忆长度?
python
from langchain_community.chat_message_histories import ChatMessageHistory
# 限制最多保留10条消息
history = ChatMessageHistory()
MAX_MESSAGES = 10
def add_message_with_limit(msg):
history.add_message(msg)
if len(history.messages) > MAX_MESSAGES:
history.messages = history.messages[-MAX_MESSAGES:]
八、总结
本文实现了 LangChain 对话记忆的核心功能:
功能 实现方式 状态
无记忆对话 普通 LCEL 链 ✅
单会话记忆 RunnableWithMessageHistory ✅
多会话管理 不同 session_id ✅
记忆查看 ChatMessageHistory.messages ✅
记忆清空 clear() 方法 ✅
实战应用 猜数字游戏 ✅
核心代码模板:
python
# 5 行代码实现对话记忆
prompt = ChatPromptTemplate.from_messages([MessagesPlaceholder("history"), ("human", "{input}")])
chain = prompt | llm | StrOutputParser()
chain_with_memory = RunnableWithMessageHistory(chain, get_session_history, input_messages_key="input", history_messages_key="history")
response = chain_with_memory.invoke({"input": "你好"}, config={"configurable": {"session_id": "user_001"}})