Bedrock Converse API

Amazon Bedrock コンバース API は、関数 / ツールの呼び出し、マルチモーダル入力、ストリーミングレスポンスなどの強化された機能を備えた会話型 AI モデル用の統合インターフェースを提供します。

Bedrock Converse API には、次のような高レベルの機能があります。

  • ツール / 関数の呼び出し: 会話中の機能定義とツールの使用をサポート

  • マルチモーダル入力: 会話中のテキストとイメージ入力の両方を処理する機能

  • ストリーミングサポート: モデルレスポンスのリアルタイムストリーミング

  • システムメッセージ: システムレベルの命令とコンテキスト設定のサポート

Bedrock Converse API は、AWS 固有の認証とインフラストラクチャに関する関心事に対応しながら、複数のモデルプロバイダー間で統一されたインターフェースを提供します。現在、Converse API 対応モデル [Amazon] には Amazon TitanAmazon NovaAI21 LabsAnthropic ClaudeCohere CommandMeta LlamaMistral AI が含まれています。

Bedrock の推奨事項に従い、Spring AI は、Spring AI のすべてのチャット会話実装に Amazon Bedrock の Converse API を使用するように移行しています。既存の InvokeModel API は会話アプリケーションをサポートしていますが、すべてのチャット会話モデルに Converse API を採用することを強くお勧めします。

Converse API は埋め込み操作をサポートしていないため、これらは現在の API に残り、既存の InvokeModel API の埋め込みモデル機能は維持されます。

前提条件

API アクセスの設定については Amazon Bedrock 入門 を参照してください

自動構成

Spring AI 自動構成、スターターモジュールのアーティファクト名に大きな変更がありました。詳細については、アップグレードノートを参照してください。

プロジェクトの Maven pom.xml または Gradle build.gradle ビルドファイルに spring-ai-starter-model-bedrock-converse 依存関係を追加します。

  • Maven

  • Gradle

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-bedrock-converse</artifactId>
</dependency>
dependencies {
    implementation 'org.springframework.ai:spring-ai-starter-model-bedrock-converse'
}
Spring AI BOM をビルドファイルに追加するには、"依存関係管理" セクションを参照してください。

チャットのプロパティ

プレフィックス spring.ai.bedrock.aws は、AWS Bedrock への接続を設定するためのプロパティプレフィックスです。

プロパティ 説明 デフォルト

spring.ai.bedrock.aws.region

使用する AWS リージョン

米国東部 -1

spring.ai.bedrock.aws.timeout

API 呼び出し全体の AWS 最大期間

5 分

spring.ai.bedrock.aws.connection-timeout

接続を確立するまでの最大待機時間

5 秒

spring.ai.bedrock.aws.connection-acquisition-timeout

プールからの新しい接続を待つ最大時間

30 代

spring.ai.bedrock.aws.async-read-timeout

非同期レスポンスの読み取りに費やされた最大時間

30 代

spring.ai.bedrock.aws.access-key

AWS アクセスキー

-

spring.ai.bedrock.aws.secret-key

AWS 秘密鍵

-

spring.ai.bedrock.aws.session-token

一時認証情報用の AWS セッショントークン

-

spring.ai.bedrock.aws.profile.name

AWS プロファイル名。

-

spring.ai.bedrock.aws.profile.credentials-path

AWS 認証情報ファイルのパス。

-

spring.ai.bedrock.aws.profile.configuration-path

AWS 構成ファイルのパス。

-

チャットの自動構成の有効化と無効化は、プレフィックス spring.ai.model.chat を持つ最上位プロパティを介して設定されるようになりました。

有効にするには、spring.ai.model.chat=bedrock-converse (デフォルトで有効になっています)

無効にするには、spring.ai.model.chat=none (または bedrock-converse と一致しない値)

この変更は、複数のモデルの構成を可能にするために行われます。

プレフィックス spring.ai.bedrock.converse.chat は、Converse API のチャットモデル実装を構成するプロパティプレフィックスです。

プロパティ 説明 デフォルト

spring.ai.bedrock.converse.chat.enabled (削除され、無効になりました)

Bedrock Converse チャットモデルを有効にします。

true

spring.ai.model.chat

Bedrock Converse チャットモデルを有効にします。

bedrock-converse

spring.ai.bedrock.converse.chat.model

使用するモデル ID。サポートされているモデルとモデル機能 [Amazon] を使用できます

なし。AWS Bedrock コンソールから modelId [Amazon] を選択します。

spring.ai.bedrock.converse.chat.temperature

出力のランダム性を制御します。値の範囲は [0.0,1.0] です

0.8

spring.ai.bedrock.converse.chat.top-p

サンプリング時に考慮するトークンの最大累積確率。

AWS Bedrock のデフォルト

spring.ai.bedrock.converse.chat.top-k

次のトークンを生成するためのトークン選択の数。

AWS Bedrock のデフォルト

spring.ai.bedrock.converse.chat.max-tokens

生成されたレスポンス内のトークンの最大数。

500

ランタイムオプション

ポータブル ChatOptions または BedrockChatOptions ポータブルビルダーを使用して、温度、maxToken、topP などのモデル構成を作成します。

起動時に、BedrockConverseProxyChatModel(api, options) コンストラクターまたは spring.ai.bedrock.converse.chat.* プロパティを使用してデフォルトのオプションを構成できます。

