LangChain 框架中,结合不同的后端存储系统(Redis 和 MySQL),为大模型对话链(LLM Chain)添加长期记忆能力,即:会话历史持久化,使得模型能够在多次交互中记住上下文信息。

一、Redis

Redis(Remote Dictionary Server)是一个开源的、基于内存的高性能键值(key-value)数据库,常被用作缓存、消息队列和实时数据处理系统。它支持多种数据结构,如字符串(Strings)、哈希(Hashes)、列表(Lists)、集合(Sets)、有序集合(Sorted Sets)、位图(Bitmaps)、HyperLogLogs 和地理空间索引(Geospatial Indexes)等。

以下是 Redis 的一些核心特性和常见使用场景:


 核心特性

  1. 内存存储:数据主要存储在内存中,读写速度极快(通常微秒级响应)。
  2. 持久化支持
    • RDB(快照):定期将内存中的数据保存到磁盘。
    • AOF(Append Only File):记录每个写操作,可重放恢复数据。
  3. 高可用与扩展
    • 主从复制(Replication)
    • 哨兵模式(Sentinel)实现自动故障转移
    • 集群模式(Redis Cluster)支持分片和水平扩展
  4. 丰富的数据类型:支持复杂数据结构操作,减少应用层逻辑。
  5. 原子性操作:所有 Redis 操作都是原子性的,支持 Lua 脚本保证多命令原子执行。
  6. 发布/订阅(Pub/Sub):轻量级消息通信机制。
  7. 事务支持:通过 MULTI / EXEC 实现简单事务(但不支持回滚)。

 常见使用场景

  • 缓存:减轻数据库压力,提升响应速度(如网页缓存、会话缓存)。
  • 分布式锁:利用 SET key value NX EX 实现跨服务互斥。
  • 排行榜/计数器:利用 Sorted Set 实现实时排名(如游戏积分榜)。
  • 限流:结合滑动窗口或令牌桶算法限制 API 请求频率。
  • 消息队列:使用 List 或 Stream 实现轻量级任务队列。
  • 会话存储:Web 应用中集中管理用户会话状态。

二、Langchain核心组件说明

组件 作用
ChatPromptTemplate 构建提示词模板,支持变量插入(如历史消息)
MessagesPlaceholder 占位符,用于注入历史对话记录
RunnableWithMessageHistory 将普通链封装成支持“带历史”的可运行链
RedisChatMessageHistory / SQLChatMessageHistory 提供持久化的会话历史存储
ChatOllama 使用 Ollama 运行本地 LLM(如 qwen3:4b)

三、各代码示例解析

技术点 说明
RedisChatMessageHistory 使用 Redis 存储对话历史,支持持久化和跨请求共享
RunnableWithMessageHistory LangChain 提供的封装器,用于实现带历史的记忆链
session_id 区分不同用户的唯一标识,必须保持一致才能延续对话
config={"configurable": {}} 必须传入,否则无法识别会话
MessagesPlaceholder 在 prompt 中预留位置插入历史消息

示例1:使用 Redis 存储聊天历史

from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_ollama import ChatOllama

# 初始化大语言模型 (LLM)
llm = ChatOllama(model="qwen3:4b")

# 构建提示模板
prompt = ChatPromptTemplate([
    ("system", "你是一名专业的旅游规划师"),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{input}")
])

# 构建基础 Chain
chain = prompt | llm

# 添加消息历史功能(关键步骤)
chain_with_history = RunnableWithMessageHistory(
    chain,
    lambda session_id: RedisChatMessageHistory(
        session_id=session_id,
        url="redis://localhost:6379/0"
    ),
    input_messages_key="input",
    history_messages_key="history"
)

# 第一次调用(提问) 
res1 = chain_with_history.invoke(
    {"input": "上海那些地方好玩"},
    config={"configurable": {"session_id": "532"}}
)
print(res1)

print('\n' + "=" * 100)

# 第二次调用(追问)
# 因为使用了相同的 session_id="532",系统能读取之前的历史并理解上下文。
res2 = chain_with_history.invoke(
    {"input": "刚才推荐的地方中那个最受欢迎"},
    config={"configurable": {"session_id": "532"}}
)
print(res2)
3.1构建提示模板(Prompt Template)
prompt = ChatPromptTemplate([
    ("system", "你是一名专业的旅游规划师"),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{input}")
])
  • 定义了一个聊天机器人角色:“旅游规划师”。
  • MessagesPlaceholder("history") 表示此处将插入之前的对话历史。
  • {input} 是用户当前输入的问题。

 这样设计可以确保每次生成回复时都包含上下文。

