大型语言模型 (LLMs) 是无状态的,这意味着它们不会保留有关先前交互的信息。
当您希望在多次交互中保持上下文或状态时,这可能是一个限制。
为了解决这个问题,Spring AI 提供了聊天记忆功能,允许您在与 LLM 的多次交互中存储和检索信息。
ChatMemory 抽象允许您实现不同类型的内存以支持不同的用例。
消息的底层存储由 ChatMemoryRepository 处理,其唯一职责是存储和检索消息。
由 ChatMemory 实现来决定保留哪些消息以及何时删除它们。
策略示例包括保留最后 N 条消息、在特定时间段内保留消息,或保留消息直至达到某个令牌限制。
在选择内存类型之前,了解聊天记忆 (chat memory) 和聊天历史 (chat history) 之间的区别至关重要。
聊天记忆 (Chat Memory)。大型语言模型在整个对话中保留并用于维持上下文感知的信息。
聊天历史 (Chat History)。整个对话历史,包括用户和模型之间交换的所有消息。
ChatMemory 抽象旨在管理聊天记忆。
它允许您存储和检索与当前对话上下文相关的消息。
然而,它并不最适合存储聊天历史。
如果您需要维护所有交换消息的完整记录,则应考虑使用不同的方法,例如依赖 Spring Data 来高效存储和检索完整的聊天历史。
快速入门
Spring AI 自动配置一个 ChatMemory bean,您可以直接在应用程序中使用它。
默认情况下,它使用内存存储库 (InMemoryChatMemoryRepository) 来存储消息,并使用 MessageWindowChatMemory 实现来管理对话历史。
如果已经配置了不同的存储库(例如 Cassandra、JDBC 或 Neo4j),Spring AI 将改用该存储库。
@Autowired
ChatMemory chatMemory;
以下部分将详细介绍 Spring AI 中可用的不同内存类型和存储库。
内存类型
ChatMemory 抽象允许您实现不同类型的内存以适应不同的用例。
内存类型的选择会显著影响应用程序的性能和行为。
本节介绍 Spring AI 提供的内置内存类型及其特性。
消息窗口聊天记忆 (Message Window Chat Memory)
MessageWindowChatMemory 维护一个消息窗口,最多可达到指定的最大大小。
当消息数量超过最大值时,会删除较旧的消息,同时保留系统消息。
默认窗口大小为 20 条消息。
MessageWindowChatMemory memory = MessageWindowChatMemory.builder()
.maxMessages(10)
.build();
这是 Spring AI 用于自动配置 ChatMemory bean 的默认消息类型。
内存存储
Spring AI 提供了 ChatMemoryRepository 抽象用于存储聊天记忆。
本节介绍 Spring AI 提供的内置存储库以及如何使用它们,但您也可以在需要时实现自己的存储库。
内存存储库 (In-Memory Repository)
InMemoryChatMemoryRepository 使用 ConcurrentHashMap 在内存中存储消息。
默认情况下,如果未配置其他存储库,Spring AI 会自动配置一个 InMemoryChatMemoryRepository 类型的 ChatMemoryRepository bean,您可以直接在应用程序中使用。
@Autowired
ChatMemoryRepository chatMemoryRepository;
如果您想手动创建 InMemoryChatMemoryRepository,可以按如下方式进行:
ChatMemoryRepository repository = new InMemoryChatMemoryRepository();
JdbcChatMemoryRepository
JdbcChatMemoryRepository 是一个内置实现,它使用 JDBC 将消息存储在关系数据库中。
它开箱即用地支持多种数据库,适用于需要持久存储聊天记忆的应用程序。
首先,将以下依赖项添加到您的项目中:
Maven
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>
Gradle
@Autowired
JdbcChatMemoryRepository chatMemoryRepository;
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
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 PostgresChatMemoryDialect())
.build();
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
支持的数据库和方言抽象
Spring AI 通过方言抽象支持多种关系数据库。以下数据库开箱即用支持:
PostgreSQL
MySQL / MariaDB
SQL Server
HSQLDB
使用 JdbcChatMemoryRepositoryDialect.from(DataSource) 时,可以从 JDBC URL 自动检测正确的方言。
您可以通过实现 JdbcChatMemoryRepositoryDialect 接口来扩展对其他数据库的支持。
配置属性
模式初始化
自动配置将在启动时使用特定于供应商的 SQL 脚本为您的数据库自动创建 SPRING_AI_CHAT_MEMORY 表。
默认情况下,模式初始化仅针对嵌入式数据库(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();
CassandraChatMemoryRepository
CassandraChatMemoryRepository 使用 Apache Cassandra 存储消息。
它适用于需要持久存储聊天记忆的应用程序,特别是在需要高可用性、持久性、可扩展性以及利用生存时间 (TTL) 功能时。
CassandraChatMemoryRepository 具有时间序列模式,保留所有过去的聊天窗口记录,这对于治理和审计很有价值。
建议将生存时间设置为某个值,例如三年。
要使用 CassandraChatMemoryRepository,首先将依赖项添加到您的项目:
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(CassandraChatMemoryConfig.builder().withCqlSession(cqlSession));
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
配置属性
模式初始化
自动配置将自动创建 ai_chat_memory 表。
您可以通过将属性 spring.ai.chat.memory.repository.cassandra.initialize-schema 设置为 false 来禁用模式初始化。
Neo4j ChatMemoryRepository
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();
配置属性
索引初始化
Neo4j 存储库将自动确保为会话 ID 和消息索引创建索引以优化性能。
如果您使用自定义标签,也将为这些标签创建索引。
不需要模式初始化,但您应确保您的应用程序可以访问您的 Neo4j 实例。
聊天客户端中的记忆
当使用 ChatClient API 时,您可以提供一个 ChatMemory 实现来在多次交互中维护对话上下文。
Spring AI 提供了一些内置的顾问 (Advisors),您可以根据需要用来配置 ChatClient 的记忆行为。
目前,在执行工具调用时与大型语言模型交换的中间消息不会存储在内存中。
这是当前实现的一个限制,将在未来的版本中解决。
如果您需要存储这些消息,请参阅用户控制工具执行的说明。
MessageChatMemoryAdvisor。此顾问使用提供的 ChatMemory 实现管理对话记忆。
在每次交互时,它从记忆中检索对话历史记录,并将其作为消息集合包含在提示中。PromptChatMemoryAdvisor。此顾问使用提供的 ChatMemory 实现管理对话记忆。
在每次交互时,它从记忆中检索对话历史记录,并将其作为纯文本附加到系统提示中。VectorStoreChatMemoryAdvisor。此顾问使用提供的 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();
PromptChatMemoryAdvisor
自定义模板
PromptChatMemoryAdvisor 使用默认模板,用检索到的对话记忆来增强系统消息。
您可以通过 .promptTemplate() 构建器方法提供自己的 PromptTemplate 对象来自定义此行为。
这里提供的 PromptTemplate 自定义了顾问如何将检索到的记忆与系统消息合并。
这与在 ChatClient 本身上配置 TemplateRenderer(使用 .templateRenderer())不同,后者影响顾问运行之前初始用户/系统提示内容的渲染。
有关客户端级模板渲染的更多详细信息,请参阅 ChatClient 提示模板。
自定义 PromptTemplate 可以使用任何 TemplateRenderer 实现(默认情况下,它使用基于 StringTemplate 引擎的 StPromptTemplate)。
重要的要求是模板必须包含以下两个占位符:
一个 instructions 占位符,用于接收原始系统消息。
一个 memory 占位符,用于接收检索到的对话记忆。
VectorStoreChatMemoryAdvisor
自定义模板
VectorStoreChatMemoryAdvisor 使用默认模板,用检索到的对话记忆来增强系统消息。
您可以通过 .promptTemplate() 构建器方法提供自己的 PromptTemplate 对象来自定义此行为。
这里提供的 PromptTemplate 自定义了顾问如何将检索到的记忆与系统消息合并。
这与在 ChatClient 本身上配置 TemplateRenderer(使用 .templateRenderer())不同,后者影响顾问运行之前初始用户/系统提示内容的渲染。
有关客户端级模板渲染的更多详细信息,请参阅 ChatClient 提示模板。
自定义 PromptTemplate 可以使用任何 TemplateRenderer 实现(默认情况下,它使用基于 StringTemplate 引擎的 StPromptTemplate)。
重要的要求是模板必须包含以下两个占位符:
一个 instructions 占位符,用于接收原始系统消息。
一个 long_term_memory 占位符,用于接收检索到的对话记忆。
聊天模型中的记忆
如果您直接使用 ChatModel 而不是 ChatClient,则可以显式管理记忆:
// 创建记忆实例
ChatMemory chatMemory = MessageWindowChatMemory.builder().build();
String conversationId = "007";
// 第一次交互
UserMessage userMessage1 = new UserMessage("我的名字是詹姆斯·邦德"); // Translated "My name is James Bond"
chatMemory.add(conversationId, userMessage1);
ChatResponse response1 = chatModel.call(new Prompt(chatMemory.get(conversationId)));
chatMemory.add(conversationId, response1.getResult().getOutput());
// 第二次交互
UserMessage userMessage2 = new UserMessage("我叫什么名字?"); // Translated "What is my name?"
chatMemory.add(conversationId, userMessage2);
ChatResponse response2 = chatModel.call(new Prompt(chatMemory.get(conversationId)));
chatMemory.add(conversationId, response2.getResult().getOutput());
// 响应将包含 "詹姆斯·邦德" ("James Bond")
评论区