実行時に、Prompt 呼び出しに新しいリクエスト固有のオプションを追加することで、デフォルトのオプションをオーバーライドできます。

var options = BedrockChatOptions.builder()
        .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
        .temperature(0.6)
        .maxTokens(300)
        .toolCallbacks(List.of(FunctionToolCallback.builder("getCurrentWeather", new WeatherService())
            .description("Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.")
            .inputType(WeatherService.Request.class)
            .build()))
        .build();

String response = ChatClient.create(this.chatModel)
    .prompt("What is current weather in Amsterdam?")
    .options(options)
    .call()
    .content();

プロンプトキャッシュ

AWS Bedrock のプロンプトキャッシュ機能 [Amazon] を使用すると、頻繁に使用されるプロンプトをキャッシュすることで、コストを削減し、繰り返し発生するインタラクションのレスポンス時間を改善できます。プロンプトをキャッシュすると、後続の同一リクエストでキャッシュされたコンテンツが再利用されるため、処理される入力トークンの数が大幅に削減されます。

対応モデル

プロンプトキャッシュは、AWS Bedrock を通じて利用可能な Claude 3.x、Claude 4.x、Amazon Nova モデルでサポートされています。

トークン要件

キャッシュの有効性に関する最小トークンしきい値はモデルによって異なります。- Claude Sonnet 4 およびほとんどのモデル: 1024+ トークン - モデル固有の要件は異なる場合があります - AWS Bedrock ドキュメントを参照してください

キャッシュ戦略

Spring AI は、BedrockCacheStrategy 列挙型を通じて戦略的なキャッシュ配置を提供します。

  • NONE: プロンプトのキャッシュを完全に無効にする (default)

  • SYSTEM_ONLY: システムメッセージの内容のみをキャッシュします

  • TOOLS_ONLY: ツール定義のみをキャッシュします (Claude モデルのみ)

  • SYSTEM_AND_TOOLS: システムメッセージとツール定義の両方をキャッシュします (Claude モデルのみ)

  • CONVERSATION_HISTORY: チャットメモリシナリオで会話履歴全体をキャッシュします

この戦略的なアプローチにより、AWS Bedrock の 4 つのブレークポイント制限内に留まりながら、最適なキャッシュブレークポイントの配置が保証されます。

Amazon Nova の制限

Amazon Nova モデル (Nova Micro、Lite、Pro、Premier) は、system および messages コンテンツのキャッシュのみをサポートします。tools のキャッシュはサポートしません。

Nova モデルで TOOLS_ONLY または SYSTEM_AND_TOOLS 戦略を使用しようとすると、AWS は ValidationException を返します。Amazon Nova モデルでは SYSTEM_ONLY 戦略を使用してください。

プロンプトキャッシュを有効にする

BedrockChatOptions に cacheOptions を設定し、strategy を選択してプロンプトキャッシュを有効にします。

システムのみのキャッシュ

最も一般的な使用例 - 複数のリクエストにわたるシステム命令のキャッシュ:

// Cache system message content
ChatResponse response = chatModel.call(
    new Prompt(
        List.of(
            new SystemMessage("You are a helpful AI assistant with extensive knowledge..."),
            new UserMessage("What is machine learning?")
        ),
        BedrockChatOptions.builder()
            .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
            .cacheOptions(BedrockCacheOptions.builder()
                .strategy(BedrockCacheStrategy.SYSTEM_ONLY)
                .build())
            .maxTokens(500)
            .build()
    )
);

ツールのみのキャッシュ

システムプロンプトを動的に保ちながら、大規模なツール定義をキャッシュします (Claude モデルのみ):

// Cache tool definitions only
ChatResponse response = chatModel.call(
    new Prompt(
        "What's the weather in San Francisco?",
        BedrockChatOptions.builder()
            .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
            .cacheOptions(BedrockCacheOptions.builder()
                .strategy(BedrockCacheStrategy.TOOLS_ONLY)
                .build())
            .toolCallbacks(weatherToolCallbacks)  // Large tool definitions
            .maxTokens(500)
            .build()
    )
);
この戦略は Claude モデルでのみサポートされます。Amazon Nova モデルは ValidationException を返します。

システムとツールのキャッシュ

システム命令とツール定義の両方をキャッシュして、最大限の再利用を実現します (Claude モデルのみ)。

// Cache system message and tool definitions
ChatResponse response = chatModel.call(
    new Prompt(
        List.of(
            new SystemMessage("You are a weather analysis assistant..."),
            new UserMessage("What's the weather like in Tokyo?")
        ),
        BedrockChatOptions.builder()
            .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
            .cacheOptions(BedrockCacheOptions.builder()
                .strategy(BedrockCacheStrategy.SYSTEM_AND_TOOLS)
                .build())
            .toolCallbacks(weatherToolCallbacks)
            .maxTokens(500)
            .build()
    )
);
この戦略では、2 つのキャッシュブレークポイント(ツール用とシステム用)を使用します。Claude モデルでのみサポートされます。

会話履歴のキャッシュ

マルチターンのチャットボットとアシスタントの会話履歴をキャッシュします。

// Cache conversation history with ChatClient and memory
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultSystem("You are a personalized career counselor...")
    .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
    .build();

