【LangChain】存储与管理对话历史

0. 代码演示

from langchain_community.chat_message_histories import SQLChatMessageHistory

def get_session_history(session_id):
    # 通过 session_id 区分对话历史,并存储在 sqlite 数据库中
    return SQLChatMessageHistory(session_id, "sqlite:///memory.db")

from langchain_core.messages import HumanMessage
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser

model = ChatOpenAI(model="gpt-4o-mini", temperature=0)

runnable = model | StrOutputParser()

runnable_with_history = RunnableWithMessageHistory(
    runnable, # 指定 runnable
    get_session_history, # 指定自定义的历史管理方法
)

runnable_with_history.invoke(
    [HumanMessage(content="你好,我叫麦酷")],
    config={"configurable": {"session_id": "wzr"}},
)

 '你好,麦酷!很高兴再次见到你。有什么想聊的或者需要帮助的呢?'

runnable_with_history.invoke(
    [HumanMessage(content="你知道我叫什么名字")],
    config={"configurable": {"session_id": "wzr"}},
)

'是的,你叫麦酷。有什么我可以帮助你的吗?'

runnable_with_history.invoke(
    [HumanMessage(content="你知道我叫什么名字")],
    config={"configurable": {"session_id": "test"}},
)

'抱歉,我无法知道你的名字。你可以告诉我你的名字,或者如果你有其他问题,我也很乐意帮助你!'

代码功能解析

这段代码实现了一个带持久化历史记忆的对话系统,通过 session_id 区分不同用户的对话历史,并存储到 SQLite 数据库中。以下是核心模块的解析:


1. 对话历史管理模块

from langchain_community.chat_message_histories import SQLChatMessageHistory

