标签 ConversationBufferMemory 下的文章

在构建问答Agent时,多轮对话的上下文记忆是核心需求——让Agent能记住历史对话内容,结合「历史问题+历史回答+当前问题」给出连贯回复,而非孤立回答每个问题。

LangChain中的ConversationBufferMemory轻量、易用的短期记忆组件,核心作用是按顺序缓存对话历史,并将历史内容注入到模型的输入提示中,实现问答Agent的短期记忆能力,适合中小长度的多轮对话场景。

本文将基于LangChain框架,从核心原理、完整可运行代码、关键细节、进阶优化四个维度,教你为问答Agent集成ConversationBufferMemory,支持OpenAI/国产大模型(通义千问/文心一言),代码可直接复用。

一、核心概念铺垫

1.1 ConversationBufferMemory 核心作用

  • 键值对形式按时间顺序存储对话历史(问题+回答);
  • 支持将对话历史格式化为字符串/消息对象,注入到LLM的输入提示中;
  • 提供清空记忆、获取记忆、修改记忆的便捷方法;
  • 轻量无依赖,无需额外存储,对话历史保存在内存中(会话结束即销毁,符合「短期记忆」定位)。

1.2 核心搭配

ConversationBufferMemory通常与ConversationChain(通用对话链)/RetrievalQA(知识库问答链)搭配使用,本文先实现基础问答Agent(基于ConversationChain),后续补充带知识库的问答Agent优化方案。

1.3 关键参数

参数名作用常用值
memory_key记忆在提示模板中的变量名(需与提示模板一致)chat_history(推荐)
return_messages记忆返回格式:True返回消息对象(HumanMessage/AIMessage)False返回拼接字符串False(基础场景)/True(复杂场景)
input_key输入问题的变量名input(默认,无需修改)
output_key输出回答的变量名output(默认,无需修改)

二、环境准备

安装LangChain核心依赖+大模型适配依赖(以OpenAI/通义千问为例,二选一即可):

# 核心依赖:LangChain核心+社区组件
pip install langchain-core langchain-community -i https://pypi.tuna.tsinghua.edu.cn/simple

# 可选1:OpenAI模型依赖(GPT-3.5/GPT-4)
pip install langchain-openai -i https://pypi.tuna.tsinghua.edu.cn/simple

# 可选2:国产大模型依赖(通义千问/文心一言/智谱清言)
pip install langchain-qianfan langchain-dashscope -i https://pypi.tuna.tsinghua.edu.cn/simple

三、完整实现:基础问答Agent+短期记忆

3.1 方案1:基于OpenAI模型(GPT-3.5/GPT-4)

# 1. 导入核心模块
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
from langchain.prompts import PromptTemplate
import os

# 2. 配置环境(OpenAI API密钥)
# 国内用户需配置代理:os.environ["HTTP_PROXY"] = "http://127.0.0.1:7890"
os.environ["OPENAI_API_KEY"] = "你的OpenAI API密钥"

# 3. 初始化LLM模型
llm = ChatOpenAI(
    model="gpt-3.5-turbo",  # 推荐gpt-3.5-turbo,性价比高
    temperature=0.1,        # 越低回答越稳定,适合问答场景
    max_tokens=2048
)

# 4. 初始化ConversationBufferMemory(核心:短期记忆)
memory = ConversationBufferMemory(
    memory_key="chat_history",  # 记忆变量名,需与提示模板中的{chat_history}一致
    return_messages=False,      # 返回字符串格式的对话历史,适合基础场景
    input_key="input"           # 输入问题的变量名,默认input即可
)

# 5. 自定义带记忆的提示模板(必须包含{chat_history}和{input})
# 模板说明:chat_history=历史对话,input=当前问题,让模型结合两者回答
prompt = PromptTemplate(
    input_variables=["chat_history", "input"],  # 必须包含记忆变量和输入变量
    template="""你是一个专业的问答助手,善于结合历史对话内容回答当前问题。
    历史对话:{chat_history}
    当前问题:{input}
    请简洁、准确地回答当前问题,无需额外赘述。"""
)

