聊天记忆

本站(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 实现。

记忆类型

ChatMemory 抽象层支持实现多种记忆类型以适应不同场景。记忆类型(Memory Type)的选择将显著影响应用性能与行为特征。本节详解 Spring AI 内置的记忆类型及其特性。

MessageWindowChatMemory

MessageWindowChatMemory 维护固定容量的消息窗口(默认 20 条)。当消息超限时,自动移除较早的对话消息(始终保留系统消息)。

MessageWindowChatMemory memory = MessageWindowChatMemory.builder()
    .maxMessages(10)
    .build();

此为 Spring AI 自动配置 ChatMemory Bean 时采用的默认消息类型。

记忆存储

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 接口可扩展其他数据库支持。

配置属性

属性

说明

默认值

spring.ai.chat.memory.repository.jdbc.initialize-schema

控制初始化 Schema 的时机。可选值:embedded(默认)、alwaysnever

embedded

spring.ai.chat.memory.repository.jdbc.schema

用于初始化的 Schema 脚本位置。支持 classpath: URL 及平台占位符。

classpath:org/springframework/ai/chat/memory/repository/jdbc/schema-@@platform@@.sql

spring.ai.chat.memory.repository.jdbc.platform

若初始化脚本中使用 @@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

扩展方言

要新增数据库支持,需实现 JdbcChatMemoryRepositoryDialect 接口并提供消息查询、插入及删除的 SQL 语句。随后可将自定义方言传入 Repository Builder。

ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder()
    .jdbcTemplate(jdbcTemplate)
    .dialect(new MyCustomDbDialect())
    .build();

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();

配置属性

属性

说明

默认值

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 Key 空间名称(keyspace)

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-schemafalse 禁用 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();

配置属性

属性

说明

默认值

spring.ai.chat.memory.repository.neo4j.sessionLabel

存储对话会话的节点的标签

Session

spring.ai.chat.memory.repository.neo4j.messageLabel

存储信息的节点的标签

Message

spring.ai.chat.memory.repository.neo4j.toolCallLabel

存储工具调用的节点(如 Assistant 信息中)的标签

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 和消息索引的优化索引。若使用自定义标签,系统同样会为这些标签建立索引。虽无需初始化 Schema,但需确保应用可访问 Neo4j 实例。

聊天客户端的 Memory

使用 ChatClient API时,可通过注入 ChatMemory 实现来维护跨多轮交互的会话上下文。

Spring AI 提供多种内置 Advisor,用于按需配置 ChatClient 的记忆行为。

当前版本存在限制:执行工具调用时与大型语言模型交互的中间消息不会存入记忆。该问题将在后续版本修复。如需存储此类消息,请参阅 用户控制工具执行 章节的操作指南。
  • MessageChatMemoryAdvisor:通过指定 ChatMemory 实现管理会话记忆。每次交互时从记忆库检索历史消息,并将其作为消息集合注入提示词。

  • PromptChatMemoryAdvisor:基于指定 ChatMemory 实现管理会话记忆。每次交互时从记忆库检索历史对话,并以纯文本形式追加至系统(system)提示词。

  • VectorStoreChatMemoryAdvisor:通过指定 VectorStore 实现管理会话记忆。每次交互时从向量存储检索历史对话,并以纯文本形式追加至系统(system)消息。

例如,若需结合 MessageWindowChatMemoryMessageChatMemoryAdvisor,可按如下方式配置:

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"