String response = chatClient.prompt()
    .user("What career advice would you give me?")
    .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
    .options(BedrockChatOptions.builder()
        .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
        .cacheOptions(BedrockCacheOptions.builder()
            .strategy(BedrockCacheStrategy.CONVERSATION_HISTORY)
            .build())
        .maxTokens(500)
        .build())
    .call()
    .content();

マルチブロックシステムメッセージキャッシング

アドバイザー(RAG コンテキストインジェクションなど)を使用する場合、システムプロンプトは複数の SystemMessage インスタンスで構成されることがあります。静的なプレフィックス(基本命令)と動的なサフィックス(リクエストごとに変化するアドバイザーインジェクションされたコンテキスト)です。

デフォルトでは、SYSTEM_ONLY または SYSTEM_AND_TOOLS キャッシュが有効になっている場合、各 SystemMessage は個別の SystemContentBlock として発行され、キャッシュポイントは最後のブロックの後に配置されます。リクエスト間でそのコンテンツの一部が変更されると、システムキャッシュ全体が無効化され、結果として、はるかに安価なキャッシュ読み取りではなく、すべてのリクエストで 1.25x キャッシュ書き込みコストが発生します。

multiBlockSystemCaching オプションは、キャッシュポイントを最後から 2 番目のシステムブロックの後(つまり、静的プレフィックスと動的サフィックスの境界)に配置することでこの問題を解決します。これにより、最後のブロックが自由に変化しても、静的な部分はキャッシュされたままになります。

BedrockCacheOptions cacheOptions = BedrockCacheOptions.builder()
    .strategy(BedrockCacheStrategy.SYSTEM_ONLY)
    .multiBlockSystemCaching(true)
    .build();

BedrockChatOptions chatOptions = BedrockChatOptions.builder()
    .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
    .cacheOptions(cacheOptions)
    .build();

// Static system prompt (will be cached)
SystemMessage staticPrompt = new SystemMessage("You are an expert assistant...");
// Dynamic RAG context (injected by advisor, changes each request)
SystemMessage dynamicContext = new SystemMessage("Relevant context: " + ragResults);

Prompt prompt = new Prompt(List.of(staticPrompt, dynamicContext, new UserMessage("...")), chatOptions);

これにより、[text(static), cachePoint, text(dynamic)] の形式の Bedrock system 配列が生成され、静的プレフィックスのみがキャッシュキーに関与します。

SystemMessage が 1 つだけ存在する場合、キャッシュポイントはその 1 つのブロックの後に配置されます(デフォルトの動作と同じです)。multiBlockSystemCaching オプションは、システムメッセージが 2 つ以上存在する場合にのみ配置を変更します。
Message ordering matters. The cache point is always placed after the second-to-last system block, so static content must come before dynamic content in the message list. If you register your base instructions as the first SystemMessage and let advisors append dynamic context after, this ordering is naturally satisfied.

ChatClient Fluent API の使用

String response = ChatClient.create(chatModel)
    .prompt()
    .system("You are an expert document analyst...")
    .user("Analyze this large document: " + document)
    .options(BedrockChatOptions.builder()
        .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
        .cacheOptions(BedrockCacheOptions.builder()
            .strategy(BedrockCacheStrategy.SYSTEM_ONLY)
            .build())
        .build())
    .call()
    .content();

使用例

以下は、コスト追跡を伴うプロンプトキャッシュを示す完全な例です。

// Create system content that will be reused multiple times
String largeSystemPrompt = "You are an expert software architect specializing in distributed systems...";
// (Ensure this is 1024+ tokens for cache effectiveness)

// First request - creates cache
ChatResponse firstResponse = chatModel.call(
    new Prompt(
        List.of(
            new SystemMessage(largeSystemPrompt),
            new UserMessage("What is microservices architecture?")
        ),
        BedrockChatOptions.builder()
            .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
            .cacheOptions(BedrockCacheOptions.builder()
                .strategy(BedrockCacheStrategy.SYSTEM_ONLY)
                .build())
            .maxTokens(500)
            .build()
    )
);

// Access cache-related token usage from metadata
Integer cacheWrite1 = (Integer) firstResponse.getMetadata()
    .getMetadata()
    .get("cacheWriteInputTokens");
Integer cacheRead1 = (Integer) firstResponse.getMetadata()
    .getMetadata()
    .get("cacheReadInputTokens");

System.out.println("Cache creation tokens: " + cacheWrite1);
System.out.println("Cache read tokens: " + cacheRead1);

// Second request with same system prompt - reads from cache
ChatResponse secondResponse = chatModel.call(
    new Prompt(
        List.of(
            new SystemMessage(largeSystemPrompt),  // Same prompt - cache hit
            new UserMessage("What are the benefits of event sourcing?")
        ),
        BedrockChatOptions.builder()
            .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
            .cacheOptions(BedrockCacheOptions.builder()
                .strategy(BedrockCacheStrategy.SYSTEM_ONLY)
                .build())
            .maxTokens(500)
            .build()
    )
);

Integer cacheWrite2 = (Integer) secondResponse.getMetadata()
    .getMetadata()
    .get("cacheWriteInputTokens");
Integer cacheRead2 = (Integer) secondResponse.getMetadata()
    .getMetadata()
    .get("cacheReadInputTokens");