# 6. 构建带记忆的问答链(核心:将LLM、记忆、提示模板绑定)
conversation_chain = ConversationChain(
    llm=llm,
    memory=memory,
    prompt=prompt,
    verbose=True  # 开启详细日志,可查看输入的提示内容(含历史对话)
)

# 7. 测试多轮问答(验证记忆效果)
if __name__ == "__main__":
    # 第一轮问答
    print("===== 第一轮 =====")
    res1 = conversation_chain.invoke({"input": "什么是大语言模型?"})
    print("回答:", res1["output"], "\n")

    # 第二轮问答(结合历史:问大语言模型的核心优势)
    print("===== 第二轮 =====")
    res2 = conversation_chain.invoke({"input": "它的核心优势是什么?"})
    print("回答:", res2["output"], "\n")

    # 第三轮问答(结合历史:问该优势的应用场景)
    print("===== 第三轮 =====")
    res3 = conversation_chain.invoke({"input": "这些优势能用到哪些领域?"})
    print("回答:", res3["output"], "\n")

    # 手动查看记忆中的对话历史
    print("===== 查看短期记忆 =====")
    print(memory.load_memory_variables({}))

    # 清空记忆(可选)
    # memory.clear()
    # print("清空记忆后:", memory.load_memory_variables({}))

3.2 方案2:基于国产模型(通义千问,国内用户推荐)

替换上述步骤2和步骤3即可,其余代码完全不变,适配性拉满:

# 2. 配置环境(通义千问API密钥,从阿里云DashScope获取)
os.environ["DASHSCOPE_API_KEY"] = "你的通义千问API密钥"

# 3. 初始化通义千问模型(替换OpenAI)
from langchain_dashscope import ChatDashScope
llm = ChatDashScope(
    model="qwen-plus",  # 通义千问轻量版,免费额度足够测试
    temperature=0.1,
    max_tokens=2048
)

3.3 运行结果与关键日志

核心输出(记忆生效)

===== 第一轮 =====
回答: 大语言模型是基于大尺度语料训练、具备强大自然语言理解与生成能力的人工智能模型,能完成文本创作、问答、翻译等多种自然语言处理任务。

===== 第二轮 =====
回答: 大语言模型的核心优势包括:1. 强大的上下文理解与语义分析能力;2. 灵活的自然语言生成能力,可输出流畅、贴合语境的文本;3. 泛化能力强,能处理未见过的新问题;4. 多任务适配,无需单独训练即可完成多种NLP任务。

===== 第三轮 =====
回答: 这些优势可应用在智能客服、内容创作、教育辅导、代码开发、数据分析、机器翻译、智能助手等领域,覆盖互联网、教育、金融、制造业等多个行业。

===== 查看短期记忆 =====
{'chat_history': 'Human: 什么是大语言模型?\nAI: 大语言模型是基于大尺度语料训练、具备强大自然语言理解与生成能力的人工智能模型,能完成文本创作、问答、翻译等多种自然语言处理任务。\nHuman: 它的核心优势是什么?\nAI: 大语言模型的核心优势包括:1. 强大的上下文理解与语义分析能力;2. 灵活的自然语言生成能力,可输出流畅、贴合语境的文本;3. 泛化能力强,能处理未见过的新问题;4. 多任务适配,无需单独训练即可完成多种NLP任务。\nHuman: 这些优势能用到哪些领域?\nAI: 这些优势可应用在智能客服、内容创作、教育辅导、代码开发、数据分析、机器翻译、智能助手等领域,覆盖互联网、教育、金融、制造业等多个行业。'}

Verbose日志(关键:验证历史对话注入)

开启verbose=True后,可看到模型的实际输入提示包含了历史对话,这是记忆生效的核心:

> Entering new ConversationChain chain...
Prompt after formatting:
你是一个专业的问答助手,善于结合历史对话内容回答当前问题。
    历史对话:Human: 什么是大语言模型?
AI: 大语言模型是基于大尺度语料训练、具备强大自然语言理解与生成能力的人工智能模型,能完成文本创作、问答、翻译等多种自然语言处理任务。
    当前问题:它的核心优势是什么?
    请简洁、准确地回答当前问题,无需额外赘述。
> Finished chain.

四、关键细节:避免记忆失效的核心要点

