Chat Memory

大型语言模型(LLM)是无状态的,这意味着它们不保留有关以前交互的信息。当您想在多个交互中维护上下文或状态时,这可能是一个限制。为了解决这个问题,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将消息存储在关系数据库中。它支持开箱即用的多个数据库,适用于需要持久存储聊天内存的应用程序。
首先,将以下依赖项添加到您的项目中:


    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();
Supported Databases and Dialect Abstraction

Spring AI通过方言抽象支持多个关系数据库。开箱即用支持以下数据库:
PostgreSQL
MySQL/MariaDB
SQL Server
嵌入式数据库
使用JdbcChatMemoryRepositoryAlect.from(DataSource)时,可以从JDBC URL自动检测到正确的方言。您可以通过实现JdbcChatMemoryRepositoryDialect接口来扩展对其他数据库的支持。

Configuration Properties

Property

Description

Default Value

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

Controls when to initialize the schema. Values: embedded (default), alwaysnever.

embedded

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

Location of the schema script to use for initialization. Supports classpath: URLs and platform placeholders.

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

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

Platform to use in initialization scripts if the @@platform@@ placeholder is used.

auto-detected

Schema Initialization

自动配置将在启动时使用特定于供应商的数据库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 # 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)

要覆盖架构脚本位置,请使用:

spring.ai.chat.memory.repository.jdbc.schema=classpath:/custom/path/schema-mysql.sql
Extending Dialects

要添加对新数据库的支持,请实现JdbcChatMemoryRepositoryAlect接口,并提供用于选择、插入和删除消息的SQL。然后,您可以将自定义方言传递给存储库构建器。

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

CassandraChatMemoryRepository

CassandraChatMemoryRepository使用Apache Cassandra来存储消息。它适用于需要持久存储聊天内存的应用程序,特别是在可用性、耐用性、可扩展性以及利用生存时间(TTL)功能时。
CassandraChatMemoryRepository有一个时间序列模式,记录了所有过去的聊天窗口,对治理和审计很有价值。建议将寿命设定为一定值,例如三年。
要先使用CassandraChatMemoryRepository,请将依赖项添加到您的项目中:
 


    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,可以通过提供一个CassandraChat MemoryRepositoryConfig实例来实现:

ChatMemoryRepository chatMemoryRepository = CassandraChatMemoryRepository
    .create(CassandraChatMemoryConfig.builder().withCqlSession(cqlSession));

ChatMemory chatMemory = MessageWindowChatMemory.builder()
    .chatMemoryRepository(chatMemoryRepository)
    .maxMessages(10)
    .build();
Configuration Properties

Property

Description

Default Value

spring.cassandra.contactPoints

Host(s) to initiate cluster discovery

127.0.0.1

spring.cassandra.port

Cassandra native protocol port to connect to

9042

spring.cassandra.localDatacenter

Cassandra datacenter to connect to

datacenter1

spring.ai.chat.memory.cassandra.time-to-live

Time to live (TTL) for messages written in Cassandra

spring.ai.chat.memory.cassandra.keyspace

Cassandra keyspace

springframework

spring.ai.chat.memory.cassandra.messages-column

Cassandra column name for messages

springframework

spring.ai.chat.memory.cassandra.table

Cassandra table

ai_chat_memory

spring.ai.chat.memory.cassandra.initialize-schema

Whether to initialize the schema on startup.

true

Schema Initialization

自动配置将自动创建ai_chat_memory表。
您可以通过将属性spring.ai.chat.memory.repository.cassandra.initialize-schema设置为false来禁用架构初始化。

Neo4j ChatMemoryRepository

Neo4jChatMemoryRepository是一个内置的实现,它使用Neo4j将聊天消息作为节点和关系存储在属性图数据库中。它适用于希望利用Neo4j的图形功能实现聊天内存持久性的应用程序。
首先,将以下依赖项添加到您的项目中:


    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

Property

Description

Default Value

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

The label for the nodes that store conversation sessions

Session

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

The label for the nodes that store messages

Message

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

The label for nodes that store tool calls (e.g. in Assistant Messages)

ToolCall

spring.ai.chat.memory.repository.neo4j.metadataLabel

The label for nodes that store message metadata

Metadata

spring.ai.chat.memory.repository.neo4j.toolResponseLabel

The label for the nodes that store tool responses

ToolResponse

spring.ai.chat.memory.repository.neo4j.mediaLabel

The label for the nodes that store media associated with a message

Media

Index Initialization

Neo4j存储库将自动确保为会话ID和消息索引创建索引,以优化性能。如果使用自定义标签,也将为这些标签创建索引。不需要模式初始化,但您应该确保您的应用程序可以访问您的Neo4j实例。

Memory in Chat Client

当使用ChatClient API时,您可以提供ChatMemory实现来维护多个交互中的会话上下文。
Spring AI提供了一些内置的顾问,您可以根据需要使用它们来配置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

Custom Template

PromptChatMemoryAdvisor使用默认模板用检索到的对话内存来增强系统消息。您可以通过.PromptTemplate()构建器方法提供自己的PromptTemplate对象来自定义此行为。

自定义PromptTemplate可以使用任何TemplateRenderer实现(默认情况下,它使用基于StringTemplate引擎的StPromptTemplate)。重要的要求是模板必须包含以下两个占位符:
用于接收原始系统消息的指令占位符。
存储器占位符,用于接收检索到的对话存储器。

VectorStoreChatMemoryAdvisor

Custom Template

VectorStoreChatMemoryAdvisor使用默认模板用检索到的对话内存来增强系统消息。您可以通过.PromptTemplate()构建器方法提供自己的PromptTemplate对象来自定义此行为。
此处提供的PromptTemplate可自定义顾问如何将检索到的内存与系统消息合并。这与在ChatClient本身上配置TemplateRenderer(使用.templateRender())不同,后者会影响顾问运行前初始用户/系统提示内容的呈现。有关客户端级模板呈现的更多详细信息,请参阅ChatClient提示模板。
自定义PromptTemplate可以使用任何TemplateRenderer实现(默认情况下,它使用基于StringTemplate引擎的StPromptTemplate)。重要的要求是模板必须包含以下两个占位符:
用于接收原始系统消息的指令占位符。
长时记忆占位符,用于接收检索到的对话记忆。
 

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"

你可能感兴趣的:(Spring,AI,ai,人工智能)