聊天记忆
大型语言模型(LLMs)是无状态的,这意味着它们不会保留关于之前交互的信息。当您希望跨多次交互保持上下文或状态时,这可能会成为一个限制。为了解决这一问题,Spring AI 提供了聊天记忆功能,允许您在与 LLM 的多次交互中存储和检索信息。
ChatMemory 抽象层 让您能够实现多种类型的内存以支持不同的应用场景。消息的底层存储由 ChatMemoryRepository
处理,其唯一职责是存储
和检索消息
。ChatMemory
的实现则需决定保留哪些消息以及何时删除它们。策略示例包括保留最近的 N 条消息、保留特定时间段内的消息,或保留至某一令牌上限的消息。
在选择记忆类型之前,理解聊天记忆与聊天历史之间的区别至关重要。
- 聊天记忆。大型语言模型在对话过程中保留并用于维持上下文意识的信息。
- 聊天历史。完整的对话记录,包括用户与模型之间交换的所有消息。
ChatMemory
抽象层旨在管理聊天记忆,它允许您存储和检索与当前对话上下文相关的消息。然而,它并非存储完整聊天历史的最佳选择。若需保留所有交换消息的完整记录,您应考虑采用其他方法,例如依赖 Spring Data 以实现高效存储与检索完整的聊天历史。
快速入门(Quick Start)
Spring AI 自动配置了一个 ChatMemory Bean,您可以直接在应用程序中使用。默认情况下,它使用内存存储库来保存消息InMemoryChatMemoryRepository
,并通过 MessageWindowChatMemory
实现来管理对话历史。如果已经配置了不同的存储库(例如 Cassandra、JDBC 或 Neo4j),Spring AI 则会使用该存储库。
@Autowired
ChatMemory chatMemory;
接下来的章节将详细介绍 Spring AI 中不同的内存类型及其存储库。
内存类型(Memory Types)
ChatMemory 抽象化允许您实现多种类型的内存以适应不同的使用场景。内存类型的选择能显著影响应用程序的性能和行为。本节将介绍 Spring AI 提供的内置内存类型及其特性。
消息窗口聊天记忆(Message Window Chat Memory)
MessageWindowChatMemory
维护着一个指定最大容量的消息窗口。当消息数量超过最大值时,较旧的消息会被移除,但系统消息会被保留。默认的窗口大小为 20 条消息。
MessageWindowChatMemory memory = MessageWindowChatMemory.builder()
.maxMessages(10)
.build();
这是 Spring AI 用于自动配置 ChatMemory Bean 的默认消息类型。
内存存储(Memory Storage)
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 pom.xml
文件中:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>
或者,在你的 Gradle 构建文件 build.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 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
接口,您可以扩展对其他数据库的支持。
配置属性(Configuration Properties)
属性 | 描述 | 默认值 |
---|---|---|
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 |
存储消息的Cassandra列名 | springframework |
spring.ai.chat.memory.cassandra.table |
Cassandra表名 | ai_chat_memory |
spring.ai.chat.memory.cassandra.initialize-schema |
是否在启动时初始化schema | true |
Schema 初始化
自动配置将自动创建 ai_chat_memory
表。
您可以通过将属性 spring.ai.chat.memory.repository.cassandra.initialize-schema
设置为 ‘false‘ 来禁用模式初始化。
Neo4j ChatMemoryRepository
Neo4jChatMemoryRepository 是一个内置实现,它利用 Neo4j 将聊天消息作为节点和关系存储在属性图数据库中。它适用于那些希望利用 Neo4j 的图功能来实现聊天记忆持久化的应用程序。
请将以下依赖项添加到项目的 Maven pom.xml
文件中:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-neo4j</artifactId>
</dependency>
或者,在你的 Gradle 构建文件 build.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();
配置属性(Configuration Properties)
属性 | 描述 | 默认值 |
---|---|---|
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 实例。
聊天客户端中的记忆功能(Memory in Chat Client)
使用 ChatClient API 时,您可以提供一个 ChatMemory 实现,以在多次交互中维护对话上下文。
Spring AI 提供了几个内置的 Advisor,您可以根据需要用来配置 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 可以使用任何 TemplateRenderer 实现(默认情况下,它使用基于 StringTemplate 引擎的 StPromptTemplate)。重要的要求是模板必须包含以下两个占位符:
- 一个
instructions
占位符,用于接收原始系统消息。 - 一个
memory
占位符,用于接收检索到的对话记忆。
VectorStoreChatMemoryAdvisor
自定义模板
VectorStoreChatMemoryAdvisor
采用默认模板来增强系统消息,结合检索到的对话记忆。您可以通过 .promptTemplate () 构建器方法提供自定义的 PromptTemplate 对象,以定制这一行为。
自定义的 PromptTemplate 可以使用任何 TemplateRenderer 实现(默认情况下,它使用基于 StringTemplate 引擎的 StPromptTemplate)。重要的要求是模板必须包含以下两个占位符:
- 一个
instructions
占位符,用于接收原始系统消息。 - 一个
memory
占位符,用于接收检索到的对话记忆。
Memory in Chat Model
如果您直接与 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"
最后编辑:Jeebiz 更新时间:2025-08-31 23:07