AI Gateway を活用したプロンプト管理とコスト最適化

|
AI Cloudflare AI Gateway LLM Cost プロンプト管理 OpenAI

Cloudflare AI GatewayによるLLM APIの統合管理・キャッシュ・コスト可視化の実践。複数AIプロバイダーを束ねてプロンプト管理とAPIコストを最適化する設計パターン。

はじめに:LLM を本番投入したときに直面するコスト・可観測性の問題

LLM API を本番環境に組み込んだとき、最初に直面するのは「動く」より先に「いくらかかるかわからない」問題だ。

LLM 本番運用でよくある問題:

  コストの問題:
    ・同じプロンプトを1日100回 LLM に送り続けている
    ・どのエンドポイントが費用の大半を占めているかわからない
    ・トークン使用量の急増に気づくのが請求書が来てから

  信頼性の問題:
    ・OpenAI がダウンしたら全機能が止まる
    ・プロバイダー切り替えのたびに全コードを修正する
    ・レートリミット超過でユーザーにエラーが返る

  観測性の問題:
    ・どのリクエストが失敗しているかログがない
    ・プロンプトのどのバリエーションが良い応答を返すか計測できない
    ・ユーザーフィードバックを応答ログに紐付けられない

これらは「LLM 自体の問題」ではなく、LLM API と自前のアプリケーションの間に管理レイヤーがないことによる問題だ。Cloudflare AI Gateway はそのレイヤーを担う。


Part 1:AI Gateway の位置づけと設計上の役割

AI Gateway はリバースプロキシとして、アプリケーションと LLM プロバイダーの間に入る。

AI Gateway の位置:

  Without AI Gateway:
  ┌──────────┐           ┌───────────┐
  │ Workers  │ ────────→ │  OpenAI   │
  │  (Hono)  │           │  Gemini   │
  └──────────┘           │  Workers AI│
                         └───────────┘
  問題: プロバイダー直結。可視化なし。フォールバックなし。

  With AI Gateway:
  ┌──────────┐    ┌─────────────────┐    ┌───────────┐
  │ Workers  │ →  │  AI Gateway     │ →  │  OpenAI   │
  │  (Hono)  │    │  ・キャッシュ    │    │  Gemini   │
  └──────────┘    │  ・レートリミット │    │  Workers AI│
                  │  ・フォールバック │    └───────────┘
                  │  ・ログ/分析     │
                  │  ・コスト可視化  │
                  └─────────────────┘
  利点: 1行のエンドポイント変更で全機能が有効になる

AI Gateway が提供するのは4つの機能だ。

機能 効果
キャッシュ 同一プロンプトへのレスポンスを再利用。LLM への課金リクエストを削減
レートリミット 1ユーザーあたりのリクエスト数を制限。暴走コストを防止
フォールバック プロバイダー障害時に自動で別モデルへ切り替え
ログ・分析 リクエストごとのトークン数・コスト・レイテンシをダッシュボードで可視化

コアの機能(ログ・キャッシュ・レートリミット)は無料で使える。課金は Workers のリクエスト数に乗る形(月1,000万リクまで $5/月)。


Part 2:セットアップと基本的な接続

接続方法は2パターン

AI Gateway への接続には「URL 置換」と「Worker Binding」の2つのアプローチがある。

パターン①:URL 置換(最速・プロバイダー非依存)

既存コードの baseURL を AI Gateway のエンドポイントに変えるだけで動く。

// Before: OpenAI に直接接続
import OpenAI from 'openai'
const client = new OpenAI({
  apiKey: env.OPENAI_API_KEY,
})

// After: AI Gateway 経由に変更(1行変更)
import OpenAI from 'openai'
const client = new OpenAI({
  apiKey: env.OPENAI_API_KEY,
  baseURL: `https://gateway.ai.cloudflare.com/v1/${ACCOUNT_ID}/${GATEWAY_ID}/openai`,
  //  ↑ この1行を追加するだけで、キャッシュ・ログ・レートリミットが有効になる
})

// 使い方は変わらない
const response = await client.chat.completions.create({
  model:    'gpt-4o-mini',
  messages: [{ role: 'user', content: prompt }],
})

パターン②:Worker Binding(型安全・Cloudflare 統合)

