在这里插入图片描述

前言

在前面的系列文章中,我们探讨了 LangChain 的基础记忆模块以及如何通过窗口限制方法(如 ConversationBufferWindowMemory)来管理内存。我们发现,完整保存所有对话历史(ConversationBufferMemory)会迅速消耗大量内存和 Token,而简单地按对话条数或 Token 数量进行“硬截断”又可能丢失关键的上下文信息。

有没有一种两全其美的方法,既能节省宝贵的内存资源,又能确保对话的核心信息不丢失呢?

答案是肯定的。LangChain 提供了更为智能的解决方案:摘要式记忆。本文,我们将深入剖析两种强大的摘要记忆模块:ConversationSummaryMemoryConversationSummaryBufferMemory


ConversationSummaryMemory:用 AI 为你的对话写总结

想象一下,你不需要记住每一次对话的每个字,而只需要一个智能助手帮你把过去的聊天内容提炼成一段精准的摘要。这就是 ConversationSummaryMemory 的核心思想。

它不再存储原始的、冗长的对话文本,而是利用一个大语言模型(LLM)将对话历史动态地生成一份精简的摘要。

核心特点

  • 智能摘要生成: 利用 LLM 的强大理解能力,将多轮对话压缩成一段包含核心信息的摘要。这不仅是文本的删减,更是语义的提炼。
  • 动态更新: 随着新对话的加入,它会“阅读”旧的摘要和新的对话内容,然后生成一份全新的、与时俱进的摘要。
  • 上下文优化: 特别适合需要长期记忆的对话场景。无论对话进行多久,模型总能通过这份浓缩的摘要快速把握全局上下文。

代码实战

场景1:从零开始的对话

如果我们的对话是全新的,没有任何历史消息,可以像下面这样直接实例化 ConversationSummaryMemory

from langchain_openai import ChatOpenAI
from langchain.memory import ConversationSummaryMemory

# 初始化 ConversationSummaryMemory,它需要一个 llm 实例来生成摘要
memory = ConversationSummaryMemory(llm=llm)

# 模拟对话过程
memory.save_context({"input": "你好"}, {"output": "你好!有什么可以帮助你的吗?"})
memory.save_context({"input": "我想了解一下 LangChain 的记忆模块"}, {"output": "当然,LangChain 提供了多种记忆模块,比如 Buffer, Window, Token, 和 Summary。你对哪种感兴趣?"})
memory.save_context({"input": "今天我们先聊聊 Summary 相关的吧"}, {"output": "好的,Summary 相关的记忆主要有两种:ConversationSummaryMemory 和 ConversationSummaryBufferMemory。"})

# 加载记忆,查看 LLM 生成的摘要
print(memory.load_memory_variables({}))

输出结果分析

{
  "history": "The human greets the AI, and the AI responds politely. The human expresses interest in learning about LangChain's memory modules. The AI lists several types and asks which one the human is interested in. The human specifies a desire to discuss Summary-related memory modules, and the AI explains that there are two main types: ConversationSummaryMemory and ConversationSummaryBufferMemory."
}

从输出可以看到,原始的三轮对话被 LLM 智能地总结成了一段通顺流畅的英文叙述。它准确地捕捉了对话的核心:用户想了解 LangChain 的 Summary 记忆模块,AI 则引出了两种具体的类型。 这份摘要就是未来对话的上下文基础。


场景2:加载已有的对话历史

在很多应用中,我们需要从数据库或其他地方加载已有的对话记录来继续交流。这时,可以使用 from_messages() 方法来快速初始化记忆。

from langchain.schema import HumanMessage, AIMessage

# 假设这是从数据库中取出的历史消息
past_messages = [
    HumanMessage(content="你好,我上周是不是咨询过一个关于订单 #12345 的问题?"),
    AIMessage(content="您好,是的。根据记录,您当时咨询了关于订单 #12345 的物流进度问题。"),
    HumanMessage(content="对,那现在这个订单到哪里了?"),
    AIMessage(content="我帮您查询一下... 请稍等。"),
]