System.out.println("Cache creation tokens: " + cacheWrite2); // Should be 0
System.out.println("Cache read tokens: " + cacheRead2);      // Should be > 0

トークン使用状況の追跡

AWS Bedrock は、レスポンスを通じてキャッシュ固有のメトリクスを提供します。キャッシュメトリクスには、以下の 2 つの方法でアクセスできます。

可観測性ハンドラーとメトリクス収集の場合、ネイティブ TokenUsage オブジェクトを介してキャッシュメトリクスにアクセスします。

import software.amazon.awssdk.services.bedrockruntime.model.TokenUsage;

ChatResponse response = chatModel.call(/* ... */);

// Access cache metrics from native TokenUsage object
TokenUsage tokenUsage = (TokenUsage) response.getMetadata()
    .getUsage()
    .getNativeUsage();

if (tokenUsage != null) {
    Integer cacheWrite = tokenUsage.cacheWriteInputTokens();
    Integer cacheRead = tokenUsage.cacheReadInputTokens();
    System.out.println("Cache write: " + cacheWrite + ", Cache read: " + cacheRead);
}

メタデータマップ (下位互換性あり)

キャッシュメトリクスは、下位互換性のためにメタデータマップ経由でも使用できます。

ChatResponse response = chatModel.call(/* ... */);

// Access cache metrics from metadata Map
Integer cacheWrite = (Integer) response.getMetadata()
    .getMetadata()
    .get("cacheWriteInputTokens");
Integer cacheRead = (Integer) response.getMetadata()
    .getMetadata()
    .get("cacheReadInputTokens");

キャッシュ固有のメトリクスには次のものが含まれます。

  • cacheWriteInputTokens: キャッシュエントリを作成するときに使用されるトークンの数を返します

  • cacheReadInputTokens: 既存のキャッシュエントリから読み取られたトークンの数を返します

キャッシュされたプロンプトを初めて送信する場合: - cacheWriteInputTokens は 0 より大きくなります - cacheReadInputTokens は 0 になります

同じキャッシュされたプロンプトを再度送信する場合(5 分 TTL 以内): - cacheWriteInputTokens は 0 になります - cacheReadInputTokens は 0 より大きくなります

実際のユースケース

複数の質問にわたってドキュメントのコンテンツをキャッシュすることで、大規模な法的契約書やコンプライアンスドキュメントを効率的に分析します。

// Load a legal contract (PDF or text)
String legalContract = loadDocument("merger-agreement.pdf"); // ~3000 tokens

// System prompt with legal expertise
String legalSystemPrompt = "You are an expert legal analyst specializing in corporate law. " +
    "Analyze the following contract and provide precise answers about terms, obligations, and risks: " +
    legalContract;

// First analysis - creates cache
ChatResponse riskAnalysis = chatModel.call(
    new Prompt(
        List.of(
            new SystemMessage(legalSystemPrompt),
            new UserMessage("What are the key termination clauses and associated penalties?")
        ),
        BedrockChatOptions.builder()
            .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
            .cacheOptions(BedrockCacheOptions.builder()
                .strategy(BedrockCacheStrategy.SYSTEM_ONLY)
                .build())
            .maxTokens(1000)
            .build()
    )
);

// Subsequent questions reuse cached document - 90% cost savings
ChatResponse obligationAnalysis = chatModel.call(
    new Prompt(
        List.of(
            new SystemMessage(legalSystemPrompt), // Same content - cache hit
            new UserMessage("List all financial obligations and payment schedules.")
        ),
        BedrockChatOptions.builder()
            .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
            .cacheOptions(BedrockCacheOptions.builder()
                .strategy(BedrockCacheStrategy.SYSTEM_ONLY)
                .build())
            .maxTokens(1000)
            .build()
    )
);

バッチコードレビュー

レビューガイドラインをキャッシュしながら、一貫したレビュー条件で複数のコードファイルを処理します。

// Define comprehensive code review guidelines
String reviewGuidelines = """
    You are a senior software engineer conducting code reviews. Apply these criteria:
    - Security vulnerabilities and best practices
    - Performance optimizations and memory usage
    - Code maintainability and readability
    - Testing coverage and edge cases
    - Design patterns and architecture compliance
    """;

List<String> codeFiles = Arrays.asList(
    "UserService.java", "PaymentController.java", "SecurityConfig.java"
);

List<String> reviews = new ArrayList<>();

for (String filename : codeFiles) {
    String sourceCode = loadSourceFile(filename);

    ChatResponse review = chatModel.call(
        new Prompt(
            List.of(
                new SystemMessage(reviewGuidelines), // Cached across all reviews
                new UserMessage("Review this " + filename + " code:\n\n" + sourceCode)
            ),
            BedrockChatOptions.builder()
                .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
                .cacheOptions(BedrockCacheOptions.builder()
                    .strategy(BedrockCacheStrategy.SYSTEM_ONLY)
                    .build())
                .maxTokens(800)
                .build()
        )
    );

    reviews.add(review.getResult().getOutput().getText());
}

// Guidelines cached after first request, subsequent reviews are faster and cheaper

ナレッジベースによるカスタマーサポート

一貫性のある正確なレスポンスを実現するために、製品ナレッジベースをキャッシュする顧客サポートシステムを作成します。

