当前位置: 首页 > news >正文

【SpringAI篇03】:聊天记录持久化(仍保留上下文)

上文【SpringAI篇02】,我们实现了连续对话的上下文记忆功能以及会话隔离,现在我们进一步完善,增加持久化等功能

目录

1. 默认内存存储方案的分析

2. 引入持久化的方式

2.1 依赖项、数据库配置

2.1.1 导入依赖:

2.1.2 创建表 SPRING_AI_CHAT_MEMORY

2.1.3 配置 application.yaml

2.2 注入使用 JdbcChatMemoryRepository 

3. 效果展示


1. 默认内存存储方案的分析

我们之前在初始化 ChatMemory 的时候,使用的 ChatMemoryRepository 是框架提供的一个基于内存存储的 InMemoryChatMemroyRepository(底层是基于 ConcurrentHashMap 实现),虽然实现了上下文,但是不支持持久化:

对话数据全部存储在内存中不太好,且 ChatMemory 的默认唯一实现 MessageWindowChatMemory 对于消息数量做了一定的限制,一旦在单次会话中消息数量超过限制,就会移除旧消息,保留新消息,导致未持久化的旧消息丢失:

 可以通过下面表格捋清上述提到的类、接口关系(默认使用的实现类):

ChatMemory (接口)MessageWindowChatMemory (实现类)ChatMemoryRepository (接口)InMemoryChatMemoryRepository (实现类)
核心作用定义了聊天记忆的基本行为,如记住消息、获取消息等。是最高层次的抽象实现了 ChatMemory 接口,管理聊天记忆,并引入了“消息窗口”的概念,限制记忆的消息数量定义了对聊天记忆进行 CRUD (创建、读取、更新、删除) 操作的契约。是低层次的存储抽象实现了 ChatMemoryRepository 接口,将聊天记忆存储在内存中,不进行持久化。
与上下层的关系被上层(如 ChatClient 或自定义业务逻辑)使用。底层通过 ChatMemoryRepository 实现。实现了 ChatMemory 接口,并依赖于 ChatMemoryRepository 来进行实际的数据存储和检索。被 ChatMemory 的实现类(如 MessageWindowChatMemory)依赖,提供数据存储服务。可以有多种实现(如数据库、文件等)。实现了 ChatMemoryRepository 接口,是 MessageWindowChatMemory 默认使用的存储实现。
在 SpringAI 中的作用定义了聊天记忆的抽象,方便不同实现的切换。提供了基于消息窗口的聊天记忆管理策略,是 SpringAI 默认的聊天记忆实现。定义了聊天记忆数据的存储和检索方式,是聊天记忆持久化的基础。提供了最简单的聊天记忆存储方式,适用于开发、测试或不需要持久化的场景。

2. 引入持久化的方式

SpringAI 提供了多种持久化的方式,这里演示 关系型数据库 MySQL 的操作(也可以自己实现 ChatMemory 持久化到 Redis 中)

2.1 依赖项、数据库配置

2.1.1 导入依赖:

<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>

2.1.2 创建表 SPRING_AI_CHAT_MEMORY

或者创建名为 schema-mysql.sql.sql文件(与后面配置有关),并且放在项目资源目录下,后续通过配置指定自动初始化也可以

CREATE TABLE IF NOT EXISTS SPRING_AI_CHAT_MEMORY
(conversation_id VARCHAR(36) NOT NULL,content         TEXT        NOT NULL,type            VARCHAR(10) NOT NULL,`timestamp`     TIMESTAMP   NOT NULL,CONSTRAINT TYPE_CHECK CHECK (type IN ('USER', 'ASSISTANT', 'SYSTEM', 'TOOL'))
);

 

注意:这一步的前提是先配置MySQL数据源的四项(自行完成)

2.1.3 配置 application.yaml

新增 yaml 部分:

完整 applitcation.yaml:

