一、指南概述

LangChain记忆系统是解决大语言模型(LLM)“无状态天性”与“对话连续性需求”矛盾的核心组件,本质是“上下文管理中间件”。其核心价值在于实现对话状态的持久化存储与动态调用,让AI应用从“单次问答工具”升级为“智能交互助手”,广泛适用于聊天机器人、智能客服、个人助手等场景。

本指南将从核心概念、实战案例、高级拓展到生产落地,系统讲解LangChain记忆系统的使用方法,所有案例均提供完整可运行代码,兼顾入门开发者与进阶需求。

二、核心概念与基础架构

2.1 核心抽象基类

LangChain记忆系统基于两大抽象基类构建,所有具体记忆实现均遵循统一接口规范:

  • BaseMemory:通用记忆逻辑层基类,定义load_memory_variables(加载记忆)、save_context(保存上下文)、clear(清空记忆)三大核心方法。
  • BaseChatMessageHistory:对话历史存储基类,专注于对话消息的增删查,核心方法包括add_message(添加消息)、get_messages(获取消息)、clear(清空消息)。

2.2 核心分类维度

按存储范围划分

  • 会话级记忆:存储单次会话内的历史交互,会话结束后记忆清空(如ConversationBufferMemory)。
  • 实体级记忆:存储跨会话的实体信息(如用户偏好、属性),支持长期复用(如ConversationEntityMemory)。

按上下文格式划分

  • 原始文本类:直接存储完整对话历史,上下文完整性高(如ConversationBufferMemory)。
  • 结构化类:将历史信息转换为摘要、实体属性等结构化数据,降低Token消耗(如ConversationSummaryMemory)。

2.3 核心工作流程

  1. 存储:将用户输入、AI响应等信息持久化到指定介质(内存、数据库等)。
  2. 提取:新请求到来时,从存储中提取相关历史信息。
  3. 注入:将历史信息与当前输入拼接为完整Prompt,传递给LLM。
  4. 更新:LLM返回结果后,将新的交互信息追加到记忆中,完成更新闭环。

三、核心记忆类型实战

3.1 会话级记忆实战

3.1.1 ConversationBufferMemory(完整会话记忆)

  • 核心逻辑:逐句存储完整对话历史,无裁剪或压缩。
  • 适用场景:短对话、调试场景,需完整保留上下文。
  • 实战代码:

    # 安装依赖
    # pip install langchain-openai langchain-core
    
    from langchain_openai import ChatOpenAI
    from langchain.memory import ConversationBufferMemory
    from langchain.chains import ConversationChain
    
    # 初始化LLM
    llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0, api_key="your-api-key")
    
    # 初始化记忆组件
    memory = ConversationBufferMemory(
      memory_key="chat_history",  # 记忆在Prompt中的键名
      return_messages=True  # 返回Message对象(而非字符串)
    )
    
    # 构建对话链
    conversation_chain = ConversationChain(
      llm=llm,
      memory=memory,
      verbose=True  # 打印执行过程
    )
    
    # 多轮对话测试
    conversation_chain.invoke({"input": "我叫张三,计划去北京旅游3天"})
    conversation_chain.invoke({"input": "我刚才提到的名字是什么?"})
    conversation_chain.invoke({"input": "结合我的旅行天数,推荐一下必去景点"})
  • 优缺点:逻辑简单、上下文完整;长对话易超Token限制,Token消耗高。

3.1.2 ConversationBufferWindowMemory(窗口会话记忆)

  • 核心逻辑:仅保留最近N轮对话,通过k参数控制窗口大小。
  • 适用场景:通用多轮对话,需控制上下文长度。
  • 实战代码:

    from langchain.memory import ConversationBufferWindowMemory
    
    # 初始化窗口记忆(保留最近2轮对话)
    memory = ConversationBufferWindowMemory(
      k=2,  # 窗口大小:仅保留最近2轮
      memory_key="chat_history",
      return_messages=True
    )
    
    # 构建并测试对话链(其余代码同3.1.1)
    conversation_chain = ConversationChain(llm=llm, memory=memory, verbose=True)
    conversation_chain.invoke({"input": "我叫张三,计划去北京旅游3天"})
    conversation_chain.invoke({"input": "北京10月份天气怎么样?"})
    conversation_chain.invoke({"input": "我刚才提到的旅行天数是多少?"})  # 能正常回答(在窗口内)
    conversation_chain.invoke({"input": "我叫什么名字?"})  # 无法回答(超出窗口范围)
  • 优缺点:自动截断历史,Token消耗可控;可能丢失早期关键信息。

