Amazon Bedrock プロンプトキャッシュについて PHPで試してみた

に公開

はじめに

Fusicのレオナです。前回のブログではAmazon BedrockとPHP, Laravelを組み合わせてPDFの文字起こしを試しました。

https://zenn.dev/fusic/articles/81e0a78a5aec67

本ブログはその続きとして、Amazon Bedrockのプロンプトキャッシュ機能について解説とPHPで試してみた内容をまとめます。プロンプトキャッシュは推論レスポンスのレイテンシーとコストを削減できる便利な仕組みです。繰り返し利用するコンテキストをキャッシュとして保存し、後続のリクエストではキャッシュ済みの部分を再計算せずに回答してもらえるため、長文ドキュメントを扱う際に非常に効果的です。

プロンプトキャッシュ(Prompt Caching)とは

Amazon Bedrockのプロンプトキャッシュは、入力したプロンプトのうち再利用が期待される部分をキャッシュに保存し、その後のリクエストではキャッシュから読み出すことで処理コストを削減する機能です。長いシステムプロンプトや同じドキュメントを何度も参照するチャットボットのようなユースケースに向いています。

キャッシュされたトークンは通常の入力トークンより割安で課金されますが、キャッシュに書き込む際にはモデルによっては割高な料金が設定される場合もあります。そのため、キャッシュする内容やタイミングを適切に設計することが重要です。

対応しているモデル

Amazon Bedrockでプロンプトキャッシュに対応している基盤モデルについてまとめてみました。

モデル名 モデルID 最小トークン数/キャッシュポイント 最大キャッシュポイント数 キャッシュ可能フィールド
Claude Opus 4 anthropic.claude-opus-4-20250514-v1:0 1,024 4 system, messages, tools
Claude Sonnet 4 anthropic.claude-sonnet-4-20250514-v1:0 1,024 4 system, messages, tools
Claude 3.7 Sonnet anthropic.claude-3-7-sonnet-20250219-v1:0 1,024 4 system, messages, tools
Novaシリーズ
- Nova Micro amazon.nova-micro-v1:0 1,000※ 4 system, messages
- Nova Lite amazon.nova-lite-v1:0 1,000※ 4 system, messages
- Nova Pro amazon.nova-pro-v1:0 1,000※ 4 system, messages
- Nova Premier amazon.nova-premier-v1:0 1,000※ 4 system, messages

※ Novaシリーズは最大20,000トークンまでプロンプトキャッシュをサポートされています。

キャッシュポイントとは

キャッシュポイントとは「ここまでの内容を保存する」という目印です。一度キャッシュした内容は次回以降のリクエストでもまったく同じ内容でなければ無効になってしまいます。キャッシュポイントは最大数の制限内で積み重ねて使うことができ、以下のように段階的に保存されていきます。

例:レストランのメニュー説明

前菜メニュー(2,048トークン)
↓ キャッシュポイント1 ← ここまで保存

メインメニュー(2,048トークン)  
↓ キャッシュポイント2 ← 前菜+メインを保存

デザートメニュー(2,048トークン)
↓ キャッシュポイント3 ← 前菜+メイン+デザートを保存

ドリンクメニュー(2,048トークン)
↓ キャッシュポイント4 ← 全部保存(最大8,192トークン)

「今日のおすすめは?」← 質問

同じ内容を複数回使用する場合はこのキャッシュポイントでトークンを節約できます。

Converse APIでの使用例

プロンプトキャッシュを利用するには、systemmessagestoolsのいずれかの配列内にcachePointオブジェクトを挿入します。キャッシュポイントは最低でも指定のトークン数以上のテキストの後に設置する必要があります。以下は基本的なAPI利用例です。

  1. messagesフィールドでの設定:
"messages": [
   {
        "role": "user",
        "content": [
            {
                "image": {
                    "bytes": "base64."
                }
            },
            {
                "text": "What's in this image?"
            },
            {
                "cachePoint": {
                    "type": "default"
                }
            }
      ]
  }
]
  1. systemフィールドでの設定:
"system": [ 
    {
        "text": "あなたは優秀なコンサルタントです。ユーザーの質問に答えてください"
    },
    {
        "cachePoint": {
            "type": "default"
        }
    }
]
  1. toolsフィールドでの設定(Claudeモデルのみ):
"toolConfig": {
    "tools": [
        {
            "toolSpec": {
                "name": "get_weather",
                "description": "天気を取得するツール",
                "inputSchema": {
                    "json": {
                        "type": "object",
                        "properties": {
                            "result": {
                                "type": "string",
                                "description": "."
                            }
                        },
                        "required": ["result"]
                    }
                }
            }
        },
        {
            "cachePoint": {
                "type": "default"
            }
        }
    ]
}

レスポンスの確認

Converse APIのレスポンスにはプロンプトキャッシュ専用のフィールドが含まれています。CacheReadInputTokensはキャッシュから読み取られたトークン数、CacheWriteInputTokensはキャッシュに書き込まれたトークン数です。これらの値は課金時の計算に利用されます。

今回はAnthropic社の“Claude 3.7 Sonnet”モデルを使用して検証しました。

参照:
https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html

環境構築

  • PHP 8.3 / Laravel 12
  • AWS SDK for PHP
  • Laravelプロジェクトのcomposer.jsonは以下のように設定
