# 本番LLMアプリのキャッシュ戦略 2026：プロンプトキャッシュ＋セマンティックキャッシュでレイテンシとコストを削減

> 生成AI/LLMアプリのレイテンシとコストを削減する2種類のキャッシュを実装ガイド。プロバイダのプロンプトキャッシュ（Anthropic cache_control・OpenAI自動・Google context caching）と、アプリ層のセマンティックキャッシュ（埋め込み類似・pgvector）を、使い分け・偽ヒット対策・TTL設計・実TypeScriptコードで解説します。

- 公開日: 2026-07-23
- 著者: 友田 陽大
- タグ: 生成AI, LLM, キャッシュ, コスト最適化, パフォーマンス, RAG, pgvector, AIエージェント, TypeScript, アーキテクチャ設計
- URL: https://tomodahinata.com/blog/llm-application-caching-prompt-semantic-latency-cost-guide
- カテゴリ: 本番LLMアプリ実装（信頼できるAI）
- 総合ガイド: https://tomodahinata.com/blog/production-llm-application-engineering-guide

## 要点

- LLMは1呼び出しが高コスト・高レイテンシ。同じ/似た入力を毎回モデルに投げるのは無駄。キャッシュで劇的に削減できる。
- 2種類を使い分ける。プロンプトキャッシュ=プロバイダが安定プレフィックスを再利用し入力を割引（Anthropicは読み取り約0.1×）。セマンティックキャッシュ=アプリ層で埋め込み類似により『言い換えた質問』にも既存応答を返し、呼び出し自体を省く。
- プロンプトキャッシュは静的コンテンツを『前』に置くとヒットが増える。OpenAIは自動（1024トークン以上）、Anthropicは cache_control で明示。
- セマンティックキャッシュの最大のリスクは偽ヒット（意味は似ても意図が違う）。高い類似度閾値（0.90〜0.95から）・TTL・スコープ・評価での品質監視で管理する。
- TTLはデータの揮発性で決める。リアルタイム（価格・在庫）は5〜15分、静的な事実は24時間が目安。

---

LLMは、**1回の呼び出しが高コストで高レイテンシ**です。同じシステムプロンプトや長いコンテキストを毎回丸ごと処理させ、似た質問に毎回ゼロから生成させる——本番でこれを放置すると、請求書とp95レイテンシが静かに膨らみます。**キャッシュは、LLMアプリのコストとレイテンシを最も安く削減できるレバー**です。

ただし「キャッシュ」には性質の異なる2種類があり、**使い分け**が要点です。

1. **プロンプトキャッシュ（プロバイダ機能）** — プロンプトの**安定したプレフィックス**（システム指示・長いコンテキスト）を再利用し、その**入力トークンを割引**する。完全一致ベース。
2. **セマンティックキャッシュ（アプリ層）** — **埋め込みの類似度**で「言い換えた似た質問」に過去の応答を返し、**LLM呼び出し自体を省く**。閾値ベース（＝偽ヒットのリスクがある）。

この記事は、[pgvectorによる本番RAG](/blog/pgvector-postgres-production-rag-hybrid-search)の知見をキャッシュに応用し、**各プロバイダの公式ドキュメントを検証**した上で、TypeScriptの実コードで両方を設計します。これは、レイテンシ・コスト面から[信頼性・回復性](/blog/llm-application-reliability-resilience-fallback-retry-guide)を補完する「本番AI」の一手です。

> ⚠️ 本記事の価格・トークン閾値は**2026年時点**の公式値で、モデル名（Opus 4.8 / GPT-5系 / Gemini 3.x など）に紐づきます。頻繁に変わるため、実装前に必ず各出典を再確認してください。割引率は**モデル依存**です。

## 2種類のキャッシュの違い

| | プロンプトキャッシュ | セマンティックキャッシュ |
|---|---|---|
| **層** | プロバイダ（API機能） | アプリ（自前 / GPTCache 等） |
| **一致方法** | プレフィックスの**完全一致** | 埋め込みの**意味的類似**（言い換えを拾う） |
| **効果** | 入力トークンを**割引** | LLM呼び出し**そのものを省略** |
| **リスク** | ほぼ無い（安全） | **偽ヒット**（要・閾値/TTL/評価） |
| **向く用途** | 大きな固定コンテキスト（RAG・システム指示・few-shot） | FAQ・定型質問・言い換えの多い問い合わせ |

