langchain学习总结-缓冲窗口记忆组件学习笔记

9 阅读5分钟

缓冲窗口记忆组件学习笔记

一、是什么

缓冲窗口记忆组件(Buffer Window Memory)是一种带有限容量的对话历史管理机制,它能够:

  1. 保存对话历史:将用户和AI的对话消息按照时间顺序存储
  2. 限制Token数量:当对话历史的总Token数超过设定的阈值时,自动删除最旧的消息
  3. 动态管理:在每次添加新消息后检查并调整历史记录,确保不会超出Token限制

二、有什么用

核心价值

  1. 节省API成本

    • LLM API按照Token数量计费,限制历史Token数可以有效控制成本
    • 避免每次请求都传递全部历史记录
  2. 提升响应速度

    • 减少每次请求的输入Token数量,加快模型推理速度
    • 降低网络传输延迟
  3. 避免超出上下文窗口

    • 每个模型都有最大Token限制(如4k、8k、32k等)
    • 防止历史记录过多导致超出模型处理能力
  4. 保持对话连贯性

    • 在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"

六、最佳实践

  1. 合理设置Token限制

    • 根据模型上下文窗口大小设置(如8k模型可设置2000-3000)
    • 考虑单次对话的平均长度
  2. 持久化存储

    • 生产环境建议使用数据库(Redis、PostgreSQL等)替代文件存储
    • 定期清理过期会话历史
  3. 会话管理

    • 为每个用户或会话分配唯一的session_id
    • 实现会话超时机制
  4. 性能优化

    • 考虑使用异步操作提升并发性能
    • 对于高频场景,可以添加缓存层

七、总结

缓冲窗口记忆组件是构建生产级LLM应用的关键组件,它通过限制历史Token数量来平衡:

  • 成本控制 vs 对话连贯性
  • 响应速度 vs 上下文理解

选择合适的实现方式(SimpleMemory vs RunnableWithMessageHistory)取决于应用场景和复杂度需求。