LangChain 记忆系统


目录

  1. 记忆系统(Memory)

    • 问题引入:无记忆的对话

    • 基础方案:InMemoryChatMessageHistory

    • 进阶方案:RunnableWithMessageHistory

    • 持久化方案:RedisChatMessageHistory


1. 记忆系统(Memory)

1.1 问题引入:无记忆的对话

核心问题

大语言模型本身是无状态的。每次对话请求都是独立的,模型不会自动记住之前的对话内容。

代码演示
from langchain.chat_models import init_chat_model
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
import os
​
# 初始化模型
llm = init_chat_model(
    model="qwen-plus",
    model_provider="openai",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
​
# 构建简单链
prompt = PromptTemplate.from_template("请回答我的问题:{question}")
parser = StrOutputParser()
chain = prompt | llm | parser
​
# 第一轮对话
print(chain.invoke({"question": "我叫张三,你叫什么?"}))
​
# 第二轮对话
print(chain.invoke({"question": "你知道我是谁吗?"}))
输出结果
# 第一轮:模型知道"我叫张三"
你好,张三!我叫通义千问(Qwen),是阿里云研发的超大规模语言模型。
​
# 第二轮:模型"遗忘"了
不知道你是谁。我是一个AI助手,没有能力识别或获取用户的身份信息。
问题分析
┌─────────────────────────────────────────────────────────┐
│                    独立调用(无记忆)                     │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  invoke({"question": "我叫张三..."})  ──►  LLM ──► 输出  │
│                          │                              │
│                          ▼                              │
│  invoke({"question": "你知道..."})  ──► LLM ──► 输出    │
│                                                         │
│  ⚠️ 两次调用完全独立,模型无法跨请求记住上下文             │
└─────────────────────────────────────────────────────────┘

1.2 基础方案:InMemoryChatMessageHistory

核心思想

手动将对话历史存储在内存中,每次调用时将历史消息一起发送给模型。

原理图
┌─────────────────────────────────────────────────────────┐
│              手动管理历史消息(InMemory)                 │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   history.add_user_message("我叫张三...")               │
│                        │                                │
│                        ▼                                │
│   ┌─────────────────────────────────┐                   │
│   │  InMemoryChatMessageHistory     │                   │
│   │  ├── HumanMessage: 我叫张三...  │                   │
│   │  └── AIMessage: 我是通义千问... │                   │
│   └─────────────────────────────────┘                   │
│                        │                                │
│                        ▼                                │
│   llm.invoke(history.messages)  ──► LLM                 │
│                                                         │
└─────────────────────────────────────────────────────────┘
完整代码
from langchain.chat_models import init_chat_model
from langchain_core.chat_history import InMemoryChatMessageHistory
from loguru import logger
import os
from dotenv import load_dotenv
load_dotenv(encoding='utf-8')
​
# 初始化模型
llm = init_chat_model(
    model="qwen-plus",
    model_provider="openai",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
​
# 创建内存历史记录
history = InMemoryChatMessageHistory()
​
# ===== 第一轮对话 =====
# 添加用户消息
history.add_user_message("我叫张三,我的爱好是学习")
# 调用模型
ai_message = llm.invoke(history.messages)
logger.info(f"第一次回答\n{ai_message.content}")
# 将AI回复添加到历史
history.add_message(ai_message)
​
# ===== 第二轮对话 =====
# 添加新用户消息
history.add_user_message("我叫什么?我的爱好是什么?")
# 再次调用模型(携带完整历史)
ai_message2 = llm.invoke(history.messages)
logger.info(f"第二次回答\n{ai_message2.content}")
history.add_message(ai_message2)
​
# 查看完整历史
for message in history.messages:
    logger.info(message.content)
输出示例
第一次回答
你好,张三!很高兴认识你!学习是一个非常棒的习惯...
​
第二次回答
你叫张三,你的爱好是学习。
优点与局限
优点 局限
简单直接 程序重启后历史丢失
适合单次会话 无法跨进程共享
调试方便 不适合生产环境

1.3 进阶方案:RunnableWithMessageHistory

文件

  • 07_memory/Memory_RunnableWithMessageHistory.py(全局 history 对象)

  • 07_memory/Memory_RunnableWithMessageHistoryV2.py(store 字典管理多会话)

核心思想

使用 RunnableWithMessageHistory 装饰器自动管理历史消息,无需手动调用 add_user_message()add_message()

方案一:全局 history 对象

文件Memory_RunnableWithMessageHistory.py

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableWithMessageHistory, RunnableConfig
from langchain.chat_models import init_chat_model
from langchain_core.chat_history import InMemoryChatMessageHistory
from loguru import logger
import os
from dotenv import load_dotenv
load_dotenv(encoding='utf-8')
​
# 初始化模型
llm = init_chat_model(
    model="qwen-plus",
    model_provider="openai",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
​
# 定义 Prompt(使用 MessagesPlaceholder 注入历史)
prompt = ChatPromptTemplate.from_messages([
    MessagesPlaceholder(variable_name="history"),  # 历史消息注入点
    ("human", "{input}")                            # 用户当前输入
])
​
# 构建链
parser = StrOutputParser()
chain = prompt | llm | parser
​
# 创建内存历史
history = InMemoryChatMessageHistory()
​
# 包装为带历史的 Runnable
runnable = RunnableWithMessageHistory(
    chain,
    get_session_history=lambda session_id: history,  # 获取历史的函数
    input_messages_key="input",                       # 用户输入的 key
    history_messages_key="history"                    # 历史消息的 key
)
​
# 清空历史(可选)
history.clear()
​
# 配置会话
config = RunnableConfig(configurable={"session_id": "user-001"})
​
# 对话
logger.info(runnable.invoke({"input": "我叫张三,我爱好学习。"}, config))
logger.info(runnable.invoke({"input": "我叫什么?爱好是什么?"}, config))
方案二:使用 store 管理多会话

文件Memory_RunnableWithMessageHistoryV2.py

from langchain.chat_models import init_chat_model
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
import os
from dotenv import load_dotenv
load_dotenv(encoding='utf-8')
​
# 初始化模型
llm = init_chat_model(
    model="qwen-plus",
    model_provider="openai",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
​
# 全局会话存储(支持多用户/多会话)
store = {}
​
def get_session_history(session_id: str):
    """根据 session_id 获取或创建历史记录"""
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]
​
# 定义 Prompt
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个友好的中文助理,会根据上下文回答问题。"),
    MessagesPlaceholder("history"),  # 注入历史消息
    ("human", "{question}")          # 当前用户输入
])
​
# 构建链
memory_chain = prompt | llm | StrOutputParser()
​
# 包装为带历史的 Runnable
with_history = RunnableWithMessageHistory(
    memory_chain,
    get_session_history,                    # 函数:获取/创建历史
    input_messages_key="question",           # prompt 中用户输入的变量名
    history_messages_key="history"           # prompt 中历史消息的变量名
)
​
# 使用 session_id 区分不同用户
cfg = {"configurable": {"session_id": "user-001"}}
​
# 第一次对话
print("用户:我叫张三。")
print("AI:", with_history.invoke({"question": "我叫张三。"}, cfg))
​
# 第二次对话(模型记得之前的内容)
print("\n用户:我叫什么?")
print("AI:", with_history.invoke({"question": "我叫什么?"}, cfg))
原理图
┌─────────────────────────────────────────────────────────┐
│         RunnableWithMessageHistory 工作流程              │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   用户输入: "我叫什么?"                                  │
│            │                                            │
│            ▼                                           │
│   ┌─────────────────────────────────┐                   │
│   │  MessagesPlaceholder("history") │ ◄── 历史消息自动注入 │
│   └─────────────────────────────────┘                   │
│            │                                            │
│            ▼                                           │
│   ┌─────────────────────────────────┐                   │
│   │  ChatPromptTemplate             │                   │
│   │  ├── system: 你是一个友好助理   │                   │
│   │  ├── history: [张三说过的话...] │                   │
│   │  └── human: 我叫什么?          │                   │
│   └─────────────────────────────────┘                   │
│            │                                            │
│            ▼                                           │
│   ┌─────────────────────────────────┐                   │
│   │  LLM (qwen-plus)                │                   │
│   └─────────────────────────────────┘                   │
│            │                                            │
│            ▼                                           │
│   ┌─────────────────────────────────┐                   │
│   │  输出: "你叫张三"                │                   │
│   └─────────────────────────────────┘                   │
│            │                                            │
│            ▼                                           │
│   ┌─────────────────────────────────┐                   │
│   │  自动保存到历史记录              │                   │
│   └─────────────────────────────────┘                   │
│                                                         │
└─────────────────────────────────────────────────────────┘
核心组件说明
组件 作用
MessagesPlaceholder("history") 模板中的占位符,历史消息会自动填充到这里
input_messages_key 指定用户当前输入在 prompt 中的变量名
history_messages_key 指定历史消息在 prompt 中的变量名(与占位符名称一致)
get_session_history 函数,根据 session_id 返回对应的历史记录对象

1.4 持久化方案:RedisChatMessageHistory

核心思想

使用 Redis 作为持久化存储,实现跨进程、跨会话的记忆保存。

原理图
┌─────────────────────────────────────────────────────────┐
│              Redis 持久化记忆系统                        │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   ┌──────────┐         ┌─────────────┐                  │
│   │  Client  │ ──────► │   Redis     │                  │
│   └──────────┘         │  (持久化)   │                  │
│                        └─────────────┘                  │
│                              ▲                          │
│                              │                          │
│   ┌──────────────────────────────────┐                 │
│   │  RedisChatMessageHistory         │                 │
│   │  • session_id: "user-001"         │                 │
│   │  • 自动从 Redis 读写消息          │                 │
│   └──────────────────────────────────┘                 │
│                                                         │
└─────────────────────────────────────────────────────────┘
完整代码
from langchain.chat_models import init_chat_model
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableConfig
import os
import redis
from loguru import logger
from dotenv import load_dotenv
load_dotenv(encoding='utf-8')
​
# Redis 连接配置
REDIS_URL = "redis://localhost:26379"
redis_client = redis.Redis.from_url(REDIS_URL, decode_responses=True)
​
# 初始化模型
llm = init_chat_model(
    model="qwen-plus",
    model_provider="openai",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
​
# 定义 Prompt
prompt = ChatPromptTemplate.from_messages([
    MessagesPlaceholder("history"),
    ("human", "{question}")
])
​
def get_session_history(session_id: str) -> RedisChatMessageHistory:
    """获取或创建 Redis 中的会话历史"""
    return RedisChatMessageHistory(
        session_id=session_id,
        url=REDIS_URL,
        # ttl=3600  # 可选:设置过期时间(秒)
    )
​
# 创建带历史的链
chain = RunnableWithMessageHistory(
    prompt | llm,
    get_session_history,
    input_messages_key="question",
    history_messages_key="history"
)
​
# 配置会话
config = RunnableConfig(configurable={"session_id": "user-001"})
​
# 对话循环
print("开始对话(输入 'quit' 退出)")
while True:
    question = input("\n输入问题:")
    if question.lower() in ['quit', 'exit', 'q']:
        break
    response = chain.invoke({"question": question}, config)
    logger.info(f"AI回答: {response.content}")
    redis_client.save()  # 强制持久化到 dump.rdb
Redis 环境检查

文件07_memory/RedisEnvCheck.py

import redis
​
try:
    print(f"redis 版本: {redis.__version__}")
    print("✅ redis 包导入成功!")
except ModuleNotFoundError:
    print("❌ 未找到 redis 包,请先安装: pip install redis==5.3.1")
记忆系统对比总结
方案 存储 持久化 适用场景
InMemoryChatMessageHistory 内存 开发调试、一次性会话
RunnableWithMessageHistory + InMemory 内存 单进程多会话
RedisChatMessageHistory Redis 生产环境、分布式部署

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