推論リクエストの繰り返しをキャッシュする

CloudflareBeginner
オンラインで実践に進む

はじめに

通常、AI モデルはアプリケーションから呼び出されるたびに新しい応答を生成します。同じリクエストにモデルが直前に回答していたとしても、この処理には時間がかかり、モデルの使用量も消費します。キャッシュは、再利用できる応答を一定時間保持します。これにより、同じリクエストにはモデルを再度呼び出さずに応答できます。

キャッシュが役立つのは、再利用しても安全な場合だけです。公開された固定の FAQ 質問は、誰が呼び出しても同じ回答を受け取れるため、キャッシュに適しています。一方、個別化されたサポート用プロンプトは適していません。速度を上げるためだけに、2 人の顧客を共有キャッシュキーにまとめてはいけません。AI Gateway のデフォルトキャッシュキーには、プロバイダー、エンドポイント、モデル、プロバイダー認証情報、完全なリクエスト本文が含まれます。そのため、本文が少しでも変わると別のエントリになります。

この実験では、5 分間のキャッシュ有効期間を設定した、使い捨ての認証済みゲートウェイを 1 つ作成します。小さな公開 Workers AI 質問を送信してキャッシュ MISS を確認し、同じリクエストを再送して HIT を証明します。その後、質問を変更して別の MISS を確認します。最後に、最新の出力が必要な場合に既存のキャッシュ済み回答をバイパスし、ゲートウェイログでリクエストがモデルに到達したことを確認します。

このコースに直接アクセスした場合は、まず LabEx を Cloudflare アカウントに接続する を完了してください。LabEx VM のターミナル、Wrangler のデバイス認証、学習用アカウントの確認、明示的なアカウント ID について学べます。また、この実験では分離されたゲートウェイとアップストリーム認証の境界を再利用するため、先に ゲートウェイ経由で推論をルーティングする も完了してください。

この実験では、Cloudflare がホストする @cf/meta/llama-3.3-70b-instruct-fp8-fast モデルを、Standard Workers AI 課金で使用します。Workers Paid、Unified Billing、外部プロバイダーのアカウントは必要ありません。モデルに到達するリクエストは 3 件だけです。完全に同じリクエストはキャッシュから処理されます。共有の 1 日あたりの Workers AI 割り当てを利用できない場合は、何度も再試行せずに停止してください。

セットアップでは、Node.js 22.22.0 とプロジェクトローカルの Wrangler 4.132.0 を /home/labex/project/ai-gateway-cache にインストールします。独立した読み取り専用の評価を準備しますが、Wrangler の認証、クラウドリソースの作成、モデルへのトラフィック送信は行いません。実験終了時に LabEx は一時 VM を破棄します。ただし、VM の破棄だけではクラウドリソースを削除できないため、ログアウトする前にゲートウェイとトークンを削除してください。

VM を認証し、キャッシュ実験の名前を付ける

このステップでは、新しい VM を学習用アカウントに接続し、使い捨てのゲートウェイとトークンに付ける名前を生成します。

キャッシュは共有インフラストラクチャです。そのため、利用範囲を意図的に決める必要があります。この実験では、一意の名前を付けたゲートウェイを 1 つ使用し、合成した公開質問だけを送信します。ランダムなサフィックスにより、同じ学習用アカウント内の別のゲートウェイとの衝突を防ぎます。

準備済みのプロジェクトに移動し、固定バージョンの CLI を確認して、この VM を認証します。

cd /home/labex/project/ai-gateway-cache
npx wrangler --version
npx wrangler login --device --browser=false --scopes account:read user:read ai:write

表示されたリンクを開き、コードを入力して、対象の学習用アカウントを認証します。構造化された ID 情報を確認します。

npx wrangler whoami --json

Wrangler 4.132.0 と loggedIn: true が表示されることを確認してください。以下の YOUR_ACCOUNT_ID を、対象アカウントに表示された実際の 32 文字の ID に置き換えます。

