标签 Memory 下的文章

一、核心概念

1.1 什么是Memory?

Memory是LangChain框架中负责维护Chain状态并整合过去运行上下文的核心组件。默认情况下,所有链式模型和代理模型都是无状态的(独立处理每个查询,不保留历史信息),而在对话系统等场景中,记住先前交互至关重要,Memory正是为此设计的。

1.2 Memory的基本操作

Memory系统支持两个核心操作:

  • 读取(Load):在Chain执行前,从记忆中获取历史信息,增强用户输入
  • 写入(Save):在Chain执行后,将当前输入/输出保存到记忆中,供后续使用

1.3 内存的分类

LangChain将内存分为两大类:

  • 短期内存(Short-term memory):线程范围内存,追踪当前对话,在会话结束后通常会被清除
  • 长期内存(Long-term memory):跨会话存储,可在任意线程中随时访问,通常需要配置持久化存储

二、Memory类体系结构

2.1 核心类层次

BaseMemory
├── BaseChatMemory
│   ├── ConversationBufferMemory
│   ├── ConversationBufferWindowMemory
│   ├── ConversationSummaryMemory
│   ├── ConversationSummaryBufferMemory
│   ├── ConversationEntityMemory
│   └── ConversationKGMemory
└── VectorStoreRetrieverMemory

注:完整列表可参考API文档

2.2 BaseMemory接口(所有内存的基类)