ConversationBufferMemory使用简单,但容易因参数不匹配、提示模板错误导致记忆失效,以下是必须遵守的3条铁律:

4.1 提示模板必须包含memory_key指定的变量

比如memory_key="chat_history",则提示模板中必须有{chat_history},且输入变量列表要包含该变量:

# 正确:input_variables包含chat_history和input
prompt = PromptTemplate(
    input_variables=["chat_history", "input"],
    template="历史对话:{chat_history}  当前问题:{input}"
)

# 错误:缺少chat_history,记忆无法注入
prompt = PromptTemplate(
    input_variables=["input"],
    template="当前问题:{input}"
)

4.2 invoke入参必须是字典,且键为input_key

默认input_key="input",因此调用时必须传{"input": "你的问题"},而非直接传字符串:

# 正确
conversation_chain.invoke({"input": "什么是大语言模型?"})

# 错误:入参不是字典,记忆无法关联当前问题
conversation_chain.invoke("什么是大语言模型?")

4.3 避免手动修改对话历史(除非特殊需求)

ConversationBufferMemory自动追加每次的inputoutput到记忆中,无需手动修改:

# 自动追加:无需干预
conversation_chain.invoke({"input": "问题1"})  # 记忆中添加问题1+回答1
conversation_chain.invoke({"input": "问题2"})  # 记忆中追加问题2+回答2

# 手动修改(特殊需求时使用)
memory.save_context(
    inputs={"input": "手动添加的问题"},
    outputs={"output": "手动添加的回答"}
)

五、进阶优化:适配更复杂的问答场景

5.1 优化1:带知识库的问答Agent+短期记忆

实际场景中,问答Agent通常需要结合私有知识库(如PDF/文档),此时将ConversationChain替换为RetrievalQA,并搭配ConversationBufferMemory即可实现「知识库+多轮记忆」的问答能力:

# 新增:导入知识库相关模块
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain_text_splitters import CharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain.chains import RetrievalQA

# 1. 加载知识库(以文本文件为例,可替换为PDF/Word加载器)
loader = TextLoader("your_knowledge_base.txt")  # 你的知识库文件
documents = loader.load()
# 分割文本为小片段
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
texts = text_splitter.split_documents(documents)
# 构建向量库
embeddings = OpenAIEmbeddings()
db = FAISS.from_documents(texts, embeddings)
retriever = db.as_retriever(search_kwargs={"k": 3})  # 每次检索3个相关片段

# 2. 初始化记忆(与基础版一致)
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=False,
    input_key="question"  # 注意:RetrievalQA的默认输入键是question,需修改
)

# 3. 构建带记忆的知识库问答链
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",  # 适合小片段文本,简单高效
    retriever=retriever,
    memory=memory,
    chain_type_kwargs={
        "prompt": PromptTemplate(
            input_variables=["chat_history", "context", "question"],
            template="""结合历史对话和知识库内容回答当前问题,若知识库无相关内容,仅结合历史对话回答。
            历史对话:{chat_history}
            知识库内容:{context}
            当前问题:{question}
            回答要求:简洁、准确,基于知识库内容,不要编造。"""
        )
    },
    verbose=True
)

# 4. 测试:结合知识库+历史对话的多轮问答
qa_chain.invoke({"question": "知识库中提到的大语言模型有哪些应用?"})
qa_chain.invoke({"question": "这些应用中,哪个在教育领域的落地效果最好?"})  # 结合历史

5.2 优化2:限制记忆长度(避免历史对话过长)

ConversationBufferMemory无限制追加对话历史,当对话轮数过多时,会导致提示词过长、推理成本增加、模型注意力分散

解决方案:使用ConversationBufferWindowMemory(窗口记忆),仅保留最近N轮对话,本质是ConversationBufferMemory的进阶版,参数完全兼容:

from langchain.memory import ConversationBufferWindowMemory

# 仅保留最近2轮对话,超出的自动丢弃
memory = ConversationBufferWindowMemory(
    memory_key="chat_history",
    k=2,  # 核心参数:保留最近k轮对话
    return_messages=False
)