memory = ConversationSummaryMemory.from_messages(
    llm=llm,
    chat_memory=past_messages,
    return_messages=True # 建议设置为 True,以 Message 对象形式返回
)

# 查看加载历史消息后生成的摘要
print(memory.load_memory_variables({}))

这种方式非常适合需要“断点续聊”的客服或助手场景。


ConversationSummaryBufferMemory:近期细节与长远记忆的完美平衡

ConversationSummaryMemory 非常强大,但它有一个小缺点:所有的历史都被压缩成了摘要,可能会丢失最近几轮对话的精确措辞和细节。在某些场景下,这些细节至关重要。

为了解决这个问题,ConversationSummaryBufferMemory 应运而生。它是一种混合型记忆机制,完美结合了 ConversationBufferMemory(保留原始对话)和 ConversationSummaryMemory(生成摘要)的优点。

它的工作逻辑是:

  1. 在内存中开辟一个“缓冲区”(Buffer),用于存储最近的几轮原始对话。
  2. 通过 max_token_limit 参数设定这个缓冲区的大小。
  3. 当新的对话使缓冲区中的 Token 总数超过 max_token_limit 时,它会自动将最旧的那部分对话提炼成摘要,并存入摘要区,然后将新的对话存入缓冲区。

核心特点

  • 保留最近细节: 确保最近的 N 条对话是原汁原味的,让模型能够精确响应。
  • 摘要较早历史: 对超出缓冲区的旧对话进行智能压缩,有效控制内存增长。
  • 平衡细节与效率: 既不会因摘要丢失关键细节,又能处理超长对话,是大多数场景下的“最优解”。

代码实战

让我们通过一个例子来看看它是如何工作的。

from langchain.memory import ConversationSummaryBufferMemory

# 初始化,设置一个较小的 max_token_limit 以便观察效果
# 注意:token 的计算方式与模型有关,这里设置为 60 只是为了演示
memory = ConversationSummaryBufferMemory(
    llm=llm,
    max_token_limit=60, # 设置一个小的 token 限制来触发摘要
    return_messages=True
)

# 第1轮对话
memory.save_context({"input": "你好,我叫张三"}, {"output": "你好张三!很高兴认识你。"})

# 第2轮对话
memory.save_context({"input": "我是一名软件工程师"}, {"output": "哇,那很酷!你主要使用什么技术栈呢?"})

# 第3轮对话(这将触发摘要机制)
memory.save_context({"input": "我主要用 Python 和 Go"}, {"output": "非常棒的组合!Python 和 Go 在后端开发中都很流行。"})

# 查看记忆内容
print(memory.load_memory_variables({}))

输出结果解析

{
  "history": [
    SystemMessage(content='The human, Zhang San, introduces himself as a software engineer. The AI responds positively and asks about his technology stack.'),
    HumanMessage(content='我主要用 Python 和 Go'),
    AIMessage(content='非常棒的组合!Python 和 Go 在后端开发中都很流行。')
  ]
}

让我们来仔细解析这个输出:

  1. SystemMessage (摘要部分): 注意看,对比ConversationSummaryMemory,第一条消息变成了 SystemMessage。它的内容 "The human, Zhang San, introduces himself as a software engineer..." 正是前两轮对话的摘要!因为第三轮对话的加入使得总 Token 数超过了我们设定的 max_token_limit=60,所以最早的对话被 LLM 总结并压缩了。
  2. HumanMessage & AIMessage (缓冲区部分): 最近的一轮对话(“我主要用 Python 和 Go”...)则被完整地保留了下来。

这个结果清晰地展示了 ConversationSummaryBufferMemory 的混合机制:旧对话被总结,新对话被保留


完整项目实例:智能电商客服12345

在这个示例中,我们将模拟一个用户与电商客服的对话。用户会先咨询一个产品 (“VisionPro Max 笔记本电脑”),然后切换到另一个完全不相关的产品 (“SilentStep 机械键盘”),最后再回头询问第一个产品的信息。我们将观察 ConversationSummaryBufferMemory 如何帮助 AI 客服应对这种上下文切换。

