AI Agent从无到有57: 上下文记忆系统——基于 Redis 的长短期记忆实现
纲要
- 核心挑战:上下文窗口限制、多用户会话隔离
- 技术选型:
Redis、RedisChatMessageHistory - 架构组件:
ConversationBufferMemory、总结链、session_id键空间隔离 - 实现模块:
MemoryManager类、自动压缩触发逻辑 - 关键设计:人格统一的第一人称摘要
- 可运行示例与验证
记忆系统的核心挑战
在构建基于大语言模型的对话智能体时,记忆机制是实现连续、个性化交互的基石。一个健壮的对话记忆系统需要直面并解决两个核心问题:
- 上下文窗口限制:主流大语言模型对单次推理可处理的 Token 数量存在硬性上限(即上下文窗口)。随着对话轮次的增加,原始的历史消息逐条累积,极易触及窗口边界,导致早期关键信息被截断,或引发请求错误。
- 多用户会话隔离:在生产环境中,智能体需同时服务大量独立用户。不同用户的对话历史、个人偏好和任务状态必须严格隔离,防止发生信息“串扰”,即一个用户的数据被错误地关联到另一个用户的会话中。
一个设计良好的记忆系统,其核心目标是在“尽可能保留完整历史信息”与“严格控制在上下文窗口限制内”之间取得动态平衡,同时确保用户间数据的绝对隔离。
技术选型:为何选择 Redis
在众多存储方案中,Redis 作为内存数据结构存储系统,在对话记忆场景下具备显著优势:
- 极速读写:基于内存的操作特性,使其能够轻松应对高并发、高频次的消息存取压力,满足实时交互的延迟要求。
- 数据结构匹配:对话历史本质上是按时间顺序排列的消息序列,与
Redis的 List(列表)数据结构在逻辑上高度契合,可实现高效的消息追加和范围读取。 - 原生键空间隔离:通过设计合理的 Key 策略(如
conversation:{session_id}),即可轻松实现多用户、多会话的数据隔离,逻辑清晰且操作高效。
LangChain 生态官方提供了 RedisChatMessageHistory 组件,它封装了将对话消息以标准格式(如 HumanMessage、AIMessage)存取至 Redis 的底层逻辑,使开发者无需手动处理序列化与反序列化细节。
系统架构设计
本方案的核心工作流如下:每次读取记忆时,系统会检查当前会话的消息总量。若条数超过预设的阈值(例如 80 条),则触发一个“总结链”,将全部历史消息压缩为一段结构化摘要。随后,系统会清空该会话的原始消息列表,仅保留该摘要作为后续对话的长期记忆锚点。此机制确保了上下文窗口长期处于可控状态,同时最大限度地留存了关键信息。
完整实现方案
本文实现基于以下技术栈版本:
langchain≈ 0.3.xlangchain-openai≈ 0.2.xlangchain-community≈ 0.3.xredis>= 5.0.0python-dotenv>= 1.0.0
项目结构
memory_agent_demo/
├── config.py # 配置管理
├── memory_manager.py # 核心记忆管理器
├── main.py # 示例主程序
└── .env # 环境变量
核心依赖安装
pip install langchain langchain-openai langchain-community redis python-dotenv
配置管理模块 config.py
该模块负责从环境变量中加载配置,确保敏感信息(如 API Key)与代码分离。
# config.py
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
OPENAI_API_KEY: str = os.getenv("OPENAI_API_KEY", "")
OPENAI_BASE_URL: str = os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
REDIS_URL: str = os.getenv("REDIS_URL", "redis://localhost:6379/0")
# 触发摘要压缩的历史消息数量阈值
MAX_HISTORY_LENGTH: int = int(os.getenv("MAX_HISTORY_LENGTH", "80"))
核心记忆管理器 memory_manager.py
MemoryManager 类封装了记忆读取、写入和自动压缩的核心逻辑。它与 LangChain 的记忆组件深度集成,对外提供简洁的 API。
- 初始化与总结链构建:在
__init__方法中,除了初始化 LLM 客户端,还构建了一条专用的“总结链”。该链的核心提示词模板会将系统提示词注入摘要生成过程,确保压缩后的摘要仍以智能体的第一人称视角叙述,从而维持角色人格的统一性。 - 记忆读取与自动压缩:
get_memory方法是整个系统的关键。它首先通过_get_history获取RedisChatMessageHistory实例。当消息数量超过MAX_HISTORY_LENGTH时,会触发压缩流程:格式化所有历史消息 -> 调用总结链生成摘要 -> 清空 Redis 列表 -> 将摘要作为新的对话上下文存入 Redis。 - 记忆写入:
save_memory方法负责将每一轮的用户输入和 AI 输出实时追加到 Redis 列表中。
# memory_manager.py
from typing import List, Optional
from langchain.memory import ConversationBufferMemory
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.messages import BaseMessage
from config import Config
class MemoryManager:
"""具备自动摘要压缩能力的对话记忆管理器。"""
def __init__(self, system_prompt: str = ""):
self.memory_key = "chat_history"
self.system_prompt = system_prompt
self.llm = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0.0,
openai_api_key=Config.OPENAI_API_KEY,
base_url=Config.OPENAI_BASE_URL,
)
# 构建总结链
# 注意:将系统提示词嵌入摘要模板,以保持角色人格统一
summary_template = (
system_prompt + "\n\n"
"以下是你和用户之间的对话记录。请以第一人称「我」的视角,对这段对话进行详细总结,"
"需提取所有关键信息、用户的核心诉求以及你给出的处理结果。\n"
"对话记录:\n{chat_history}\n\n摘要:"
)
self.summary_prompt = ChatPromptTemplate.from_template(summary_template)
self.summary_chain = self.summary_prompt | self.llm | StrOutputParser()
def _get_history(self, session_id: str) -> RedisChatMessageHistory:
"""获取指定会话 ID 对应的 Redis 历史记录管理器。"""
return RedisChatMessageHistory(
session_id=session_id,
url=Config.REDIS_URL,
# key_prefix 可选,用于区分不同应用或场景
)
def _format_messages(self, messages: List[BaseMessage]) -> str:
"""将消息列表格式化为供 LLM 处理的纯文本。"""
chat_lines = []
for msg in messages:
role = "用户" if msg.type == "human" else "助手"
chat_lines.append(f"{role}: {msg.content}")
return "\n".join(chat_lines)
def get_memory(self, session_id: str) -> ConversationBufferMemory:
"""获取会话记忆,并在消息超限时自动触发压缩。"""
history = self._get_history(session_id)
messages = history.messages
if len(messages) > Config.MAX_HISTORY_LENGTH:
# 1. 格式化历史消息
chat_text = self._format_messages(messages)
# 2. 调用总结链生成摘要
summary = self.summary_chain.invoke({"chat_history": chat_text})
# 3. 清空 Redis 中该会话的所有旧消息
history.clear()
# 4. 将摘要作为新的上下文存入 Redis
# 通过 user -> assistant 的对话形式,使摘要自然融入对话流
history.add_user_message("【历史对话摘要】")
history.add_ai_message(summary)
# 返回 LangChain 标准记忆对象
# 设置 return_messages=True 确保返回的是消息对象列表,而非字符串
return ConversationBufferMemory(
memory_key=self.memory_key,
chat_memory=history,
return_messages=True,
)
def save_memory(self, session_id: str, user_input: str, ai_output: str) -> None:
"""保存一轮新的对话到记忆存储中。"""
history = self._get_history(session_id)
history.add_user_message(user_input)
history.add_ai_message(ai_output)
示例主程序 main.py
该脚本模拟了多用户场景,用于验证会话隔离和自动压缩功能。它定义了一个简单的天气查询工具,并分别为两个用户创建了独立的智能体执行器。
# main.py
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.tools import tool
from config import Config
from memory_manager import MemoryManager
@tool
def get_weather(city: str) -> str:
"""查询指定城市的实时天气情况。"""
return f"{city} 当前天气晴朗,气温 25°C。"
tools = [get_weather]
def run_agent_for_user(user_id: str, memory_manager: MemoryManager):
"""为指定用户创建并运行一个完整的对话 Agent。"""
print(f"\n--- 开始新会话: {user_id} ---")
# 1. 获取该用户的独立记忆
memory = memory_manager.get_memory(user_id)
# 2. 构建提示词模板
system_prompt = "你是小浪助手,一位友好且知识渊博的助理。"
prompt = ChatPromptTemplate.from_messages([
("system", system_prompt),
MessagesPlaceholder(variable_name="chat_history"),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
# 3. 创建 Agent 和执行器
llm = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0.0,
openai_api_key=Config.OPENAI_API_KEY,
base_url=Config.OPENAI_BASE_URL,
)
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(
agent=agent,
tools=tools,
memory=memory,
verbose=False,
handle_parsing_errors=True,
)
# 4. 模拟多轮对话
# 第一轮:自我介绍,让 Agent 记住用户名字
input1 = f"你好,我叫 {user_id}。请记住我的名字。"
response1 = executor.invoke({"input": input1})
print(f"用户: {input1}")
print(f"助手: {response1['output']}")
memory_manager.save_memory(user_id, input1, response1['output'])
# 第二轮:询问名字,测试记忆能力
input2 = "你还记得我叫什么名字吗?"
response2 = executor.invoke({"input": input2})
print(f"用户: {input2}")
print(f"助手: {response2['output']}")
memory_manager.save_memory(user_id, input2, response2['output'])
# 第三轮:查询天气,测试工具调用
input3 = "北京今天天气怎么样?"
response3 = executor.invoke({"input": input3})
print(f"用户: {input3}")
print(f"助手: {response3['output']}")
memory_manager.save_memory(user_id, input3, response3['output'])
# 输出该用户的 Redis 消息总数,用于观察压缩是否触发
history = memory_manager._get_history(user_id)
print(f"--- {user_id} 当前 Redis 中存储的消息总数: {len(history.messages)} ---")
def main():
# 初始化全局记忆管理器(不同的用户会通过 session_id 自然隔离)
memory_manager = MemoryManager(system_prompt="你是小浪助手。")
# 模拟两个不同用户
run_agent_for_user("alice", memory_manager)
run_agent_for_user("bob", memory_manager)
if __name__ == "__main__":
main()
运行与验证
- 启动 Redis 服务:确保本地或远程的 Redis 服务已运行,且
REDIS_URL配置正确(例如,redis://localhost:6379/0)。 - 配置环境变量:在项目根目录创建
.env文件,填入有效的 OpenAI API 密钥。示例内容如下:
OPENAI_API_KEY="your-api-key"
OPENAI_BASE_URL="your-base-url" # 可选
REDIS_URL="redis://localhost:6379/0"
MAX_HISTORY_LENGTH=80
- 执行程序:在终端中运行
python main.py。 - 观察输出:可以看到两个用户(
alice和bob)的对话完全独立,Agent 能够准确记住各自的名字。通过调整MAX_HISTORY_LENGTH为一个较小值(如 3),可以快速触发压缩逻辑并观察其效果。
API 速览
| 组件 / API | 所属库 | 说明 |
|---|---|---|
RedisChatMessageHistory |
langchain_community.chat_message_histories |
核心存储适配器,实现了将对话消息持久化至 Redis 列表的增删改查。关键参数:session_id (会话ID), url (Redis连接字符串)。 |
ConversationBufferMemory |
langchain.memory |
基础记忆缓冲区,用于在内存中缓存对话历史。本方案中将其 chat_memory 参数设为 RedisChatMessageHistory 实例,实现了内存与持久化存储的结合。 |
ChatPromptTemplate.from_template |
langchain.prompts |
用于从模板字符串创建提示词模板,支持变量插值。 |
StrOutputParser |
langchain_core.output_parsers |
标准输出解析器,用于将 LLM 的生成结果解析为字符串。 |
参考文档
- 官方文档
- LangChain 记忆模块官方指南: https://python.langchain.com/docs/modules/memory/
- RedisChatMessageHistory API 参考: https://python.langchain.com/docs/integrations/memory/redis_chat_message_history
- Redis 官方文档: https://redis.io/documentation
- 参考链接
- OpenAI API 文档: https://platform.openai.com/docs/api-reference
- LangChain Agent 模块官方指南: https://python.langchain.com/docs/modules/agents/
总结
本文详细剖析了构建智能体长期记忆系统的关键技术路径,并提供了一个基于 Redis 和 LangChain 的完整实现。该方案通过巧妙的“触发式摘要压缩”机制,有效解决了上下文窗口受限的痛点,同时利用 session_id 实现了严格的会话隔离。
特别地,通过在摘要生成提示词中注入系统角色设定,确保了压缩后的记忆仍能保持智能体的统一人格。此设计模式可直接应用于生产环境的单智能体项目,为构建具有长时记忆能力的对话应用提供了坚实的技术基座。
更多推荐


所有评论(0)