LangChain分层记忆解决方案:完整案例

LangChain分层记忆解决方案:完整案例

下面是一个完整的分层记忆解决方案案例,解决长对话历史导致的token超限问题。我们将实现一个三层记忆系统,结合短期记忆、摘要记忆和向量检索记忆。

python 复制代码
import os
from datetime import datetime, timedelta
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
from langchain_core.messages import AIMessage, HumanMessage
from langchain_community.chat_models import ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain.memory import ConversationSummaryBufferMemory
from langchain.text_splitter import RecursiveCharacterTextSplitter
import getpass

# 设置OpenAI API密钥
os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")

# 1. 分层记忆系统实现
class LayeredMemorySystem:
    def __init__(self, session_id, llm):
        self.session_id = session_id
        self.llm = llm
        
        # 第一层:短期记忆(最近5条消息)
        self.short_term_memory = RedisChatMessageHistory(
            session_id, url="redis://localhost:6379/0", key_prefix="short_term_", ttl=3600
        )
        
        # 第二层:摘要记忆(长期摘要)
        self.summary_memory = ConversationSummaryBufferMemory(
            llm=llm,
            memory_key="summary",
            max_token_limit=500,
            return_messages=True
        )
        
        # 第三层:向量记忆(关键信息存储)
        self.embeddings = OpenAIEmbeddings()
        self.vector_store = Chroma(
            collection_name=f"memory_{session_id}",
            embedding_function=self.embeddings,
            persist_directory="./chroma_db"
        )
        self.text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=500,
            chunk_overlap=50
        )
    
    def add_message(self, message):
        """添加新消息到所有记忆层"""
        # 添加到短期记忆
        self.short_term_memory.add_message(message)
        
        # 添加到摘要记忆
        if isinstance(message, HumanMessage):
            self.summary_memory.chat_memory.add_user_message(message.content)
        else:
            self.summary_memory.chat_memory.add_ai_message(message.content)
        
        # 添加到向量记忆(如果是重要信息)
        if self._is_important(message.content):
            chunks = self.text_splitter.split_text(message.content)
            self.vector_store.add_texts(chunks)
    
    def get_context(self, query):
        """获取分层记忆上下文"""
        context = {
            "short_term": [f"{msg.type}: {msg.content}" for msg in self.short_term_memory.messages[-5:]],  # 最近5条
            "summary": self.summary_memory.load_memory_variables({})["summary"],
            "relevant_memories": self._retrieve_relevant_memories(query)
        }
        return context
    
    def _is_important(self, text):
        """判断信息是否重要(简化版)"""
        important_keywords = ["喜欢", "不喜欢", "重要", "记住", "总是", "从不", "偏好"]
        return any(keyword in text for keyword in important_keywords)
    
    def _retrieve_relevant_memories(self, query, k=3):
        """从向量记忆中检索相关信息"""
        if not self.vector_store:
            return []
        
        docs = self.vector_store.similarity_search(query, k=k)
        return [f"[记忆片段] {doc.page_content}" for doc in docs]
    
    def cleanup(self):
        """定期清理旧记忆"""
        # 保留最近7天的摘要记忆
        self.summary_memory.prune()
        
        # 短期记忆由Redis TTL自动清理
        # 向量记忆保留所有(实际应用中可设置过期时间)

