聊天記憶

大型語言模型 (LLMs) 是無狀態的,這意味著它們不保留關於之前互動的資訊。當您希望在多次互動中保持上下文或狀態時,這可能是一個限制。為了解決這個問題,Spring AI 提供了一個 ChatMemory 抽象,允許您在與 LLM 的多次互動中儲存和檢索資訊。

快速入門

Spring AI 會自動配置一個 ChatMemory bean,您可以直接在應用程式中使用它。預設情況下,它使用記憶體儲存庫 (InMemoryChatMemoryRepository) 來儲存訊息,並使用 MessageWindowChatMemory 實現來管理對話歷史。如果已經配置了不同的儲存庫(例如 Cassandra、JDBC 或 Neo4j),Spring AI 將會使用它。

@Autowired
ChatMemory chatMemory;

以下章節將進一步描述 Spring AI 中可用的不同記憶體型別和儲存庫。

記憶體型別

ChatMemory 抽象允許您實現各種型別的記憶體以適應不同的用例。記憶體型別的選擇會顯著影響應用程式的效能和行為。本節描述了 Spring AI 提供的內建記憶體型別及其特性。

訊息視窗聊天記憶

MessageWindowChatMemory 維護一個最多包含指定最大數量訊息的視窗。當訊息數量超過最大值時,較舊的訊息會被移除,同時保留系統訊息。預設的視窗大小為 20 條訊息。

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

這是 Spring AI 用於自動配置 ChatMemory bean 的預設訊息型別。

記憶體儲存

Spring AI 提供了 ChatMemoryRepository 抽象用於儲存聊天記憶。本節描述了 Spring AI 提供的內建儲存庫以及如何使用它們,但如果需要,您也可以實現自己的儲存庫。

記憶體儲存庫

InMemoryChatMemoryRepository 使用 ConcurrentHashMap 將訊息儲存在記憶體中。

預設情況下,如果尚未配置其他儲存庫,Spring AI 會自動配置一個型別為 InMemoryChatMemoryRepositoryChatMemoryRepository bean,您可以直接在應用程式中使用它。

@Autowired
ChatMemoryRepository chatMemoryRepository;

如果您更願意手動建立 InMemoryChatMemoryRepository,可以按照以下方式進行

ChatMemoryRepository repository = new InMemoryChatMemoryRepository();

JDBC 儲存庫

JdbcChatMemoryRepository 是一個內建實現,它使用 JDBC 將訊息儲存在關係資料庫中。它適用於需要持久儲存聊天記憶的應用程式。

首先,向您的專案新增以下依賴項

  • Maven

  • Gradle

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-chat-memory-jdbc</artifactId>
</dependency>
dependencies {
    implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-jdbc'
}

Spring AI 為 JdbcChatMemoryRepository 提供了自動配置,您可以直接在應用程式中使用它。

@Autowired
JdbcChatMemoryRepository chatMemoryRepository;

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

如果您更願意手動建立 JdbcChatMemoryRepository,可以透過提供一個 JdbcTemplate 例項來完成

ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder()
    .jdbcTemplate(jdbcTemplate)
    .build();

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

配置屬性

屬性

描述

預設值

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

是否在啟動時初始化資料庫 schema。

true

Schema 初始化

自動配置將使用 JDBC 驅動程式自動建立 ai_chat_memory 表。目前,僅支援 PostgreSQL 和 MariaDB。

您可以透過將屬性 spring.ai.chat.memory.repository.jdbc.initialize-schema 設定為 false 來停用 schema 初始化。

如果您的專案使用 Flyway 或 Liquibase 等工具來管理資料庫 schema,您可以停用 schema 初始化,並參考 這些 SQL 指令碼 來配置這些工具以建立 ai_chat_memory 表。

在聊天客戶端中使用記憶

使用 ChatClient API 時,您可以提供一個 ChatMemory 實現,以便在多次互動中保持對話上下文。

Spring AI 提供了幾個內建的 Advisors,您可以根據需要使用它們來配置 ChatClient 的記憶行為。

當前,與大型語言模型進行工具呼叫時交換的中間訊息不會儲存在記憶中。這是當前實現的一個限制,將在未來的版本中解決。如果您需要儲存這些訊息,請參考 使用者控制的工具執行 的說明。
  • MessageChatMemoryAdvisor. 此 Advisor 使用提供的 ChatMemory 實現來管理對話記憶。在每次互動時,它會從記憶中檢索對話歷史,並將其作為訊息集合包含在 prompt 中。

  • PromptChatMemoryAdvisor. 此 Advisor 使用提供的 ChatMemory 實現來管理對話記憶。在每次互動時,它會從記憶中檢索對話歷史,並將其作為純文字附加到系統 prompt 中。

  • VectorStoreChatMemoryAdvisor. 此 Advisor 使用提供的 VectorStore 實現來管理對話記憶。在每次互動時,它會從向量儲存中檢索對話歷史,並將其作為純文字附加到系統訊息中。

例如,如果您想將 MessageWindowChatMemoryMessageChatMemoryAdvisor 一起使用,可以按如下方式配置它

ChatMemory chatMemory = MessageChatMemoryAdvisor.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(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, conversationId))
    .call()
    .content();

結構化輸出 工具呼叫

如果您直接使用 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"