news 2026/5/12 8:05:31

LangChain 经典回顾:ConversationBufferMemory 与 ConversationChain

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LangChain 经典回顾:ConversationBufferMemory 与 ConversationChain

随着 LangChain 的快速迭代,LCEL (LangChain Expression Language) 逐渐成为主流。然而,在很多现有的生产系统和教程中,我们依然频繁看到经典(Legacy)组件的身影。

本文将带你深入了解 LangChain 最经典的对话组合:ConversationBufferMemoryConversationChain

1. ConversationBufferMemory:最纯粹的记忆

1.1 什么是 ConversationBufferMemory?

ConversationBufferMemory是 LangChain 中最基础的记忆组件。它的逻辑非常简单粗暴:它把你说过的每一句话、AI 回复的每一句话,都原封不动地塞进一个变量里。

1.2 工作原理

想象一个无限长的记事本:

  1. 用户说:“Hi” -> 记事本写:“Human: Hi”
  2. AI 回复:“Hello” -> 记事本写:“AI: Hello”
  3. 下次对话时,LangChain 会把记事本里的所有内容复制粘贴到 Prompt 的{history}位置。

1.3 优缺点分析

  • 优点
    • 无损:完全保留了对话的所有细节。
    • 简单:易于理解和调试。
  • 缺点
    • Token 爆炸:随着对话变长,历史记录会越来越长,最终超出 LLM 的 Context Window(上下文窗口)限制,导致报错或费用激增。

2. ConversationChain:开箱即用的对话链

2.1 什么是 ConversationChain?

ConversationChain是一个预封装好的 Chain。它帮你把以下三样东西组装在了一起:

  1. LLM(大语言模型)
  2. Memory(记忆组件)
  3. Prompt(提示词模板)

你不需要自己写 Prompt Template,不需要自己处理history变量注入。它内置了一个默认的 Prompt(通常是“The following is a friendly conversation…”)。

2.2 代码实战

让我们通过一个简单的 Python 脚本 (src/examples/memory/demo_conversation_buffer_memory.py) 来演示它们的配合。

fromlangchain_classic.memoryimportConversationBufferMemoryfromlangchain_classic.chainsimportConversationChainfromsrc.llm.gemini_chat_modelimportget_gemini_llm# 1. 初始化 Memory# 它负责在内存中维护一个不断增长的字符串memory=ConversationBufferMemory()# 2. 初始化 Chain# 自动将 Memory 挂载到 LLM 上conversation=ConversationChain(llm=get_gemini_llm(),memory=memory,verbose=True# 开启 verbose 可以看到它到底发给了 LLM 什么)# 3. 第一轮对话conversation.predict(input="Hi, my name is Alice.")# Memory 更新: "Human: Hi, my name is Alice.\nAI: Hello Alice!"# 4. 第二轮对话conversation.predict(input="What is my name?")# Memory 再次更新...

2.3 运行结果解析

当我们运行上述代码时,ConversationChain会自动构建如下 Prompt 发送给 LLM:

The following is a friendly conversation between a human and an AI... Current conversation: Human: Hi, my name is Alice. AI: Hello Alice! Human: What is my name? AI:

这就是为什么 AI 能够回答 “Your name is Alice”,因为它在 Prompt 里“看到”了之前的对话记录。

3. 进阶:如何查看记忆内容?

你可以随时调用load_memory_variables来查看当前 Buffer 里存了什么:

print(memory.load_memory_variables({}))# 输出:# {'history': "Human: Hi...\nAI: Hello..."}

4. 进阶:如何限制历史消息上限?

用户提问:ConversationBufferMemory可以限制历史消息的上限吗?

回答:不可以,它默认保存所有历史。

如果你需要限制历史记录(为了节省 Token 或防止 Context Window 溢出),你需要切换到它的兄弟组件

  • ConversationBufferWindowMemory: 按轮数限制(如只保留最近 5 轮)。
  • ConversationTokenBufferMemory: 按 Token 数限制(如只保留最近 2000 token)。
  • ConversationSummaryMemory: 自动调用 LLM 对旧历史进行摘要总结。

5. 进阶:如何持久化到数据库?

用户提问:ConversationBufferMemory只能把消息保存到内存吗?数据库是否可以?

回答:它可以支持数据库,但需要配合chat_memory参数。

默认情况下,ConversationBufferMemory在内部创建了一个临时的内存列表 (ChatMessageHistory)。一旦程序重启,数据就丢失了。

如果你想把它保存到数据库(如 Redis, Postgres),你需要替换掉这个临时的内存列表,换成一个连接数据库的 History 对象

代码示例 (伪代码)

fromlangchain_community.chat_message_historiesimportRedisChatMessageHistoryfromlangchain_classic.memoryimportConversationBufferMemory# 1. 创建一个连接 Redis 的 History 对象# 这不是普通的 list,而是一个读写 Redis 的代理message_history=RedisChatMessageHistory(url="redis://localhost:6379/0",session_id="my-session")# 2. 将这个 History 对象传给 BufferMemory# 此时 Memory 不再把数据存在 RAM 里,而是直接读写 Redismemory=ConversationBufferMemory(chat_memory=message_history)# 后续用法完全一样,但数据会自动持久化到 Redisconversation=ConversationChain(llm=llm,memory=memory)

