LangChain 框架-长期记忆能力
特性Redis 示例MySQL 示例存储类型内存数据库(快速读写)关系型数据库(结构化、持久)适用场景缓存、实时性强的应用需要事务、备份、审计的场景部署要求安装 Redis 服务安装 MySQL 并配置权限扩展性易于集群扩展支持主从复制、分库分表数据结构简单键值对表格形式,支持 JOIN 查询自动建表❌✅(首次运行时自动创建)通过结合或,可以在 LangChain 中轻松实现基于会话的长期记忆功能
LangChain 框架中,结合不同的后端存储系统(Redis 和 MySQL),为大模型对话链(LLM Chain)添加长期记忆能力,即:会话历史持久化,使得模型能够在多次交互中记住上下文信息。
一、Redis
Redis(Remote Dictionary Server)是一个开源的、基于内存的高性能键值(key-value)数据库,常被用作缓存、消息队列和实时数据处理系统。它支持多种数据结构,如字符串(Strings)、哈希(Hashes)、列表(Lists)、集合(Sets)、有序集合(Sorted Sets)、位图(Bitmaps)、HyperLogLogs 和地理空间索引(Geospatial Indexes)等。
以下是 Redis 的一些核心特性和常见使用场景:
核心特性
- 内存存储:数据主要存储在内存中,读写速度极快(通常微秒级响应)。
- 持久化支持:
- RDB(快照):定期将内存中的数据保存到磁盘。
- AOF(Append Only File):记录每个写操作,可重放恢复数据。
- 高可用与扩展:
- 主从复制(Replication)
- 哨兵模式(Sentinel)实现自动故障转移
- 集群模式(Redis Cluster)支持分片和水平扩展
- 丰富的数据类型:支持复杂数据结构操作,减少应用层逻辑。
- 原子性操作:所有 Redis 操作都是原子性的,支持 Lua 脚本保证多命令原子执行。
- 发布/订阅(Pub/Sub):轻量级消息通信机制。
- 事务支持:通过
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 查询 |
| 自动建表 | ❌ | ✅(首次运行时自动创建) |
五、关键技术要点提炼
-
RunnableWithMessageHistory是关键封装器- 把普通链变成“带历史记忆”的链。
- 依赖外部存储(Redis/SQL)保存消息。
-
session_id是唯一标识- 控制哪些消息属于同一个对话流。
- 不同
session_id互不影响。
-
config={"configurable": {"session_id": "..."}}必须传入- 用于指定当前请求属于哪个会话。
-
支持多种后端存储
- LangChain 支持:Redis、Elasticsearch、MongoDB、SQLite、MySQL 等。
- 选择依据:性能需求、运维成本、是否需要结构化查询。
-
消息结构清晰
input_messages_key:输入字段名(如 "question" 或 "input")history_messages_key:历史消息字段名(通常是 "history")
六、应用建议
- 如果追求高性能、低延迟 → 优先选 Redis
- 如果需要数据分析、备份恢复、权限管理 → 选 MySQL
- 生产环境中建议配合 Redis + MySQL:Redis 作缓存层,MySQL 作归档层
- 对敏感数据,注意加密传输和访问控制(特别是数据库密码)
总结:
通过
RunnableWithMessageHistory结合RedisChatMessageHistory或SQLChatMessageHistory,可以在 LangChain 中轻松实现基于会话的长期记忆功能,让大模型具备持续对话的能力,并根据实际需求选择合适的后端存储方案。
更多推荐


所有评论(0)