news 2026/5/2 4:03:18

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/4/27 6:13:01

提示工程架构师:用户交互优化的最新技术

提示工程架构师:用户交互优化的最新技术 引言 在当今数字化时代,用户交互的质量对于软件产品、应用程序乃至各种数字服务的成功起着决定性作用。良好的用户交互可以提高用户满意度、增加用户留存率,并最终提升业务价值。作为提示工程架构师,理解并应用最新的用户交互优化…

作者头像 李华
网站建设 2026/4/22 0:42:39

深入解析JDK1.8 HashMap优化之道

好的,我们来深入分析 HashMap 的核心机制,重点关注从 JDK 1.7 到 1.8 的重大改进,特别是解决死循环问题和引入高低位映射优化。 1. JDK 1.7 HashMap 的结构与潜在问题 在 JDK 1.7 中,HashMap 采用 数组 链表 的结构&#xff1a…

作者头像 李华
网站建设 2026/4/19 15:22:40

【课程设计/毕业设计】基于springboot+BS构架的失物招领系统设计与实现失物发布、招领管理、感谢信发表【附源码、数据库、万字文档】

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

作者头像 李华
网站建设 2026/4/20 0:38:54

Java毕设项目推荐-基于Java社区失物招领系统的设计与实现解决物品遗失问题基于springboot+BS构架的失物招领系统设计与实现【附源码+文档,调试定制服务】

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

作者头像 李华
网站建设 2026/4/19 20:24:13

Java计算机毕设之基于SpringBoot+Vue的城市公交查询系统的详细设计和实现基于springboot+bs架构的城市公交查询系统设计与实现(完整前后端代码+说明文档+LW,调试定制等)

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

作者头像 李华