作为一名长期深耕Java生态的后端开发者,最近在探索Spring AI框架时发现其对话记忆功能非常实用。但默认的内存存储方式在服务重启后会丢失所有对话记录,这显然不符合生产环境需求。本文将详细介绍如何通过JDBC实现对话记录的持久化存储,并分享我在实际项目中总结的打断对话流和记录管理的实战经验。
首先需要在pom.xml中添加两个关键依赖:
xml复制<!-- 会话记忆JDBC持久化 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
<version>1.1.0</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
这里特别说明版本选择的考量:
创建数据库时建议使用utf8mb4字符集以支持完整的Unicode字符(特别是表情符号):
sql复制CREATE DATABASE `spring-ai` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
然后在resources/sql/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'))
);
注意:timestamp字段加了反引号是因为它是MySQL保留字,直接使用会导致语法错误
application.yml中的关键配置项:
yaml复制spring:
ai:
chat:
memory:
repository:
jdbc:
initialize-schema: always # 启动时自动初始化表结构
schema: classpath:sql/schema-mysql.sql # 建表SQL路径
datasource:
url: jdbc:mysql://localhost:3306/spring-ai?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: yourpassword
driver-class-name: com.mysql.cj.jdbc.Driver
实际项目中我推荐以下优化配置:
yaml复制url: jdbc:mysql://localhost:3306/spring-ai?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true&connectionTimeout=3000&socketTimeout=5000
SPRING_AI_CHAT_MEMORY表的设计有几个精妙之处:
启动应用后,系统会自动:
当进行对话时:
实际项目中,我们经常需要实现对话打断功能。核心原理是控制Flux流的输出:
java复制@GetMapping("/chat")
public Flux<String> chatStream(@RequestParam String message,
@RequestParam String conversationId) {
return chatClient.prompt()
.user(message)
.advisors(new MemoryChatMemoryAdvisor(chatMemory))
.stream()
.takeUntil(signal -> {
// 添加中断条件判断
if(shouldInterrupt.get()) {
shouldInterrupt.set(false);
return true;
}
return false;
});
}
// 中断端点
@PostMapping("/interrupt")
public void interrupt() {
shouldInterrupt.set(true);
}
重要提示:打断只是终止了数据流返回,AI模型实际上仍在继续生成内容,仍然会产生费用。真正的成本控制需要在服务端实现超时或字数限制。
在高并发场景下,建议:
sql复制CREATE INDEX idx_conversation_time ON SPRING_AI_CHAT_MEMORY(conversation_id, timestamp);
问题1:启动时报表已存在错误
问题2:中文乱码
SHOW VARIABLES LIKE 'character_set%'确认服务器配置问题3:对话记忆不生效
实现对话删除功能:
java复制@DeleteMapping("/conversations/{conversationId}")
public ResponseEntity<Void> deleteConversation(
@PathVariable String conversationId) {
jdbcTemplate.update(
"DELETE FROM SPRING_AI_CHAT_MEMORY WHERE conversation_id = ?",
conversationId);
return ResponseEntity.noContent().build();
}
java复制@GetMapping("/conversations/{conversationId}")
public Page<ChatMessage> getConversation(
@PathVariable String conversationId,
@PageableDefault(size = 20) Pageable pageable) {
return jdbcChatMemoryRepository.findByConversationId(
conversationId, pageable);
}
安全方面:
性能方面:
数据管理:
在实际项目中,我们还将这套系统与ELK日志系统集成,实现了对话分析和用户行为分析功能。通过Kibana可以直观看到不同会话的对话热图和用户关注点,为产品优化提供了数据支持。
Spring AI的对话记忆功能虽然简单,但通过合理的扩展和优化,完全可以满足企业级应用的需求。特别是在需要长期维护客户关系的场景下,持久化的对话记录能有效提升用户体验和服务连续性。