GATEWAY_ID="labex-c09-g03-$(openssl rand -hex 6)"
TOKEN_NAME="$GATEWAY_ID-token"
cat > .labex/state.json <<JSON
{
  "accountId": "YOUR_ACCOUNT_ID",
  "gatewayId": "$GATEWAY_ID",
  "tokenName": "$TOKEN_NAME"
}
JSON
cat .labex/state.json

これらの秘密ではない識別子をローカルのインベントリに保存します。以降の読み取り、検証、クリーンアップが、この実験で作成したリソースだけを対象にするためです。

短いキャッシュ有効期間で認証済みゲートウェイを作成する

このステップでは、ゲートウェイを作成し、キャッシュ済みの回答に 5 分間の 有効期間(time to live、TTL) を設定します。TTL は、エントリを再利用できる最長時間です。TTL を過ぎるとエントリは古くなり、モデルから再取得する必要があります。

Cloudflare Dashboard を開き、AI → AI Gateway → Create gateway → Custom gateway を選択します。保存した gatewayId をゲートウェイ名として使用します。リクエストログとゲートウェイ認証は有効のままにし、Cache responses を有効にして、TTL を正確に 300 秒に設定します。レート制限、支出制限、リトライは無効のままにし、Workers AI の課金は Standard のままにします。

認証、ログ記録、300 秒のレスポンスキャッシュが設定されたゲートウェイ設定

作成後、パンくずリストで一意のゲートウェイ ID を確認します。短い TTL はこの実験を繰り返すには十分ですが、サンプル回答が必要以上に残り続けることを防ぎます。

Create an AI Gateway authentication token を選択します。保存した tokenName を使用し、対象の学習用アカウントだけを含め、権限を次のとおり正確に設定します。

  • 認証済みゲートウェイに入るための AI Gateway — Run
  • キャッシュの証拠を読み取り、この使い捨てゲートウェイを削除するための AI Gateway — Edit

Workers AI の権限は追加しないでください。Wrangler が別の短期間のアップストリーム認証情報を提供します。アカウントと権限を確認してからトークンを作成し、一度だけ表示される値を画面に表示せず保存します。

bash -c '
while :; do
  read -ersp "Paste the AI Gateway token: " GATEWAY_TOKEN
  printf "\n"
  [ -n "$GATEWAY_TOKEN" ] && break
  printf "Token cannot be empty; paste it again.\n" >&2
done
umask 077
printf "%s" "$GATEWAY_TOKEN" > .labex/gateway-token
unset GATEWAY_TOKEN
chmod 600 .labex/gateway-token
'

認証済み管理 API を使って、キャッシュ設定が正確であることを確認します。

ACCOUNT_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).accountId')
GATEWAY_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).gatewayId')
GATEWAY_TOKEN=$(cat .labex/gateway-token)
curl --http1.1 -fsS \
  -H "Authorization: Bearer $GATEWAY_TOKEN" \
  "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-gateway/gateways/$GATEWAY_ID" \
  | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const b=JSON.parse(s),g=b.result||{};console.log(JSON.stringify({success:b.success,id:g.id,collect_logs:g.collect_logs,authentication:g.authentication,cache_ttl:g.cache_ttl},null,2))})'
unset GATEWAY_TOKEN

保存した ID、collect_logs: trueauthentication: truecache_ttl: 300 が表示されることを確認してください。

最初の公開 FAQ リクエストを送信する

このステップでは、すべての学習者が安全に再利用できる小さな公開質問を送信します。新しいゲートウェイにはまだエントリがないため、最初の対象リクエストはキャッシュ MISS になるはずです。MISS は、AI Gateway がリクエストを Workers AI に転送し、成功した回答を保存したことを示します。

デフォルトのキャッシュキーには、アップストリームのプロバイダー認証情報が含まれます。この VM の現在の Wrangler 認証情報を安全に保存し、4 件すべてのリクエストで同じ制御されたキーを使用します。これは短期間だけ使う実験用ファイルであり、本番環境での秘密情報管理パターンではありません。

umask 077
npx wrangler auth token --json \
  | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>process.stdout.write(JSON.parse(s).token))' \
  > .labex/upstream-token
chmod 600 .labex/upstream-token

最初のリクエストを送信し、どちらの認証情報も表示せずに、レスポンスヘッダー、本文、HTTP ステータスを保存します。