// Load comprehensive product knowledge
String knowledgeBase = """
    PRODUCT DOCUMENTATION:
    - API endpoints and authentication methods
    - Common troubleshooting procedures
    - Billing and subscription details
    - Integration guides and examples
    - Known issues and workarounds
    """ + loadProductDocs(); // ~2500 tokens

@Service
public class CustomerSupportService {

    public String handleCustomerQuery(String customerQuery, String customerId) {
        ChatResponse response = chatModel.call(
            new Prompt(
                List.of(
                    new SystemMessage("You are a helpful customer support agent. " +
                        "Use this knowledge base to provide accurate solutions: " + knowledgeBase),
                    new UserMessage("Customer " + customerId + " asks: " + customerQuery)
                ),
                BedrockChatOptions.builder()
                    .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
                    .cacheOptions(BedrockCacheOptions.builder()
                        .strategy(BedrockCacheStrategy.SYSTEM_ONLY)
                        .build())
                    .maxTokens(600)
                    .build()
            )
        );

        return response.getResult().getOutput().getText();
    }
}

// Knowledge base is cached across all customer queries
// Multiple support agents can benefit from the same cached content

マルチテナント SaaS アプリケーション

テナントごとにシステムプロンプトをカスタマイズしながら、異なるテナント間で共有ツール定義をキャッシュします。

// Shared tool definitions (cached once, used across all tenants)
List<FunctionToolCallback> sharedTools = createLargeToolRegistry(); // ~2000 tokens

// Tenant-specific configuration
@Service
public class MultiTenantAIService {

    public String processRequest(String tenantId, String userQuery) {
        // Load tenant-specific system prompt (changes per tenant)
        String tenantPrompt = loadTenantSystemPrompt(tenantId);

        ChatResponse response = chatModel.call(
            new Prompt(
                List.of(
                    new SystemMessage(tenantPrompt), // Tenant-specific, not cached
                    new UserMessage(userQuery)
                ),
                BedrockChatOptions.builder()
                    .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
                    .cacheOptions(BedrockCacheOptions.builder()
                        .strategy(BedrockCacheStrategy.TOOLS_ONLY)
                        .build())
                    .toolCallbacks(sharedTools) // Shared tools - cached
                    .maxTokens(500)
                    .build()
            )
        );

        return response.getResult().getOutput().getText();
    }
}

// Tools cached once, each tenant gets customized system prompt

ベストプラクティス

  1. 適切な戦略を選択する :

    • 再利用可能なシステムプロンプトと指示には SYSTEM_ONLY を使用します (すべてのモデルで動作します)

    • 大きな安定したツールがあるが、動的なシステムプロンプトがある場合は TOOLS_ONLY を使用します。(Claude のみ)

    • システムとツールの両方が大きく安定している場合は SYSTEM_AND_TOOLS を使用する (Claude のみ)

    • 複数ターンの会話には CONVERSATION_HISTORY と ChatClient メモリを使用します

    • NONE を使用してキャッシュを明示的に無効にする

  2. トークン要件を満たす : 最小トークン要件 (ほとんどのモデルでは 1024+ トークン) を満たすコンテンツのキャッシュに重点を置きます。

  3. 同一コンテンツの再利用 : キャッシュはプロンプトの内容が完全に一致する場合に最適に機能します。小さな変更でも、新しいキャッシュエントリが必要になります。

  4. トークンの使用状況を監視する : メタデータメトリクスを使用してキャッシュの有効性を追跡します。

    Integer cacheWrite = (Integer) response.getMetadata().getMetadata().get("cacheWriteInputTokens");
    Integer cacheRead = (Integer) response.getMetadata().getMetadata().get("cacheReadInputTokens");
    if (cacheRead != null && cacheRead > 0) {
        System.out.println("Cache hit: " + cacheRead + " tokens saved");
    }
  5. 戦略的なキャッシュ配置 : この実装では、選択した戦略に基づいて最適な場所にキャッシュブレークポイントが自動的に配置され、AWS Bedrock の 4 つのブレークポイント制限に準拠します。

  6. キャッシュの有効期間 : AWS Bedrock キャッシュの TTL(Time To Live)は 5 分に固定されています。キャッシュにアクセスするたびにタイマーがリセットされます。

  7. モデルの互換性 : モデル固有の制限に注意してください:

    • Claude モデル : すべてのキャッシュ戦略をサポート

    • Amazon Nova モデル SYSTEM_ONLY と CONVERSATION_HISTORY のみサポート (ツールキャッシュはサポートされていません)

  8. ツールの安定性 TOOLS_ONLYSYSTEM_AND_TOOLSCONVERSATION_HISTORY 戦略を使用する場合は、ツールが安定していることを確認してください。ツール定義を変更すると、カスケード無効化により、下流のすべてのキャッシュブレークポイントが無効になります。

キャッシュの無効化とカスケード動作

AWS Bedrock は、カスケード無効化を備えた階層型キャッシュモデルに従います。

キャッシュ階層 Tools → System → Messages

各レベルで変更を行うと、そのレベルとそれ以降のすべてのレベルが無効になります。

何が変わるのか ツールキャッシュ システムキャッシュ メッセージキャッシュ

ツール

❌ 無効

❌ 無効

❌ 無効

システム

✅ 有効

❌ 無効

❌ 無効

メッセージ

✅ 有効

✅ 有効

❌ 無効