3.1.3 ConversationTokenBufferMemory(Token窗口记忆)

  • 核心逻辑:按Token数量控制上下文,超出阈值时裁剪早期内容。
  • 适用场景:需精准控制Token消耗的场景,避免模型调用失败。
  • 实战代码:

    from langchain.memory import ConversationTokenBufferMemory
    
    # 初始化Token窗口记忆
    memory = ConversationTokenBufferMemory(
      llm=llm,  # 依赖LLM计算Token数
      max_token_limit=300,  # 最大Token限制
      memory_key="chat_history",
      return_messages=True
    )
    
    # 构建并测试对话链(其余代码同3.1.1)
    conversation_chain.invoke({"input": "我叫张三,计划去北京旅游3天,想看看故宫、长城、颐和园等景点,还想尝尝北京烤鸭和炸酱面"})
    conversation_chain.invoke({"input": "推荐一下长城附近的住宿"})
  • 优缺点:精准控制Token消耗;需额外依赖Token计算工具(如tiktoken)。

3.1.4 ConversationSummaryMemory(会话摘要记忆)

  • 核心逻辑:通过LLM将历史对话压缩为摘要,仅存储摘要信息。
  • 适用场景:超长对话,需大幅降低Token消耗。
  • 实战代码:

    from langchain.memory import ConversationSummaryMemory
    
    # 初始化摘要记忆
    memory = ConversationSummaryMemory(
      llm=llm,  # 用于生成摘要的LLM
      memory_key="chat_history",
      return_messages=True
    )
    
    # 构建并测试对话链
    conversation_chain = ConversationChain(llm=llm, memory=memory, verbose=True)
    conversation_chain.invoke({"input": "我叫张三,计划去北京旅游3天,第一天想逛故宫和天安门广场"})
    conversation_chain.invoke({"input": "第二天想去八达岭长城,听说那里的景色最壮观"})
    conversation_chain.invoke({"input": "第三天打算去颐和园和南锣鼓巷,体验老北京风情"})
    conversation_chain.invoke({"input": "总结一下我的旅行计划"})  # 基于摘要精准总结
  • 优缺点:Token消耗低,支持超长对话;需额外调用LLM生成摘要,可能丢失细节。

3.1.5 ConversationSummaryBufferMemory(混合摘要记忆)

  • 核心逻辑:结合原始文本与摘要,未超Token阈值时保留原文,超出后摘要早期内容。
  • 适用场景:大多数生产环境,平衡上下文完整性与Token消耗。
  • 实战代码:

    from langchain.memory import ConversationSummaryBufferMemory
    
    # 初始化混合摘要记忆
    memory = ConversationSummaryBufferMemory(
      llm=llm,
      max_token_limit=500,  # 原文最大Token限制
      memory_key="chat_history",
      return_messages=True
    )
    
    # 构建并测试对话链(其余代码同3.1.1)
    conversation_chain.invoke({"input": "我叫张三,计划去北京旅游3天"})
    conversation_chain.invoke({"input": "第一天想逛故宫和天安门广场,故宫需要提前预约吗?"})
    conversation_chain.invoke({"input": "第二天想去八达岭长城,交通怎么安排比较方便?"})
    conversation_chain.invoke({"input": "第三天打算去颐和园和南锣鼓巷,推荐一下当地美食"})
    conversation_chain.invoke({"input": "我刚才问了哪些关于交通和预约的问题?"})  # 精准回应
  • 优缺点:兼顾上下文完整性与Token效率;生产环境首选平衡方案。