原理ConversationBufferMemory是一个逻辑层(负责把消息格式化为 Prompt),而chat_memory是存储层(负责物理存取)。将存储层替换为数据库实现即可。

6. 新旧对比:本质区别是什么?

除了使用方式的不同,两者在架构设计上有着本质的区别:

1. 架构模式:实例绑定 (Stateful) vs 动态注入 (Stateless)

用户提问:ConversationChain不是也可以传入不同的 Memory 吗?为什么说它是耦合的?

回答:这里的耦合是指运行时的实例级绑定

  • ConversationChain (旧)
    当你初始化chain = ConversationChain(memory=mem)时,这个chain对象就和特定的那份mem对象绑定死了。

    • 后果:这个chain实例变成了“有状态”的对象。它里面存着“用户A”的聊天记录。你不能把这个chain实例拿去服务“用户B”,否则用户B会看到用户A的记录。
    • 并发问题:在 Web 服务中,你必须为每个用户new一个新的 Chain 实例,无法通过全局单例复用。
  • RunnableWithMessageHistory (新)
    它本身不持有任何具体的 Memory 对象。它持有的只是一个工厂函数get_session_history)。

    • 后果:这个 Wrapper 对象是“无状态”的。它不知道也不关心当前在服务谁。
    • 动态性:每次调用invoke时,它才根据传入的config={"session_id": "B"}动态地去工厂里找“用户B”的记录。
    • 并发优势:你可以创建一个全局单例runnable,让它同时并发服务成千上万个用户,完全线程安全。

2. 逻辑灵活性:硬编码 vs 组合

  • ConversationChain (旧):它的内部逻辑(Load -> Prompt -> LLM -> Save)是硬编码在 Python 类里的。如果你想在“保存历史”之前加一个“敏感词过滤”步骤,你必须继承并重写这个类。
  • RunnableWithMessageHistory (新):它遵循 LCEL 组合原则。内部的 Chain 可以是任意复杂的 DAG(有向无环图)。你可以在任何环节插入自定义逻辑,Wrapper 只负责最外层的历史管理。
  • RunnableWithMessageHistory (新):Chain 对象本身是无状态的。状态(记忆)并不存在 Chain 对象里,而是根据config={"session_id": "..."}动态加载的。这意味着同一个 Chain 实例可以并发服务成千上万个用户(只要 session_id 不同)。这是生产环境的高并发神器。

对比总结表

特性ConversationChain (Legacy)RunnableWithMessageHistory (LCEL)
架构模式强耦合单体解耦包装器
并发模型有状态(Stateful) - 难以并发无状态(Stateless) - 天然并发
灵活性低 (Prompt 固定)高 (Prompt/逻辑完全自定义)
流式支持较弱原生支持
推荐场景快速原型、本地单人脚本生产级 Web 服务、高并发系统

结论:如果你正在构建一个多用户的 Web 应用,请务必使用RunnableWithMessageHistory

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/11 15:50:12

寒假学习10(HAL库1+模数电10)

硬件系统的组成 :主控芯片 外围设备 一 。 主控芯片的选型 1 体系结构: arm : 移动端 功耗低/算力/处理能力/主频 缺点:不开源 ARM 架构 Cortex-A : MPU : 树莓派(博通,学习资料多)&#x…

作者头像 李华
网站建设 2026/5/12 8:05:18

Java计算机毕设之基于Java的自驾游攻略查询系统的设计与实现基于Java的自驾游攻略查询系统的设计与实现(完整前后端代码+说明文档+LW,调试定制等)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华
网站建设 2026/5/10 8:15:34

Java RESTful开发:从入门到精通

好的,这是一份关于 Java RESTful 接口开发的进阶指南: Java RESTful 接口开发:从入门到精通 RESTful API 是现代 Web 应用和服务之间通信的核心。使用 Java 开发高效、健壮、易用的 RESTful 接口是后端开发的重要技能。以下是从入门到精通的…

作者头像 李华
网站建设 2026/5/11 2:00:05

深入解析Java栈帧机制

Java方法栈帧深度解析1. 栈帧基本结构在Java虚拟机(JVM)执行过程中,每个方法调用都会在栈内存中创建一个栈帧(Stack Frame),包含以下核心组件:局部变量表:存储方法参数和方法内定义的…

作者头像 李华
网站建设 2026/5/10 8:33:01

【计算机毕业设计案例】基于springboot+BS构架的失物招领平台失物招领系统设计与实现(程序+文档+讲解+定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华
网站建设 2026/5/9 13:45:19

Java毕设选题推荐:基于Sprinboot的失物招领系统设计与实现基于springboot+BS构架的失物招领系统设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华