チャットメモリ

大規模言語モデル(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 で使用できるさまざまなメモリ型とリポジトリについて詳しく説明します。

メモリの種類

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 は、アプリケーションで直接使用できる型 InMemoryChatMemoryRepository の ChatMemoryRepository Bean を自動構成します。

@Autowired
ChatMemoryRepository chatMemoryRepository;

InMemoryChatMemoryRepository を手動で作成する場合は、次のようにします。

ChatMemoryRepository repository = new InMemoryChatMemoryRepository();

JdbcChatMemoryRepository

JdbcChatMemoryRepository は、JDBC を使用してメッセージをリレーショナルデータベースに保存する組み込み実装です。複数のデータベースを標準でサポートしており、チャットメモリの永続的な保存を必要とするアプリケーションに適しています。

まず、プロジェクトに次の依存関係を追加します。

<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

スキーマを初期化するタイミングを制御します。値: embedded (デフォルト)、alwaysnever

embedded

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

初期化に使用するスキーマスクリプトの場所。classpath: URL とプラットフォームプレースホルダーをサポートします。

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

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

@@platform@@ プレースホルダーが使用されている場合に初期化スクリプトで使用するプラットフォーム。

自動検出

スキーマの初期化

自動構成により、起動時にデータベースのベンダー固有の 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

ダイアレクトの拡張

新しいデータベースのサポートを追加するには、JdbcChatMemoryRepositoryDialect インターフェースを実装し、メッセージの選択、挿入、削除を行う SQL 文を用意します。その後、カスタムダイアレクトをリポジトリビルダーに渡すことができます。

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

CassandraChatMemoryRepository

CassandraChatMemoryRepository は Apache Cassandra を使用してメッセージを保存します。これは、特に可用性、耐久性、拡張性、Time-to-Live(TTL)機能を活用するために、チャットメモリの永続的な保存を必要とするアプリケーションに適しています。

CassandraChatMemoryRepository は時系列スキーマを備えており、過去のすべてのチャットウィンドウの記録を保持します。これはガバノンスと監査に役立ちます。有効期限(TTL)を 3 年など、適切な値に設定することをお勧めします。

まず 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 キースペース

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

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

true

スキーマの初期化

自動構成により、ai_chat_memory テーブルが自動的に作成されます。

プロパティ spring.ai.chat.memory.repository.cassandra.initialize-schema を false に設定することで、スキーマの初期化を無効にすることができます。

ネオ 4j 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();

プロパティの構成

プロパティ

説明

デフォルト値

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

会話セッションを保存するノードのラベル

Session

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

メッセージを保存するノードのラベル

Message

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

ツール呼び出しを保存するノードのラベル (例: アシスタントメッセージ)

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 とメッセージインデックスのインデックスを自動的に作成します。カスタムラベルを使用する場合は、それらのラベルにもインデックスが作成されます。スキーマの初期化は不要ですが、アプリケーションから Neo4j インスタンスにアクセスできることを確認してください。

チャットクライアントのメモリ

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

カスタムテンプレート

PromptChatMemoryAdvisor はデフォルトのテンプレートを使用して、取得した会話メモリをシステムメッセージに追加します。この動作は、.promptTemplate() ビルダーメソッドを介して独自の PromptTemplate オブジェクトを提供することでカスタマイズできます。

ここで提供される PromptTemplate は、アドバイザーが取得したメモリとシステムメッセージをマージする方法をカスタマイズします。これは、アドバイザー実行のユーザー / システムプロンプトの初期コンテンツのレンダリングに影響を与える ChatClient 自体に TemplateRenderer を設定すること(.templateRenderer() を使用)とは異なります。クライアントレベルのテンプレートレンダリングの詳細については、ChatClient プロンプトテンプレートを参照してください。

カスタム PromptTemplate では、任意の TemplateRenderer 実装を使用できます(デフォルトでは、StringTemplate (英語) エンジンに基づく StPromptTemplate を使用します)。重要な要件として、テンプレートには以下の 2 つのプレースホルダが含まれている必要があります。

  • 元のシステムメッセージを受信するための instructions プレースホルダー。

  • 取得した会話メモリを受け取るための memory プレースホルダー。

VectorStoreChatMemoryAdvisor

カスタムテンプレート

VectorStoreChatMemoryAdvisor はデフォルトのテンプレートを使用して、取得した会話メモリをシステムメッセージに追加します。この動作は、.promptTemplate() ビルダーメソッドを介して独自の PromptTemplate オブジェクトを提供することでカスタマイズできます。

ここで提供される PromptTemplate は、アドバイザーが取得したメモリとシステムメッセージをマージする方法をカスタマイズします。これは、アドバイザー実行のユーザー / システムプロンプトの初期コンテンツのレンダリングに影響を与える ChatClient 自体に TemplateRenderer を設定すること(.templateRenderer() を使用)とは異なります。クライアントレベルのテンプレートレンダリングの詳細については、ChatClient プロンプトテンプレートを参照してください。

カスタム PromptTemplate では、任意の TemplateRenderer 実装を使用できます(デフォルトでは、StringTemplate (英語) エンジンに基づく StPromptTemplate を使用します)。重要な要件として、テンプレートには以下の 2 つのプレースホルダが含まれている必要があります。

  • 元のシステムメッセージを受信するための instructions プレースホルダー。

  • 取得した会話メモリを受け取るための long_term_memory プレースホルダー。

チャットモデルのメモリ

ChatClient ではなく ChatModel を直接操作する場合は、メモリを明示的に管理できます。

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