大型语言模型(LLM)是无状态的,这意味着它们不会保留先前交互的信息。当您希望在多次交互中保持上下文或状态时,这可能是一个限制。为了解决这个问题,Spring AI 提供了聊天记忆功能,允许您在与 LLM 的多次交互中存储和检索信息。
ChatMemory 抽象允许您实现各种类型的记忆以支持不同的用例。消息的底层存储由 ChatMemoryRepository 处理,其唯一职责是存储和检索消息。由 ChatMemory 实现来决定保留哪些消息以及何时删除它们。策略示例可以包括保留最后 N 条消息,保留特定时间段内的消息,或保留达到特定令牌限制的消息。
在选择记忆类型之前,理解聊天记忆和聊天历史之间的区别至关重要。
聊天记忆。大型语言模型保留并用于在整个对话中保持上下文感知的信息。
聊天历史。整个对话历史,包括用户和模型之间交换的所有消息。
ChatMemory 抽象旨在管理聊天记忆。它允许您存储和检索与当前对话上下文相关的消息。然而,它不适用于存储聊天历史。如果您需要维护所有交换消息的完整记录,您应该考虑使用不同的方法,例如依靠 Spring Data 来高效存储和检索完整的聊天历史。
1. 快速入门
Spring AI 会自动配置一个 ChatMemory bean,您可以直接在应用程序中使用。默认情况下,它使用内存存储库来存储消息 (InMemoryChatMemoryRepository) 和 MessageWindowChatMemory 实现来管理对话历史。如果已配置不同的存储库(例如 Cassandra、JDBC 或 Neo4j),Spring AI 将使用该存储库。
@Autowired
ChatMemory chatMemory;以下部分将进一步描述 Spring AI 中可用的不同记忆类型和存储库。
2. 记忆类型
ChatMemory 抽象允许您实现各种类型的记忆以适应不同的用例。记忆类型的选择可以显著影响应用程序的性能和行为。本节描述了 Spring AI 提供的内置记忆类型及其特性。
2.1. 消息窗口聊天记忆
MessageWindowChatMemory 维护一个消息窗口,最大大小为指定值。当消息数量超过最大值时,较旧的消息将被删除,同时保留系统消息。默认窗口大小为 20 条消息。
MessageWindowChatMemory memory = MessageWindowChatMemory.builder()
.maxMessages(10)
.build();这是 Spring AI 自动配置 ChatMemory bean 时使用的默认消息类型。
3. 记忆存储
Spring AI 提供了 ChatMemoryRepository 抽象来存储聊天记忆。本节描述了 Spring AI 提供的内置存储库以及如何使用它们,但您也可以根据需要实现自己的存储库。
3.1. 内存存储库
InMemoryChatMemoryRepository 使用 ConcurrentHashMap 将消息存储在内存中。
默认情况下,如果尚未配置其他存储库,Spring AI 会自动配置一个类型为 InMemoryChatMemoryRepository 的 ChatMemoryRepository bean,您可以直接在应用程序中使用。
@Autowired
ChatMemoryRepository chatMemoryRepository;如果您希望手动创建 InMemoryChatMemoryRepository,可以按如下方式操作:
ChatMemoryRepository repository = new InMemoryChatMemoryRepository();3.2. JdbcChatMemoryRepository
JdbcChatMemoryRepository 是一个内置实现,它使用 JDBC 将消息存储在关系数据库中。它开箱即用支持多种数据库,适用于需要持久存储聊天记忆的应用程序。
首先,将以下依赖项添加到您的项目:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>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();3.2.1. 支持的数据库和方言抽象
Spring AI 通过方言抽象支持多种关系数据库。以下数据库开箱即用:
PostgreSQL
MySQL / MariaDB
SQL Server
HSQLDB
Oracle Database
使用 JdbcChatMemoryRepositoryDialect.from(DataSource) 时,可以从 JDBC URL 自动检测到正确的方言。您可以通过实现 JdbcChatMemoryRepositoryDialect 接口来扩展对其他数据库的支持。
3.2.2. 配置属性
3.2.3. Schema 初始化
自动配置将在启动时自动创建 SPRING_AI_CHAT_MEMORY 表,使用特定于您的数据库的供应商 SQL 脚本。默认情况下,schema 初始化仅针对嵌入式数据库(H2、HSQL、Derby 等)运行。
您可以使用 spring.ai.chat.memory.repository.jdbc.initialize-schema 属性控制 schema 初始化:
spring.ai.chat.memory.repository.jdbc.initialize-schema=embedded # Only for embedded DBs (default)
spring.ai.chat.memory.repository.jdbc.initialize-schema=always # Always initialize
spring.ai.chat.memory.repository.jdbc.initialize-schema=never # Never initialize (useful with Flyway/Liquibase)要覆盖 schema 脚本位置,请使用:
spring.ai.chat.memory.repository.jdbc.schema=classpath:/custom/path/schema-mysql.sql3.2.4. 扩展方言
要添加对新数据库的支持,请实现 JdbcChatMemoryRepositoryDialect 接口并提供用于选择、插入和删除消息的 SQL。然后,您可以将自定义方言传递给存储库构建器。
ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder()
.jdbcTemplate(jdbcTemplate)
.dialect(new MyCustomDbDialect())
.build();3.3. CassandraChatMemoryRepository
CassandraChatMemoryRepository 使用 Apache Cassandra 存储消息。它适用于需要持久存储聊天记忆的应用程序,特别是为了可用性、持久性、可伸缩性以及利用生存时间(TTL)功能。
CassandraChatMemoryRepository 具有时间序列 schema,记录所有过去的聊天窗口,这对于治理和审计很有价值。建议将生存时间设置为某个值,例如三年。
要使用 CassandraChatMemoryRepository,首先将依赖项添加到您的项目:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-cassandra</artifactId>
</dependency>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();3.3.1. 配置属性
3.3.2. Schema 初始化
自动配置将自动创建 ai_chat_memory 表。
您可以通过将属性 spring.ai.chat.memory.repository.cassandra.initialize-schema 设置为 false 来禁用 schema 初始化。
3.4. Neo4j ChatMemoryRepository
Neo4jChatMemoryRepository 是一个内置实现,它使用 Neo4j 将聊天消息存储为属性图数据库中的节点和关系。它适用于希望利用 Neo4j 的图功能进行聊天记忆持久化的应用程序。
首先,将以下依赖项添加到您的项目:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-neo4j</artifactId>
</dependency>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();3.4.1. 配置属性
3.4.2. 索引初始化
Neo4j 存储库将自动确保为会话 ID 和消息索引创建索引,以优化性能。如果您使用自定义标签,也会为这些标签创建索引。无需 schema 初始化,但您应确保您的 Neo4j 实例可供应用程序访问。
3.5. CosmosDBChatMemoryRepository
CosmosDBChatMemoryRepository 是一个内置实现,它使用 Azure Cosmos DB NoSQL API 存储消息。它适用于需要全球分布式、高度可扩展的文档数据库来持久化聊天记忆的应用程序。该存储库使用会话 ID 作为分区键,以确保高效的数据分发和快速检索。
首先,将以下依赖项添加到您的项目:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-cosmos-db</artifactId>
</dependency>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();3.5.1. 配置属性
3.5.2. 身份验证
Cosmos DB 聊天记忆存储库支持两种身份验证方法:
基于密钥的身份验证:提供
spring.ai.chat.memory.repository.cosmosdb.key属性以及您的 Cosmos DB 主密钥或辅助密钥。Azure Identity 身份验证:如果未提供密钥,存储库将使用 Azure Identity (
DefaultAzureCredential) 通过托管标识、服务主体或其他 Azure 凭据源进行身份验证。
3.5.3. Schema 初始化
自动配置将自动创建指定的数据库和容器(如果它们不存在)。容器配置使用会话 ID 作为分区键(/conversationId),以确保聊天记忆操作的最佳性能。无需手动设置 schema。
您可以使用上述配置属性自定义数据库和容器名称。
3.6. MongoChatMemoryRepository
ongoChatMemoryRepository 是一个内置实现,它使用 MongoDB 存储消息。它适用于需要灵活的、面向文档的数据库来持久化聊天记忆的应用程序。
首先,将以下依赖项添加到您的项目:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-mongodb</artifactId>
</dependency>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();3.6.1. 配置属性
3.6.2. 集合初始化
自动配置将在启动时自动创建 ai_chat_memory 集合(如果它尚不存在)。
4. 聊天客户端中的记忆
使用 ChatClient API 时,您可以提供 ChatMemory 实现以在多次交互中维护对话上下文。
Spring AI 提供了一些内置的 Advisor,您可以根据需要使用它们来配置 ChatClient 的记忆行为。
目前,在执行工具调用时与大型语言模型交换的中间消息不会存储在内存中。这是当前实现的一个限制,将在未来版本中解决。如果您需要存储这些消息,请参阅 用户控制的工具执行 的说明。
MessageChatMemoryAdvisor。此 Advisor 使用提供的ChatMemory实现管理对话记忆。在每次交互时,它从记忆中检索对话历史并将其作为消息集合包含在提示中。PromptChatMemoryAdvisor。此 Advisor 使用提供的ChatMemory实现管理对话记忆。在每次交互时,它从记忆中检索对话历史并将其作为纯文本附加到系统提示中。VectorStoreChatMemoryAdvisor。此 Advisor 使用提供的VectorStore实现管理对话记忆。在每次交互时,它从向量存储中检索对话历史并将其作为纯文本附加到系统消息中。
例如,如果您想将 MessageWindowChatMemory 与 MessageChatMemoryAdvisor 一起使用,可以按如下方式配置它:
ChatMemory chatMemory = MessageWindowChatMemory.builder().build();
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();当调用 ChatClient 时,记忆将由 MessageChatMemoryAdvisor 自动管理。对话历史将根据指定的对话 ID 从记忆中检索:
String conversationId = "007";
chatClient.prompt()
.user("Do I have license to code?")
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
.call()
.content();4.1. PromptChatMemoryAdvisor
4.1.1. 自定义模板
PromptChatMemoryAdvisor 使用默认模板通过检索到的对话记忆来增强系统消息。您可以通过 .promptTemplate() 构建器方法提供自己的 PromptTemplate 对象来自定义此行为。
此处提供的 PromptTemplate 自定义了 Advisor 如何将检索到的记忆与系统消息合并。这与在 ChatClient 本身配置 TemplateRenderer(使用 .templateRenderer())是不同的,后者会影响 Advisor 运行之前初始用户/系统提示内容的渲染。有关客户端级别模板渲染的更多详细信息,请参阅 ChatClient 提示模板。
自定义 PromptTemplate 可以使用任何 TemplateRenderer 实现(默认情况下,它使用基于 StringTemplate 引擎的 StPromptTemplate)。重要的要求是模板必须包含以下两个占位符:
一个
instructions占位符用于接收原始系统消息。一个
memory占位符用于接收检索到的对话记忆。
4.2. VectorStoreChatMemoryAdvisor
4.2.1. 自定义模板
VectorStoreChatMemoryAdvisor 使用默认模板通过检索到的对话记忆来增强系统消息。您可以通过 .promptTemplate() 构建器方法提供自己的 PromptTemplate 对象来自定义此行为。
此处提供的 PromptTemplate 自定义了 Advisor 如何将检索到的记忆与系统消息合并。这与在 ChatClient 本身配置 TemplateRenderer(使用 .templateRenderer())是不同的,后者会影响 Advisor 运行之前初始用户/系统提示内容的渲染。有关客户端级别模板渲染的更多详细信息,请参阅 ChatClient 提示模板。
自定义 PromptTemplate 可以使用任何 TemplateRenderer 实现(默认情况下,它使用基于 StringTemplate 引擎的 StPromptTemplate)。重要的要求是模板必须包含以下两个占位符:
一个
instructions占位符用于接收原始系统消息。一个
long_term_memory占位符用于接收检索到的对话记忆。
5. 聊天模型中的记忆
如果您直接使用 ChatModel 而不是 ChatClient,您可以显式管理记忆:
// Create a memory instance
ChatMemory chatMemory = MessageWindowChatMemory.builder().build();
String conversationId = "007";
// First interaction
UserMessage userMessage1 = new UserMessage("My name is James Bond");
chatMemory.add(conversationId, userMessage1);
ChatResponse response1 = chatModel.call(new Prompt(chatMemory.get(conversationId)));
chatMemory.add(conversationId, response1.getResult().getOutput());
// Second interaction
UserMessage userMessage2 = new UserMessage("What is my name?");
chatMemory.add(conversationId, userMessage2);
ChatResponse response2 = chatModel.call(new Prompt(chatMemory.get(conversationId)));
chatMemory.add(conversationId, response2.getResult().getOutput());
// The response will contain "James Bond"
评论区