**まず安全なプロンプトキャッシュから入れ、次にセマンティックキャッシュを慎重に足す**——が実務の順序です。

## プロンプトキャッシュ：静的コンテンツを「前」に置く

プロンプトキャッシュの効果は、**プロンプトの構造**で決まります。共通する原則は、**変わらない大きな部分（システム指示・長いコンテキスト・few-shot例）をプロンプトの前に、変わる部分（ユーザー入力）を後ろに置く**こと。プレフィックスが一致するほどキャッシュが効きます。

- **OpenAI** は**自動**です。コード変更なしに、一定長（**1,024トークン**）以上のプロンプトで効き、以降は128トークン単位でヒットします。静的コンテンツを前に置くだけで恩恵を受けられます。キャッシュ読み取りの割引は**モデル依存**（GPT-4o世代は約50%、GPT-5系は最大90%）。
- **Anthropic** は `cache_control`（ephemeral）で**明示**します。最大4つのブレークポイント、既定TTLは5分（1時間オプションあり）。価格は**キャッシュ書き込み1.25×（5分）/2×（1時間）、読み取り0.1×**（基本入力比、2026年時点）。
- **Google Gemini** は明示的な `CachedContent` API に加え、2.5系以降は**暗黙キャッシュ**が既定で効きます（キャッシュ保存に時間課金がある点に注意）。

Vercel AI SDK なら、最も簡単なのは AI Gateway の自動キャッシュです。

```ts
import { generateText } from "ai";

// 最も簡単：AI Gateway の自動キャッシュ。プロバイダに応じ最適な戦略を適用する
// （Anthropic系は cache_control を注入、OpenAI/Google は暗黙キャッシュを利用）。
const { text } = await generateText({
  model: "anthropic/claude-opus-4-8",
  messages,
  providerOptions: { gateway: { caching: "auto" } },
});
```

明示制御するなら、安定プレフィックスに `cacheControl` を置きます。

```ts
// 明示制御：静的コンテンツを"前"に置き、そこに cacheControl を付ける。
const { usage } = await generateText({
  model: "anthropic/claude-opus-4-8",
  messages: [
    {
      role: "system",
      content: largeStaticContext, // 大きく・変わらない部分（RAG文脈やfew-shot）
      providerOptions: { anthropic: { cacheControl: { type: "ephemeral" } } },
    },
    { role: "user", content: userQuestion }, // 変わる部分は後ろ
  ],
});
// usage.inputTokenDetails.cacheReadTokens でヒットを計測（読み取りは入力の約0.1×課金）
```

プロンプトキャッシュは**ほぼノーリスク**（完全一致なので誤答は起きない）。まずここから入れるのが鉄則です。

## セマンティックキャッシュ：呼び出しそのものを省く

一段踏み込むと、**「似た質問には、そもそもLLMを呼ばない」**——これがセマンティックキャッシュです。質問を埋め込みベクトルにし、過去の質問と**コサイン類似度**で照合。閾値を超えたら過去の応答を返します。プロンプトキャッシュが「完全一致のプレフィックス再利用」なのに対し、こちらは**言い換え（paraphrase）まで拾える**のが強みです。

判定ロジック（閾値・鮮度）は**純粋関数に分離**してテスト容易にします。

```ts
// セマンティックキャッシュ：埋め込み類似で「言い換え」まで拾う。判定は純粋関数で分離。
interface CacheHit {
  readonly answer: string;
  readonly similarity: number; // 0..1（コサイン類似）
  readonly ageMs: number;
}

// 純粋・テスト容易：閾値と鮮度(TTL)の両方を満たすヒットだけ採用する（SRP）。
function acceptCacheHit(hit: CacheHit | null, minSimilarity: number, ttlMs: number): boolean {
  return hit !== null && hit.similarity >= minSimilarity && hit.ageMs <= ttlMs;
}

// cache-aside：ヒットなら即返す（LLM呼び出しを省く）、ミスなら生成して保存。
async function cachedGenerate(query: string, scope: string): Promise<string> {
  const embedding = await embed(query);
  // pgvector で最近傍を1件（scope でユーザー/地域を隔離し、他者の応答を返さない）
  const hit = await cache.nearest(embedding, scope);
  if (acceptCacheHit(hit, 0.95, 3_600_000)) return hit!.answer; // 高閾値で偽ヒットを抑制
  const answer = await callLlm(query);
  await cache.put({ query, embedding, answer, scope }); // ミス：保存して次回に備える
  return answer;
}
```

