チャットメモリ

大規模言語モデル(LLM)は状態を持たないため、以前のインタラクションに関する情報を保持しません。これは、複数のインタラクションにわたってコンテキストや状態を維持したい場合に制約となる可能性があります。この問題に対処するため、Spring AI は ChatMemory 抽象化を提供し、LLM との複数のインタラクションにわたって情報を保存および取得できるようにします。

クイックスタート

Spring AI は、アプリケーションで直接使用できる ChatMemory Bean を自動構成します。デフォルトでは、メッセージの保存にはメモリ内リポジトリ(InMemoryChatMemoryRepository)を使用し、会話履歴の管理には MessageWindowChatMemory 実装を使用します。別のリポジトリ(Cassandra、JDBC、Neo4j など)がすでに構成されている場合、Spring AI は代わりにそれを使用します。

@Autowired
ChatMemory chatMemory;

次のセクションでは、Spring AI で使用できるさまざまなメモリ型とリポジトリについて詳しく説明します。

Memory Types

The ChatMemory abstraction allows you to implement various types of memory to suit different use cases. The choice of memory type can significantly impact the performance and behavior of your application. This section describes the built-in memory types provided by Spring AI and their characteristics.

Message Window Chat Memory

MessageWindowChatMemory maintains a window of messages up to a specified maximum size. When the number of messages exceeds the maximum, older messages are removed while preserving system messages. The default window size is 20 messages.

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

This is the default message type used by Spring AI to auto-configure a ChatMemory bean.

Memory Storage

Spring AI offers the ChatMemoryRepository abstraction for storing chat memory. This section describes the built-in repositories provided by Spring AI and how to use them, but you can also implement your own repository if needed.

インメモリリポジトリ

InMemoryChatMemoryRepository stores messages in memory using a ConcurrentHashMap.

By default, if no other repository is already configured, Spring AI auto-configures a ChatMemoryRepository bean of type InMemoryChatMemoryRepository that you can use directly in your application.

@Autowired
ChatMemoryRepository chatMemoryRepository;

If you’d rather create the InMemoryChatMemoryRepository manually, you can do so as follows:

ChatMemoryRepository repository = new InMemoryChatMemoryRepository();

JDBC リポジトリ

JdbcChatMemoryRepository is a built-in implementation that uses JDBC to store messages in a relational database. It is suitable for applications that require persistent storage of chat memory.

First, add the following dependency to your project:

<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 provides auto-configuration for the JdbcChatMemoryRepository, that you can use directly in your application.

@Autowired
JdbcChatMemoryRepository chatMemoryRepository;

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

If you’d rather create the JdbcChatMemoryRepository manually, you can do so by providing a JdbcTemplate instance:

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

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

プロパティの構成

プロパティ

説明

デフォルト値

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

起動時にスキーマを初期化するかどうか。

true

スキーマの初期化

The auto-configuration will automatically create the ai_chat_memory table using the JDBC driver. Currently, only PostgreSQL and MariaDB are supported.

You can disable the schema initialization by setting the property spring.ai.chat.memory.repository.jdbc.initialize-schema to false.

If your project uses a tool like Flyway or Liquibase to manage your database schemas, you can disable the schema initialization and refer to these SQL scripts [GitHub] (英語) for configuring those tools to create the ai_chat_memory table.

Memory in Chat Client

When using the ChatClient API, you can provide a ChatMemory implementation to maintain conversation context across multiple interactions.

Spring AI provides a few built-in Advisors that you can use to configure the memory behavior of the ChatClient, based on your needs.

Currently, the intermediate messages exchanged with a large-language model when performing tool calls are not stored in the memory. This is a limitation of the current implementation and will be addressed in future releases. If you need to store these messages, refer to the instructions for the User Controlled Tool Execution.
  • MessageChatMemoryAdvisor。This advisor manages the conversation memory using the provided ChatMemory implementation. On each interaction, it retrieves the conversation history from the memory and includes it in the prompt as a collection of messages.

  • PromptChatMemoryAdvisor。This advisor manages the conversation memory using the provided ChatMemory implementation. On each interaction, it retrieves the conversation history from the memory and appends it to the system prompt as plain text.

  • VectorStoreChatMemoryAdvisor。This advisor manages the conversation memory using the provided VectorStore implementation. On each interaction, it retrieves the conversation history from the vector store and appends it to the system message as plain text.

For example, if you want to use MessageWindowChatMemory with the MessageChatMemoryAdvisor, you can configure it as follows:

ChatMemory chatMemory = MessageChatMemoryAdvisor.builder().build();

ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
    .build();

When performing a call to the ChatClient, the memory will be automatically managed by the MessageChatMemoryAdvisor. The conversation history will be retrieved from the memory based on the specified conversation 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();

Memory in Chat Model

If you’re working directly with a ChatModel instead of a ChatClient, you can manage the memory explicitly:

// 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"