composer.json
{
    "require": {
        "php": "^8.3",
        "aws/aws-sdk-php": "^3.351",
        "laravel/framework": "^12.0"
    }
}
  • Bedrock用の認証情報は.envファイルで定義
.env
AWS_BEDROCK_REGION=us-west-2
AWS_BEDROCK_ACCESS_KEY_ID=your_access_key
AWS_BEDROCK_SECRET_ACCESS_KEY=your_secret_key
AWS_BEDROCK_MODEL_ID=us.anthropic.claude-3-7-sonnet-20250219-v1:0

実装

app/Console/Commands/BedrockPdfAnalysisCommand.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Aws\BedrockRuntime\BedrockRuntimeClient;
use Aws\Exception\AwsException;

class BedrockPdfAnalysisCommand extends Command
{
    protected $signature = 'bedrock:pdf-analysis';
    protected $description = 'Analyze PDF content using Amazon Bedrock Converse API';

    public function handle()
    {
        // クライアントの初期化
        $client = new BedrockRuntimeClient([
            'region'  => env('AWS_BEDROCK_REGION'),
            'version' => 'latest',
            'credentials' => [
                'key'    => env('AWS_BEDROCK_ACCESS_KEY_ID'),
                'secret' => env('AWS_BEDROCK_SECRET_ACCESS_KEY'),
            ],
        ]);

        // PDFを読み込む
        $filePath = storage_path('app/report.pdf');
        if (!file_exists($filePath)) {
            $this->error("PDFが見つかりません: {$filePath}");
            return 1;
        }
        $documentBytes = file_get_contents($filePath);

        // ファイル名をサニタイズ
        $name = basename($filePath);
        $cleanName = preg_replace('/[^A-Za-z0-9 \-\(\)\[\]]+/', '', $name);
        $cleanName = preg_replace('/ {2,}/', ' ', $cleanName);
        $cleanName = trim($cleanName);

        // システムプロンプトをキャッシュポイントとして定義(最低1,024トークン必要)
        $systemPrompt = 'あなたはPDF文書の内容を解析する高度な専門家です。長年の経験と専門知識を持ち、様々な種類のPDF文書を的確に分析し、重要な情報を抽出して整理することができます。
~長い文章なので割愛~ 1024トークン数を超える必要があります。
';
        
        $system = [
            [
                'text' => $systemPrompt
            ],
            [
                'cachePoint' => [
                    'type' => 'default'
                ]
            ]
        ];

        // メッセージの組み立て
        $conversation = [
            [
                'role'    => 'user',
                'content' => [
                    ['text' => 'このPDFの主要な内容を解析し、要点をまとめてください。'],
                    [
                        'document' => [
                            'format' => 'pdf',
                            'name'   => $cleanName,
                            'source' => ['bytes' => $documentBytes],
                        ],
                    ],
                ],
            ],
        ];

        try {
            // Converse APIを呼び出す
            $response = $client->converse([
                'modelId'  => env('AWS_BEDROCK_MODEL_ID'),
                'system'   => $system,
                'messages' => $conversation,
                'inferenceConfig' => [
                    'maxTokens'   => 4000,
                    'temperature' => 0.0,
                ],
            ]);

            // 解析結果を表示
            $analysis = $response['output']['message']['content'][0]['text'];
            $this->info("=== Analysis Result ===");
            $this->line($analysis);

            // キャッシュ使用情報を表示
            $this->info("\n=== Cache Usage Information ===");
            if (isset($response['usage'])) {
                $usage = $response['usage'];
                $this->line("Total Input Tokens: " . ($usage['inputTokens'] ?? 'N/A'));
                $this->line("Total Output Tokens: " . ($usage['outputTokens'] ?? 'N/A'));
                $this->line("Total Tokens: " . ($usage['totalTokens'] ?? 'N/A'));
                
                // キャッシュ関連の情報
                if (isset($usage['cacheReadInputTokens'])) {
                    $this->info("Cache Read Input Tokens: " . $usage['cacheReadInputTokens']);
                } else {
                    $this->line("Cache Read Input Tokens: 0");
                }
                
                if (isset($usage['cacheWriteInputTokens'])) {
                    $this->info("Cache Write Input Tokens: " . $usage['cacheWriteInputTokens']);
                } else {
                    $this->line("Cache Write Input Tokens: 0");
                }
            }

            return 0;
        } catch (AwsException $e) {
            $this->error('Converse APIエラー: ' . $e->getAwsErrorMessage());
            return 1;
        }
    }
}

実行

  • PDFを指定したパスに配置し、以下のコマンドで実行します。
php artisan bedrock:pdf-analysis

出力結果

レスポンスには解析結果とともに、プロンプトキャッシュの利用状況が含まれます。例えば下記のように出力されました。

=== Analysis Result ===
(ここに要約された内容が表示されます)

=== Cache Usage Information ===
Total Input Tokens: 1138
Total Output Tokens: 760
Total Tokens: 3179
Cache Read Input Tokens: 1281
Cache Write Input Tokens: 0

最後に

本ブログではAmazon Bedrockのプロンプトキャッシュ機能をPHPの実装方法を紹介しました。キャッシュポイントをうまく活用すれば、長いシステムプロンプトや繰り返し使うドキュメントを効率的に扱うことができます。トークン数が多いワークロードではコスト面でも大きなメリットがあると思います。

Fusic 技術ブログ

Discussion