You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将RedisChatMessageHistory接入ConversationSummaryBufferMemory优化Token用量?

问题分析与解决方案

问题原因

你遇到的AttributeError是因为类型接口不匹配:

  • RunnableWithMessageHistory要求传入的是BaseChatMessageHistory类型的实例(比如RedisChatMessageHistory),这类对象拥有.messages属性来直接获取对话消息列表。
  • ConversationSummaryBufferMemory属于BaseMemory类型,它的核心接口是load_memory_variables(),并不暴露.messages属性,所以直接将其传给RunnableWithMessageHistory会触发属性不存在的错误。

你的核心思路(通过汇总历史降低Token消耗)是正确的,只是选错了组件组合方式。

可行解决方案

方案1:自定义带汇总功能的Redis聊天历史类

继承BaseChatMessageHistory,内部整合RedisChatMessageHistory的持久化能力和ConversationSummaryBufferMemory的摘要逻辑,让对象同时满足RunnableWithMessageHistory的接口要求和历史汇总需求。

示例代码:

from langchain.memory import BaseChatMessageHistory, ConversationSummaryBufferMemory
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain.schema import BaseMessage, HumanMessage, AIMessage
from langchain.llms.base import BaseLLM

class SummarizedRedisChatMessageHistory(BaseChatMessageHistory):
    def __init__(self, redis_url: str, session_id: str, llm: BaseLLM, max_token_limit: int = 1000):
        # 初始化Redis持久化历史
        self.redis_history = RedisChatMessageHistory(redis_url=redis_url, session_id=session_id)
        # 初始化摘要内存,绑定Redis历史作为数据源
        self.summary_memory = ConversationSummaryBufferMemory(
            llm=llm,
            max_token_limit=max_token_limit,
            chat_memory=self.redis_history,
            return_messages=True
        )
    
    @property
    def messages(self):
        # 返回汇总后的历史:摘要消息 + 最新未被汇总的对话
        summary_content = self.summary_memory.load_memory_variables({})["history"]
        summary_msg = AIMessage(content=f"对话历史摘要:{summary_content}")
        # 保留最新的几条原始对话(可根据token阈值调整数量)
        recent_messages = self.redis_history.messages[-3:]
        return [summary_msg] + recent_messages
    
    def add_message(self, message: BaseMessage) -> None:
        # 同步消息到Redis
        self.redis_history.add_message(message)
        # 更新摘要内存
        if isinstance(message, HumanMessage):
            self.summary_memory.save_context({"input": message.content}, {"output": ""})
        elif isinstance(message, AIMessage):
            self.summary_memory.save_context({"input": ""}, {"output": message.content})
    
    def clear(self) -> None:
        # 清空Redis历史和摘要内存
        self.redis_history.clear()
        self.summary_memory.clear()

使用方式:
将这个自定义类传入RunnableWithMessageHistory的get_session_history参数即可:

chain = RunnableWithMessageHistory(
    retrieval_chain,
    get_session_history=lambda session_id: SummarizedRedisChatMessageHistory(
        redis_url="redis://localhost:6379",
        session_id=session_id,
        llm=your_llm_instance
    ),
    input_messages_key="question",
    history_messages_key="chat_history"
)

方案2:手动在历史获取环节做汇总

不依赖ConversationSummaryBufferMemory,直接在每次获取历史时,判断历史长度,超过阈值则调用LLM生成摘要,再将摘要+最新对话拼接成精简上下文。

示例代码:

from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain.schema import AIMessage, Document
from langchain.chains.summarize import load_summarize_chain

def get_summarized_history(session_id: str, llm, max_history_rounds: int = 5):
    history = RedisChatMessageHistory(redis_url="redis://localhost:6379", session_id=session_id)
    total_messages = len(history.messages)
    
    if total_messages <= max_history_rounds:
        # 历史较短,直接返回原始消息
        return history.messages
    else:
        # 生成历史摘要
        history_text = "\n".join([f"{msg.type}: {msg.content}" for msg in history.messages[:-max_history_rounds]])
        summary_chain = load_summarize_chain(llm, chain_type="stuff")
        summary = summary_chain.run([Document(page_content=history_text)])
        # 返回摘要+最新N轮对话
        summary_msg = AIMessage(content=f"对话历史摘要:{summary}")
        return [summary_msg] + history.messages[-max_history_rounds:]

# 构建带汇总历史的检索链
chain = RunnableWithMessageHistory(
    retrieval_chain,
    get_session_history=lambda session_id: get_summarized_history(session_id, your_llm_instance),
    input_messages_key="question",
    history_messages_key="chat_history"
)

总结

你的核心思路(减少历史Token消耗)是正确的,错误在于混淆了BaseChatMessageHistory和BaseMemory的接口差异。通过自定义兼容接口的历史类,或者手动在历史获取环节做摘要处理,就能实现Redis持久化+历史汇总的需求。

内容的提问来源于stack exchange,提问作者seb835

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 05:30:09