聊天记忆
大型语言模型(LLM)是无状态的,这意味着它们不会保留先前交互的信息。当您希望在多次交互中保持上下文或状态时,这可能是一个限制。为了解决这个问题,Spring AI 提供了聊天记忆功能,允许您在与 LLM 的多次交互中存储和检索信息。
ChatMemory
抽象允许您实现各种类型的记忆以支持不同的用例。消息的底层存储由 ChatMemoryRepository
处理,其唯一职责是存储和检索消息。ChatMemory
实现负责决定保留哪些消息以及何时删除它们。策略示例可以包括保留最后 N 条消息、保留特定时间段内的消息或保留达到特定令牌限制的消息。
在选择记忆类型之前,了解聊天记忆和聊天历史之间的区别至关重要。
-
聊天记忆。大型语言模型保留并用于在整个对话中保持上下文感知的信息。
-
聊天历史。完整的对话历史,包括用户和模型之间交换的所有消息。
ChatMemory
抽象旨在管理 聊天记忆。它允许您存储和检索与当前对话上下文相关的消息。但是,它不是存储 聊天历史 的最佳选择。如果您需要维护所有已交换消息的完整记录,您应该考虑使用不同的方法,例如依靠 Spring Data 来高效存储和检索完整的聊天历史。
快速开始
Spring AI 会自动配置一个 ChatMemory
bean,您可以直接在应用程序中使用它。默认情况下,它使用内存存储库来存储消息(InMemoryChatMemoryRepository
),并使用 MessageWindowChatMemory
实现来管理对话历史。如果已经配置了不同的存储库(例如 Cassandra、JDBC 或 Neo4j),Spring AI 将使用该存储库。
@Autowired
ChatMemory chatMemory;
以下章节将进一步描述 Spring AI 中可用的不同记忆类型和存储库。
记忆存储
Spring AI 提供 ChatMemoryRepository
抽象用于存储聊天记忆。本节描述了 Spring AI 提供的内置存储库以及如何使用它们,但您也可以根据需要实现自己的存储库。
内存存储库
InMemoryChatMemoryRepository
使用 ConcurrentHashMap
在内存中存储消息。
默认情况下,如果尚未配置其他存储库,Spring AI 会自动配置一个 InMemoryChatMemoryRepository
类型的 ChatMemoryRepository
bean,您可以直接在应用程序中使用它。
@Autowired
ChatMemoryRepository chatMemoryRepository;
如果您想手动创建 InMemoryChatMemoryRepository
,可以按如下方式操作:
ChatMemoryRepository repository = new InMemoryChatMemoryRepository();
JdbcChatMemoryRepository
JdbcChatMemoryRepository
是一个内置实现,它使用 JDBC 将消息存储在关系数据库中。它开箱即用支持多种数据库,适用于需要持久存储聊天记忆的应用程序。
首先,将以下依赖项添加到您的项目中:
-
Maven
-
Gradle
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>
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
使用 JdbcChatMemoryRepositoryDialect.from(DataSource)
时,可以从 JDBC URL 自动检测到正确的方言。您可以通过实现 JdbcChatMemoryRepositoryDialect
接口来扩展对其他数据库的支持。
配置属性
属性 |
描述 |
默认值 |
|
控制何时初始化 schema。值: |
|
|
用于初始化的 schema 脚本的位置。支持 |
|
|
如果使用 @@platform@@ 占位符,则在初始化脚本中使用的平台。 |
自动检测 |
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 # 仅适用于嵌入式数据库 (默认)
spring.ai.chat.memory.repository.jdbc.initialize-schema=always # 始终初始化
spring.ai.chat.memory.repository.jdbc.initialize-schema=never # 从不初始化 (与 Flyway/Liquibase 结合使用时很有用)
要覆盖 schema 脚本位置,请使用:
spring.ai.chat.memory.repository.jdbc.schema=classpath:/custom/path/schema-mysql.sql
CassandraChatMemoryRepository
CassandraChatMemoryRepository
使用 Apache Cassandra 存储消息。它适用于需要持久存储聊天记忆的应用程序,特别是为了可用性、持久性、扩展性以及利用生命周期(TTL)功能。
CassandraChatMemoryRepository
具有时间序列 schema,记录所有过去的聊天窗口,这对于治理和审计很有价值。建议将生命周期设置为某个值,例如三年。
要使用 CassandraChatMemoryRepository
,首先将依赖项添加到您的项目中:
-
Maven
-
Gradle
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-cassandra</artifactId>
</dependency>
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();
配置属性
属性 |
描述 |
默认值 |
|
启动集群发现的主机 |
|
|
连接的 Cassandra 原生协议端口 |
|
|
连接的 Cassandra 数据中心 |
|
|
写入 Cassandra 的消息的生命周期 (TTL) |
|
|
Cassandra 键空间 |
|
|
消息的 Cassandra 列名 |
|
|
Cassandra 表 |
|
|
是否在启动时初始化 schema。 |
|
Neo4j ChatMemoryRepository
Neo4jChatMemoryRepository
是一个内置实现,它使用 Neo4j 将聊天消息存储为属性图数据库中的节点和关系。它适用于希望利用 Neo4j 的图功能进行聊天记忆持久化的应用程序。
首先,将以下依赖项添加到您的项目中:
-
Maven
-
Gradle
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-neo4j</artifactId>
</dependency>
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();
配置属性
属性 |
描述 |
默认值 |
|
存储对话会话的节点的标签 |
|
|
存储消息的节点的标签 |
|
|
存储工具调用的节点的标签(例如在助手消息中) |
|
|
存储消息元数据的节点的标签 |
|
|
存储工具响应的节点的标签 |
|
|
存储与消息关联的媒体的节点的标签 |
|
聊天客户端中的记忆
使用 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();
PromptChatMemoryAdvisor
自定义模板
PromptChatMemoryAdvisor
使用默认模板通过检索到的对话记忆来增强系统消息。您可以通过 .promptTemplate()
构建器方法提供自己的 PromptTemplate
对象来定制此行为。
此处提供的 |
自定义 PromptTemplate
可以使用任何 TemplateRenderer
实现(默认情况下,它使用基于 StringTemplate 引擎的 StPromptTemplate
)。重要的要求是模板必须包含以下两个占位符:
-
一个
instructions
占位符,用于接收原始系统消息。 -
一个
memory
占位符,用于接收检索到的对话记忆。
VectorStoreChatMemoryAdvisor
自定义模板
VectorStoreChatMemoryAdvisor
使用默认模板通过检索到的对话记忆来增强系统消息。您可以通过 .promptTemplate()
构建器方法提供自己的 PromptTemplate
对象来定制此行为。
此处提供的 |
自定义 PromptTemplate
可以使用任何 TemplateRenderer
实现(默认情况下,它使用基于 StringTemplate 引擎的 StPromptTemplate
)。重要的要求是模板必须包含以下两个占位符:
-
一个
instructions
占位符,用于接收原始系统消息。 -
一个
long_term_memory
占位符,用于接收检索到的对话记忆。
聊天模型中的记忆
如果您直接使用 ChatModel
而不是 ChatClient
,您可以显式管理记忆:
// 创建一个记忆实例
ChatMemory chatMemory = MessageWindowChatMemory.builder().build();
String conversationId = "007";
// 第一次交互
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());
// 第二次交互
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());
// 响应将包含 "James Bond"