SYSTEM_AND_TOOLS 戦略の例 :

// Request 1: Cache both tools and system
ChatResponse r1 = chatModel.call(
    new Prompt(
        List.of(new SystemMessage("System prompt"), new UserMessage("Question")),
        BedrockChatOptions.builder()
            .cacheOptions(BedrockCacheOptions.builder()
                .strategy(BedrockCacheStrategy.SYSTEM_AND_TOOLS)
                .build())
            .toolCallbacks(tools)
            .build()
    )
);
// Result: Both caches created

// Request 2: Change only system prompt (tools same)
ChatResponse r2 = chatModel.call(
    new Prompt(
        List.of(new SystemMessage("DIFFERENT system prompt"), new UserMessage("Question")),
        BedrockChatOptions.builder()
            .cacheOptions(BedrockCacheOptions.builder()
                .strategy(BedrockCacheStrategy.SYSTEM_AND_TOOLS)
                .build())
            .toolCallbacks(tools) // SAME tools
            .build()
    )
);
// Result: Tools cache HIT (reused), system cache MISS (recreated)

// Request 3: Change tools (system same as Request 2)
ChatResponse r3 = chatModel.call(
    new Prompt(
        List.of(new SystemMessage("DIFFERENT system prompt"), new UserMessage("Question")),
        BedrockChatOptions.builder()
            .cacheOptions(BedrockCacheOptions.builder()
                .strategy(BedrockCacheStrategy.SYSTEM_AND_TOOLS)
                .build())
            .toolCallbacks(newTools) // DIFFERENT tools
            .build()
    )
);
// Result: BOTH caches MISS (tools change invalidates everything downstream)

実装の詳細

Spring AI のプロンプトキャッシュの実装は、次の主要な設計原則に従います。

  1. 戦略的なキャッシュ配置 : キャッシュブレークポイントは、選択した戦略に基づいて最適な場所に自動的に配置され、AWS Bedrock の 4 つのブレークポイント制限に準拠します。

  2. プロバイダーのポータビリティ : キャッシュ構成は個別のメッセージではなく BedrockChatOptions を通じて行われるため、異なる AI プロバイダー間で切り替えるときに互換性が維持されます。

  3. スレッドセーフ : キャッシュブレークポイントトラッキングは、同時リクエストを正しく処理するためのスレッドセーフメカニズムを使用して実装されています。

  4. UNION 型パターン : AWS SDK は UNION 型を使用し、キャッシュポイントはプロパティではなく個別のブロックとして追加されます。これは直接的な API アプローチとは異なりますが、型安全性と API コンプライアンスを保証します。

  5. 増分キャッシュ CONVERSATION_HISTORY 戦略は、最後のユーザーメッセージにキャッシュブレークポイントを配置し、各会話ターンが前のキャッシュされたプレフィックスに基づいて構築される増分キャッシュを有効にします。

コストの考慮

プロンプトキャッシュの AWS Bedrock 料金 (概算、モデルによって異なります):

  • キャッシュ書き込み : 基本入力トークンより約 25% 高負荷

  • キャッシュ読み取り : 約 90% 安い (基本入力トークン価格のわずか 10%)

  • 損益分岐点 : たった 1 回のキャッシュ読み取りでコスト削減

コスト計算の例 :

// System prompt: 2000 tokens
// User question: 50 tokens

// Without caching (5 requests):
// Cost: 5 × (2000 + 50) = 10,250 tokens at base rate

// With caching (5 requests):
// Request 1: 2000 tokens × 1.25 (cache write) + 50 = 2,550 tokens
// Requests 2-5: 4 × (2000 × 0.10 (cache read) + 50) = 4 × 250 = 1,000 tokens
// Total: 2,550 + 1,000 = 3,550 tokens equivalent

// Savings: (10,250 - 3,550) / 10,250 = 65% cost reduction

ツール呼び出し

BedrockProxyChatModel はツール呼び出しを内部的に実行しません。ツール実行は、サポートされている 2 つの方法のいずれかを使用して外部で処理する必要があります。

  • ChatClient と ToolCallingAdvisor — ほとんどのユースケースにおいて推奨されるアプローチです。ToolCallingAdvisor は自動的に登録され、ツール呼び出しループを透過的に管理します。

  • ユーザー制御によるツール実行 — use DefaultToolCallingManager directly when you need full control over the loop (for example, when combining tool calling with prompt caching).

The simplest way to use tools is with the ChatClient high-level API. ToolCallingAdvisor handles the tool-call loop automatically for both synchronous and streaming calls.

Here’s an example using @Tool -annotated methods:

public class WeatherService {

    @Tool(description = "Get the weather in location")
    public String weatherByLocation(@ToolParam(description = "City or state name") String location) {
        // ...
    }
}

String response = ChatClient.create(this.chatModel)
        .prompt("What's the weather like in Boston?")
        .tools(new WeatherService())
        .call()
        .content();

You can also register a ToolCallback bean and pass it directly:

@Bean
ToolCallback weatherFunction() {
    return FunctionToolCallback.builder("weatherFunction", new MockWeatherService())
        .description("Get the weather in location. Return temperature in 36°F or 36°C format.")
        .inputType(Request.class)
        .build();
}

String response = ChatClient.create(this.chatModel)
        .prompt("What's the weather like in Boston?")
        .tools(weatherFunction)
        .call()
        .content();

