简介
大语言模型(LLM)是无状态的,这意味着它们不会保留关于先前交互的信息。当你需要在多次交互中维护上下文或状态时,这会成为一种限制。为解决该问题,Spring AI 提供了对话记忆功能,支持你在与大语言模型的多次交互中存储和检索信息。
ChatMemory 抽象层允许你实现多种类型的记忆,以适配不同的使用场景。消息的底层存储由 ChatMemoryRepository 处理,其唯一职责是存储和检索消息。由 ChatMemory 实现类决定保留哪些消息以及何时删除它们。常见的策略包括保留最近 N 条消息、保留特定时间段内的消息,或保留不超过指定令牌限制的消息。
在选择记忆类型之前,理解对话记忆和对话历史的区别至关重要。
对话记忆:大语言模型保留并用于在整个对话中维持上下文感知的信息。
对话历史:完整的对话记录,包括用户与模型之间交换的所有消息。
ChatMemory 抽象层专为管理对话记忆设计,支持你存储和检索与当前对话上下文相关的消息。但它并非存储完整对话历史的最佳选择。如果你需要维护所有交换消息的完整记录,建议采用其他方案,例如依托 Spring Data 高效存储和检索完整的对话历史。
快速开始
Spring AI 自动配置一个 ChatMemory Bean,你可以在应用中直接使用。默认情况下,它使用内存仓库存储消息(InMemoryChatMemoryRepository),并通过 MessageWindowChatMemory 实现类管理对话历史。如果已配置其他仓库(例如 Cassandra、JDBC 或 Neo4j),Spring AI 会优先使用该仓库。
@Autowired ChatMemory chatMemory;
后续章节将详细介绍 Spring AI 中提供的不同记忆类型和仓库。
记忆类型
ChatMemory 抽象层允许你实现多种类型的记忆,以满足不同的使用场景。记忆类型的选择会显著影响应用的性能和行为。本章节介绍 Spring AI 内置的记忆类型及其特性。
消息窗口对话记忆
MessageWindowChatMemory 会维护一个指定最大容量的消息窗口。当消息数量超过最大值时,会删除较早的消息,同时保留系统消息。默认窗口大小为 20 条消息。
MessageWindowChatMemory memory = MessageWindowChatMemory.builder() .maxMessages(10) .build();
这是 Spring AI 用于自动配置 ChatMemory Bean 的默认消息类型。
记忆存储
Spring AI 提供 ChatMemoryRepository 抽象层用于存储对话记忆。本章节介绍 Spring AI 内置的仓库及其使用方法,你也可以根据需要自定义实现仓库。
内存仓库
InMemoryChatMemoryRepository 使用 ConcurrentHashMap 在内存中存储消息。
默认情况下,如果未配置其他仓库,Spring AI 会自动配置一个 InMemoryChatMemoryRepository 类型的 ChatMemoryRepository Bean,你可以在应用中直接使用。
@Autowired ChatMemoryRepository chatMemoryRepository;
如果你希望手动创建 InMemoryChatMemoryRepository,可通过以下方式实现:
ChatMemoryRepository repository = new InMemoryChatMemoryRepository();
JDBC 对话记忆仓库
JdbcChatMemoryRepository 是内置实现类,基于 JDBC 将消息存储在关系型数据库中。它开箱支持多种数据库,适用于需要持久化存储对话记忆的应用。
消息按时间戳升序(从旧到新)检索,这是大语言模型对话历史的标准格式。
首先,向项目中添加以下依赖:
Maven
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId> </dependency>
Gradle
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-jdbc'
}Spring AI 为 JdbcChatMemoryRepository 提供自动配置,你可以在应用中直接使用。
@Autowired JdbcChatMemoryRepository chatMemoryRepository; ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryRepository(chatMemoryRepository) .maxMessages(10) .build();
如果你希望手动创建 JdbcChatMemoryRepository,可提供 JdbcTemplate 实例和 JdbcChatMemoryRepositoryDialect 方言:
ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder() .jdbcTemplate(jdbcTemplate) .dialect(new PostgresChatMemoryRepositoryDialect()) .build(); ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryRepository(chatMemoryRepository) .maxMessages(10) .build();
支持的数据库与方言抽象
Spring AI 通过方言抽象支持多种关系型数据库,开箱支持的数据库如下:
PostgreSQL、MySQL / MariaDB、SQL Server、HSQLDB、Oracle Database
使用 JdbcChatMemoryRepositoryDialect.from(DataSource) 时,可从 JDBC URL 自动检测正确的方言。你可以通过实现 JdbcChatMemoryRepositoryDialect 接口扩展对其他数据库的支持。
配置属性
| 属性 | 描述 | 默认值 |
|---|---|---|
| spring.ai.chat.memory.repository.jdbc.initialize-schema | 控制模式初始化时机,可选值:embedded、always、never | embedded |
| spring.ai.chat.memory.repository.jdbc.schema | 初始化使用的模式脚本路径,支持 classpath: 地址和平台占位符 | classpath:org/springframework/ai/chat/memory/repository/jdbc/schema-@@platform@@.sql |
| spring.ai.chat.memory.repository.jdbc.platform | 初始化脚本中使用 @@platform@@ 占位符时的平台名称 | 自动检测 |
模式初始化
自动配置会在启动时自动创建 SPRING_AI_CHAT_MEMORY 表,使用对应数据库的专用 SQL 脚本。默认情况下,模式初始化仅针对嵌入式数据库(H2、HSQL、Derby 等)执行。
你可以通过 spring.ai.chat.memory.repository.jdbc.initialize-schema 属性控制模式初始化:
spring.ai.chat.memory.repository.jdbc.initialize-schema=embedded # 仅嵌入式数据库(默认) spring.ai.chat.memory.repository.jdbc.initialize-schema=always # 始终初始化 spring.ai.chat.memory.repository.jdbc.initialize-schema=never # 从不初始化(适用于 Flyway/Liquibase)
如需覆盖模式脚本路径,可配置:
spring.ai.chat.memory.repository.jdbc.schema=classpath:/custom/path/schema-mysql.sql
扩展方言
如需新增数据库支持,实现 JdbcChatMemoryRepositoryDialect 接口并提供消息查询、插入、删除的 SQL 语句,随后将自定义方言传入仓库构建器即可。
ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder() .jdbcTemplate(jdbcTemplate) .dialect(new MyCustomDbDialect()) .build();
Cassandra 对话记忆仓库
CassandraChatMemoryRepository 基于 Apache Cassandra 存储消息,适用于需要持久化存储对话记忆的应用,尤其适合追求高可用性、高持久性、可扩展性,且需要使用数据存活时间(TTL)功能的场景。
CassandraChatMemoryRepository 采用时序模式,记录所有历史对话窗口,适用于治理和审计场景。建议设置数据存活时间(例如 3 年)。
消息按时间戳升序(从旧到新)检索,符合大语言模型对话历史的标准格式。
首先,向项目中添加以下依赖:
Maven
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-chat-memory-repository-cassandra</artifactId> </dependency>
Gradle
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-cassandra'
}Spring AI 为 CassandraChatMemoryRepository 提供自动配置,你可以在应用中直接使用。
@Autowired CassandraChatMemoryRepository chatMemoryRepository; ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryRepository(chatMemoryRepository) .maxMessages(10) .build();
如果你希望手动创建 CassandraChatMemoryRepository,可提供 CassandraChatMemoryRepositoryConfig 实例:
ChatMemoryRepository chatMemoryRepository = CassandraChatMemoryRepository .create(CassandraChatMemoryRepositoryConfig.builder().withCqlSession(cqlSession)); ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryRepository(chatMemoryRepository) .maxMessages(10) .build();
配置属性
| 属性 | 描述 | 默认值 |
|---|---|---|
| spring.cassandra.contactPoints | 用于集群发现的主机地址 | 127.0.0.1 |
| spring.cassandra.port | Cassandra 原生协议端口 | 9042 |
| spring.cassandra.localDatacenter | 连接的 Cassandra 数据中心 | datacenter1 |
| spring.ai.chat.memory.cassandra.time-to-live | Cassandra 中消息的存活时间(TTL) | - |
| spring.ai.chat.memory.cassandra.keyspace | Cassandra 键空间 | springframework |
| spring.ai.chat.memory.cassandra.messages-column | 存储消息的列名 | springframework |
| spring.ai.chat.memory.cassandra.table | Cassandra 表名 | ai_chat_memory |
| spring.ai.chat.memory.cassandra.initialize-schema | 启动时是否初始化模式 | true |
模式初始化
自动配置会自动创建 ai_chat_memory 表。
你可以将 spring.ai.chat.memory.repository.cassandra.initialize-schema 设置为 false 禁用模式初始化。
Neo4j 对话记忆仓库
Neo4jChatMemoryRepository 是内置实现类,基于 Neo4j 图数据库将对话消息存储为节点和关系,适用于需要利用 Neo4j 图能力持久化对话记忆的应用。
消息按消息索引升序(从旧到新)检索,符合大语言模型对话历史的标准格式。
首先,向项目中添加以下依赖:
Maven
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-chat-memory-repository-neo4j</artifactId> </dependency>
Gradle
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-neo4j'
}Spring AI 为 Neo4jChatMemoryRepository 提供自动配置,你可以在应用中直接使用。
@Autowired Neo4jChatMemoryRepository chatMemoryRepository; ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryRepository(chatMemoryRepository) .maxMessages(10) .build();
如果你希望手动创建 Neo4jChatMemoryRepository,可提供 Neo4j Driver 实例:
ChatMemoryRepository chatMemoryRepository = Neo4jChatMemoryRepository.builder() .driver(driver) .build(); ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryRepository(chatMemoryRepository) .maxMessages(10) .build();
配置属性
| 属性 | 描述 | 默认值 |
|---|---|---|
| spring.ai.chat.memory.repository.neo4j.sessionLabel | 存储对话会话的节点标签 | Session |
| spring.ai.chat.memory.repository.neo4j.messageLabel | 存储消息的节点标签 | Message |
| spring.ai.chat.memory.repository.neo4j.toolCallLabel | 存储工具调用的节点标签 | ToolCall |
| spring.ai.chat.memory.repository.neo4j.metadataLabel | 存储消息元数据的节点标签 | Metadata |
| spring.ai.chat.memory.repository.neo4j.toolResponseLabel | 存储工具响应的节点标签 | ToolResponse |
| spring.ai.chat.memory.repository.neo4j.mediaLabel | 存储消息关联媒体的节点标签 | Media |
索引初始化
Neo4j 仓库会自动为对话 ID 和消息索引创建索引以优化性能。如果使用自定义标签,也会为这些标签创建索引。无需手动初始化模式,但需确保应用可访问 Neo4j 实例。
Azure Cosmos DB 对话记忆仓库
CosmosDBChatMemoryRepository 是内置实现类,基于 Azure Cosmos DB NoSQL API 存储消息,适用于需要全局分布式、高可扩展文档数据库持久化对话记忆的应用。该仓库使用对话 ID 作为分区键,确保高效的数据分发和快速检索。
消息按时间戳升序(从旧到新)检索,符合大语言模型对话历史的标准格式。
首先,向项目中添加以下依赖:
Maven
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-chat-memory-repository-cosmos-db</artifactId> </dependency>
Gradle
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-cosmos-db'
}Spring AI 为 CosmosDBChatMemoryRepository 提供自动配置,你可以在应用中直接使用。
@Autowired CosmosDBChatMemoryRepository chatMemoryRepository; ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryRepository(chatMemoryRepository) .maxMessages(10) .build();
如果你希望手动创建 CosmosDBChatMemoryRepository,可提供 CosmosDBChatMemoryRepositoryConfig 实例:
ChatMemoryRepository chatMemoryRepository = CosmosDBChatMemoryRepository
.create(CosmosDBChatMemoryRepositoryConfig.builder()
.withCosmosClient(cosmosAsyncClient)
.withDatabaseName("chat-memory-db")
.withContainerName("conversations")
.build());
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();配置属性
| 属性 | 描述 | 默认值 |
|---|---|---|
| spring.ai.chat.memory.repository.cosmosdb.endpoint | Azure Cosmos DB 端点 URI,自动配置必填项 | - |
| spring.ai.chat.memory.repository.cosmosdb.key | Azure Cosmos DB 主密钥或辅助密钥,未配置则使用 Azure 身份认证 | - |
| spring.ai.chat.memory.repository.cosmosdb.connection-mode | Cosmos DB 客户端连接模式(direct 或 gateway) | gateway |
| spring.ai.chat.memory.repository.cosmosdb.database-name | 数据库名称 | SpringAIChatMemory |
| spring.ai.chat.memory.repository.cosmosdb.container-name | 容器名称 | ChatMemory |
| spring.ai.chat.memory.repository.cosmosdb.partition-key-path | 容器分区键路径 | /conversationId |
身份认证
Cosmos DB 对话记忆仓库支持两种认证方式:
1. 密钥认证:配置 spring.ai.chat.memory.repository.cosmosdb.key 属性为 Cosmos DB 主密钥或辅助密钥。
2. Azure 身份认证:未提供密钥时,仓库使用 Azure 身份认证(DefaultAzureCredential),支持托管标识、服务主体等 Azure 凭据源。
模式初始化
自动配置会自动创建指定的数据库和容器(若不存在)。容器将对话 ID 配置为分区键(/conversationId),确保对话记忆操作的最优性能,无需手动设置模式。
你可以通过上述配置属性自定义数据库和容器名称。
MongoDB 对话记忆仓库
MongoChatMemoryRepository 是内置实现类,基于 MongoDB 存储消息,适用于需要灵活的面向文档数据库持久化对话记忆的应用。
消息按时间戳升序(从旧到新)检索,符合大语言模型对话历史的标准格式,该排序规则在所有对话记忆仓库实现中保持一致。
首先,向项目中添加以下依赖:
Maven
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-chat-memory-repository-mongodb</artifactId> </dependency>
Gradle
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-mongodb'
}Spring AI 为 MongoChatMemoryRepository 提供自动配置,你可以在应用中直接使用。
@Autowired MongoChatMemoryRepository chatMemoryRepository; ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryRepository(chatMemoryRepository) .maxMessages(10) .build();
如果你希望手动创建 MongoChatMemoryRepository,可提供 MongoTemplate 实例:
ChatMemoryRepository chatMemoryRepository = MongoChatMemoryRepository.builder() .mongoTemplate(mongoTemplate) .build(); ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryRepository(chatMemoryRepository) .maxMessages(10) .build();
配置属性
| 属性 | 描述 | 默认值 |
|---|---|---|
| spring.ai.chat.memory.repository.mongo.create-indices | 启动时是否自动创建或重建索引 | false |
| spring.ai.chat.memory.repository.mongo.ttl | MongoDB 中消息的存活时间(秒),未设置则永久存储 | 0 |
集合初始化
自动配置会在启动时自动创建 ai_chat_memory 集合(若不存在)。
聊天客户端中的记忆
使用 ChatClient API 时,你可以传入 ChatMemory 实现类,在多次交互中维护对话上下文。
Spring AI 提供了一些内置通知器(Advisors),你可以根据需求配置 ChatClient 的记忆行为。
注意:当前实现中,执行工具调用时与大语言模型交换的中间消息不会存储在记忆中,这是现有版本的限制,将在后续版本中修复。如需存储这些消息,请参考用户控制工具执行的说明。
MessageChatMemoryAdvisor
该通知器通过传入的 ChatMemory 实现类管理对话记忆。每次交互时,它会从记忆中获取对话历史,并将其作为消息集合加入提示词中。这是管理对话记忆的推荐通知器。
PromptChatMemoryAdvisor(已弃用)
自 1.1.3 版本起弃用,推荐使用 MessageChatMemoryAdvisor。该通知器会获取对话历史,并以 XML 标签、HTML 实体转义的格式追加到系统提示词中,不再支持自定义系统提示词模板。
VectorStoreChatMemoryAdvisor
该通知器通过传入的 VectorStore 实现类管理对话记忆。每次交互时,它从向量存储中获取对话历史,并追加到系统消息中。检索到的文档会封装在带类型的 <memory-entry> XML 元素中,内容进行 XML 转义,降低提示词注入风险。
示例:结合使用 MessageWindowChatMemory 和 MessageChatMemoryAdvisor:
ChatMemory chatMemory = MessageWindowChatMemory.builder().build(); ChatClient chatClient = ChatClient.builder(chatModel) .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build();
调用 ChatClient 时,MessageChatMemoryAdvisor 会自动管理记忆,根据指定的对话 ID 从记忆中获取对话历史。
所有记忆通知器都必须传入 ChatMemory.CONVERSATION_ID 参数,省略该参数的调用会在运行时抛出 IllegalArgumentException 异常,无默认对话 ID。
String conversationId = "007";
chatClient.prompt()
.user("我有编码权限吗?")
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
.call()
.content();已弃用:PromptChatMemoryAdvisor
PromptChatMemoryAdvisor 自 1.1.3 版本起弃用,将在后续版本中移除。请迁移至 MessageChatMemoryAdvisor,它以类型化消息对象传递对话历史,而非嵌入系统提示词,且无下述限制。
// 弃用写法 PromptChatMemoryAdvisor advisor = PromptChatMemoryAdvisor.builder(chatMemory).build(); // 推荐写法 MessageChatMemoryAdvisor advisor = MessageChatMemoryAdvisor.builder(chatMemory).build();
PromptChatMemoryAdvisor 将对话历史嵌入系统提示词,每条消息用 XML 标签包裹(角色名为标签名),消息内容进行 HTML 实体转义:
<user>用户消息内容</user> <assistant>助手响应内容</assistant>
自定义模板:不支持自定义系统提示词模板,systemPromptTemplate() 构建方法会抛出 UnsupportedOperationException 异常,防止意外使用省略内置编码的模板。若之前依赖自定义模板,请迁移至 MessageChatMemoryAdvisor。
VectorStoreChatMemoryAdvisor
安全注意事项:检索到的文档内容会进行 XML 转义,并封装在带类型的 <memory-entry> XML 元素中,再插入系统提示词:
<memory-entry type="user">用户消息内容</memory-entry> <memory-entry type="assistant">助手响应内容</memory-entry>
默认系统提示词模板会指示模型将 LONG_TERM_MEMORY 部分仅视为历史数据,而非指令。这是一种约定层面的控制,可降低但无法完全消除来自用户内容的提示词注入风险。对于具备工具访问权限的智能体配置,建议使用 MessageChatMemoryAdvisor,将用户内容保留在类型化的 Message 对象中。
自定义模板:VectorStoreChatMemoryAdvisor 使用默认模板将检索到的对话记忆合并到系统消息中。你可以通过 .promptTemplate() 构建方法传入自定义 PromptTemplate 对象修改该行为。
此处传入的 PromptTemplate 自定义通知器合并记忆与系统消息的方式,与在 ChatClient 上配置 TemplateRenderer(使用 .templateRenderer())不同,后者会在通知器执行前影响初始用户/系统提示词的渲染。
自定义 PromptTemplate 可使用任意 TemplateRenderer 实现(默认基于 StringTemplate 引擎的 StPromptTemplate),必须包含以下两个占位符:
instructions:接收原始系统消息
long_term_memory:接收检索到的对话记忆
聊天模型中的记忆
如果直接使用 ChatModel 而非 ChatClient,你可以显式管理记忆:
// 创建记忆实例
ChatMemory chatMemory = MessageWindowChatMemory.builder().build();
String conversationId = "007";
// 第一次交互
UserMessage userMessage1 = new UserMessage("我叫詹姆斯·邦德");
chatMemory.add(conversationId, userMessage1);
ChatResponse response1 = chatModel.call(new Prompt(chatMemory.get(conversationId)));
chatMemory.add(conversationId, response1.getResult().getOutput());
// 第二次交互
UserMessage userMessage2 = new UserMessage("我的名字是什么?");
chatMemory.add(conversationId, userMessage2);
ChatResponse response2 = chatModel.call(new Prompt(chatMemory.get(conversationId)));
chatMemory.add(conversationId, response2.getResult().getOutput());
// 响应会包含“詹姆斯·邦德”