このバージョンはまだ開発中であり、まだ安定しているとは見なされていません。最新の安定バージョンについては、Spring AI 1.1.7 を使用してください! |
チャットメモリ
大規模言語モデル(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 は、指定された最大サイズまでのメッセージのスライディングウィンドウを維持します。メッセージ数が最大サイズを超えると、古いメッセージが削除されますが、SystemMessage インスタンスは常に保持されます。デフォルトのウィンドウサイズは 20 メッセージです。
MessageWindowChatMemory memory = MessageWindowChatMemory.builder()
.maxMessages(10)
.build(); これは、Spring AI が ChatMemory Bean を自動構成するために使用するデフォルトのメッセージ型です。
ターン境界からの退去
When eviction is needed, MessageWindowChatMemory always removes whole turns rather than cutting mid-turn. A turn starts at a UserMessage and includes all subsequent assistant replies, tool calls, and tool responses up to the next UserMessage. If the raw eviction point lands on a non-user message (for example, an assistant reply in the middle of a tool-calling exchange), the cut is advanced forward to the next UserMessage so that the kept window always begins at a complete turn.
This means maxMessages is an upper bound on the number of messages stored — the actual count may be somewhat lower when snapping is needed to find the next turn boundary.
If maxMessages is smaller than a single complete turn (for example, a multi-step tool-calling exchange that exceeds the window size), all non-system messages may be evicted until a new UserMessage is added. Size maxMessages generously enough to hold at least one representative turn for your use case. |
メモリストレージ
Spring AI は、チャットメモリを保存するための ChatMemoryRepository 抽象化を提供しています。このセクションでは、Spring AI が提供する組み込みリポジトリとその使用方法について説明しますが、必要に応じて独自のリポジトリを実装することもできます。
| Azure Cosmos DB chat memory repository support is available as an external module maintained by the Azure Cosmos DB team. See azurecosmosdb.github.io/spring-ai/docs/index.html (英語) for more information. |
インメモリリポジトリ
InMemoryChatMemoryRepository は、ConcurrentHashMap を使用してメッセージをメモリに保存します。
デフォルトでは、他のリポジトリがまだ構成されていない場合、Spring AI は、アプリケーションで直接使用できる型 InMemoryChatMemoryRepository の ChatMemoryRepository Bean を自動構成します。
@Autowired
ChatMemoryRepository chatMemoryRepository;InMemoryChatMemoryRepository を手動で作成する場合は、次のようにします。
ChatMemoryRepository repository = new InMemoryChatMemoryRepository();JdbcChatMemoryRepository
JdbcChatMemoryRepository は、JDBC を使用してメッセージをリレーショナルデータベースに保存する組み込み実装です。複数のデータベースを標準でサポートしており、チャットメモリの永続的な保存を必要とするアプリケーションに適しています。
Messages are retrieved in ascending order (oldest-to-newest), which is the expected format for LLM conversation history. Ordering is maintained by a sequence_id column that records each message’s position within its conversation. Each message also stores a creation timestamp, exposed in the message metadata under the JdbcChatMemoryRepository.CONVERSATION_TS key as a java.time.Instant, so applications can display when a message was created.
まず、プロジェクトに次の依存関係を追加します。
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();Reading Message Timestamps
Each stored message records when it was created. The timestamp is available in the message metadata under the JdbcChatMemoryRepository.CONVERSATION_TS key as a java.time.Instant, which is useful for displaying message times in a UI:
List<Message> messages = chatMemory.get(conversationId);
for (Message message : messages) {
Instant createdAt = (Instant) message.getMetadata().get(JdbcChatMemoryRepository.CONVERSATION_TS);
}The timestamp is preserved when a conversation is saved again, so a message keeps its original creation time for the lifetime of the conversation.
サポートされているデータベースとダイアレクトの抽象化
Spring AI は、ダイアレクト抽象化により複数のリレーショナルデータベースをサポートします。以下のデータベースは標準でサポートされています。
PostgreSQL
MySQL/MariaDB
SQL Server
HSQLDB
Oracle データベース
JdbcChatMemoryRepositoryDialect.from(DataSource) を使用すると、JDBC URL から適切なダイアレクトが自動検出されます。JdbcChatMemoryRepositoryDialect インターフェースを実装することで、他のデータベースのサポートを継承できます。
プロパティの構成
プロパティ | 説明 | デフォルト値 |
| スキーマを初期化するタイミングを制御します。値: |
|
| 初期化に使用するスキーマスクリプトの場所。 |
|
| @@platform@@ プレースホルダーが使用されている場合に初期化スクリプトで使用するプラットフォーム。 | 自動検出 |
スキーマの初期化
自動構成により、起動時にデータベースのベンダー固有の SQL スクリプトを使用して SPRING_AI_CHAT_MEMORY テーブルが自動的に作成されます。デフォルトでは、スキーマの初期化は組み込みデータベース(H2、HSQL、Derby など)に対してのみ実行されます。
As of 2.0.0, the schema adds a sequence_id column that determines message ordering, alongside the existing timestamp column (now exposed via message metadata). Upgrading an existing application requires adding the new column; see アップグレードノート for the migration steps. |
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.sqlCassandraChatMemoryRepository
CassandraChatMemoryRepository は Apache Cassandra を使用してメッセージを保存します。これは、特に可用性、耐久性、拡張性、Time-to-Live(TTL)機能を活用するために、チャットメモリの永続的な保存を必要とするアプリケーションに適しています。
CassandraChatMemoryRepository は時系列スキーマを備えており、過去のすべてのチャットウィンドウの記録を保持します。これはガバノンスと監査に役立ちます。有効期限(TTL)を 3 年など、適切な値に設定することをお勧めします。
メッセージはタイムスタンプの昇順 (古いものから新しいものへ) で取得されます。これは、LLM 会話履歴の想定される形式です。
まず 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 テーブル |
|
| 起動時にスキーマを初期化するかどうか。 |
|
ネオ 4j ChatMemoryRepository
Neo4jChatMemoryRepository は、Neo4j を使用してチャットメッセージをプロパティグラフデータベースのノードとリレーションシップとして保存する組み込み実装です。チャットメモリの永続化に Neo4j のグラフ機能を活用したいアプリケーションに最適です。
メッセージは、メッセージインデックスの昇順 (古いものから新しいものへ) で取得されます。これは、LLM 会話履歴の想定される形式です。
まず、プロジェクトに次の依存関係を追加します。
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();プロパティの構成
プロパティ | 説明 | デフォルト値 |
| 会話セッションを保存するノードのラベル |
|
| メッセージを保存するノードのラベル |
|
| ツール呼び出しを保存するノードのラベル (例: アシスタントメッセージ) |
|
| メッセージメタデータを保存するノードのラベル |
|
| ツールのレスポンスを保存するノードのラベル |
|
| メッセージに関連付けられたメディアを保存するノードのラベル |
|
MongoChatMemoryRepository
MongoChatMemoryRepository は、MongoDB を使用してメッセージを保存する組み込み実装です。チャットのメモリ永続化に柔軟でドキュメント指向のデータベースを必要とするアプリケーションに適しています。
メッセージはタイムスタンプの昇順(古いものから新しいものへ)で取得されます。これは LLM の会話履歴で想定される形式です。この順序は、すべてのチャットメモリリポジトリ実装で一貫しています。
まず、プロジェクトに次の依存関係を追加します。
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();RedisChatMemoryRepository
RedisChatMemoryRepository は、Redis スタック(Redis クエリエンジンと RedisJSON を含む)を使用してチャットメッセージを保存する組み込み実装です。オプションの TTL(Time-to-Live)サポートと高度なクエリ機能を備えた、高パフォーマンスで低レイテンシのチャットメモリの永続性を必要とするアプリケーションに最適です。
リポジトリはメッセージを JSON ドキュメントとして保存し、効率的なクエリ実行のための検索インデックスを作成します。また、AdvancedRedisChatMemoryRepository インターフェースを介して拡張クエリ機能を提供し、コンテンツ、型、期間、メタデータでメッセージを検索できます。
メッセージはタイムスタンプの昇順 (古いものから新しいものへ) で取得されます。これは、LLM 会話履歴の想定される形式です。
まず、プロジェクトに次の依存関係を追加します。
Maven
Gradle
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-redis</artifactId>
</dependency>dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-redis'
}Spring AI は、RedisChatMemoryRepository の自動構成を提供し、アプリケーションで直接使用できます。
@Autowired
RedisChatMemoryRepository chatMemoryRepository;
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();RedisChatMemoryRepository を手動で作成したい場合は、RedisClient クライアントを提供することで作成できます。
RedisClient jedisClient = RedisClient.builder().hostAndPort("localhost", 6379).build();
ChatMemoryRepository chatMemoryRepository = RedisChatMemoryRepository.builder()
.jedisClient(jedisClient)
.indexName("my-chat-index")
.keyPrefix("my-chat:")
.timeToLive(Duration.ofHours(24))
.build();
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();プロパティの構成
プロパティ | 説明 | デフォルト値 |
| Redis サーバーホスト |
|
| Redis サーバーポート |
|
| Redis 検索インデックスの名前 |
|
| チャットメモリエントリのキープレフィックス |
|
| チャットメモリエントリの有効期間 (例: | 有効期限なし |
| 起動時に Redis スキーマを初期化するかどうか |
|
| 返される会話 ID の最大数 |
|
| 会話ごとに返されるメッセージの最大数 |
|
高度なクエリ
RedisChatMemoryRepository は、拡張クエリ機能を提供する AdvancedRedisChatMemoryRepository も実装しています。
// Cast to access advanced features
AdvancedRedisChatMemoryRepository advancedRepo = (AdvancedRedisChatMemoryRepository) chatMemoryRepository;
// Find messages by type across all conversations
List<MessageWithConversation> userMessages = advancedRepo.findByType(MessageType.USER, 100);
// Find messages containing specific content
List<MessageWithConversation> results = advancedRepo.findByContent("Spring AI", 50);
// Find messages within a time range
List<MessageWithConversation> recentMessages = advancedRepo.findByTimeRange(
conversationId,
Instant.now().minus(Duration.ofHours(1)),
Instant.now(),
100
);
// Find messages by metadata
List<MessageWithConversation> priorityMessages = advancedRepo.findByMetadata("priority", "high", 50);
// Execute custom Redis queries
List<MessageWithConversation> customResults = advancedRepo.executeQuery("@type:USER @content:Redis", 100);メタデータフィールドのインデックス作成
カスタムメタデータフィールドに対する効率的なクエリを有効にするには、メタデータフィールド定義を構成できます。
spring.ai.chat.memory.redis.metadata-fields[0].name=priority
spring.ai.chat.memory.redis.metadata-fields[0].type=tag
spring.ai.chat.memory.redis.metadata-fields[1].name=score
spring.ai.chat.memory.redis.metadata-fields[1].type=numeric
spring.ai.chat.memory.redis.metadata-fields[2].name=category
spring.ai.chat.memory.redis.metadata-fields[2].type=tag サポートされているフィールド型は: tag (完全一致フィルタリング用)、text (全文検索用)、および numeric (範囲クエリ用)。
チャットクライアントのメモリ
ChatClient API を使用する場合、複数のインタラクションにわたって会話のコンテキストを維持するために ChatMemory 実装を提供できます。
Spring AI には、ニーズに応じて ChatClient のメモリ動作を構成するために使用できる組み込みアドバイザーがいくつか用意されています。
| 現在、ツール呼び出し時に大規模言語モデルと交換される中間メッセージはメモリに保存されません。これは現在の実装の制限であり、将来のリリースで修正される予定です。これらのメッセージを保存する必要がある場合は、ユーザー制御ツールの実行の説明書を参照してください。 |
MessageChatMemoryAdvisor。このアドバイザーは、提供されているChatMemory実装を使用して会話メモリを管理します。対話ごとに、メモリから会話履歴を取得し、それをメッセージのコレクションとしてプロンプトに含めます。VectorStoreChatMemoryAdvisor。このアドバイザーは、提供されているVectorStore実装を使用して会話メモリを管理します。各インタラクションにおいて、ベクトルストアから会話履歴を取得し、それをプレーンテキストとしてシステムメッセージに追加します。
例: MessageWindowChatMemory を MessageChatMemoryAdvisor と一緒に使用したい場合は、次のように設定できます。
ChatMemory chatMemory = MessageWindowChatMemory.builder().build();
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();ChatClient への呼び出しを行う際、メモリは MessageChatMemoryAdvisor によって自動的に管理されます。呼び出し履歴は、指定された呼び出し ID に基づいてメモリから取得されます。
ChatMemory.CONVERSATION_ID パラメーターは、すべてのメモリアドバイザで必須です。このパラメーターを省略した呼び出しでは、実行時に IllegalArgumentException 例外がスローされます。デフォルトの会話 ID はありません。 |
String conversationId = "007";
chatClient.prompt()
.user("Do I have license to code?")
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
.call()
.content();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"