3.2 实体级记忆实战

3.2.1 ConversationEntityMemory(实体提取记忆)

  • 核心逻辑:自动从对话中提取“实体-属性”对,支持跨会话复用。
  • 适用场景:需记忆用户属性、偏好等实体信息的场景。
  • 实战代码:

    from langchain.memory import ConversationEntityMemory
    from langchain_core.prompts import ChatPromptTemplate
    
    # 初始化实体记忆
    memory = ConversationEntityMemory(
      llm=llm,
      memory_key="entities",  # 实体信息在Prompt中的键名
      return_messages=True
    )
    
    # 定义包含实体占位符的Prompt
    prompt = ChatPromptTemplate.from_messages([
      ("system", "你是贴心助手,需利用已知实体信息回应用户,实体信息:{entities}"),
      ("human", "{input}")
    ])
    
    # 构建对话链
    conversation_chain = ConversationChain(
      llm=llm,
      memory=memory,
      prompt=prompt,
      verbose=True
    )
    
    # 测试实体记忆功能
    conversation_chain.invoke({"input": "我叫张三,喜欢甜食,过敏芒果"})
    conversation_chain.invoke({"input": "推荐一款适合我的下午茶"})  # 结合偏好和过敏信息推荐
    conversation_chain.invoke({"input": "我刚才说我对什么过敏?"})  # 准确提取实体信息
  • 核心特性:依赖LLM的实体提取能力,自动构建实体库。

3.2.2 VectorStoreRetrieverMemory(向量检索记忆)

  • 核心逻辑:将历史信息转换为向量存储,通过相似性检索提取相关上下文。
  • 适用场景:海量历史信息、长周期多会话场景(如私人助手)。
  • 实战代码:

    from langchain.memory import VectorStoreRetrieverMemory
    from langchain_community.vectorstores import Chroma
    from langchain_openai import OpenAIEmbeddings
    
    # 初始化向量存储(Chroma)
    embeddings = OpenAIEmbeddings(api_key="your-api-key")
    vector_store = Chroma(embedding_function=embeddings)
    retriever = vector_store.as_retriever(search_kwargs={"k": 3})  # 检索Top3相关结果
    
    # 初始化向量检索记忆
    memory = VectorStoreRetrieverMemory(
      retriever=retriever,
      memory_key="chat_history",
      return_messages=True
    )
    
    # 构建对话链(其余代码同3.1.1)
    conversation_chain = ConversationChain(llm=llm, memory=memory, verbose=True)
    conversation_chain.invoke({"input": "我叫张三,计划10月去北京旅游3天"})
    conversation_chain.invoke({"input": "北京10月的平均气温是多少?"})
    conversation_chain.invoke({"input": "我的旅行时间是什么时候?"})  # 通过向量检索获取信息
  • 核心优势:支持海量数据高效检索,避免全量拼接上下文。

四、持久化存储方案实战

4.1 开发环境:内存存储(默认)

  • 核心方案:使用InMemoryChatMessageHistory,内存临时存储。
  • 适用场景:本地开发、测试,无需持久化。
  • 实战代码:

    from langchain.memory import ConversationBufferMemory
    from langchain.memory.chat_message_histories import InMemoryChatMessageHistory
    
    # 绑定内存存储
    chat_history = InMemoryChatMessageHistory()
    memory = ConversationBufferMemory(
      chat_memory=chat_history,
      return_messages=True
    )
    
    # 测试存储功能
    memory.save_context({"input": "我叫张三"}, {"output": "您好!张三"})
    print(memory.load_memory_variables({}))  # 读取记忆

4.2 生产环境:外部存储集成