wrangler.jsonc に AI バインディングを追加し、ネイティブ API を使う。

// wrangler.jsonc
{
  "name": "my-ai-app",
  "ai": {
    "binding": "AI"
  }
}
// Worker Binding を使った接続
const gateway = env.AI.gateway('my-gateway')

// gateway.getUrl() で動的に接続先 URL を取得(Vercel AI SDK 等と連携しやすい)
const baseURL = await gateway.getUrl('openai')

const client = new OpenAI({
  apiKey:  env.OPENAI_API_KEY,
  baseURL,
})

Worker Binding は patchLog / getLog といったログ操作 API にアクセスできる点が URL 置換との決定的な違いだ(後述)。


Part 3:キャッシュ設計でコストを削減する

キャッシュの仕組み

AI Gateway のキャッシュは、リクエストのハッシュ(モデル + プロンプト + パラメータ)をキーに、レスポンスを Cloudflare の CDN にキャッシュする。同一リクエストへのキャッシュヒット時は LLM プロバイダーへの課金が発生しない。

// キャッシュを有効にするリクエスト設定
const response = await client.chat.completions.create({
  model:    'gpt-4o-mini',
  messages: [{ role: 'user', content: prompt }],
}, {
  // AI Gateway のキャッシュ設定(リクエストオプションで渡す)
  headers: {
    'cf-aig-cache-ttl': '3600',  // キャッシュ有効期限: 1時間(秒)
    // 'cf-aig-skip-cache': 'true'  // キャッシュをスキップしたいときはこちら
  }
})

どんなユースケースでキャッシュが効くか

キャッシュ効果が高いユースケース:

  ✅ FAQ 応答(同じ質問が繰り返されやすい)
  ✅ コンテンツ分類・タグ付け(同じテキストを複数ユーザーが送る)
  ✅ テンプレートベースの文書生成(入力パターンが限定的)
  ✅ 開発・テスト中のプロンプト実行(同じテストケースを何度も実行)

キャッシュ効果が低いユースケース:

  ❌ ユーザー固有のコンテキストを含む会話(毎回ユニーク)
  ❌ temperature > 0 のランダム性が必要なケース
  ❌ リアルタイムデータを参照する応答

キャッシュヒット率はダッシュボードで確認できる。「キャッシュヒット率が低いにもかかわらず同じプロンプトが多い」場合は、プロンプトのノーマライズ(前後の空白除去・大文字小文字統一)でキャッシュを効かせる余地がある。


Part 4:フォールバックで可用性を設計する

Universal Endpoint によるフォールバック

AI Gateway の Universal Endpoint は、プロバイダーのリストを配列で渡し、上から順に試みるフォールバック設計を提供する。

// Hono ルートでのフォールバック実装
app.post('/api/chat', async (c) => {
  const { message } = await c.req.json()

  // Universal Endpoint に複数プロバイダーの順序を渡す
  const response = await fetch(
    `https://gateway.ai.cloudflare.com/v1/${c.env.CF_ACCOUNT_ID}/${c.env.AI_GATEWAY_ID}`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify([
        {
          // 第1候補: Workers AI(コスト安・Cloudflare ネイティブ)
          provider: 'workers-ai',
          endpoint: '@cf/meta/llama-3.3-70b-instruct-fp8-fast',
          headers: {
            Authorization: `Bearer ${c.env.CF_API_TOKEN}`,
          },
          query: {
            messages: [{ role: 'user', content: message }],
          },
        },
        {
          // 第2候補: OpenAI(Workers AI が失敗したときの保険)
          provider: 'openai',
          endpoint: 'chat/completions',
          headers: {
            Authorization: `Bearer ${c.env.OPENAI_API_KEY}`,
          },
          query: {
            model:    'gpt-4o-mini',
            messages: [{ role: 'user', content: message }],
          },
        },
      ]),
    }
  )

  // cf-aig-step ヘッダーで「どのプロバイダーが応答したか」を確認できる
  const usedStep = response.headers.get('cf-aig-step')
  // "0" = Workers AI, "1" = OpenAI にフォールバック

  const data = await response.json()
  return c.json({ ...data, _provider_step: usedStep })
})

フォールバック戦略の設計

