Spring AI会话记忆使用与底层实现
本文从架构视角介绍 Spring AI 的 Chat Memory + JDBC 持久化:如何使用、自动配置与运行时如何落库,并给出核心类图与扩展思路。
·
一、功能概述
Spring AI 的 Chat Memory 用于在多轮对话中保持上下文:每次请求可带上「会话 ID」,框架会按会话加载历史消息、拼入本次请求,并在收到模型回复后把本轮消息写回存储。
若存储使用 JDBC,历史会持久化到关系库,重启或扩容后仍可复用同一会话。
- 业务价值:多轮对话有记忆、支持按会话隔离、可做审计与分析。
- 技术要点:无 JPA 实体,表名与列名由框架约定;建表脚本可自定义,运行时 SQL 由 Dialect 固定。
二、快速使用
2.1 依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>
<!-- 按数据库选驱动,如 MySQL -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
2.2 配置
数据源(示例使用占位符,请替换为实际环境):
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://<YOUR_HOST>:3306/<YOUR_DB>?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
username: <YOUR_USERNAME>
password: <YOUR_PASSWORD>
Chat Memory JDBC(建表脚本与是否执行):
spring:
ai:
chat:
memory:
repository:
jdbc:
initialize-schema: always # 可选: always | never | embedded
schema: classpath:sql/schema-mysql.sql # 自定义建表脚本路径
2.3 建表脚本(约定表名与列名)
表名必须为 SPRING_AI_CHAT_MEMORY,列需包含:conversation_id、content、type、timestamp。示例(MySQL):
CREATE TABLE IF NOT EXISTS SPRING_AI_CHAT_MEMORY
(
id BIGINT NOT NULL AUTO_INCREMENT,
conversation_id VARCHAR(36) NOT NULL,
content TEXT NOT NULL,
type VARCHAR(10) NOT NULL,
timestamp TIMESTAMP NOT NULL,
PRIMARY KEY (id),
INDEX idx_conv_ts (conversation_id, timestamp),
CONSTRAINT type_check CHECK (type IN ('USER', 'ASSISTANT', 'SYSTEM', 'TOOL'))
);
2.4 Java 配置
@Configuration
public class SpringAIConfiguration {
@Bean
public ChatClient chatClient(DeepSeekChatModel chatModel, ChatMemory chatMemory) {
return ChatClient.builder(chatModel)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build()
)
.build();
}
@Bean
public ChatMemory chatMemory(JdbcChatMemoryRepository chatMemoryRepository) {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(20)
.build();
}
}
- JdbcChatMemoryRepository:由 starter 自动配置,无需手写。
- MessageWindowChatMemory:对单会话保留最近 N 条(如 20),超出则淘汰旧消息。
2.5 控制器中传入会话 ID
@RestController
@RequestMapping("/ai")
public class ChatController {
private final ChatClient chatClient;
@GetMapping("/chat")
public Flux<String> chat(@RequestParam String prompt,
@RequestParam String chatId) {
return chatClient
.prompt(prompt)
.advisors(as -> as.param(ChatMemory.CONVERSATION_ID, chatId))
.stream()
.content();
}
}
- 同一 chatId 即同一会话,历史从库中按
conversation_id读取并写回。

三、核心类图与调用链
3.1 类图(简化)

3.2 自动配置与 DDL 初始化
- Properties:
spring.ai.chat.memory.repository.jdbc.schema指定建表脚本路径;未配置时使用默认schema-@@platform@@.sql。 - SchemaInitializer:启动时根据配置执行该脚本,只负责建表,不参与运行时 SQL。
四、底层实现要点
4.1 无实体类:行 ↔ Message 的映射
- 框架没有为
SPRING_AI_CHAT_MEMORY提供 JPA 实体。 - ChatMemoryRepository 接口的入参/出参是
List<Message>(Spring AI 的Message接口)。 - JdbcChatMemoryRepository 内部:
- 读:用 Dialect 的
getSelectMessagesSql()查content, type,再用 MessageRowMapper 根据type构造UserMessage/AssistantMessage/SystemMessage/ToolResponseMessage。 - 写:
saveAll先按conversation_id删除,再按 Dialect 的getInsertMessageSql()批量插入,参数来自message.getText()、message.getMessageType().name()和顺序时间戳。
- 读:用 Dialect 的
4.2 表名与列名写死在 Dialect 中
- 各数据库的 Dialect 实现(如 MysqlChatMemoryRepositoryDialect)中,SQL 写死表名
SPRING_AI_CHAT_MEMORY与列名 conversation_id, content, type, timestamp。 - 因此「任意表结构」仅体现在:你可以自定义建表脚本(索引、约束、字符集等),但表名与这四列必须与 Dialect 一致,否则运行时会查错表或列。

4.3 一次请求的读写流程
- 请求进入,带
chatId(即 conversation_id)。 - MessageChatMemoryAdvisor 在调用模型前:从 ChatMemory.get(chatId) 取历史 → 实际走到 JdbcChatMemoryRepository.findByConversationId → 执行
SELECT ... FROM SPRING_AI_CHAT_MEMORY WHERE conversation_id = ? ORDER BY timestamp,再映射为List<Message>拼入上下文。 - 模型返回后,Advisor 将本轮 user/assistant 消息 ChatMemory.add(chatId, messages) → MessageWindowChatMemory 先取该会话现有消息,与新课合并并做窗口截断(如保留最近 20 条),再 saveAll(chatId, processedMessages) → JdbcChatMemoryRepository 先
DELETE ... WHERE conversation_id = ?,再批量INSERT。
五、扩展与自定义
| 需求 | 做法 |
|---|---|
| 使用自己的表名/列名 | 实现 JdbcChatMemoryRepositoryDialect,在 SQL 中写自己的表与列;或直接实现 ChatMemoryRepository,用 JdbcTemplate 操作任意表结构。 |
| 只自定义建表方式 | 保留默认 Dialect,仅通过 spring.ai.chat.memory.repository.jdbc.schema 指定自己的 DDL 脚本(表名与四列需一致)。 |
| 替换存储介质 | 实现 ChatMemoryRepository(如 Redis、Mongo),再交给 MessageWindowChatMemory 使用,无需改 ChatClient/Advisor。 |
六、小结
- 使用:引入 starter、配置数据源与 JDBC chat memory 的 schema、建好约定表、注入 ChatMemory 并挂上 MessageChatMemoryAdvisor,接口里传入 ChatMemory.CONVERSATION_ID 即可。
- 实现:表结构由 Dialect 约定(固定表名与四列);无 JPA 实体,读写由 JdbcChatMemoryRepository + MessageRowMapper / 批插完成;建表脚本可自定义,运行时 SQL 不可通过配置修改,只能通过自定义 Dialect 或自定义 Repository 实现「任意表结构」。
以上内容不涉及任何真实服务器 IP、数据库账号密码或 API Key,生产环境请自行替换占位符并做好敏感信息管理。
更多推荐

所有评论(0)