聊天記憶
大型語言模型(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
-
Oracle Database
使用 JdbcChatMemoryRepositoryDialect.from(DataSource) 時,可以從 JDBC URL 自動檢測到正確的方言。您可以透過實現 JdbcChatMemoryRepositoryDialect 介面來擴充套件對其他資料庫的支援。
配置屬性
財產 |
描述 |
預設值 |
|
控制何時初始化 schema。值: |
|
|
用於初始化的 schema 指令碼的位置。支援 |
|
|
如果使用 @@platform@@ 佔位符,用於初始化指令碼的平臺。 |
auto-detected |
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 # 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(CassandraChatMemoryRepositoryConfig.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();
配置屬性
財產 |
描述 |
預設值 |
|
儲存會話的節點的標籤 |
|
|
儲存訊息的節點的標籤 |
|
|
儲存工具呼叫(例如在助手訊息中)的節點的標籤 |
|
|
儲存訊息元資料的節點的標籤 |
|
|
儲存工具響應的節點的標籤 |
|
|
儲存與訊息關聯的媒體的節點的標籤 |
|
CosmosDBChatMemoryRepository
CosmosDBChatMemoryRepository 是一個內建實現,它使用 Azure Cosmos DB NoSQL API 儲存訊息。它適用於需要全球分散式、高度可擴充套件的文件資料庫來持久化聊天記憶的應用程式。該儲存庫使用會話 ID 作為分割槽鍵,以確保高效的資料分發和快速檢索。
首先,將以下依賴項新增到您的專案:
-
Maven
-
Gradle
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-cosmos-db</artifactId>
</dependency>
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-cosmos-db'
}
Spring AI 為 CosmosDBChatMemoryRepository 提供了自動配置,您可以直接在應用程式中使用。
@Autowired
CosmosDBChatMemoryRepository chatMemoryRepository;
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
如果您希望手動建立 CosmosDBChatMemoryRepository,可以透過提供 CosmosDBChatMemoryRepositoryConfig 例項來實現:
ChatMemoryRepository chatMemoryRepository = CosmosDBChatMemoryRepository
.create(CosmosDBChatMemoryRepositoryConfig.builder()
.withCosmosClient(cosmosAsyncClient)
.withDatabaseName("chat-memory-db")
.withContainerName("conversations")
.build());
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
配置屬性
財產 |
描述 |
預設值 |
|
Azure Cosmos DB 端點 URI。自動配置所需。 |
|
|
Azure Cosmos DB 主金鑰或輔助金鑰。如果未提供,將使用 Azure Identity 身份驗證。 |
|
|
Cosmos DB 客戶端的連線模式( |
|
|
Cosmos DB 資料庫名稱。 |
|
|
Cosmos DB 容器名稱。 |
|
|
容器的分割槽鍵路徑。 |
|
MongoChatMemoryRepository
MongoChatMemoryRepository 是一個內建實現,它使用 MongoDB 儲存訊息。它適用於需要靈活的、面向文件的資料庫來持久化聊天記憶的應用程式。
首先,將以下依賴項新增到您的專案:
-
Maven
-
Gradle
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-mongodb</artifactId>
</dependency>
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-mongodb'
}
Spring AI 為 MongoChatMemoryRepository 提供了自動配置,您可以直接在應用程式中使用。
@Autowired
MongoChatMemoryRepository chatMemoryRepository;
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
如果您希望手動建立 MongoChatMemoryRepository,可以透過提供 MongoTemplate 例項來實現:
ChatMemoryRepository chatMemoryRepository = MongoChatMemoryRepository.builder()
.mongoTemplate(mongoTemplate)
.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 自定義了 Advisor 如何將檢索到的記憶與系統訊息合併。這與在 ChatClient 本身配置 TemplateRenderer(使用 .templateRenderer())是不同的,後者會影響 Advisor 執行之前初始使用者/系統提示內容的渲染。有關客戶端級別模板渲染的更多詳細資訊,請參閱 ChatClient 提示模板。 |
自定義 PromptTemplate 可以使用任何 TemplateRenderer 實現(預設情況下,它使用基於 StringTemplate 引擎的 StPromptTemplate)。重要的要求是模板必須包含以下兩個佔位符:
-
一個
instructions佔位符用於接收原始系統訊息。 -
一個
memory佔位符用於接收檢索到的對話記憶。
VectorStoreChatMemoryAdvisor
自定義模板
VectorStoreChatMemoryAdvisor 使用預設模板透過檢索到的對話記憶來增強系統訊息。您可以透過 .promptTemplate() 構建器方法提供自己的 PromptTemplate 物件來自定義此行為。
此處提供的 PromptTemplate 自定義了 Advisor 如何將檢索到的記憶與系統訊息合併。這與在 ChatClient 本身配置 TemplateRenderer(使用 .templateRenderer())是不同的,後者會影響 Advisor 執行之前初始使用者/系統提示內容的渲染。有關客戶端級別模板渲染的更多詳細資訊,請參閱 ChatClient 提示模板。 |
自定義 PromptTemplate 可以使用任何 TemplateRenderer 實現(預設情況下,它使用基於 StringTemplate 引擎的 StPromptTemplate)。重要的要求是模板必須包含以下兩個佔位符:
-
一個
instructions佔位符用於接收原始系統訊息。 -
一個
long_term_memory佔位符用於接收檢索到的對話記憶。
聊天模型中的記憶
如果您直接使用 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"