def get_session_history(session_id):
    # 每个 session_id 对应独立的数据库记录
    return SQLChatMessageHistory(
        session_id=session_id, 
        connection_string="sqlite:///memory.db" # SQLite 数据库路径
    )
  • 核心作用: 为每个用户/会话创建独立的历史存储
  • 技术细节:
    • 使用 SQLite 数据库存储对话记录(文件名为 memory.db
    • session_id 作为主键区分不同对话(如用户ID、设备ID等)
    • 实际表结构包含 id, session_id, message, timestamp 等字段
  • 扩展性:可替换为其他存储后端(如PostgreSQL、Redis)

2. 对话链构建模块

from langchain_core.runnables.history import RunnableWithMessageHistory

model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
runnable = model | StrOutputParser()  # 基础问答链

runnable_with_history = RunnableWithMessageHistory(
    runnable=runnable,                # 原始链
    get_session_history=get_session_history, # 历史管理方法
    input_messages_key="input",       # 输入消息字段名(默认)
    history_messages_key="history"    # 历史消息字段名(默认)
)
  • 组件连接:
    用户输入
    附加历史
    大模型
    输出解析
    保存历史
  • 关键参数:
    • input_messages_key: 输入消息在上下文中的键名
    • history_messages_key: 历史消息的键名(模型需支持上下文窗口)

3. 对话调用示例

response = runnable_with_history.invoke(
    [HumanMessage(content="你好,我叫麦酷")], # 当前消息
    config={"configurable": {"session_id": "wzr"}} # 指定会话
)
  • 执行流程:
    1. 根据 session_id="wzr" 从数据库加载历史消息
    2. 将当前消息 "你好,我叫麦酷" 添加到历史记录
    3. 组合历史消息 + 当前输入 → 发送给 GPT-4
    4. 解析模型输出 → 返回最终响应
    5. 将新消息对(用户输入 + 模型回复)保存到数据库

4. 数据库操作示例

假设进行三次连续对话:

调用顺序用户输入数据库存储内容(session_id=“wzr”)
第一次“你好,我叫麦酷”[Human: 你好,我叫麦酷, AI: 回复1]
第二次“记住我的名字了吗?”添加 [Human: 记住我的名字了吗?, AI: 回复2]
第三次“我是谁?”添加 [Human: 我是谁?, AI: 回复3]

模型在第三次调用时,实际接收的上下文包含前两次对话历史,因此能正确回答姓名。


5. 关键技术点

5.1 历史注入机制
# 伪代码展示实际发送给模型的内容
full_context = [
    {"role": "user", "content": "你好,我叫麦酷"},
    {"role": "assistant", "content": "回复1"},
    {"role": "user", "content": "记住我的名字了吗?"},
    {"role": "assistant", "content": "回复2"},
    {"role": "user", "content": "我是谁?"}
]
response = model.generate(full_context)
5.2 自动历史管理
  • 自动追加:每次调用自动添加新消息到历史
  • 上下文截断:当历史超过模型窗口时需处理(此示例未展示)

6. 优化建议

6.1 历史长度控制
from langchain.memory import ConversationBufferWindowMemory

# 仅保留最近3轮对话
memory = ConversationBufferWindowMemory(k=3)
runnable_with_history.memory = memory
6.2 自定义历史格式
def custom_history_formatter(history):
    return "\n".join([f"{msg.type}: {msg.content}" for msg in history])

chain = runnable_with_history.configure(
    history_formatter=custom_history_formatter
)
6.3 多模态历史支持
from langchain_core.messages import ImageMessage

# 支持图片消息存储
history.add_message(ImageMessage(content="path/to/image.png"))

7. 常见问题排查

现象可能原因解决方案
数据库无写入文件权限问题检查 memory.db 可写权限
历史消息未生效session_id 不一致确认每次调用使用相同 session_id
响应时间越来越长历史消息过多未截断添加窗口记忆或摘要记忆
中文内容存储乱码数据库编码问题使用 sqlite:///memory.db?charset=utf8

8. 典型应用场景

  1. 客服系统

    # 根据用户手机号保持对话连续性
    session_id = user.phone_number
    
  2. 教育机器人

    # 为每个学生保存学习进度
    session_id = f"{student_id}-{course_id}"
    
  3. 多设备同步

    # 通过用户账户实现跨设备同步
    session_id = user.account_id
    

该代码展示了如何快速构建具备长期记忆能力的对话系统,通过简洁的接口实现复杂的状态管理,是开发智能对话应用的基石。

通过 LCEL,还可以实现

  1. 配置运行时变量:https://python.langchain.com/v0.2/docs/how_to/configure/
  2. 故障回退:https://python.langchain.com/v0.2/docs/how_to/fallbacks
  3. 并行调用:https://python.langchain.com/v0.2/docs/how_to/parallel/
  4. 逻辑分支:https://python.langchain.com/v0.2/docs/how_to/routing/
  5. 动态创建 Chain: https://python.langchain.com/v0.2/docs/how_to/dynamic_chain/

更多例子:https://python.langchain.com/v0.2/docs/how_to/lcel_cheatsheet/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/981887.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式L6计算机网络

Telnet不加密 socket是应用层和下面的内核

C++、C#、python调用OpenCV进行图像处理耗时对比

C、C#、python调用OpenCV进行图像处理耗时对比 一、前言二、环境介绍三、耗时对比3.1读写jpg、bmp图片耗时对比3.2灰度化、阈值分割、膨胀腐蚀耗时对比 附录(1)imread函数以不同模式读取图片(2)OpenCV读取图片格式与PIL读取图片格…

【ubuntu20】--- 搭建 gerrit 最新最详细

在编程的艺术世界里,代码和灵感需要寻找到最佳的交融点,才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里,我们将共同追寻这种完美结合,为未来的世界留下属于我们的独特印记。 【ubuntu20】--- 搭建 gerrit 最新最详细…

制服小程序的“滑手”:禁用页面左右滑动全攻略

哈哈,看来你已经很聪明地发现了小程序中左右滑动的“顽皮”行为!😄 没错,我们可以通过设置 disableScroll 属性来“管教”它,同时结合 CSS 样式让页面既禁得住横向“乱跑”,又能顺畅地上下滚动。你的方案已…

ASP .NET Core 学习(.NET9)Serilog日志整合

Serilog 是一个功能强大的 .NET 日志库,以其简洁的配置和灵活的输出方式而受到开发者喜爱。支持多种日志输出目标(如控制台、文件、数据库等),并且可以通过结构化日志的方式记录丰富的上下文信息,便于后续的日志分析和…

**NET400协议网关全系型号:多场景·全兼容工业物联解决方案**

行业痛点 工业现场需适配多样场景、差异化PLC协议、弹性扩展需求: 单一型号无法覆盖小规模车间到大型工厂的复杂组网;老旧系统升级需硬件兼容与数据平滑迁移;设备点位从数十到上千,需灵活选型控制成本。 NET400系列部分型号 按…

excel vlookup的精确查询、模糊查询、反向查询、多列查询

目录 入门 精确查询 模糊查询 反向查询 (搭配 if 函数) 多列查询 (搭配 match 函数) 入门 精确查询 需求: 查找 学生编号是008 所在的班级 操作: 在I2单元格输入公式如下,VLOOKUP(H2,B1:E12,4,FALSE), 得出结果 看一下vlookup 公式每一个参数应该怎么写? 语法: vlookup…

计算机组成原理:计算机系统层次结构

文章目录 计算机系统的组成计算机硬件冯诺依曼计算机主要特点 组成部分冯诺依曼计算机结构现代计算机结构 计算机软件计算机程序语言的发展 软件和硬件功能的逻辑等价性计算机系统的层次结构 计算机系统的组成 硬件系统和软件系统共同构成了一个完整的计算机系统。 硬件是指有…

探秘基带算法:从原理到5G时代的通信变革【二】Viterbi解码

文章目录 二、关键算法原理剖析2.1 Viterbi 解码2.1.1 卷积码与网格图基础**卷积码****网格图****生成多项式****理想情况下解码过程** 2.1.2 Viterbi 算法核心思想2.1.3 路径度量与状态转移机制2.1.4 算法流程与关键步骤详解2.1.5 译码算法举例与复杂度分析2.1.6 算法代码示例…

uniapp对接打印机和电子秤

uniapp对接打印机和电子秤 连接电子秤和打印机,最难的不是连接蓝牙和电子成,而是打印机。因为打印机涉及到向打印机写数据操作,然后这个写的数据需要做一个编码转换。难就难在编码转换。如果是java那就是一句代码的事情,而js就没有…

Java-实现PDF合同模板填写内容并导出PDF文件

可用于公司用户合同导出pdf文件 效果图 一、导入所需要jar包 <!--生成PDF--><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.11</version></dependency><dependency&…

如何排查服务器内存泄漏问题

服务器内存泄漏是一种常见的问题&#xff0c;可能导致系统性能下降甚至系统崩溃。以下是一般情况下用于排查服务器内存泄漏问题的步骤&#xff1a; 排查服务器内存泄漏问题的步骤&#xff1a; 监控系统资源&#xff1a; 使用系统监控工具&#xff08;如top、htop、free&#x…

Tampermonkey篡改猴官网,油猴脚本插件电脑版入口(含教程)

Tampermonkey&#xff08;篡改猴&#xff09;是一款功能强大的浏览器扩展工具&#xff0c;自2010年发布以来&#xff0c;已成为全球超过1000万用户的首选脚本管理器。它通过运行用户自定义的JavaScript脚本&#xff0c;赋予用户深度定制网页的能力&#xff0c;涵盖广告拦截、界…

Java高频面试之集合-03

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;说说ArrayList和LinkedList的区别 ArrayList 与 LinkedList 的详细对比 一、底层数据结构 特性ArrayListLinkedList存…

golang学习笔记——go语言安装及系统环境变量设置

文章目录 go语言安装go envgo getgoproxy测试安装 Go 插件安装 Go 插件依赖工具参考资料用户环境变量和系统环境变量用户环境变量系统环境变量示例设置环境变量的步骤设置用户环境变量设置系统环境变量 验证环境变量总结 2024年最火的5大Go框架1. Gin&#xff1a;高并发接口的“…

Composition API

为什么会产生 Composition API? Vue2 逻辑复用方式 缺点 Mixin (命名空间冲突、逻辑不清晰、不易复用)scoped slot 作用域插槽 (配置项多、代码分裂、性能差)Vue2 对 TS 支持不充分 Composition API 优点 逻辑代码更少, 更集中, 更易扩展更加丰富的 API 集成对 TS 来说,…

DeepSeek R1助力,腾讯AI代码助手解锁音乐创作新

目录 1. DeepSeekR1模型简介2. 歌词创作流程2.1 准备工作2.2 歌词生成技巧 3. 音乐制作环节3.1 主流AI音乐生成平台 4. 歌曲欣赏5. 总结展望 1. DeepSeekR1模型简介 腾讯AI代码助手最新推出的DeepSeekR1模型不仅在代码生成方面表现出色&#xff0c;其强大的自然语言处理能力也…

微信小程序接入deepseek

先上效果 话不多说&#xff0c;直接上代码&#xff08;本人用的hbuilder Xuniapp&#xff09; <template><view class"container"><!-- 聊天内容区域 --><scroll-view class"chat-list" scroll-y :scroll-top"scrollTop":…

angular+nodejs问卷调查系统

说明&#xff1a;我计划用angularmysqlnodejs&#xff0c;做一套问卷调查系统&#xff0c; 1.先设计数据库表&#xff0c; 2.然后添加模拟数据&#xff0c; 3.然后写几个查询方法 4.然后用nodejs写service服务&#xff0c;查询mysql数据 5.然后写contrller路由&#xff0c;指向…

Ubuntu20.04双系统安装及软件安装(五):VSCode

Ubuntu20.04双系统安装及软件安装&#xff08;五&#xff09;&#xff1a;VSCode 打开VScode官网&#xff0c;点击中间左侧的deb文件下载&#xff1a; 系统会弹出下载框&#xff0c;确定即可。 在文件夹的**“下载”目录**&#xff0c;可看到下载的安装包&#xff0c;在该目录下…