所有内存类必须实现以下抽象方法:

  • load_memory_variables(inputs: Dict[str, Any]) -> Dict[str, Any]:加载内存变量,返回一个字典
  • save_context(inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:保存当前运行的上下文到内存
  • clear() -> None:清除内存内容

三、内置Memory类型详解

3.1 ConversationBufferMemory(基础对话缓冲内存)

特点:简单存储完整对话历史,返回字符串格式的历史内容

# 使用示例
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory(memory_key="chat_history")
memory.chat_memory.add_user_message("Hi!")
memory.chat_memory.add_ai_message("Hello!")
print(memory.load_memory_variables({}))  # 输出: {'chat_history': 'Human: Hi!\nAI: Hello!'}

3.2 ConversationBufferWindowMemory(对话窗口缓冲内存)

特点:只保留最近k轮对话,适合高频短对话场景,避免内存溢出

# 使用示例(只保留最近2轮)
memory = ConversationBufferWindowMemory(k=2, memory_key="history")

3.3 ConversationSummaryMemory(对话摘要内存)

特点:使用LLM自动生成对话摘要,减少token占用,适合长对话场景

# 使用示例
from langchain.llms import OpenAI
from langchain.memory import ConversationSummaryMemory
llm = OpenAI(temperature=0)
memory = ConversationSummaryMemory(llm=llm, memory_key="history")

3.4 ConversationSummaryBufferMemory(对话摘要+缓冲混合内存)

特点:结合上述两种内存优点,近期消息保留原文久远内容使用摘要,平衡信息完整性与内存效率

3.5 ConversationEntityMemory(实体内存)

特点:专注于识别和存储对话中的实体(如人名、组织、地点)及其属性,适合个性化助手场景,让AI真正"认识"用户

3.6 ConversationKGMemory(知识图谱内存)

特点:构建对话知识图谱,将对话中的实体关系结构化(如"张三是产品经理"、"李华在杭州工作"),适合需要关系推理的复杂问答系统

3.7 VectorStoreRetrieverMemory(向量存储内存)

特点:将对话历史存储为向量嵌入到向量数据库(如Pinecone、Chroma、FAISS),通过语义相似度检索相关历史,适合大规模知识库集成和长期记忆场景

四、ChatMessageHistory:底层消息存储机制

4.1 基本概念

ChatMessageHistory是LangChain中负责管理和操作聊天消息的底层工具类,是几乎所有对话内存的基础支撑。它提供了简单接口来添加用户/AI消息并获取完整消息列表。

# 使用示例
from langchain.memory import ChatMessageHistory
history = ChatMessageHistory()
history.add_user_message("Hello")
history.add_ai_message("Hi there!")
print(history.messages)  # 输出消息列表

4.2 消息存储选项

ChatMessageHistory支持多种存储后端:

  • 内存存储(默认):临时存储,应用重启后丢失
  • Redis存储:分布式持久化存储,适合生产环境
  • 文件存储:简单本地文件持久化
  • 数据库存储:SQL或NoSQL数据库集成
  • 自定义存储:实现BaseChatMessageHistory接口的自定义方案

五、在Chain中使用Memory

5.1 基本集成方法

将内存集成到Chain中通常需要以下步骤:

  1. 创建Memory实例:选择合适的内存类型并配置参数
  2. 将Memory传递给Chain:在初始化Chain时设置memory参数
  3. 在Prompt中引用内存变量:确保Prompt模板包含内存返回的变量名
# LLMChain使用示例
from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory
from langchain.prompts import PromptTemplate

llm = OpenAI(temperature=0)
prompt = PromptTemplate(
    template="Previous conversation: {chat_history}\nNew question: {question}\nAnswer:",
    input_variables=["chat_history", "question"]
)
memory = ConversationBufferMemory(memory_key="chat_history")
chain = LLMChain(llm=llm, prompt=prompt, memory=memory)

# 执行Chain(只需传入question,chat_history会自动从memory中获取)
response = chain.run(question="Hello")

5.2 与ChatModel集成

当使用ChatModel(如gpt-4)时,需设置return_messages=True,使内存返回消息列表而非字符串,以适配ChatModel的输入格式:

# ChatModel集成示例
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder

llm = ChatOpenAI()
prompt = ChatPromptTemplate(
    messages=[
        SystemMessagePromptTemplate.from_template("You are a helpful assistant"),
        MessagesPlaceholder(variable_name="chat_history"),  # 必须与memory_key一致
        HumanMessagePromptTemplate.from_template("{question}")
    ]
)
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
chain = LLMChain(llm=llm, prompt=prompt, memory=memory)

5.3 内存参数详解

参数名说明适用场景
memory_key内存变量在Chain中的键名(默认为"history")当Chain需要多个内存或自定义变量名时
return_messages是否返回消息列表而非字符串(默认为False)使用ChatModel时必须设为True
input_key指定保存到内存的输入键(默认None,自动推断)当Chain有多个输入时明确指定
output_key指定保存到内存的输出键(默认None,自动推断)当Chain有多个输出时明确指定
k窗口内存保留的最近轮数(仅适用于窗口内存)限制内存大小,防止上下文过长
llm用于摘要/实体提取的LLM(仅适用于摘要/实体内存)自定义摘要/实体提取逻辑

六、在Agent中使用Memory

6.1 基本集成方法

在Agent中使用内存与普通Chain类似,但需注意以下几点:

  1. 使用支持内存的Agent类型:如ConversationalAgent
  2. 确保Agent的Prompt中包含内存变量:通常是"chat_history"
  3. 正确设置内存的memory_key,与Prompt中变量名保持一致
# Agent使用示例
from langchain.agents import ConversationalAgent
from langchain.memory import ConversationBufferMemory
from langchain.llms import OpenAI

llm = OpenAI(temperature=0)
memory = ConversationBufferMemory(memory_key="chat_history")
agent = ConversationalAgent(
    llm=llm,
    system_message="You are a helpful assistant",
    memory=memory
)
agent.run("Hello!")

6.2 内存与工具调用的结合

在Agent执行过程中,内存会自动保存以下信息:

  • 用户输入的原始查询
  • Agent生成的思考过程
  • 工具调用的输入/输出
  • 最终的回答

这使Agent能够在多轮工具调用中保持上下文一致性,理解之前的操作和结果。

七、自定义Memory开发

7.1 开发步骤

如需创建适合特定场景的自定义内存,可按以下步骤进行:

  1. 继承BaseMemory类:实现抽象方法
  2. 定义内存的存储方式:选择合适的数据结构或外部存储
  3. 实现load_memory_variables:定义如何从存储中读取数据
  4. 实现save_context:定义如何将新上下文保存到存储
  5. 实现clear:定义如何清空内存
# 简单自定义内存示例
from langchain.memory import BaseMemory
from typing import Dict, Any

class CustomMemory(BaseMemory):
    def __init__(self):
        self.data = {}
    
    @property
    def memory_variables(self) -> List[str]:
        return ["custom_var"]
    
    def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
        return {"custom_var": self.data.get("value", "default")}
    
    def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:
        self.data["value"] = outputs.get("output_key", "no output")
    
    def clear(self) -> None:
        self.data = {}

7.2 与ChatMessageHistory结合

大多数自定义对话内存可通过组合BaseChatMemoryChatMessageHistory来简化实现,这比直接继承BaseMemory更高效:

# 使用ChatMessageHistory的自定义内存
from langchain.memory import BaseChatMemory
from langchain.schema import messages_to_dict, messages_from_dict

class MyCustomChatMemory(BaseChatMemory):
    def __init__(self):
        super().__init__()
        self.chat_memory = ChatMessageHistory()
    
    def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
        return {"history": self.chat_memory.messages}
    
    def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:
        user_msg = inputs.get("input", "")
        ai_msg = outputs.get("output", "")
        self.chat_memory.add_user_message(user_msg)
        self.chat_memory.add_ai_message(ai_msg)

八、长期记忆与持久化

8.1 LangGraph:官方推荐的长期记忆方案

从v0.3版本开始,LangChain推荐使用LangGraph作为长期记忆解决方案。LangGraph提供以下优势:

  • 灵活的存储后端:支持内存、文件、数据库等多种存储
  • 命名空间(Namespace)支持:可按用户/组织隔离存储,便于管理
  • 键值对(Key-Value)结构:每个记忆有唯一键,便于精确检索
  • 跨线程/会话共享:支持在不同对话中访问相同记忆
# LangGraph基本使用示例
from langchain.storage import LangGraph
from langchain.memory import CombinedMemory

# 配置存储
store = LangGraph(backend="sqlite")

# 创建长期内存
long_term_memory = store.create_memory(namespace="user_123", key="profile")

# 使用内存
long_term_memory.save("Hello, world!")
print(long_term_memory.load())  # 输出: "Hello, world!"

8.2 其他持久化方案

除LangGraph外,还可使用以下方案实现长期记忆:

  1. 文件存储:将内存数据序列化到本地文件
  2. 数据库存储:使用SQLAlchemy或NoSQL客户端连接数据库
  3. Redis存储:适合分布式应用,提供高性能读写
  4. 向量数据库:如Chroma、Pinecone等,适合存储对话嵌入,支持语义检索

九、选择合适的Memory类型

根据不同应用场景,推荐以下内存类型:

场景推荐内存类型原因
简单聊天机器人ConversationBufferMemory实现简单,保存完整对话历史
高频短对话ConversationBufferWindowMemory只保留最近对话,减少上下文长度
长对话/知识库ConversationSummaryMemory自动摘要,减少token消耗
个性化助手ConversationEntityMemory追踪用户和实体信息,提供个性化响应
复杂关系推理ConversationKGMemory构建知识图谱,理解实体间关系
大规模知识库集成VectorStoreRetrieverMemory通过向量检索获取相关历史,支持长期记忆
生产环境/分布式系统LangGraph + Redis/PostgreSQL提供持久化、分布式存储支持

十、总结与下一步

10.1 核心要点回顾

  • Memory是LangChain中维护状态和上下文的核心组件,使无状态的LLM能够拥有"记忆"
  • 内存系统支持读取(在Chain执行前加载历史)和写入(在执行后保存新上下文)两大操作
  • LangChain提供多种内存类型,从简单的对话缓冲到复杂的知识图谱和向量存储,满足不同场景需求
  • 与Chain/Agent集成时,需确保内存变量名与Prompt中变量名一致,并根据是否使用ChatModel设置return_messages参数

10.2 推荐下一步

  1. 尝试基础示例:从ConversationBufferMemory开始,理解内存基本用法
  2. 探索高级类型:根据应用场景选择合适的内存(如窗口内存、摘要内存)
  3. 集成到实际应用:将内存与Agent或自定义Chain结合,构建有状态的对话系统
  4. 考虑持久化:对需要长期记忆的应用,研究LangGraph或其他持久化方案
注:本指南基于LangChain官方文档(v0.3.x)整理,部分功能仍标记为Beta,建议在生产环境中使用前检查最新文档。