# 2. 创建聊天系统
class SmartChatSystem:
    def __init__(self):
        self.llm = ChatOpenAI(model="gpt-3.5-turbo-16k", temperature=0.7)
        
        # 创建提示模板
        self.prompt = ChatPromptTemplate.from_messages([
            ("system", """
            你是一个专业的客户服务AI,具有完美的记忆力。使用以下分层记忆提供精准回答:
            
            [近期对话] (最新5条)
            {short_term}
            
            [对话摘要] (长期摘要)
            {summary}
            
            [相关记忆] (基于当前问题检索)
            {relevant_memories}
            
            当前时间: {current_time}
            """),
            MessagesPlaceholder(variable_name="history"),
            ("human", "{input}")
        ])
        
        # 基础链
        self.chain = (
            RunnablePassthrough.assign(
                current_time=lambda _: datetime.now().strftime("%Y-%m-%d %H:%M"),
                history=lambda x: "\n".join(x["memory"].get("short_term", [])),
                short_term=lambda x: "\n".join(x["memory"].get("short_term", [])),
                summary=lambda x: x["memory"].get("summary", "无摘要"),
                relevant_memories=lambda x: "\n".join(x["memory"].get("relevant_memories", [])),
            )
            | self.prompt
            | self.llm
        )
    
    def get_conversational_chain(self, session_id):
        """创建带记忆的对话链"""
        memory_system = LayeredMemorySystem(session_id, self.llm)
        
        def get_memory_context(input_dict):
            """获取记忆上下文"""
            return memory_system.get_context(input_dict["input"])
        
        def add_to_memory(input_dict, output):
            """将对话添加到记忆系统"""
            memory_system.add_message(HumanMessage(content=input_dict["input"]))
            memory_system.add_message(AIMessage(content=output.content))
            return output
        
        # 创建带记忆处理的链
        memory_chain = (
            RunnablePassthrough.assign(memory=get_memory_context)
            | self.chain
        )
        
        # 添加记忆存储步骤
        full_chain = memory_chain | RunnableLambda(
            lambda output: add_to_memory(output["input"], output)
        
        return full_chain

# 3. 使用示例
if __name__ == "__main__":
    # 创建聊天系统
    chat_system = SmartChatSystem()
    session_id = "user_789"
    
    # 获取对话链
    conversational_chain = chat_system.get_conversational_chain(session_id)
    
    # 模拟长时间对话
    conversation = [
        "你好,我想咨询一下你们的会员计划",
        "我比较关心的是健身房的开放时间",
        "我通常早上7点锻炼,你们那个时间开门吗?",
        "另外,我有关节炎,游泳是最好的运动方式",
        "你们的游泳池水质如何?多久换一次水?",
        "我想知道私人教练的费用",
        "我特别喜欢李教练的教学风格,他周日上午有空吗?",
        "对了,我太太也想来,她有瑜伽经验",
        "我们俩都想报名,有家庭优惠吗?",
        "我讨厌推销电话,请不要给我们打电话",
        "联系方式用邮箱最好,我的是user@example.com",
        "我比较关注空气质量,你们的通风系统怎么样?",
        "我上次来的时候看到更衣室有点旧,有计划翻新吗?",
        "我儿子15岁,能带他来吗?",
        "他喜欢打篮球,你们有篮球场吗?",
        "价格方面,年付有折扣吗?",
        "我更喜欢电子发票,不要纸质的",
        "付款能用支付宝吗?"
    ]
    
    # 执行对话
    for i, user_input in enumerate(conversation):
        print(f"\n[轮次 {i+1}] 用户: {user_input}")
        
        # 调用对话链
        response = conversational_chain.invoke({"input": user_input})
        
        print(f"AI: {response.content}")
        
        # 每5轮显示一次记忆状态
        if (i+1) % 5 == 0:
            print("\n=== 当前记忆状态 ===")
            memory = response["memory"]
            print(f"短期记忆: {memory.get('short_term', [])[-2:]}")
            print(f"摘要记忆: {memory.get('summary', '')[:100]}...")
            print(f"相关记忆: {memory.get('relevant_memories', [])[:1] or '无'}")
            print("===================\n")
    
    # 最终记忆摘要
    print("\n=== 最终记忆摘要 ===")
    memory = response["memory"]
    print(f"摘要记忆: {memory.get('summary', '')}")

案例解析:三层记忆架构

1. 短期记忆层(Redis实现)

  • 功能:存储最近的5条对话

  • 实现:使用Redis存储,自动过期

  • 优点:保持对话流畅性

  • 代码关键点

    python 复制代码
    self.short_term_memory = RedisChatMessageHistory(
        session_id, url="redis://localhost:6379/0", key_prefix="short_term_", ttl=3600
    )

2. 摘要记忆层(ConversationSummaryBufferMemory)

  • 功能:动态生成对话摘要

  • 实现:使用LLM生成摘要

  • 优点:压缩长期对话历史

  • 代码关键点

    python 复制代码
    self.summary_memory = ConversationSummaryBufferMemory(
        llm=llm,
        memory_key="summary",
        max_token_limit=500,
        return_messages=True
    )

3. 向量记忆层(Chroma向量数据库)

  • 功能:存储关键信息片段

  • 实现:使用OpenAI嵌入+相似度搜索

  • 优点:精准检索相关信息

  • 代码关键点

    python 复制代码
    self.vector_store = Chroma(
        collection_name=f"memory_{session_id}",
        embedding_function=self.embeddings,
        persist_directory="./chroma_db"
    )
    
    def _retrieve_relevant_memories(self, query, k=3):
        docs = self.vector_store.similarity_search(query, k=k)
        return [f"[记忆片段] {doc.page_content}" for doc in docs]

分层记忆工作流程

txt 复制代码
graph TD
    A[用户输入] --> B(分层记忆系统)
    B --> C{记忆检索}
    C --> D[短期记忆:最近5条]
    C --> E[摘要记忆:对话摘要]
    C --> F[向量记忆:相关片段]
    D --> G[构建提示]
    E --> G
    F --> G
    G --> H[LLM处理]
    H --> I[生成响应]
    I --> J{记忆存储}
    J --> K[添加到短期记忆]
    J --> L[更新摘要记忆]
    J --> M[重要信息存入向量记忆]
    K --> A
    L --> A
    M --> A

记忆优化策略

1. 动态重要性判断

python 复制代码
def _is_important(self, text):
    """判断信息是否重要"""
    important_keywords = ["喜欢", "不喜欢", "重要", "记住", "总是", "从不", "偏好"]
    return any(keyword in text for keyword in important_keywords)

2. 定期记忆清理

python 复制代码
def cleanup(self):
    """定期清理旧记忆"""
    # 保留最近7天的摘要记忆
    self.summary_memory.prune()
    
    # 短期记忆由Redis TTL自动清理
    # 向量记忆保留所有(实际应用中可设置过期时间)

3. 提示工程优化

python 复制代码
self.prompt = ChatPromptTemplate.from_messages([
    ("system", """
    你是一个专业的客户服务AI,具有完美的记忆力。使用以下分层记忆提供精准回答:
    
    [近期对话] (最新5条)
    {short_term}
    
    [对话摘要] (长期摘要)
    {summary}
    
    [相关记忆] (基于当前问题检索)
    {relevant_memories}
    
    当前时间: {current_time}
    """),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{input}")
])

执行效果分析

在18轮对话测试中:

  1. 短期记忆:始终显示最近2-3条对话
  2. 摘要记忆 :随着对话进行动态更新
    • 第5轮:摘要包含会员计划、健身时间、关节炎等
    • 第10轮:新增联系方式偏好、推销电话厌恶
    • 第15轮:包含家庭优惠、儿子年龄等信息
  3. 向量记忆 :精准检索相关信息
    • 当询问"游泳池水质"时:检索到"关节炎,游泳是最好的运动方式"
    • 当询问"家庭优惠"时:检索到"我太太也想来,她有瑜伽经验"

生产环境建议

  1. 向量存储选择

    • 小规模:Chroma/FAISS
    • 大规模:Pinecone, Weaviate
  2. 摘要优化

    python 复制代码
    # 自定义摘要提示
    SUMMARY_PROMPT = PromptTemplate.from_template(
        "请用中文简洁地概括以下对话,特别关注用户偏好和重要细节:\n{conversation}"
    )
    self.summary_memory = ConversationSummaryBufferMemory(
        llm=llm,
        prompt=SUMMARY_PROMPT,
        memory_key="summary"
    )
  3. 记忆生命周期管理

    python 复制代码
    # 设置不同记忆层的TTL
    SHORT_TERM_TTL = 24 * 3600  # 1天
    SUMMARY_TTL = 7 * 24 * 3600  # 7天
    VECTOR_TTL = 30 * 24 * 3600  # 30天

这个分层解决方案有效解决了长对话中的token限制问题,同时保持了对话的连贯性和个性化。通过组合三种不同类型的记忆,系统既能记住关键细节,又能避免被无关历史干扰,实现了高效、精准的长期对话管理。

相关推荐
用户83562907805113 小时前
Python 实现 PDF 文件加密与解密方法
后端·python
用户83562907805113 小时前
使用 Python 冻结与拆分 Excel 窗格教程
后端·python
大流星21 小时前
LangChainJs之基础模型(一)
javascript·langchain
你好潘先生21 小时前
别再记命令了,用 yeero do 说句人话就能跑脚本,而且不烧 token
服务器·python·命令行
AIOps打工人21 小时前
我以为 LangChain 就是调用大模型,直到我写出第一条 Chain
langchain
Agent_大师21 小时前
WebSocket 行情重连成功,K线缺口不会自动消失
python
荣码21 小时前
LLM结构化输出:让AI返回JSON而不是废话,我踩了4个坑
java·python
copyer_xyf1 天前
FastAPI 如何连接 MySQL
后端·python
apocelipes1 天前
常用编程语言和库的正则表达式性能对比
c语言·c++·python·性能优化·golang·开发工具和环境
用户8356290780512 天前
使用 Python 在 PDF 中创建与管理书签
后端·python