LangChain 对话记忆完整教程

目录

  1. 什么是对话记忆
  2. 为什么需要对话记忆
  3. 环境搭建与依赖安装
  4. 完整代码实现
  5. 代码逐行解析
  6. 运行测试
  7. 常见问题
  8. 总结

一、什么是对话记忆

对话记忆(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"}})

相关推荐
m0_475064501 小时前
langchain demo
langchain
Trouvaille ~3 小时前
零基础入门 LangChain 与 LangGraph(九):LangGraph 收官——运行时上下文、流式输出、子图、与项目结构
数据库·langchain·agent·streaming·langgraph·ai应用开发·运行上下文
倦王3 小时前
langchain 尚硅谷day4-5 记忆缓存部分!
langchain
Irissgwe4 小时前
LangChain之聊天模型核心能力(二)
人工智能·langchain·llm·langgraph
墨染天姬4 小时前
[AI]ai应用框架LangChain
人工智能·langchain
FrontAI4 小时前
深入浅出 LangGraph —— 第8章:人机交互:中断与审批流程
人工智能·langchain·人机交互·ai agent·langgraph
深海鱼在掘金14 小时前
深入浅出 LangChain — 第一章:AI Agent 开发导论
typescript·langchain·agent
深海鱼在掘金14 小时前
深入浅出 LangChain — 导读
typescript·langchain·agent
dinl_vin20 小时前
LangChain 系列·(四):RAG 基础——给大模型装上“外脑“
人工智能·算法·langchain