プロバイダー選択の優先順位設計:

  第1候補: Cloudflare Workers AI
    理由: Cloudflare アカウントで統合管理、Workers Paid の枠内で動く
    制限: 利用可能モデルが限定的(Llama 3.3、Mistral 等)

  第2候補: 専門モデル(ユースケース別)
    コード生成 → Claude 3.5 Sonnet(Anthropic)
    文書分析  → Gemini 1.5 Pro(Google)
    汎用会話  → gpt-4o-mini(OpenAI)

  第3候補: コスト重視のフォールバック
    gpt-4o → gpt-4o-mini への自動降格
    (品質より可用性を優先するとき)

Part 5:ログ・観測性の活用

patchLog でフィードバックを記録する

Worker Binding を使うと、ユーザーのフィードバックをリクエストログに後から付与できる。プロンプトの品質改善に直接使えるデータになる。

// Hono での実装例:応答 + ログ ID をフロントに返す
app.post('/api/chat', async (c) => {
  const { message } = await c.req.json()
  const gateway = c.env.AI.gateway('my-gateway')

  // AI リクエストを実行
  const response = await gateway.run({
    provider: 'openai',
    endpoint: 'chat/completions',
    headers: { Authorization: `Bearer ${c.env.OPENAI_API_KEY}` },
    query: {
      model:    'gpt-4o-mini',
      messages: [{ role: 'user', content: message }],
    },
  })

  // ログ ID を取得(後でフィードバックを紐付けるため)
  const logId = c.env.AI.aiGatewayLogId

  const data = await response.json()
  return c.json({ ...data, _log_id: logId })
})

// フィードバックエンドポイント(👍/👎 ボタン押下時に呼ぶ)
app.post('/api/feedback', async (c) => {
  const { logId, rating } = await c.req.json()
  const gateway = c.env.AI.gateway('my-gateway')

  // ログに評価とメタデータを付与
  await gateway.patchLog(logId, {
    feedback: rating === 'good' ? 1 : -1,   // +1 = 良い、-1 = 悪い
    score:    rating === 'good' ? 100 : 0,
    metadata: {
      rated_at: new Date().toISOString(),
      // ユーザー ID等を追加できる(PII に注意)
    },
  })

  return c.json({ ok: true })
})

ダッシュボードで何を見るか

AI Gateway のダッシュボードでは以下の指標が確認できる。

観測すべき主要指標:

  コスト管理:
    ・推定コスト(トークン数から計算)の時系列グラフ
    ・エンドポイント別・モデル別のコスト内訳
    ・キャッシュヒット率(ここが低いとコスト最適化の余地あり)

  パフォーマンス:
    ・P50/P95/P99 レイテンシ(モデル別)
    ・フォールバック発生率(Workers AI → OpenAI の切り替え頻度)
    ・エラー率(4xx/5xx の割合)

  品質:
    ・フィードバックスコアの分布(patchLog で記録したもの)
    ・feedback = -1 のリクエストのプロンプトパターン

Part 6:Hono + AI Gateway の実装パターン

レートリミットと認証を組み合わせた本番設計

// src/routes/ai-chat.ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

type Bindings = {
  AI:           Ai
  KV:           KVNamespace
  OPENAI_API_KEY: string
  CF_ACCOUNT_ID:  string
  AI_GATEWAY_ID:  string
}

export const aiChatRouter = new Hono<{ Bindings: Bindings }>()

const chatSchema = z.object({
  message:     z.string().min(1).max(4000),
  session_id:  z.string().optional(),
})