## セマンティックキャッシュのリスクと制御

セマンティックキャッシュの怖さは**偽ヒット**です。「2024年の売上は？」と「2025年の売上は？」は埋め込みが酷似しますが、**正解は別**。緩い閾値は誤答を量産します。制御は4点。

- **閾値** — 高めから（**0.90〜0.95**）始め、偽ヒット率を監視しながら調整（AWSも「閾値がヒット率と品質のトレードオフを決める」と明言）。
- **TTL** — データの揮発性で。リアルタイム（価格・在庫）は**5〜15分**、静的な事実は**24時間**が目安。長いTTLはヒット率を上げるが陳腐化リスクも上げる。
- **スコープ** — ユーザー・地域・商品IDでフィルタし、**パーソナライズされた応答を他者に返さない**。
- **無効化** — ソースデータ更新時に、該当エントリを**コンテンツ起点で無効化**する。

そして**ヒットの品質を評価で測る**こと。オフライン評価に「キャッシュヒットが正しいか」を含め、偽ヒット率を回帰ゲートにします（[LLMの評価・テスト](/blog/llm-application-evaluation-testing-llm-as-judge-ci-guide)）。定番のOSSは [GPTCache](https://github.com/zilliztech/GPTCache) で、閾値やベクトルストアを差し替えられます。

**キャッシュしてはいけないケースも明確に。** 常に最新性が要る、強くパーソナライズされる、監査上で毎回生成が要る——これらは偽ヒット/陳腐化のコストが便益を上回るため、素直に毎回呼びます。

## 可観測性：効果と副作用を測る

キャッシュは**測って初めて安全に使えます**。**ヒット率**・削減トークン/コスト・p95レイテンシを記録し、加えて**偽ヒット率**（評価由来）を監視します。「ヒット率は高いが偽ヒットも増えた＝閾値が緩すぎ」のように、指標がチューニングを教えてくれます。土台は [OpenTelemetryによる本番可観測性](/blog/opentelemetry-observability-production-tracing-metrics-logs) を参照してください。なお、キャッシュはモデル/インフラの経済性（自前ホスト vs API）とは別レイヤーの最適化です。そちらは [生成AIのコスト設計](/blog/generative-ai-cost-api-vs-self-hosting-decision-guide) を参照してください。

## 導入チェックリスト

- [ ] **プロンプトキャッシュ（まず安全策）** — 静的コンテンツを前に置き、OpenAIは自動、Anthropicは cacheControl（or Gateway caching:'auto'）を有効化したか。
- [ ] **構造** — 変わらない大きな部分を前、可変部分を後ろに置いているか。
- [ ] **セマンティックキャッシュ（慎重に）** — 高い類似度閾値（0.90〜）から始めているか。
- [ ] **TTL** — データの揮発性に応じて設定し、ソース更新で無効化しているか。
- [ ] **スコープ** — ユーザー/地域でヒットを隔離し、パーソナライズを漏らしていないか。
- [ ] **偽ヒット監視** — 評価にキャッシュヒットの正しさを含め、回帰ゲートにしているか。
- [ ] **非キャッシュ判断** — 最新性必須・強パーソナライズ・監査要件のクエリを除外したか。
- [ ] **可観測性** — ヒット率・削減コスト・レイテンシ・偽ヒット率を監視しているか。

## まとめ

LLMアプリのキャッシュは、**「安全なプロンプトキャッシュを土台に、セマンティックキャッシュを慎重に重ねる」**のが定石です。

- **プロンプトキャッシュ**は完全一致でほぼノーリスク。**静的コンテンツを前に**置くだけで入力コストが下がる（Anthropic読み取りは約0.1×、OpenAIは自動）。
- **セマンティックキャッシュ**は呼び出しごと省ける強力な手段だが、**偽ヒット**が本質的リスク。**高い閾値・TTL・スコープ・評価**で管理する。
- **測って運用する。** ヒット率だけでなく**偽ヒット率**を監視し、キャッシュしない判断も設計に含める。

速く・安く・正確に——この3つは、キャッシュを正しく設計すれば同時に得られます。