spring:application:name: 你的服务名称ai:openai:base-url: https://dashscope.aliyuncs.com/compatible-modeapi-key: ${BAILIAN_API_KEY}chat:options:model: qwen-maxtemperature: 0.9chat:memory:repository:jdbc:initialize-schema: alwaysschema: classpath:schema-mysql.sqldatasource:url: jdbc:mysql://${IPADDR}:3306/spring-ai?serverTimezone=Asia/Shanghai&useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowPublicKeyRetrieval=true&allowMultiQueries=true&useServerPrepStmts=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
logging: # springboot log levellevel:org.springframework.ai.chat.client.advisor: debugcom.demo.aiqqq: debug

2.2 注入使用 JdbcChatMemoryRepository 

Spring AI 为 JdbcChatMemoryRepository 提供了自动配置,可以直接注入使用,也可以手动创建、自行探索,详见:Chat Memory :: Spring AI Reference --- Chat Memory :: Spring AI Referencehttps://docs.spring.io/spring-ai/reference/api/chat-memory.html#_jdbcchatmemoryrepository所以直接修改我们配置 ChatMemory 的代码,将之注入,并且配置到 chatMemoryRepository 中即可:

package com.demo.aiqqq.config;import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 与 配置各种其他组件,如聊天记忆,记忆管理方式的配置类* @Author: qiquqiu* @Date: 2025/6/26 16:45*/
@Slf4j
@Configuration
@RequiredArgsConstructor
public class CommonConfig {/*** ChatMemory 聊天对话记忆的存储* @param jdbcChatMemoryRepository 操作数据库存储聊天记忆*/@Beanpublic ChatMemory chatMemory(JdbcChatMemoryRepository jdbcChatMemoryRepository) {log.debug("ChatMemory 初始化...");return MessageWindowChatMemory.builder().chatMemoryRepository(jdbcChatMemoryRepository) // 改动之处.maxMessages(20).build();}
}

3. 效果展示

启动项目,在之前的数据库中会自动创建出一个表: SPRING_AI_CHAT_MEMORY

我们开始对话并且测试两次启动项目,对话是否能实现持久化:

我们查看数据库的 SPRING_AI_CHAT_MEMORY 表,可以发现上面两个对话的 chatId、对话内容、 对话类型、 时间戳都保存进来了(两次一问一答):

现在,我们重启项目,再次接续 chatId=1 和 chatId=2 的对话继续

显然,既能够识别出水果数量也能识别出不同对话的水果种类,通过日志我们也可以清晰看到,在新的两次对话中,之前的对话都作为附带信息封装在请求中,说明数据库的持久化与重启项目的读取聊天记录都实现了:

相关文章:

  • 数据库单条sql优化
  • Vue3 + Element Plus Transfer 穿梭框自定义分组
  • Dubbo服务调用超时问题解决方案
  • C++基础:动态内存分配、输入输出与命名空间详解
  • 专注搜索引擎优化的专业模板平台
  • 虚拟房产炒作是否触发“非法经营罪”?
  • OpenCV模版匹配方法的衡量指标比较
  • 三大WPF MVVM框架对比:MVVMLight、CommunityToolkit.Mvvm 与 Prism
  • OpenDeepWiki:AI代码对话新纪元
  • [Meetily后端框架] 配置指南 | 后端API网关 | API文档体系
  • Feign 实战指南:从 REST 替代到性能优化与最佳实践
  • 如何利用智能文档处理(IDP)优化保险业理赔与运营流程?
  • 从0到1掌握API接口:原理解析、实战案例与高效开发指南
  • .NET MAUI跨平台串口通讯方案
  • A Machine Learning Approach for Non-blind Image Deconvolution论文阅读
  • 悦己汉服体验馆小程序(协同过滤算法、WebSocket即时聊天)
  • 中国双非高校经费TOP榜数据分析
  • linux初阶---一些指令
  • 动手学Python:从零开始构建一个“文字冒险游戏”
  • css去掉换行小工具 去掉css换行 style样式去掉换行