ユーザー制御ツールの実行

For scenarios where you need explicit control over the tool-call loop — for example, when combining tool calling with prompt caching — you can drive the loop manually using DefaultToolCallingManager.

同期

ToolCallingManager toolCallingManager = DefaultToolCallingManager.builder().build();

var options = BedrockChatOptions.builder()
        .toolCallbacks(List.of(FunctionToolCallback.builder("getCurrentWeather", new WeatherService())
            .description("Get the weather in location. Return temperature in 36°F or 36°C format.")
            .inputType(WeatherService.Request.class)
            .build()))
        .build();

var prompt = new Prompt("What's the weather like in San Francisco, Tokyo and Paris?", options);

ChatResponse response = chatModel.call(prompt);

while (response.hasToolCalls()) {
    ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, response);
    prompt = new Prompt(toolExecutionResult.conversationHistory(), options);
    response = chatModel.call(prompt);
}

System.out.println(response.getResult().getOutput().getText());

ストリーミング

Because tool calls span multiple stream chunks, each streaming call must be aggregated first using MessageAggregator before checking for tool calls.

ToolCallingManager toolCallingManager = DefaultToolCallingManager.builder().build();

var options = BedrockChatOptions.builder()
        .toolCallbacks(List.of(FunctionToolCallback.builder("getCurrentWeather", new WeatherService())
            .description("Get the weather in location. Return temperature in 36°F or 36°C format.")
            .inputType(WeatherService.Request.class)
            .build()))
        .build();

var prompt = new Prompt("What's the weather like in San Francisco, Tokyo and Paris?", options);

AtomicReference<ChatResponse> aggregatedRef = new AtomicReference<>();
new MessageAggregator().aggregate(chatModel.stream(prompt), aggregatedRef::set).collectList().block();

while (aggregatedRef.get().hasToolCalls()) {
    ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, aggregatedRef.get());
    prompt = new Prompt(toolExecutionResult.conversationHistory(), options);
    aggregatedRef.set(null);
    new MessageAggregator().aggregate(chatModel.stream(prompt), aggregatedRef::set).collectList().block();
}

System.out.println(aggregatedRef.get().getResult().getOutput().getText());

詳細については、ツールのドキュメントを参照してください。

構造化された出力

AWS Bedrock は JSON スキーマによるネイティブな構造化出力をサポートしており、モデルが指定された構造に厳密に準拠したレスポンスを生成することを保証します。この機能は、Anthropic、Claude、Amazon Nova などのサポート対象モデル [Amazon] で利用可能です。

ネイティブ構造化出力で ChatClient を使用する

構造化された出力を使用する最も簡単な方法は、ChatClient 高レベル API と ENABLE_NATIVE_STRUCTURED_OUTPUT アドバイザーを使用することです。

record ActorsFilms(String actor, List<String> movies) {}

ActorsFilms actorsFilms = ChatClient.create(chatModel).prompt()
    .options(ToolCallingChatOptions.builder()
        .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
        .build())
    .advisors(AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT)
    .user("Generate the filmography for a random actor.")
    .call()
    .entity(ActorsFilms.class);

このアプローチは自動的に次のことを実現します。

  • Java クラスから JSON スキーマを生成します

  • AWS Bedrock OutputConfig API を介して BedrockChatOptions 上の outputSchema を設定します

  • JSON レスポンスを、指定した型に解析します。

outputSchema を直接使用する

より詳細な制御を行うには、BedrockChatOptions 上で JSON スキーマを直接設定できます。

String jsonSchema = """
        {
            "type": "object",
            "properties": {
                "actor": { "type": "string" },
                "movies": {
                    "type": "array",
                    "items": { "type": "string" }
                }
            },
            "required": ["actor", "movies"],
            "additionalProperties": false
        }
        """;

ChatResponse response = chatModel.call(
    new Prompt("Generate the filmography for a random actor.",
        BedrockChatOptions.builder()
            .model("us.anthropic.claude-haiku-4-5-20251001-v1:0")
            .outputSchema(jsonSchema)
            .build()));

String content = response.getResult().getOutput().getText();
AWS Bedrock の構造化出力は、OutputConfig を構築する際に、内部的に固定スキーマ名 response_schema を使用します。スキーマ JSON は、AWS SDK の JsonSchemaDefinition に直接渡されます。

詳細については、構造化出力コンバーターのドキュメントを参照してください。

マルチモーダル

マルチモーダル性とは、テキスト、イメージ、ビデオ、PDF、DOC、HTML、MD などのデータ形式を含むさまざまなソースからの情報を同時に理解して処理するモデルの機能を指します。

Bedrock Converse API は、テキスト入力やイメージ入力などのマルチモーダル入力をサポートし、組み合わせた入力に基づいてテキストレスポンスを生成できます。

Anthropic、Claude、Amazon Nova モデルなど、マルチモーダル入力をサポートするモデルが必要です。

イメージ

Amazon Nova、Anthropic Claude、Llama 3.2 などのビジョンマルチモダリティをサポートするモデル [Amazon] の場合、Bedrock Converse API Amazon を使用すると、ペイロードに複数のイメージを含めることができます。これらのモデルは、渡されたイメージを分析して質問に答えたり、イメージを分類したり、提供された指示に基づいてイメージを要約したりできます。