ACCOUNT_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).accountId')
GATEWAY_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).gatewayId')
MODEL='@cf/meta/llama-3.3-70b-instruct-fp8-fast'
METADATA='{"lab":"g03-cache","case":"public-faq","synthetic":true}'
GATEWAY_TOKEN=$(cat .labex/gateway-token)
UPSTREAM_TOKEN=$(cat .labex/upstream-token)
STATUS=$(curl --http1.1 -sS -D .labex/first-headers.txt \
  -o .labex/first-response.json -w '%{http_code}' \
  -H "cf-aig-authorization: Bearer $GATEWAY_TOKEN" \
  -H "Authorization: Bearer $UPSTREAM_TOKEN" \
  -H "cf-aig-metadata: $METADATA" \
  -H 'Content-Type: application/json' \
  --data '{"prompt":"In one short sentence, what does an AI gateway do?","max_tokens":32}' \
  "https://gateway.ai.cloudflare.com/v1/$ACCOUNT_ID/$GATEWAY_ID/workers-ai/$MODEL")
unset GATEWAY_TOKEN UPSTREAM_TOKEN METADATA
printf '%s\n' "$STATUS" | tee .labex/first-status.txt
awk 'BEGIN{IGNORECASE=1} /^cf-aig-cache-status:/ {gsub("\r","",$2); print toupper($2)}' .labex/first-headers.txt \
  | tail -1 | tee .labex/first-cache-status.txt
node -e 'const b=require("./.labex/first-response.json"); console.log(b.result?.response ?? b.result)'

HTTP 200、キャッシュステータス MISS、短い生成回答が表示されることを確認してください。リクエスト本文には顧客データが含まれていないため、この回答を一時的に再利用しても安全です。

完全に同じリクエストを再送し、キャッシュヒットを証明する

このステップでは、同じプロバイダー、エンドポイント、モデル、認証情報、リクエスト本文を完全に同じ状態で送信します。そのため、AI Gateway は前のステップで作成したエントリを再利用できます。キャッシュ HIT は、新たにモデルを生成せず、ゲートウェイキャッシュから回答が返されたことを示します。

キャッシュの保存は非同期で行われます。最初のレスポンスが正常に返ってから数秒待ち、再送してください。

sleep 5
GATEWAY_TOKEN=$(cat .labex/gateway-token)
UPSTREAM_TOKEN=$(cat .labex/upstream-token)
METADATA='{"lab":"g03-cache","case":"public-faq","synthetic":true}'
STATUS=$(curl --http1.1 -sS -D .labex/repeat-headers.txt \
  -o .labex/repeat-response.json -w '%{http_code}' \
  -H "cf-aig-authorization: Bearer $GATEWAY_TOKEN" \
  -H "Authorization: Bearer $UPSTREAM_TOKEN" \
  -H "cf-aig-metadata: $METADATA" \
  -H 'Content-Type: application/json' \
  --data '{"prompt":"In one short sentence, what does an AI gateway do?","max_tokens":32}' \
  "https://gateway.ai.cloudflare.com/v1/$ACCOUNT_ID/$GATEWAY_ID/workers-ai/$MODEL")
unset GATEWAY_TOKEN UPSTREAM_TOKEN METADATA
printf '%s\n' "$STATUS" | tee .labex/repeat-status.txt
awk 'BEGIN{IGNORECASE=1} /^cf-aig-cache-status:/ {gsub("\r","",$2); print toupper($2)}' .labex/repeat-headers.txt \
  | tail -1 | tee .labex/repeat-cache-status.txt
cmp -s .labex/first-response.json .labex/repeat-response.json \
  && echo 'response bytes match the cached source'

HTTP 200HIT が表示されることを確認してください。本文のバイト列が一致することは追加の確認材料ですが、正式な証拠は HIT レスポンスヘッダーと、Dashboard に保存されたキャッシュログです。AI Gateway のキャッシュ保存は非同期かつ揮発的であるため、2 件のリクエストを同時に送信しないでください。順番に送信してもまだ MISS になる場合は、数秒待って、このブロックを 1 回だけそのまま再実行してください。

