【AI大模型】Spring AI 基于mysql实现对话持久存储详解

目录

一、前言

[二、Spring AI 记忆功能概述](#二、Spring AI 记忆功能概述)

[2.1 Spring AI会话记忆介绍](#2.1 Spring AI会话记忆介绍)

[2.2 常用的会话记忆存储方式](#2.2 常用的会话记忆存储方式)

[2.2.1 集成数据库持久存储会话实现步骤](#2.2.1 集成数据库持久存储会话实现步骤)

三、基于MySql实现会话记忆的实现过程

[3.1 ChatMemory 介绍](#3.1 ChatMemory 介绍)

[3.2 ChatMemory的几种实现](#3.2 ChatMemory的几种实现)

[3.2.1 InMemoryChatMemory](#3.2.1 InMemoryChatMemory)

[3.2.2 MessageWindowChatMemory](#3.2.2 MessageWindowChatMemory)

[3.2.3 MessageChatMemoryAdvisor](#3.2.3 MessageChatMemoryAdvisor)

[3.3 基于mysql实现会话记忆存储思路](#3.3 基于mysql实现会话记忆存储思路)

四、基于mysql实现会话记忆操作过程

[4.1 自定义ChatMemory实现会话记忆存储](#4.1 自定义ChatMemory实现会话记忆存储)

[4.1.1 创建一张表](#4.1.1 创建一张表)

[4.1.2 导入核心依赖](#4.1.2 导入核心依赖)

[4.1.3 增加配置信息](#4.1.3 增加配置信息)

[4.1.4 添加实体类](#4.1.4 添加实体类)

[4.1.5 增加mapper接口](#4.1.5 增加mapper接口)

[4.1.6 自定义ChatMemory](#4.1.6 自定义ChatMemory)

[4.1.7 配置 ChatClient](#4.1.7 配置 ChatClient)

[4.1.8 增加测试接口](#4.1.8 增加测试接口)

[4.1.9 效果验证](#4.1.9 效果验证)

[4.2 基于JdbcTemplate实现会话记忆存储](#4.2 基于JdbcTemplate实现会话记忆存储)

[4.2.1 前置准备](#4.2.1 前置准备)

[4.2.2 导入核心依赖](#4.2.2 导入核心依赖)

[4.2.3 添加配置文件](#4.2.3 添加配置文件)

[4.2.4 自定义配置类](#4.2.4 自定义配置类)

[4.2.5 添加测试接口](#4.2.5 添加测试接口)

[4.2.6 效果验证](#4.2.6 效果验证)

1)第一次调用

五、写在文末


一、前言

我们知道,大型语言模型 (LLM) 是无状态的,这就意味着大模型在对话时不会保存之前的交互信息。当我们希望在一次会话中,模型支持多次交互,那么我们该如何实现呢? 在 Spring AI中提供了ChatMemory功能,它允许我们在与LLM的多次交互中存储与检索信息。

二、Spring AI 记忆功能概述

2.1 Spring AI会话记忆介绍

Spring AI 的会话记忆功能是指让智能体(如AI助手、机器人、虚拟角色等)在多次交互中保持上下文或状态,从而提升交互体验和功能性。这种记忆功能使得智能体能够"记住"用户提供的信息,并在后续对话中参考和使用这些信息,从而提供更加个性化和精准的回复。官方文档:Chat Memory :: Spring AI Reference

2.2 常用的会话记忆存储方式

Spring AI通过 ChatMemory 接口来实现会话记忆功能。常用实现方式有下面几种:

  • 内存存储:

    • 使用 InMemoryChatMemory 来存储对话历史,这种方式适用于临时会话,但不适合长期存储;
  • 数据库存储:

    • 集成 MySQL 、 Mongo、Redis 或其他数据库来存储对话历史,以实现长期的会话记忆

2.2.1 集成数据库持久存储会话实现步骤

在实际应用中,一般是将会话数据进行持久化存储,通常的实现步骤如下:

  1. 集成数据库:

    1. 选择合适的数据存储组件(如MySQL、Mongo、Redis等),并导入相关依赖。

      1. 例如,使用MySQL存储对话历史需要导入 spring-ai-starter-model-chat-memory-repository-jdbc 依赖和 mysql-connector-j 依赖。
  2. 配置会话记忆:

    1. 在Spring配置中定义会话存储方式和会话记忆Advisor。

      1. 例如,使用 InMemoryChatMemory 并通过 MessageChatMemoryAdvisor 配置会话记忆。
  3. 添加会话ID:

    1. 每次会话时通过前端区分会话ID,确保同一个会话ID对应多次对话的消息列表,从而实现会话隔离。

三、基于MySql实现会话记忆的实现过程

使用Spring AI 基于MySql实现会话的记忆存储有两种实现方式,一种是基于ChatMemory的实现,另一种是基于JdbcTemplate 的方式实现,下面分别介绍。

3.1 ChatMemory 介绍

实现LLM的会话的记忆存储离不开ChatMemory,ChatMemory 是 Spring AI 中定义聊天记忆行为的核心接口,通过源码点进去查看,可以看到这是一个顶级接口

java 复制代码
public interface ChatMemory {
    default void add(String conversationId, Message message) {
        this.add(conversationId, List.of(message));
    }

    void add(String conversationId, List<Message> messages);

    List<Message> get(String conversationId, int lastN);

    void clear(String conversationId);
}

可以看到,该接口定义了三个核心操作方法:

  • add()

    • 添加消息到记忆
  • get()

    • 获取对话历史
  • clear()

    • 清除对话记忆

3.2 ChatMemory的几种实现

使用Spring AI过程中,细心的同学通过源码可以看到ChatMemory提供了内置的几种实现,下面分别说明。

3.2.1 InMemoryChatMemory

如果没有引入第三方的会话存储组件,ChatMemory 的默认实现类为InMemoryChatMemory,从源码中可以看到该类对ChatMemory 的几个接口方法进行了实现,开发者可以直接使用。

3.2.2 MessageWindowChatMemory

MessageWindowChatMemory将消息的窗口保持在指定的最大大小。当消息数超过最大值时,在保存系统消息时会删除较旧的消息。默认窗口大小为20个消息。

MessageWindowChatMemory 是 ChatMemory 的主要实现类,具有以下特点:

  • 维护一个固定大小的消息窗口

  • 自动移除旧消息,保留最新消息

  • 默认保留20条消息(可配置)

  • 特殊处理系统消息(不会被自动移除)

3.2.3 MessageChatMemoryAdvisor

MessageChatMemoryAdvisor 是 Spring AI 中用于处理会话历史记录的核心类。我们可以创建一个自定义的 MemoryAdvisor,并使用它来存储对话数据。MessageWindowChatMemory 是 ChatMemory 的主要实现类,具有以下特点:

  • 维护一个固定大小的消息窗口

  • 自动移除旧消息,保留最新消息

  • 默认保留20条消息(可配置)

  • 特殊处理系统消息(不会被自动移除)

官方文档地址:Chat Memory :: Spring AI Reference

3.3 基于mysql实现会话记忆存储思路

从上面关于ChatMemory的介绍中不难发现,实现会话记忆存储的最重要的组件就是ChatMemory,所以其核心思路如下:

  • 创建会话存储数据表;
  • 自定义操作会话记忆的类,实现ChatMemory接口,重写里面的相关方法;
    • 即对会话的增删改操作改为查询数据库;
  • 配置ChatClient 和 ChatMemory ,使用自定义的实现了ChatMemory的类;

四、基于mysql实现会话记忆操作过程

下面介绍两种具体的基于mysql的实现详细过程。

4.1 自定义ChatMemory实现会话记忆存储

4.1.1 创建一张表

使用下面的建表sql创建一个表,该表用于后面存储会话信息

sql 复制代码
CREATE TABLE `ai_chat_memory` (
  `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id',
  `chat_id` varchar(32) NOT NULL COMMENT '会话id',
  `type` varchar(10) NOT NULL DEFAULT 'user' COMMENT '消息类型',
  `content` text NOT NULL COMMENT '消息内容',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_del` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标记,0-未删除;1-已删除'
 )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

注意:

  • spring-ai默认生成的会话Id为defalut,不是UUID。

这里选择将单条消息作为数据库的一行数据,而不是单次会话,因此chat_id不是唯一的。

bash 复制代码
CREATE index idx_chat_id ON ai_chat_memory (chat_id);

4.1.2 导入核心依赖

本例使用mysql进行会话存储,使用mybatis-plus作为orm框架,在pom中导入如下依赖

java 复制代码
<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring-ai.version>1.0.0-M6</spring-ai.version>
    <spring-ai-alibaba.version>1.0.0-M6.1</spring-ai-alibaba.version>
</properties>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.3</version>
    <relativePath/>
</parent>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>${spring-ai.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-mcp-client-webflux-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter</artifactId>
        <version>${spring-ai-alibaba.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        <version>3.5.10.1</version>
    </dependency>

    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.26</version> <!-- 使用适合你的MySQL驱动版本 -->
    </dependency>

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

</dependencies>

<repositories>
    <repository>
        <name>Central Portal Snapshots</name>
        <id>central-portal-snapshots</id>
        <url>https://central.sonatype.com/repository/maven-snapshots/</url>
        <releases>
            <enabled>false</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/snapshot</url>
        <releases>
            <enabled>false</enabled>
        </releases>
    </repository>
</repositories>

<build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

4.1.3 增加配置信息

在工程的配置文件中增加如下配置信息

java 复制代码
spring:
  ai:
    dashscope:
      api-key: 你的百炼平台的apikey
      chat:
        options:
          model: qwen-max
    chat:
      client:
        enabled: false

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://IP:3306/test
    username: root
    password: 123456

#  data:
#    redis:
#      host: localhost
#      port: 6379

# mybatis-plus配置
mybatis-plus:
  type-aliases-package: com.congge.domain
  configuration:
    # 下划线转驼峰
    map-underscore-to-camel-case: true
  # 全局配置
  global-config:
    db-config:
      # 数据库id配置
      id-type: auto
      logic-delete-field: is_del # 全局逻辑删除字段名
      logic-delete-value: 1   # 逻辑已删除值。可选,默认值为 1
      logic-not-delete-value: 0 # 逻辑未删除值。可选,默认值为 0
  mapper-locations: classpath:/mapper/**.xml

4.1.4 添加实体类

实体类与数据表映射

java 复制代码
package com.congge.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.util.Date;

@Data
@TableName("ai_chat_memory")
public class AiChatMemory {

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    private String chatId;

    private String type;

    private String content;

    private Date createTime;

    private Date updateTime;

    private Integer isDel;

}

4.1.5 增加mapper接口

java 复制代码
package com.congge.mapper;


import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.congge.domain.AiChatMemory;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface AiChatMemoryMapper extends BaseMapper<AiChatMemory> {

}

4.1.6 自定义ChatMemory

增加一个自定义类,实现ChatMemory,并重写里面的方法

java 复制代码
package com.congge.config.mysql;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.congge.domain.AiChatMemory;
import com.congge.mapper.AiChatMemoryMapper;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.*;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 基于MySQL ChatMemory实现
 */
@Component
public class InMySqlChatMemory implements ChatMemory {

    @Resource
    private AiChatMemoryMapper aiChatMemoryMapper;

    @Override
    public void add(String conversationId, List<Message> messages) {
        List<AiChatMemory> aiChatMemorieList = new ArrayList<>();
        messages.forEach(message -> {
            AiChatMemory aiChatMemory = new AiChatMemory();
            aiChatMemory.setChatId(conversationId);
            aiChatMemory.setType(message.getMessageType().getValue());
            aiChatMemory.setContent(message.getText());
            aiChatMemorieList.add(aiChatMemory);
        });
        aiChatMemoryMapper.insertOrUpdate(aiChatMemorieList);
    }

    @Override
    public List<Message> get(String conversationId, int lastN) {
        if (lastN >0){
            List<AiChatMemory> aiChatMemoryList = aiChatMemoryMapper.selectList(new QueryWrapper<AiChatMemory>()
                    .eq("chat_id", conversationId)
                    .orderByDesc("create_time")
                    .last("limit " + lastN));
            if (CollectionUtils.isEmpty(aiChatMemoryList)){
                return List.of();
            }
            return aiChatMemoryList.stream()
                    .map(aiChatMemory -> {
                        String type = aiChatMemory.getType();
                        String content = aiChatMemory.getContent();
                        Message message;
                        return switch (type) {
                            case "system" -> message = new SystemMessage(content);
                            case "user" -> message = new UserMessage(content);
                            case "assistant" -> message = new AssistantMessage(content);
                            default -> throw new IllegalArgumentException("Unknown message type: " + type);
                        };
                    })
                    .toList();
        }
        return List.of();
    }

    @Override
    public void clear(String conversationId) {
        aiChatMemoryMapper.delete(new QueryWrapper<AiChatMemory>()
                .eq(conversationId!=null,"chat_id",conversationId));
    }
}

4.1.7 配置 ChatClient

增加一个配置类,将ChatClient作为全局bean配置进去

java 复制代码
package com.congge.config.mysql;

import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MysqlChatClientConfig {

    @Value("${spring.ai.dashscope.api-key}")
    public String apiKey;

    @Resource
    ChatMemory InMySqlChatMemory;

    @Bean
    public ChatClient qwenPlusInMemoryChatClient(){
        if (apiKey == null)
            throw new RuntimeException("apiKey is null");

        return ChatClient.builder(new DashScopeChatModel(new DashScopeApi(apiKey),
                        DashScopeChatOptions.builder().withModel("qwen-max").build()
                ))
                //.defaultSystem(LOVE_PROMPT)
                .defaultAdvisors(
                        //自定义持久化记忆advisor
                        new MessageChatMemoryAdvisor(InMySqlChatMemory)
                )
                .build();
    }
}

4.1.8 增加测试接口

如下,增加两个测试接口,用于观察效果

java 复制代码
package com.congge.web;

import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/memory/store")
@RestController
@Slf4j
public class MySqlChatStoreController {

    @Autowired
    private ChatClient chatClient;

    @Autowired
    private ChatMemory chatMemory;

    //localhost:8082/memory/store/chat?userId=1&inputMsg=我叫小王
    //localhost:8082/memory/store/chat?userId=1&inputMsg=你知道我是谁吗
    @GetMapping(value = "/chat")
    public String chat(@RequestParam String userId, @RequestParam String inputMsg) {
        String response = chatClient.prompt()
                .user(inputMsg)
                .advisors(new MessageChatMemoryAdvisor(chatMemory, userId, 10))
                .call()
                .content();
        return response;
    }

    //localhost:8082/memory/store/chat/v2?message=你是谁呢
    @GetMapping("/chat/v2")
    public String simpleChat(String message) {
        return chatClient.prompt()
                .user(message)
                .call().content();
    }

}

4.1.9 效果验证

启动工程后,分别做下面的测试。

1)第一次调用

2)第二次调用

通过上述的2次调用可以看到,通过上面的方式也能够实现与大模型对话的上下文记忆功能,此时查看数据库,可以看到会话数据存储到了数据表中,由于上面代码中没有设置系统角色,这里显示的是deafult

4.2 基于JdbcTemplate实现会话记忆存储

这种实现方式相对来说要简单一些,不过对Spring AI的版本要求较高,这个在实际整合应用中需要注意,下面看完整的操作过程。

4.2.1 前置准备

去硅基流动网站注获取apikey 硅基流动用户系统,统一登录 SSO

去spring ai官网获取open ai依赖 ,此种实现方式需要依赖最新的版本:Upgrade Notes :: Spring AI Reference

mysql数据库创建一张数据表,用于保存会话信息

java 复制代码
CREATE TABLE `spring_ai_chat_memory` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `conversation_id` varchar(255) DEFAULT NULL COMMENT '会话ID',
  `content` varchar(10000) DEFAULT NULL COMMENT '内容',
  `type` varchar(255) DEFAULT NULL COMMENT '类型',
  `timestamp` datetime DEFAULT NULL COMMENT '时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

4.2.2 导入核心依赖

在pom文件中导入如下核心依赖

java 复制代码
<properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!--<spring-ai.version>1.0.0-M6</spring-ai.version>-->
        <spring-ai.version>1.0.0</spring-ai.version>
        <spring-ai-alibaba.version>1.0.0-M6.1</spring-ai-alibaba.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.3</version>
        <relativePath/>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--<dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter</artifactId>
            <version>${spring-ai-alibaba.version}</version>
        </dependency>-->

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-openai</artifactId>
            <version>1.0.0</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.10.1</version>
        </dependency>

        <!-- MySQL 驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version> <!-- 使用适合你的MySQL驱动版本 -->
        </dependency>

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

    </dependencies>

    <repositories>
        <repository>
            <name>Central Portal Snapshots</name>
            <id>central-portal-snapshots</id>
            <url>https://central.sonatype.com/repository/maven-snapshots/</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
    </repositories>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

4.2.3 添加配置文件

在工程的配置文件中添加下面的配置信息

java 复制代码
server:
  port: 8083

spring:
  ai:
    openai:
      api-key: 你的硅基流动apikey
      base-url: https://api.siliconflow.cn
      chat:
        options:
          model: deepseek-ai/DeepSeek-R1
    chat:
      memory:
        repository:
          jdbc:
            initialize-schema: never
            #platform: mariadb
            #schema: classpath:org/springframework/ai/chat/memory/repository/jdbc/schema-mariadb.sql

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://你的数据库IP:3306/ry?useUnicode=true&characterEncoding=utf8&autoReconnectForPools=true&useSSL=false
    username: 用户名
    password: 密码

logging:
  level:
    # 用于支持llm模型输入前和输入后的日志打印
    org.springframework.ai.chat.client.advisor: debug

# mybatis-plus配置
#mybatis-plus:
#  type-aliases-package: com.congge.domain
#  configuration:
#    # 下划线转驼峰
#    map-underscore-to-camel-case: true
#  # 全局配置
#  global-config:
#    db-config:
#      # 数据库id配置
#      id-type: auto
#      logic-delete-field: is_del # 全局逻辑删除字段名
#      logic-delete-value: 1   # 逻辑已删除值。可选,默认值为 1
#      logic-not-delete-value: 0 # 逻辑未删除值。可选,默认值为 0
#  mapper-locations: classpath:/mapper/**.xml

4.2.4 自定义配置类

自定义ChatClient和ChatMemory全局bean,参考下面的代码

java 复制代码
package com.congge.config;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.ChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository;
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepositoryDialect;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

@Configuration
public class JdbcChatConfig {

    @Bean
    public JdbcChatMemoryRepository jdbcChatMemoryRepository(JdbcTemplate jdbcTemplate, DataSource dataSource) {
        JdbcChatMemoryRepositoryDialect dialect = JdbcChatMemoryRepositoryDialect.from(dataSource);
        return JdbcChatMemoryRepository.builder().jdbcTemplate(jdbcTemplate).dialect(dialect).build();
    }

    @Bean
    public ChatMemory chatMemory(ChatMemoryRepository jdbcChatMemoryRepository){
        return MessageWindowChatMemory.builder()
                .chatMemoryRepository(jdbcChatMemoryRepository)
                // 每个会话最多记录20条信息,可以根据实际情况设置
                .maxMessages(20)
                .build();
    }

    @Bean
    public ChatClient chatClient(OpenAiChatModel openAiChatModel, ChatMemory chatMemory){
        // 配置模型 (因为我们使用的是 ollama, 所以此处写的是 OllamaChatModel)
        return ChatClient.builder(openAiChatModel)
                // 默认系统提示词
                //.defaultSystem("你是一个擅长进行情绪管理的AI助手。")
                // 添加模型输入前和输入后日志打印
                .defaultAdvisors(new SimpleLoggerAdvisor(),
                        // 配置 chat memory advisor
                        MessageChatMemoryAdvisor.builder(chatMemory).build())
                .build();
    }

}

4.2.5 添加测试接口

添加一个自定义接口,方便测试看效果,参考下面的代码

java 复制代码
package com.congge.web;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/memory")
public class ChatController {

    @Autowired
    private ChatClient chatClient;

    //localhost:8083/memory/chat?userId=1&inputMsg=我叫小王
    //localhost:8083/memory/chat?userId=1&inputMsg=你知道我是谁吗
    @GetMapping(value = "/chat")
    public String chat(@RequestParam String userId, @RequestParam String inputMsg) {
        String response = chatClient.prompt()
                .user(inputMsg)
                .advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION_ID, userId))
                //.advisors(new MessageChatMemoryAdvisor(chatMemory, userId, 10))
                .call()
                .content();
        return response;
    }

}

4.2.6 效果验证

下面分别调用一下接口进行测试。

1)第一次调用

调用接口,向大模型发起提问

2)第二次调用

调用接口,向大模型发起提问,测试会话记忆是否生效

通过上面的两次接口测试,可以看到,会话记忆能够实现,同时检查数据表,对话的数据进行了持久化存储

3)第三次调用

将服务停掉,然后重启服务,再次调用接口2,可以看到,由于历史会话进行了存储,所以当发起与历史会话相关的提问时,大模型仍然保持着会话记忆功能。

五、写在文末

本文通过较大的篇幅详细介绍了基于mysql实现会话记忆的功能,希望对看到的同学有用,本篇到此结束,感谢观看。

相关推荐
booooooty1 天前
基于Spring AI Alibaba的多智能体RAG应用
java·人工智能·spring·多智能体·rag·spring ai·ai alibaba
weixin_4250230013 天前
Spring Boot使用MCP服务器
服务器·spring boot·后端·spring ai·mcp
程序员阿超的博客17 天前
Java大模型开发入门 (13/15):拥抱官方标准 - Spring AI框架入门与实践
人工智能·langchain·大模型·spring ai·langchain4j
迢迢星万里灬18 天前
Java求职者面试:Spring AI、MCP、RAG、向量数据库与Embedding模型技术解析
java·面试·向量数据库·rag·spring ai·embedding模型·mcp
llwszx19 天前
Spring Boot 整合 Spring AI 与 MCP 开发智能体工具指南
人工智能·spring boot·spring·智能体·spring ai·mcp
Hanson Huang23 天前
【Spring AI 1.0.0】Spring AI 1.0.0框架快速入门(2)——Prompt(提示词)
java·人工智能·spring·spring ai
huan_19931 个月前
Spring AI中使用ChatMemory实现会话记忆功能
ai·spring ai·模型记忆·springai开发·chatmemory
忠于明白1 个月前
Spring AI 核心工作流
人工智能·spring·大模型应用开发·spring ai·ai 应用商业化
BOB-wangbaohai1 个月前
Spring AI 官方文档 AIGC入门到实战 (1) 认识Spring AI
人工智能·aigc·spring ai