4.2.1 Redis存储(高并发场景)

  • 适用场景:分布式系统、高吞吐场景。
  • 实战代码:

    # 安装依赖:pip install redis langchain-community
    from langchain.memory.chat_message_histories import RedisChatMessageHistory
    from langchain.memory import ConversationBufferMemory
    
    # 初始化Redis存储(需提前启动Redis服务)
    chat_history = RedisChatMessageHistory(
      session_id="user-001",  # 会话ID,用于多用户隔离
      redis_url="redis://localhost:6379/0"
    )
    
    # 绑定Redis存储到记忆组件
    memory = ConversationBufferMemory(
      chat_memory=chat_history,
      return_messages=True
    )
    
    # 测试持久化功能
    memory.save_context({"input": "我叫张三"}, {"output": "您好!张三"})
    new_memory = ConversationBufferMemory(chat_memory=RedisChatMessageHistory("user-001", "redis://localhost:6379/0"))
    print(new_memory.load_memory_variables({}))  # 重启后仍可读取

4.2.2 PostgreSQL存储(长期存储场景)

  • 适用场景:生产环境、需要长期稳定存储的场景。
  • 实战代码:

    # 安装依赖:pip install psycopg2-binary langchain-community
    from langchain.memory.chat_message_histories import SQLChatMessageHistory
    from langchain.memory import ConversationBufferMemory
    
    # 初始化PostgreSQL存储(需提前创建数据库)
    chat_history = SQLChatMessageHistory(
      session_id="user-001",
      connection_string="postgresql://user:password@localhost:5432/langchain_db"
    )
    
    # 绑定数据库存储
    memory = ConversationBufferMemory(chat_memory=chat_history, return_messages=True)
    memory.save_context({"input": "我计划去北京旅游"}, {"output": "已为你记录旅行计划"})

4.2.3 SQLite存储(轻量生产场景)

  • 适用场景:轻量部署、低并发生产环境。
  • 实战代码:

    from langchain.memory.chat_message_histories import SQLChatMessageHistory
    
    # 初始化SQLite存储(文件存储,无需额外服务)
    chat_history = SQLChatMessageHistory(
      session_id="user-001",
      connection_string="sqlite:///langchain_memory.db"
    )
    
    # 后续使用同PostgreSQL方案

五、高级实战:Agent与记忆系统集成

5.1 带记忆的工具调用Agent

  • 核心目标:让Agent在调用工具时保留对话状态,支持多轮工具交互。
  • 实战代码:

    from langchain_openai import ChatOpenAI
    from langchain.agents import AgentExecutor, create_openai_functions_agent
    from langchain.memory import ConversationBufferWindowMemory
    from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
    from langchain.tools import Tool
    import os
    
    # 设置API Key
    os.environ["OPENAI_API_KEY"] = "your-api-key"
    
    # 定义工具:获取当前时间
    def get_current_time():
      from datetime import datetime
      return f"当前时间是 {datetime.now().strftime('%Y年%m月%d日 %H:%M')}"
    
    tools = [
      Tool(
          name="GetTime",
          func=get_current_time,
          description="获取当前精确时间,当用户询问时间相关问题时调用"
      )
    ]
    
    # 初始化LLM
    llm = ChatOpenAI(model="gpt-4-turbo", temperature=0)
    
    # 构建Prompt(包含记忆占位符)
    prompt = ChatPromptTemplate.from_messages([
      ("system", "你是有记忆的智能助手,可调用工具回答问题,需结合历史对话提供连贯回应"),
      MessagesPlaceholder(variable_name="chat_history"),  # 记忆注入点
      ("human", "{input}"),
      MessagesPlaceholder(variable_name="agent_scratchpad")  # 工具调用日志
    ])
    
    # 初始化记忆(保留最近2轮)
    memory = ConversationBufferWindowMemory(
      k=2,
      memory_key="chat_history",
      return_messages=True
    )
    
    # 创建Agent并绑定记忆
    agent = create_openai_functions_agent(llm, tools, prompt)
    agent_executor = AgentExecutor(
      agent=agent,
      tools=tools,
      memory=memory,
      verbose=True
    )
    
    # 多轮测试
    agent_executor.invoke({"input": "现在几点了?"})
    agent_executor.invoke({"input": "我刚才问的是什么问题?"})  # 记忆工具调用上下文