3.2添加历史记忆能力(核心)
chain_with_history = RunnableWithMessageHistory(
    chain,
    lambda session_id: RedisChatMessageHistory(
        session_id=session_id,
        url="redis://localhost:6379/0"
    ),
    input_messages_key="input",
    history_messages_key="history"
)
关键参数说明:
参数 作用
chain 基础链
lambda session_id: ... 根据 session_id 获取对应的会话历史存储实例
url="redis://..." Redis 地址,使用默认端口 6379,数据库 0
input_messages_key="input" 用户输入字段名(在 invoke 中传入的 key)
history_messages_key="history" 存储历史消息的字段名

实现了:同一 session_id 下的所有请求共享历史记录

 3.3功能:
  • 使用 Redis 作为会话历史的持久化存储。
  • 所有具有相同 session_id 的请求将共享同一份聊天历史。
  • 实现了跨请求的上下文连续性
3.4关键点:
  • url="redis://..." 指定 Redis 地址和数据库编号。
  • session_id 是区分不同用户或会话的关键标识。

示例2:使用 MySQL 存储聊天历史

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_ollama import ChatOllama
from langchain_core.runnables.history import RunnableWithMessageHistory # 给普通的链(chain)添加消息历史管理能力
from langchain_community.chat_message_histories import SQLChatMessageHistory

llm = ChatOllama(model="qwen3:4b")

prompt = ChatPromptTemplate([
    ("system", "你是一名专业的旅游规划师"),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{input}")
])

chain = prompt | llm

chain_with_history = RunnableWithMessageHistory(
    chain,
    lambda session_id: SQLChatMessageHistory(
        session_id=session_id,
        connection="mysql+pymysql://root:XXXX@localhost:3306/test_db",
        input_messages_key="input",
        history_messages_key="history"
    )
)

res1 = chain_with_history.invoke({"input": "上海那里好玩"}, config={"configurable": {"session_id": "u123"}})
print(res1)

print("\n\n" + "=" * 100)

res2 = chain_with_history.invoke({"input": "我刚问的问题中最推荐哪一个地点"}, config={"configurable": {"session_id": "u123"}})
print(res2)
功能:
  • 使用 MySQL 存储会话历史。
  • 适用于需要复杂查询、权限控制或高可靠性的生产环境。
  • 同样通过 session_id 来关联上下文。
关键点:
  • 使用 SQLAlchemy 风格的连接字符串:mysql+pymysql://...
  • 要求数据库中已存在对应的表结构(由 SQLChatMessageHistory 自动创建)

四、总结对比

特性 Redis 示例 MySQL 示例
存储类型 内存数据库(快速读写) 关系型数据库(结构化、持久)
适用场景 缓存、实时性强的应用 需要事务、备份、审计的场景
部署要求 安装 Redis 服务 安装 MySQL 并配置权限
扩展性 易于集群扩展 支持主从复制、分库分表
数据结构 简单键值对 表格形式,支持 JOIN 查询
自动建表 ✅(首次运行时自动创建)

五、关键技术要点提炼

  1. RunnableWithMessageHistory 是关键封装器

    • 把普通链变成“带历史记忆”的链。
    • 依赖外部存储(Redis/SQL)保存消息。
  2. session_id 是唯一标识

    • 控制哪些消息属于同一个对话流。
    • 不同 session_id 互不影响。
  3. config={"configurable": {"session_id": "..."}} 必须传入

    • 用于指定当前请求属于哪个会话。
  4. 支持多种后端存储

    • LangChain 支持:Redis、Elasticsearch、MongoDB、SQLite、MySQL 等。
    • 选择依据:性能需求、运维成本、是否需要结构化查询。
  5. 消息结构清晰

    • input_messages_key:输入字段名(如 "question" 或 "input")
    • history_messages_key:历史消息字段名(通常是 "history")

六、应用建议

  • 如果追求高性能、低延迟 → 优先选 Redis
  • 如果需要数据分析、备份恢复、权限管理 → 选 MySQL
  • 生产环境中建议配合 Redis + MySQL:Redis 作缓存层,MySQL 作归档层
  • 对敏感数据,注意加密传输和访问控制(特别是数据库密码)

总结:

通过 RunnableWithMessageHistory 结合 RedisChatMessageHistorySQLChatMessageHistory,可以在 LangChain 中轻松实现基于会话的长期记忆功能,让大模型具备持续对话的能力,并根据实际需求选择合适的后端存储方案。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