调用大模型部分:

import os
import dotenv
from langchain_openai import ChatOpenAI

dotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
os.environ["OPENAI_BASE_URL"] = os.getenv("OPENAI_BASE_URL")

llm = ChatOpenAI(
    model=os.environ.get("CHAT_MODEL")
)

Memory 部分:

from langchain.chains import ConversationChain
from langchain.memory import ConversationSummaryBufferMemory
from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate

# 创建一个精心设计的 Prompt 模板
prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(
        """
        你是一个名为 "12345" 的在线电子商店的AI客服助手。
        你的风格应该是友好、健谈,并能提供关于产品的具体细节。
        请根据下面的对话历史和用户最新的问题来提供帮助。

        对话摘要和近期历史:
        {history}
        """
    ),
    HumanMessagePromptTemplate.from_template("{input}")
])

# 初始化 ConversationSummaryBufferMemory
# 这是本示例的核心!
# + 我们提供一个 llm 用于生成摘要。
# + max_token_limit 设置得比较小(例如 150),以便我们可以快速看到摘要的生成过程。
# 在真实应用中,这个值可以设置得更大,比如 500 或 1000。
memory = ConversationSummaryBufferMemory(
    llm=llm,
    max_token_limit=150,
    memory_key="history", # 必须与 prompt 中的占位符一致
    return_messages=True # 以 LangChain Message 对象列表的形式返回历史记录
)

# 创建 ConversationChain
# 我们将 llm, memory, 和 prompt 组装在一起
# verbose=True 会在控制台打印出完整的 prompt,方便我们观察 memory 的变化
conversation_chain = ConversationChain(
    llm=llm,
    memory=memory,
    prompt=prompt,
    verbose=True
)

# 开始模拟对话
print("--- 对话开始 ---")

# 第一轮对话:咨询笔记本电脑
response = conversation_chain.predict(input="你好,我想了解一下 VisionPro Max 笔记本电脑。")
print(f"AI: {response}\n")

# 第二轮对话:询问具体配置
response = conversation_chain.predict(input="它的内存和存储是多大的?")
print(f"AI: {response}\n")

# 第三轮对话:切换到另一个产品 (这很可能会触发摘要)
response = conversation_chain.predict(input="好的,谢谢。另外,你们有 SilentStep 机械键盘吗?我想了解下它的特点。")
print(f"AI: {response}\n")

# 第四轮对话:继续询问键盘
response = conversation_chain.predict(input="这个键盘是无线连接的吗?")
print(f"AI: {response}\n")

# 第五轮对话:回头询问最初的产品
# 这是关键测试!AI 是否还记得 "那台笔记本" 指的是什么?
print("--- 关键测试:AI 是否还记得之前的笔记本电脑? ---")
response = conversation_chain.predict(input="听起来不错。回到刚才那台笔记本电脑,它有什么折扣活动吗?")
print(f"AI: {response}\n")

print("--- 对话结束 ---")

整个应用在进行第一轮与第二轮对话时,未超出 max_token_limit 限制,故 memory 中存储的是完整的原始对话记录。传递给 LLM 的 {history} 部分是完整的对话列表。第三轮对话开始,超出了限制,ConversationSummaryBufferMemory 自动启动了摘要机制,将最早的内容进行汇总总结。第四轮…第五轮…


总结

告别生硬的对话截断,LangChain 的摘要式记忆为我们构建真正智能的、有长期记忆的对话应用提供了强大的武器。

  • ConversationSummaryMemory 适用于需要保留全局核心上下文的超长对话,如知识问答、心理咨询机器人等。它的优点是极致的内存效率。
  • ConversationSummaryBufferMemory 一个更加通用和强大的“瑞士军刀”。它在保留近期对话细节和压缩远期历史之间取得了完美平衡,是构建大多数现代聊天机器人(如智能客服、个人助手)的首选。

在下一篇文章中,我们将继续探索 LangChain 中更高级的记忆类型,例如如何让 AI 记住对话中的“实体”(Entity),敬请期待!


2025.10.11 北京·西直门

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