5.2 自定义记忆组件

  • 核心方法:继承BaseMemory类,实现load_memory_variablessave_context方法。
  • 实战代码(实体提取记忆示例):

    from langchain.memory import BaseMemory
    from langchain_core.pydantic_v1 import BaseModel
    from typing import Dict, List
    import spacy
    
    # 加载NLP模型(需提前安装:pip install spacy && python -m spacy download en_core_web_lg)
    nlp = spacy.load("en_core_web_lg")
    
    class CustomEntityMemory(BaseMemory, BaseModel):
      # 存储实体信息的字典
      entities: Dict[str, str] = {}
      # 记忆键名
      memory_key: str = "entities"
    
      @property
      def memory_variables(self) -> List[str]:
          return [self.memory_key]
    
      def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, str]:
          # 加载实体信息,格式化为字符串
          entity_str = "\n".join([f"{k}: {v}" for k, v in self.entities.items()])
          return {self.memory_key: entity_str}
    
      def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:
          # 从输入中提取实体(人名、组织、偏好等)
          user_input = inputs["input"]
          doc = nlp(user_input)
          for ent in doc.ents:
              self.entities[ent.label_] = ent.text
          # 提取用户偏好类信息(简单规则示例)
          if "喜欢" in user_input:
              preference = user_input.split("喜欢")[-1].strip()
              self.entities["偏好"] = preference
    
    # 使用自定义记忆
    memory = CustomEntityMemory()
    conversation_chain = ConversationChain(
      llm=llm,
      memory=memory,
      prompt=ChatPromptTemplate.from_messages([
          ("system", "利用实体信息回应:{entities}"),
          ("human", "{input}")
      ]),
      verbose=True
    )
    
    conversation_chain.invoke({"input": "我叫张三,喜欢登山"})
    print(memory.load_memory_variables({}))  # 输出提取的实体信息

六、性能优化与最佳实践

6.1 记忆优先级管理

  • 时间窗口清理:使用ConversationBufferWindowMemoryk参数,保留近期对话。
  • 内容去重清理:通过语义相似度计算(如Embedding相似度>0.9)删除冗余信息。
  • 时效性清理:对时间敏感信息(如临时任务)设置过期时间,自动清理。

6.2 Token消耗优化

  • 短对话用ConversationBufferMemory,长对话切换为SummaryBufferMemory
  • ConversationTokenBufferMemory设置合理的max_token_limit,避免超阈值。
  • 向量检索记忆结合上下文压缩,进一步降低Token消耗。

6.3 生产环境关键配置

  • 多用户隔离:通过session_id区分不同用户的记忆,避免交叉污染。
  • 定期清理:设置对话过期策略(如7天无交互自动清理),防止存储膨胀。
  • 敏感信息加密:对用户手机号、地址等敏感信息加密存储,符合隐私规范。
  • 高可用部署:Redis存储配置主从复制,数据库存储开启备份机制。

6.4 常见问题排查

问题现象排查方向解决方案
长对话报错“Token超限”Token消耗失控切换为SummaryBufferMemoryTokenBufferMemory
记忆信息丢失会话隔离失效确认session_id唯一,检查存储介质是否持久化
实体信息提取不准确LLM实体提取能力不足自定义实体提取规则,或更换更强的LLM模型
向量检索结果无关嵌入模型不匹配更换与业务场景适配的Embedding模型,调整k

七、总结

LangChain记忆系统通过标准化接口和丰富的实现类,为LLM应用提供了灵活的状态管理能力。核心选择逻辑为:短对话用ConversationBufferMemory,通用场景用SummaryBufferMemory,实体记忆用ConversationEntityMemory,海量数据用VectorStoreRetrieverMemory

生产落地时需重点关注持久化存储、多用户隔离和Token消耗优化,结合具体业务场景选择合适的记忆类型与存储方案。


AIAgent研究
7.2k 声望12.8k 粉丝

一群有AI的人 研究AI-Agent的开发,做优秀的AI应用;