aiChatRouter.post(
  '/',
  zValidator('json', chatSchema),
  async (c) => {
    const { message, session_id } = c.req.valid('json')

    // ① ユーザーごとのレートリミット(KV ベース)
    const userId = c.req.header('X-User-ID') ?? 'anonymous'
    const rateLimitKey = `ratelimit:ai:${userId}`
    const currentCount = parseInt(await c.env.KV.get(rateLimitKey) ?? '0')

    if (currentCount >= 20) {  // 1分間に20リクエストまで
      return c.json({ error: 'Rate limit exceeded. Try again in a minute.' }, 429)
    }
    await c.env.KV.put(rateLimitKey, String(currentCount + 1), { expirationTtl: 60 })

    // ② セッション履歴の取得(KV から)
    const historyKey = `chat:history:${session_id ?? userId}`
    const history: { role: string; content: string }[] =
      JSON.parse(await c.env.KV.get(historyKey) ?? '[]')

    const messages = [
      { role: 'system', content: 'あなたは親切なアシスタントです。' },
      ...history.slice(-10),  // 直近10ターンのコンテキスト
      { role: 'user', content: message },
    ]

    // ③ AI Gateway 経由でリクエスト(フォールバック付き)
    const gateway = c.env.AI.gateway(c.env.AI_GATEWAY_ID)

    let response: Response
    try {
      response = await gateway.run({
        provider: 'openai',
        endpoint: 'chat/completions',
        headers: {
          Authorization: `Bearer ${c.env.OPENAI_API_KEY}`,
          'cf-aig-cache-ttl': '0',  // 会話はキャッシュ不要
        },
        query: {
          model:       'gpt-4o-mini',
          messages,
          max_tokens:  1000,
          temperature: 0.7,
        },
      })
    } catch (err) {
      return c.json({ error: 'AI service unavailable' }, 503)
    }

    const data  = await response.json() as any
    const reply = data.choices?.[0]?.message?.content ?? ''
    const logId = c.env.AI.aiGatewayLogId

    // ④ 履歴を更新して保存(TTL: 1時間)
    const updatedHistory = [
      ...history,
      { role: 'user',      content: message },
      { role: 'assistant', content: reply },
    ]
    await c.env.KV.put(historyKey, JSON.stringify(updatedHistory), {
      expirationTtl: 3600,
    })

    return c.json({
      reply,
      log_id:     logId,  // フロントがフィードバック送信に使う
      session_id: session_id ?? userId,
    })
  }
)

Part 7:コスト最適化の実践チェックリスト

AI Gateway のログとダッシュボードを参照しながら実施できる最適化をまとめる。

コスト削減のアクションリスト:

  ① キャッシュヒット率を上げる
    → 同じプロンプトが繰り返されるエンドポイントに TTL を設定
    → ユーザー入力をノーマライズ(trim・lowercase)してからリクエスト

  ② モデルの使い分けを見直す
    → 分類・要約タスクは gpt-4o → gpt-4o-mini に降格
    → コード生成以外では Claude Opus → Claude Haiku に降格
    → Workers AI(Llama)で代替できるタスクは移行

  ③ トークン数を削減する
    → システムプロンプトは簡潔に(1,000 トークン以下を目標)
    → 会話履歴は直近 N ターンのみ保持(古いコンテキストはカット)
    → max_tokens を用途に応じて制限

  ④ フォールバック先のコストを考慮する
    → Workers AI → gpt-4o-mini の順番(高品質だが高コストを後回しに)
    → フォールバック発生率をモニタリングして根本原因を修正

  ⑤ レートリミットで暴走を防ぐ
    → IP ベース・ユーザー ID ベースの二重リミット
    → AI Gateway のレートリミット設定 + KV での独自制限

Conclusion:AI Gateway は「インフラとしての LLM 管理」の出発点

LLM を「機能として使う」フェーズから「インフラとして管理する」フェーズへの移行で、AI Gateway は最小限の変更で最大の可観測性を得られる手段だ。

AI Gateway なし AI Gateway あり
コスト把握 月の請求書で把握 リアルタイムでトークン単価×量を可視化
プロバイダー障害 全機能停止 自動フォールバックで継続稼働
同一プロンプトの再計算 毎回 LLM へ課金 キャッシュヒットでコストゼロ
デバッグ ログなし・再現困難 ログ ID でリクエスト単位の追跡
品質改善 ユーザー反応を計測できない patchLog でフィードバックを記録

重要なのは、AI Gateway はコードを大きく書き換えずに導入できる点だ。baseURL の1行変更から始めて、フォールバックや patchLog は必要になった時点で追加すれば良い。漸進的に導入できるレイヤー設計こそが、このアーキテクチャの実用的な価値だ。

この記事をシェア

Twitter / X LinkedIn
記事一覧に戻る
🤖
Cloud Assistant
IBM Watson powered

こんにちは!クラウドエンジニアのポートフォリオサイトへようこそ。AWS構成・副業サービス・お仕事のご相談など、何でも聞いてください 👋