現在、Bedrock Converse は、image/jpegimage/pngimage/gifimage/webp MIME 型の base64 エンコードされたイメージをサポートしています。

Spring AI の Message インターフェースは、Media 型を導入することで、マルチモーダル AI モデルをサポートします。これには、Spring の org.springframework.util.MimeType と、生のメディアデータ用の java.lang.Object を使用して、メッセージ内のメディア添付ファイルに関するデータと情報が含まれます。

以下は、ユーザーテキストとイメージの組み合わせを示す簡単なコード例です。

String response = ChatClient.create(chatModel)
    .prompt()
    .user(u -> u.text("Explain what do you see on this picture?")
        .media(Media.Format.IMAGE_PNG, new ClassPathResource("/test.png")))
    .call()
    .content();

logger.info(response);

入力イメージ test.png :

Multimodal Test Image

「この写真に何が写っているか説明してください」というテキストメッセージとともに、次のようなレスポンスが生成されます。

The image shows a close-up view of a wire fruit basket containing several pieces of fruit.
...

ビデオ

Amazon Nova モデル を使用すると、ペイロードに 1 つのビデオを含めることができます。このビデオは、base64 形式または Amazon S3 URI を通じて提供できます。

現在、Bedrock Nova は video/x-matroskavideo/quicktimevideo/mp4video/webmvideo/x-flvvideo/mpegvideo/x-ms-wmvvideo/3gpp MIME 型のビデオをサポートしています。

Spring AI の Message インターフェースは、Media 型を導入することで、マルチモーダル AI モデルをサポートします。これには、Spring の org.springframework.util.MimeType と、生のメディアデータ用の java.lang.Object を使用して、メッセージ内のメディア添付ファイルに関するデータと情報が含まれます。

以下は、ユーザーテキストとビデオの組み合わせを示す簡単なコード例です。

String response = ChatClient.create(chatModel)
    .prompt()
    .user(u -> u.text("Explain what do you see in this video?")
        .media(Media.Format.VIDEO_MP4, new ClassPathResource("/test.video.mp4")))
    .call()
    .content();

logger.info(response);

入力イメージ test.video.mp4 :

Multimodal Test Video

「このビデオで何が見えますか?」というテキストメッセージとともに、次のようなレスポンスが生成されます。

The video shows a group of baby chickens, also known as chicks, huddled together on a surface
...

文書

一部のモデルでは、Bedrock では、Converse API ドキュメントサポートを通じてペイロードにドキュメントを含めることができます。ドキュメントはバイト単位で提供できます。ドキュメントサポートには、以下で説明する 2 つの異なるバリエーションがあります。

  • テキストドキュメントの種類 (txt、csv、html、md など) では、テキストの理解に重点が置かれます。これらのユースケースには、ドキュメントのテキスト要素に基づいて回答することが含まれます。

  • メディアドキュメントの種類 (pdf、docx、xlsx) では、質問に答えるための視覚ベースの理解に重点が置かれています。これらのユースケースには、チャートやグラフなどに基づいて質問に答えることが含まれます。

現在、Anthropic PDF サポート (ベータ) (英語) および Amazon Bedrock Nova モデルはドキュメントのマルチモーダル性をサポートしています。

以下は、ユーザーテキストとメディアドキュメントの組み合わせを示す簡単なコード例です。

String response = ChatClient.create(chatModel)
    .prompt()
    .user(u -> u.text(
            "You are a very professional document summarization specialist. Please summarize the given document.")
        .media(Media.Format.DOC_PDF, new ClassPathResource("/spring-ai-reference-overview.pdf")))
    .call()
    .content();

logger.info(response);

入力として spring-ai-reference-overview.pdf ドキュメントを受け取ります:

Multimodal Test PNG

「非常にプロフェッショナルなドキュメント要約の専門家です。指定されたドキュメントを要約してください。」というテキストメッセージとともに、次のようなレスポンスが生成されます。

**Introduction:**
- Spring AI is designed to simplify the development of applications with artificial intelligence (AI) capabilities, aiming to avoid unnecessary complexity.
...

サンプルコントローラー

新しい Spring Boot プロジェクトを作成し、依存関係に spring-ai-starter-model-bedrock-converse を追加します。

src/main/resources に application.properties ファイルを追加します。

spring.ai.bedrock.aws.region=eu-central-1
spring.ai.bedrock.aws.timeout=10m
spring.ai.bedrock.aws.access-key=${AWS_ACCESS_KEY_ID}
spring.ai.bedrock.aws.secret-key=${AWS_SECRET_ACCESS_KEY}
# session token is only required for temporary credentials
spring.ai.bedrock.aws.session-token=${AWS_SESSION_TOKEN}

spring.ai.bedrock.converse.chat.temperature=0.8
spring.ai.bedrock.converse.chat.top-k=15

以下はチャットモデルを使用するコントローラーの例です。

@RestController
public class ChatController {

    private final ChatClient chatClient;

    @Autowired
    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    @GetMapping("/ai/generate")
    public Map generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
        return Map.of("generation", this.chatClient.prompt(message).call().content());
    }

    @GetMapping("/ai/generateStream")
    public Flux<ChatResponse> generateStream(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
        return this.chatClient.prompt(message).stream().content();
    }
}