收藏本站,收获最前沿的人工智能与编程资讯!!

Spring AI 对话记忆

技术文档 13℃ 0

简介

大语言模型(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、neverembedded
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.portCassandra 原生协议端口9042
spring.cassandra.localDatacenter连接的 Cassandra 数据中心datacenter1
spring.ai.chat.memory.cassandra.time-to-liveCassandra 中消息的存活时间(TTL)-
spring.ai.chat.memory.cassandra.keyspaceCassandra 键空间springframework
spring.ai.chat.memory.cassandra.messages-column存储消息的列名springframework
spring.ai.chat.memory.cassandra.tableCassandra 表名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.endpointAzure Cosmos DB 端点 URI,自动配置必填项-
spring.ai.chat.memory.repository.cosmosdb.keyAzure Cosmos DB 主密钥或辅助密钥,未配置则使用 Azure 身份认证-
spring.ai.chat.memory.repository.cosmosdb.connection-modeCosmos 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.ttlMongoDB 中消息的存活时间(秒),未设置则永久存储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());

// 响应会包含“詹姆斯·邦德”

相关推荐