聊天记忆
本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。 |
大语言模型(LLM)本质上是无状态的,这意味着它们不会保留历史交互信息。当需要跨多轮交互保持上下文时,这一特性会带来局限。为此,Spring AI 提供了聊天记忆功能,支持在 LLM 交互过程中存储和检索上下文数据。
ChatMemory
抽象层支持实现多种记忆类型以满足不同场景需求。消息的底层存储由 ChatMemoryRepository
处理,其唯一职责是存储和检索消息。 ChatMemory
实现类可自主决定消息保留策略 — 例如保留最近 N 条消息、按时间周期保留或基于 Token 总量限制保留。
选择记忆类型前,需明确聊天记忆与聊天历史的区别:
-
聊天记忆:大语言模型在对话过程中保留并用于维持上下文感知的信息。
-
聊天历史:完整的对话记录,包含用户与模型之间交换的所有消息。
ChatMemory
抽象层专为管理聊天记忆设计,支持存储和检索当前会话相关的上下文消息。但若需完整记录所有历史消息,建议采用其他方案(如基于 Spring Data 实现高效的全量聊天历史存储与检索)。
快速入门
Spring AI 自动配置 ChatMemory
Bean 供直接使用。默认采用内存存储(InMemoryChatMemoryRepository
)及 MessageWindowChatMemory
实现管理会话历史。若已配置其他 Repository(如 Cassandra/JDBC/Neo4j),则自动切换至对应实现。
@Autowired
ChatMemory chatMemory;
以下章节将详细介绍 Spring AI 中可用的不同记忆类型与 Repository 实现。
记忆存储
Spring AI 通过 ChatMemoryRepository
抽象层实现聊天记忆存储。本节介绍内置 Repository 及其用法,同时支持自定义实现。
内存 Repository
InMemoryChatMemoryRepository
基于 ConcurrentHashMap
实现内存存储。
默认情况下,若未配置其他 Repository,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 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
接口可扩展其他数据库支持。
配置属性
属性 |
说明 |
默认值 |
|
控制初始化 Schema 的时机。可选值: |
|
|
用于初始化的 Schema 脚本位置。支持 |
|
|
若初始化脚本中使用 @@platform@@ 占位符,则指定其对应的平台标识。 |
auto-detected |
Schema 初始化
自动配置将在启动时使用特定于供应商的 SQL 脚本创建 SPRING_AI_CHAT_MEMORY
表。默认情况下,仅针对嵌入式数据库(H2、HSQL、Derby 等)执行 Schema 初始化。
可通过 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.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 Key 空间名称(keyspace) |
|
|
Cassandra 消息列名 |
|
|
Cassandra 表 |
|
|
是否在启动时初始化 Schema 结构 |
|
Neo4jChatMemoryRepository
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();
配置属性
属性 |
说明 |
默认值 |
|
存储对话会话的节点的标签 |
|
|
存储信息的节点的标签 |
|
|
存储工具调用的节点(如 Assistant 信息中)的标签 |
|
|
存储消息元数据的节点的标签 |
|
|
存储工具响应的节点的标签 |
|
|
存储消息相关的媒体的节点标签 |
|
聊天客户端的 Memory
使用 ChatClient
API时,可通过注入 ChatMemory
实现来维护跨多轮交互的会话上下文。
Spring AI 提供多种内置 Advisor,用于按需配置 ChatClient
的记忆行为。
当前版本存在限制:执行工具调用时与大型语言模型交互的中间消息不会存入记忆。该问题将在后续版本修复。如需存储此类消息,请参阅 用户控制工具执行 章节的操作指南。 |
-
MessageChatMemoryAdvisor
:通过指定ChatMemory
实现管理会话记忆。每次交互时从记忆库检索历史消息,并将其作为消息集合注入提示词。 -
PromptChatMemoryAdvisor
:基于指定ChatMemory
实现管理会话记忆。每次交互时从记忆库检索历史对话,并以纯文本形式追加至系统(system)提示词。 -
VectorStoreChatMemoryAdvisor
:通过指定VectorStore
实现管理会话记忆。每次交互时从向量存储检索历史对话,并以纯文本形式追加至系统(system)消息。
例如,若需结合 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 用于定制 Advisor 如何将检索到的记忆与系统消息合并。这与在 ChatClient 上配置 TemplateRenderer (通过 .templateRenderer() )不同 — 后者影响 Advisor 运行前初始 user/system 提示内容的渲染。客户端级模板渲染详见 ChatClient提示模板 章节。
|
自定义 PromptTemplate
可采用任何 TemplateRenderer
实现(默认使用基于 StringTemplate
引擎的 StPromptTemplate
)。关键要求是模板必须包含以下两个占位符:
-
接收原始系统消息的指令(
instructions
)占位符。 -
接收检索到的会话记忆(
memory
)的记忆占位符。
VectorStoreChatMemoryAdvisor
自定义模板
VectorStoreChatMemoryAdvisor
通过默认模板将检索到的会话记忆增强至系统消息。通过 .promptTemplate()
构建方法注入自定义 PromptTemplate
对象可覆盖该行为。
此处提供的P romptTemplate 专门用于配置 Advisor 如何整合检索记忆与系统消息,这与 ChatClient 自身的 TemplateRenderer 配置(通过 .templateRenderer() )有本质区别 — 后者控制 Advisor 执行前原始 user/system 提示内容的渲染逻辑。客户端级模板渲染详见 ChatClient提示模板 章节说明。
|
自定义 PromptTemplate
可采用任意 TemplateRenderer
实现(默认使用基于 StringTemplate
引擎的 StPromptTemplate
)。模板必须包含以下两个占位符:
-
接收原始系统消息的
instructions
占位符。 -
接收检索到的长期会话记忆的
long_term_memory
占位符。
聊天模型中的记忆
若直接使用 ChatModel
而非 ChatClient
,需显式管理记忆存储:
// 创建 memory 实例
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"