缓冲窗口记忆组件学习笔记
一、是什么
缓冲窗口记忆组件(Buffer Window Memory)是一种带有限容量的对话历史管理机制,它能够:
- 保存对话历史:将用户和AI的对话消息按照时间顺序存储
- 限制Token数量:当对话历史的总Token数超过设定的阈值时,自动删除最旧的消息
- 动态管理:在每次添加新消息后检查并调整历史记录,确保不会超出Token限制
二、有什么用
核心价值
-
节省API成本
- LLM API按照Token数量计费,限制历史Token数可以有效控制成本
- 避免每次请求都传递全部历史记录
-
提升响应速度
- 减少每次请求的输入Token数量,加快模型推理速度
- 降低网络传输延迟
-
避免超出上下文窗口
- 每个模型都有最大Token限制(如4k、8k、32k等)
- 防止历史记录过多导致超出模型处理能力
-
保持对话连贯性
- 在Token限制内保留最近的对话内容
- 确保AI能够理解最近的上下文
适用场景
- 长时间对话场景(如智能客服)
- 需要控制成本的大规模应用
- 对话历史较长的复杂任务
- 多用户并发聊天系统
三、实现方式对比
方式一:使用SimpleMemory(手动实现)
from langchain_core.messages import HumanMessage, AIMessage
class SimpleMemory:
"""简单的缓冲窗口记忆实现"""
def __init__(self, max_token_limit=2000):
self.history = []
self.max_token_limit = max_token_limit
def add_user_message(self, message: str):
"""添加用户消息"""
self.history.append(HumanMessage(content=message))
self._trim_history()
def add_ai_message(self, message: str):
"""添加AI消息"""
self.history.append(AIMessage(content=message))
self._trim_history()
def _trim_history(self):
"""修剪历史记录以保持Token限制"""
# 简单的token估算: 1 token ≈ 4 字符
total_chars = sum(len(msg.content) for msg in self.history)
max_chars = self.max_token_limit * 4
while total_chars > max_chars and len(self.history) > 2:
# 移除最旧的消息
removed = self.history.pop(0)
total_chars -= len(removed.content)
def get_history(self):
"""获取当前历史记录"""
return self.history
方式二:使用RunnableWithMessageHistory(集成方案)
from langchain_community.chat_message_histories import FileChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
# 1. 定义历史记忆存储
store = {}
# 2. 工厂函数,获取指定会话的聊天历史
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = FileChatMessageHistory(f"chat_history_{session_id}.txt")
return store[session_id]
# 3. 包装链以添加历史记忆功能
with_message_chain = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="query",
history_messages_key="history",
)
四、完整示例代码
示例1:基础缓冲窗口记忆(SimpleMemory)
import dotenv
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage
dotenv.load_dotenv()
# 1. 创建SimpleMemory类
class SimpleMemory:
def __init__(self, max_token_limit=2000):
self.history = []
self.max_token_limit = max_token_limit
def add_user_message(self, message: str):
self.history.append(HumanMessage(content=message))
self._trim_history()
def add_ai_message(self, message: str):
self.history.append(AIMessage(content=message))
self._trim_history()
def _trim_history(self):
"""修剪历史记录以保持Token限制"""
total_chars = sum(len(msg.content) for msg in self.history)
max_chars = self.max_token_limit * 4
while total_chars > max_chars and len(self.history) > 2:
removed = self.history.pop(0)
total_chars -= len(removed.content)
def get_history(self):
return self.history
# 2. 初始化记忆
memory = SimpleMemory(max_token_limit=2000)
# 3. 创建提示模板
prompt = ChatPromptTemplate.from_messages([
("system", "你是OpenAI开发的聊天机器人,请根据对应的上下文回复用户问题"),
MessagesPlaceholder("history"),
("human", "{query}"),
])
# 4. 创建LLM和链
llm = ChatOpenAI(model="moonshot-v1-8k")
chain = RunnablePassthrough.assign(
history=lambda x: memory.get_history()
) | prompt | llm | StrOutputParser()
# 5. 对话循环
while True:
query = input("Human: ")
if query == "q":
break
response = chain.stream({"query": query})
print("AI: ", flush=True, end="")
output = ""
for chunk in response:
output += chunk
print(chunk, flush=True, end="")
# 保存对话到记忆
memory.add_user_message(query)
memory.add_ai_message(output)
print(f"\n当前历史消息数: {len(memory.get_history())}")
示例2:持久化缓冲记忆(FileChatMessageHistory)
import dotenv
from langchain_community.chat_message_histories import FileChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI
dotenv.load_dotenv()
# 1. 定义历史记忆存储
store = {}
# 2. 工厂函数
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
# 使用文件持久化存储聊天历史
store[session_id] = FileChatMessageHistory(f"chat_history_{session_id}.txt")
return store[session_id]
# 3. 构建提示模板与LLM
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个强大的聊天机器人,请根据用户的需求回复问题。"),
MessagesPlaceholder("history"),
("human", "{query}"),
])
llm = ChatOpenAI(model="moonshot-v1-8k")
# 4. 构建链
chain = prompt | llm | StrOutputParser()
# 5. 包装链以添加历史记忆
with_message_chain = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="query",
history_messages_key="history",
)
# 6. 对话循环
while True:
query = input("Human: ")
if query == "q":
break
# 运行链并传递配置信息
response = with_message_chain.stream(
{"query": query},
config={"configurable": {"session_id": "user_123"}}
)
print("AI: ", flush=True, end="")
for chunk in response:
print(chunk, flush=True, end="")
print("")
五、关键参数说明
| 参数 | 说明 | 推荐值 |
|---|---|---|
max_token_limit | 最大Token数量限制 | 2000-4000 |
session_id | 会话唯一标识符 | 用户ID或会话UUID |
history_messages_key | 历史消息在提示中的占位符名称 | "history" |
input_messages_key | 用户输入在提示中的占位符名称 | "query" |
六、最佳实践
-
合理设置Token限制
- 根据模型上下文窗口大小设置(如8k模型可设置2000-3000)
- 考虑单次对话的平均长度
-
持久化存储
- 生产环境建议使用数据库(Redis、PostgreSQL等)替代文件存储
- 定期清理过期会话历史
-
会话管理
- 为每个用户或会话分配唯一的session_id
- 实现会话超时机制
-
性能优化
- 考虑使用异步操作提升并发性能
- 对于高频场景,可以添加缓存层
七、总结
缓冲窗口记忆组件是构建生产级LLM应用的关键组件,它通过限制历史Token数量来平衡:
- 成本控制 vs 对话连贯性
- 响应速度 vs 上下文理解
选择合适的实现方式(SimpleMemory vs RunnableWithMessageHistory)取决于应用场景和复杂度需求。