Dashboard でゲートウェイの Logs ビューを開きます。2 件の public-faq リクエストを見つけ、キャッシュの表示、処理時間、トークン使用量を比較します。一方の行にはモデルを使用した MISS、もう一方の行にはキャッシュから返された HIT が表示されるはずです。

ゲートウェイログに、最初の公開 FAQ の MISS と完全一致リクエストの HIT が表示されている

質問を変更し、新しい MISS を確認する

このステップでは、プロンプトだけを変更します。デフォルトのキャッシュキーには完全なリクエスト本文が含まれるため、新しい質問には前の回答が返されません。

GATEWAY_TOKEN=$(cat .labex/gateway-token)
UPSTREAM_TOKEN=$(cat .labex/upstream-token)
METADATA='{"lab":"g03-cache","case":"changed-question","synthetic":true}'
STATUS=$(curl --http1.1 -sS -D .labex/changed-headers.txt \
  -o .labex/changed-response.json -w '%{http_code}' \
  -H "cf-aig-authorization: Bearer $GATEWAY_TOKEN" \
  -H "Authorization: Bearer $UPSTREAM_TOKEN" \
  -H "cf-aig-metadata: $METADATA" \
  -H 'Content-Type: application/json' \
  --data '{"prompt":"In one short sentence, name one benefit of an AI gateway.","max_tokens":32}' \
  "https://gateway.ai.cloudflare.com/v1/$ACCOUNT_ID/$GATEWAY_ID/workers-ai/$MODEL")
unset GATEWAY_TOKEN UPSTREAM_TOKEN METADATA
printf '%s\n' "$STATUS" | tee .labex/changed-status.txt
awk 'BEGIN{IGNORECASE=1} /^cf-aig-cache-status:/ {gsub("\r","",$2); print toupper($2)}' .labex/changed-headers.txt \
  | tail -1 | tee .labex/changed-cache-status.txt
node -e 'const b=require("./.labex/changed-response.json"); console.log(b.result?.response ?? b.result)'

HTTP 200MISS が表示されることを確認してください。この完全一致の動作は、意味的な類似性より意図的に厳密です。関連しているように見える 2 つの質問でも、本文が異なれば別のキャッシュエントリになります。

個別化されたプロンプトに対して、support-answer のような 1 つの共有キーでデフォルトキーを置き換えないでください。カスタムキーを安全に使えるのは、そのキーにまとめられたすべてのリクエストが同じレスポンスを受け取る権限を持つ場合だけです。

最新性が必要な場合にキャッシュをバイパスする

このステップでは、最初の質問に戻りますが、キャッシュ済みの回答を明示的にスキップします。バイパスとは、有効なキャッシュエントリが存在していても「今すぐプロバイダーに問い合わせる」ことです。特定のリクエストで最新の出力が必要な場合に役立ちます。

cf-aig-skip-cache: true ヘッダーは、このリクエストだけを制御します。他の呼び出し元に対するゲートウェイのキャッシュを無効にはしません。

GATEWAY_TOKEN=$(cat .labex/gateway-token)
UPSTREAM_TOKEN=$(cat .labex/upstream-token)
METADATA='{"lab":"g03-cache","case":"fresh-bypass","synthetic":true}'
STATUS=$(curl --http1.1 -sS -D .labex/bypass-headers.txt \
  -o .labex/bypass-response.json -w '%{http_code}' \
  -H "cf-aig-authorization: Bearer $GATEWAY_TOKEN" \
  -H "Authorization: Bearer $UPSTREAM_TOKEN" \
  -H "cf-aig-metadata: $METADATA" \
  -H 'cf-aig-skip-cache: true' \
  -H 'Content-Type: application/json' \
  --data '{"prompt":"In one short sentence, what does an AI gateway do?","max_tokens":32}' \
  "https://gateway.ai.cloudflare.com/v1/$ACCOUNT_ID/$GATEWAY_ID/workers-ai/$MODEL")