# 用法与ConversationBufferMemory完全一致,无需修改其他代码
conversation_chain = ConversationChain(llm=llm, memory=memory, prompt=prompt)

5.3 优化3:记忆格式为消息对象(适合复杂提示)

当提示模板需要更精细的对话格式时,将return_messages=True,记忆会返回HumanMessage/AIMessage对象,而非拼接字符串,便于灵活格式化:

# 初始化记忆:返回消息对象
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True,  # 核心:返回消息对象
    input_key="input"
)

# 自定义提示模板:遍历消息对象格式化
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder

# 使用MessagesPlaceholder接收消息对象,无需手动拼接
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是专业的问答助手,结合历史对话回答问题。"),
    MessagesPlaceholder(variable_name="chat_history"),  # 匹配memory_key
    ("human", "{input}")  # 匹配input_key
])

# 构建链(用法不变)
conversation_chain = ConversationChain(llm=llm, memory=memory, prompt=prompt)

六、常用操作:记忆的增删改查

ConversationBufferMemory提供了便捷的方法操作记忆,满足个性化需求:

# 1. 查看记忆内容
memory.load_memory_variables({})  # 返回字典,键为memory_key

# 2. 清空记忆(会话结束/切换用户时使用)
memory.clear()

# 3. 手动添加记忆
memory.save_context(
    inputs={"input": "手动添加的问题"},
    outputs={"output": "手动添加的回答"}
)

# 4. 手动删除记忆(需先获取记忆,再修改,最后重新保存)
# 步骤1:获取记忆内容
chat_history = memory.load_memory_variables({})["chat_history"]
# 步骤2:修改/删除内容(如删除最后一行)
chat_history = "\n".join(chat_history.split("\n")[:-2])
# 步骤3:重新保存
memory.chat_memory.add_user_message("")  # 清空原有记忆
memory.chat_memory.add_ai_message("")
memory.save_context(inputs={"input": ""}, outputs={"output": chat_history})

七、总结

7.1 核心流程回顾

为问答Agent添加ConversationBufferMemory的核心步骤仅5步:

  1. 安装LangChain核心依赖+大模型依赖;
  2. 初始化LLM模型(OpenAI/国产模型);
  3. 初始化ConversationBufferMemory,指定memory_key
  4. 构建包含记忆变量的提示模板;
  5. 将LLM、记忆、提示模板绑定到对话链,调用invoke实现多轮问答。

7.2 记忆组件选型建议

记忆组件核心特点适用场景
ConversationBufferMemory无限制保存所有对话历史,轻量易用短对话、测试场景
ConversationBufferWindowMemory保留最近N轮对话,限制长度常规多轮问答场景(推荐)
ConversationSummaryMemory对长对话历史做摘要压缩,节省令牌超长对话、高成本模型场景
VectorStoreRetrieverMemory将对话历史存入向量库,按需检索相关历史需精准匹配历史对话的复杂场景

7.3 性能优化建议

  1. 优先使用ConversationBufferWindowMemory,并设置合理的k值(如3-5轮);
  2. 降低LLM的max_tokens,避免无意义的长回答;
  3. 开启verbose=False(生产环境),减少日志开销;
  4. 生产环境中,可将记忆与会话ID绑定,实现多用户隔离。

八、常见问题排查

问题1:记忆失效,模型不结合历史对话回答

  • 原因:提示模板缺少memory_key变量,或input_variables未包含该变量;
  • 解决:检查提示模板,确保包含{chat_history}(或自定义的memory_key),且input_variables列表包含该变量。

问题2:调用时提示「key error: input」

  • 原因:invoke入参不是字典,或键与input_key不匹配;
  • 解决:调用时传{"input": "你的问题"}(默认input_key="input"),若修改了input_key,则传对应键。

问题3:知识库问答Agent记忆失效

  • 原因:RetrievalQA的默认输入键是question,而非input,记忆的input_key不匹配;
  • 解决:初始化记忆时设置input_key="question"

问题4:对话历史过长,模型推理变慢

  • 原因:ConversationBufferMemory无限制追加历史;
  • 解决:替换为ConversationBufferWindowMemory,设置k值限制轮数。

一、核心概念

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,建议在生产环境中使用前检查最新文档。