unset GATEWAY_TOKEN UPSTREAM_TOKEN METADATA
printf '%s\n' "$STATUS" | tee .labex/bypass-status.txt
awk 'BEGIN{IGNORECASE=1} /^cf-aig-cache-status:/ {gsub("\r","",$2); print toupper($2)}' .labex/bypass-headers.txt \
  | tail -1 | tee .labex/bypass-cache-status.txt
node -e 'const b=require("./.labex/bypass-response.json"); console.log(b.result?.response ?? b.result)'

HTTP 200HIT ではないステータスが表示されることを確認してください。現在のゲートウェイレスポンスによっては、ヘッダーにバイパスが示される場合もあれば、単に HIT 以外のままの場合もあります。正式な証拠はゲートウェイログです。fresh-bypass のログで cached: false になっていることを確認してください。

Dashboard の Logs に戻り、fresh-bypass リクエストを開きます。キャッシュされた public-faq の行と比較してください。リクエスト単位のバイパスがゲートウェイのデフォルト設定を上書きしたため、同じ質問が Workers AI に到達しています。

fresh-bypass の詳細に、元の質問に対するモデル処理時間とトークン使用量が表示されている

使い捨てゲートウェイを削除する

このステップでは、管理用認証情報で削除されたことを確認できる状態で、ゲートウェイを削除します。この所有しているゲートウェイを削除すると、短期間のキャッシュ名前空間とログも削除されます。

ACCOUNT_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).accountId')
GATEWAY_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).gatewayId')
GATEWAY_TOKEN=$(cat .labex/gateway-token)
curl --http1.1 -fsS -X DELETE \
  -H "Authorization: Bearer $GATEWAY_TOKEN" \
  "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-gateway/gateways/$GATEWAY_ID" \
  | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const b=JSON.parse(s);if(!b.success)process.exit(1);console.log("gateway deletion accepted")})'
unset GATEWAY_TOKEN

GATEWAY_TOKEN=$(cat .labex/gateway-token)
curl --http1.1 -fsS \
  -H "Authorization: Bearer $GATEWAY_TOKEN" \
  "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-gateway/gateways" \
  > .labex/gateways-after-delete.json
unset GATEWAY_TOKEN
node -e 'const b=require("./.labex/gateways-after-delete.json"),id=process.argv[1],found=(b.result||[]).some(g=>g.id===id);console.log("gateway absent:",!found);if(found)process.exit(1)' "$GATEWAY_ID"

gateway absent: true が表示されることを確認してください。この認証済みインベントリにより、ログアウトやネットワーク障害によってページが見つからないだけの状態と、実際に削除された状態を区別できます。

トークンを削除してログアウトする

このステップでは、残っているクラウド認証情報を無効にし、一時的なトークンのコピーを両方削除して、VM を切断します。

Cloudflare Dashboard で My Profile → API Tokens を開きます。保存した正確な tokenName を見つけ、Actions を開いて Delete を選択します。確認内容を確認し、そのトークンだけを削除してください。ゲートウェイの削除がすでに証明されているため、ここで無効にしても安全です。

ゲートウェイとアップストリームのトークンファイルを削除し、Wrangler の別の認証も終了します。

shred -u .labex/gateway-token .labex/upstream-token
npx wrangler logout
npx wrangler whoami --json || true
test ! -e .labex/gateway-token -a ! -e .labex/upstream-token \
  && echo "local token files removed"

loggedIn: falselocal token files removed が表示されることを確認してください。Dashboard のセッションは別に管理されるため、ログイン状態のまま残ります。実験終了時に LabEx は、この一時 VM を保存せずに破棄します。

まとめ

安全な公開質問 1 件に対して、短い AI Gateway レスポンスキャッシュを設定しました。最初のリクエストは MISS になり、完全に同じリクエストを再送すると HIT になり、入力を変更すると別のエントリが作成されました。次に、最新性が必要な場合にリクエスト単位のバイパスを使い、ログでモデルがキャッシュ済みコピーではなくリクエストを処理したことを確認しました。

次の実験では、トラフィック制御を追加します。リクエストの到着頻度を制限する方法と、ゲートウェイが使用できるモデル使用量を制限する方法の違いを学びます。テスト量とコストは意図的に小さく保ちます。