KV Cache は、「Transformerの理論」と「LLMのエンジニアリングおよびデプロイ」を結ぶ重要な概念です。これを理解すれば、「モデルがどう計算するか」から「モデルがどう動くか」までの最後のつながりが見えてきます。
KV Cache詳解:Transformerの基礎原理からAPIコスト最適化まで
AI Application Engineerに向けた知識解説、原理の理解、実践的なエンジニアリングガイド
1. KV Cacheとは何か——「なぜ必要なのか」から考える
1.1 Transformer推論の中心的な矛盾
GPTやClaudeなどのLLMは、Decoder-only Transformerを基盤としています。推論時には、自己回帰(autoregressive) 方式で文章を生成します。一度に一つのtokenだけを生成し、それをシーケンス末尾へ追加して次のtokenを生成する処理を、終了tokenが出るまで繰り返します。
各生成ステップでSelf-Attentionは、現在のtokenのQueryと、シーケンスにある 既存のすべてのtoken のKeyとの内積を計算し、既存のすべてのtokenのValueを重みづけして合計します。これがAttentionの式$ ext{softmax}( rac{QK^T}{sqrt{d_k}})V$の実際の意味です。
ところがTransformerモデル自体は ステートレス です。各forward passは純粋関数のように動作します。入力を受け取って一度計算し、結果を出力すると、各tokenのKおよびVベクトルを含むすべての中間計算結果が消えます。前回のforward passで計算した内容を、モデルが自動的に「覚える」ことはありません。
つまり最適化を行わなければ、新しいtokenを一つ生成するたびに、以前処理したすべてのtokenを含む シーケンス全体 を最初からモデルへ入力し直し、すべてのtokenのKとVを再計算しなければなりません。シーケンスが長くなるほど重複計算量は二次関数的に増え、エンジニアリング上は許容できなくなります。
1.2 KV Cacheによる解決
KV Cacheの中心的な考え方は非常に単純です。各ステップですでに計算したKeyとValueのベクトルをGPUメモリにキャッシュし、次のステップでそのまま再利用して重複計算を避けます。
本質的には、KV Cacheがステートレスなモデルへ状態を加え、以前のステップで計算した結果を「記憶」できるようにします。
1.3 具体例
モデルが「The cat sat on the mat.」を生成するとします。
Step 1: promptとして「The cat sat on the」(位置1〜5)を入力します。モデルは5個のtokenそれぞれのKとVを一括して並列計算し、注意計算を完了します。位置5の出力を予測headへ渡し、次のtokenとして「mat」を予測します。
Step 2(KV Cacheなし): 次のtoken(位置7)を予測するとき、現在のシーケンスは「The cat sat on the mat」という6個のtokenです。モデルは6個すべてをもう一度入力し、6個すべてのKとVを再計算して、完全な注意計算を行う必要があります。しかし位置1〜5のKとVはStep 1の結果とまったく同じです。モデルの重みも入力も変わっていないからです。この再計算は完全な無駄です。
Step 2(KV Cacheあり): 位置1〜5のKとVは、すでにGPUメモリへキャッシュされています。次のtoken(位置7)を予測するとき、モデルは「mat」という一つのtokenだけを入力し、そのQ、K、Vを計算します。新しいK、Vをキャッシュへ追加し、Qを使って「mat」自身のKを含むキャッシュ内の6個すべてのKとの注意計算を行います(注1)。計算量は6個のtokenの処理から1個のtokenの処理へ減ります。
注1:
Causal Maskの正確な定義
Causal Maskの規則は、位置$i$は位置1、2、…、$i-1$、$i$を見ることができる というものです。つまり位置$i$は自分自身を見ることができます。
なぜ自分自身を見る必要があるのでしょうか。
最も単純な場合、シーケンス最初のtokenである位置1を考えます。
causal maskによって位置$i$が1から$i-1$までしか見られないとすると、位置1が見られるのは位置1から0まで、つまり何もありません。そのtokenの注意出力は空になり、情報をまったく持たないため、モデルは意味のある計算を行えません。
実際には、位置1のQは自分自身のKと内積を計算し、自分自身への注意重みを得る必要があります。見えるtokenが一つしかない場合、その重みは1.0です。そして自分のVを注意出力として使うことで、位置1の情報が正常に後続へ伝わります。
したがって、完全な情報の流れは次のようになります。
一つのTransformer層における位置$i$の注意出力は次の式です。
$$ o_i = \sum_{j=1}^{i} \alpha_{ij} \cdot v_j $$和の範囲は$j = 1$から$j = i$までで、$i$自身を含みます。$alpha_{ii}$、つまり位置$i$から自分自身への注意重みは、通常ゼロではありません。したがって、各tokenの出力には自身のVの寄与が含まれます。
このため、「位置$i$の隠れ状態は位置1から$i$までの情報を統合している」と言うとき、この$i$は正確であり、$i-1$ではありません。
2. TransformerアーキテクチャにおけるKV Cacheの正確な位置
2.1 存在する場所
KV Cacheは、すべてのTransformer層にある、すべての注意head に存在します。
典型的なLLM、たとえば32層、32個の注意head、headごとの次元が128のモデルでは、推論時に各層と各headの組み合わせごとにK行列とV行列を保存する必要があります。長さ$n$のシーケンスに対するKV Cache全体のサイズは、次のとおりです。
$$ 2 \times n_{\text{layers}} \times n_{\text{heads}} \times n_{\text{seq}} \times d_{\text{head}} \times \text{bytes\_per\_element} $$128Kや1M tokensの長いcontext windowがGPUメモリにとって大きな課題となる理由はここにあります。KV Cacheのメモリ使用量はシーケンス長に比例して増え、モデルパラメータとは別にメモリを消費します。
2.2 推論における二つの段階
KV Cacheを理解するには、推論中の本質的に異なる二つの段階を区別する必要があります。
Prefill段階(事前充填): ユーザーのpromptが入力されると、モデルはすべてのprompt tokenを一度に並列処理し、各層・各headについてKとVを計算して保存します。この段階は学習時のforward passに似た 計算律速(compute-bound) です。この処理時間が、ユーザーのリクエスト送信から最初のtokenが出力されるまでの時間である Time to First Token(TTFT) を決めます。
Decode段階(復号・生成): output tokenを一つずつ生成します。各ステップでは一つのtokenのQ/K/Vだけを計算し、新しいK/Vをキャッシュへ追加し、新しいQでキャッシュ全体を検索します。この段階は、毎回GPUメモリからKV Cache全体を読み出す必要があるため、メモリ帯域律速(memory-bound) です。
2.3 各予測ステップで実際に起きていること
ここには、混同しやすい点があります。
「N+1番目のtokenを予測する」と言うとき、モデルが実際に行うのは、シーケンス最後のtokenである 位置Nの出力表現を計算すること です。具体的な流れは次のとおりです。
- 位置NのtokenがEmbedding層を通り、ベクトル$x_N$を得ます。
- $x_N$はすべてのTransformer層を順番に通り、各層で$W_Q$、$W_K$、$W_V$によって射影され、$q_N$、$k_N$、$v_N$になります。
- $q_N$と位置1〜NにあるすべてのKとの内積から注意重みを求め、その重みですべてのVを加重合計します。
- 注意出力がFFNなどのモジュールを通り、位置Nの最終隠れ状態$h_N$になります。
- $h_N$はモデル最上部の Output Head(Linear層 + Softmax) へ渡されます。Linear層の重み行列は$d_{\text{model}} \times V$の形状を持ち、$V$は語彙数です。$h_N$を$V$次元のlogitベクトルへ写像し、Softmaxで確率分布へ変換して、サンプリングまたはargmaxによりN+1番目のtokenを得ます。
重要な理解は、「N+1番目のtokenを予測すること」と「位置Nの出力を計算すること」が同じだ という点です。N+1番目のtokenは予測されるまで存在しないため、「位置N+1のQ」も存在しません。Qは常に、現在のシーケンスの最後にあるtokenから得られます。
さらに、Causal Attention Maskによって位置$i$は位置1〜$i$の情報だけを見ることができます。そのため$h_i$は、「位置1〜$i$のすべてのtokenが既知であるとき、次に最も現れやすいtoken」という意味を符号化しています。学習時にはシーケンス全体を一度に処理し、すべての位置が同時に予測を出し、各位置でlossを計算できます。これが、一つの学習サンプルから複数の学習信号を得られる理由です。推論時には、最後の位置だけが実際にまだ現れていない次のtokenを予測するため、その出力だけを使います。
2.4 なぜKとVだけをキャッシュし、Qはキャッシュしないのか
Decode段階の各ステップで必要なのは、現在の最終位置にあるQ を使って、過去の全位置にあるKとVを検索することだけです。このQは、その時点でシーケンスへ追加された新しいtokenからリアルタイムに計算されます。以前の位置にある古いQはどうなるのでしょうか。それぞれ以前のステップで、次の位置のtokenを予測するという役割を終えており、現在のステップではまったく不要です。一方、現在のQはすべての過去のKと内積を計算し、すべての過去のVを加重合計するため、全位置のKとVは毎ステップ参照されます。したがってキャッシュが必要です。
3. 「単一リクエスト内のKV Cache」から「リクエストをまたぐPrompt Caching」へ
3.1 二つの層を区別する
これは極めて重要な概念上の区別です。
第1層:単一リクエスト内のKV Cache——Transformer推論エンジン内部の最適化です。一度のAPIリクエストにおけるdecode段階で、新しいtokenを生成するたびに過去のtokenすべてのK/Vを再計算することを防ぎます。APIユーザーには完全に透過的で見えず、vLLMやTensorRT-LLMなどの推論フレームワークが自動で処理します。
第2層:リクエストをまたぐPrompt Caching——API提供者がインフラストラクチャ層で行う最適化です。一度のAPIリクエストが終わった後も、サーバーはそのリクエストのprefill段階で計算したKV Cacheを 破棄せず、一定時間保持します。後続の新しいリクエストが同じprompt prefixを持つ場合、既存のKV Cacheをそのまま読み込み、重複するprefixのprefill計算を省略できます。
3.2 リクエストをまたぐPrompt Cachingの仕組み
長い文書について複数回質問する場面を例にします。
最初のリクエストのprompt構成:[システム指示] + [50,000 tokenの文書] + [ユーザーの質問A]
二回目のリクエストのprompt構成:[システム指示] + [50,000 tokenの文書] + [ユーザーの質問B]
二つのリクエストでは、先頭の50,000を超えるtokenが完全に同じで、最後のユーザー質問だけが異なります。
Prompt Cachingなし: 二回目のリクエストでは、prefill段階で50,000を超えるtokenを最初から再計算します。まったく同じ計算へ二度、通常料金を支払うことになります。
Prompt Cachingあり: 最初のリクエストのKV Cacheがサーバーに保持されます。二回目のリクエストが届くと、システムは同一のprefixを認識し、既存のKV Cacheを直接読み込みます。新しいユーザー質問に含まれる数十tokenだけをprefillして、decode段階へ進みます。
3.3 なぜ「同一のprefix」でなければならないのか(注2)
これは因果注意マスク(Causal Mask)の性質によって決まります。causal attentionでは、位置$i$のKとVは位置1〜$i$の入力だけに依存します。したがって、prompt先頭の$n$個のtokenが完全に同一なら、その$n$個のtokenについて各層で計算されるKとVも必ず完全に同一となり、後ろに続く内容には影響されません。
しかしprefixの中で一つでもtokenが異なると、たとえば冒頭にタイムスタンプを追加すると、その位置以降のKとVがすべて変わり、残りのキャッシュは無効になります。
注2:
「同一のprefix」とは何か
prefixとは、promptの先頭から連続して同一である部分です。最初のtokenから順に比較し、二つのリクエストで完全に一致している連続部分が「同一のprefix」です。最初に異なるtokenが現れた位置からキャッシュは無効になります。
例1:典型的なキャッシュヒット
最初のリクエストのprompt:
[System] あなたは法務文書の分析アシスタントです。以下の契約書に基づいて質問へ答えてください。
[契約書全文](50,000 tokens)
[User] この契約の解除条項は何ですか?
二回目のリクエストのprompt:
[System] あなたは法務文書の分析アシスタントです。以下の契約書に基づいて質問へ答えてください。
[契約書全文](50,000 tokens、前回と完全に同一)
[User] 契約で定められた支払周期はどのくらいですか?
最初のtokenから順に比較すると、System指示も契約書全文も同じで、ユーザー質問から差が生じます。先頭のおよそ50,000を超えるtokenが同一のprefixとなり、cache hitします。モデルは最後の異なるユーザー質問だけをprefillすれば済みます。
例2:冒頭にタイムスタンプを加えるとキャッシュ全体が無効になる
最初のリクエスト:
[timestamp: 2025-03-05 10:00:00]
[System] あなたは法務文書の分析アシスタントです...
[契約書全文](50,000 tokens)
[User] 解除条項は何ですか?
二回目のリクエスト:
[timestamp: 2025-03-05 10:02:35]
[System] あなたは法務文書の分析アシスタントです...
[契約書全文](50,000 tokens、完全に同一)
[User] 支払周期はどのくらいですか?
最初のtokenから比較すると、2025-03-05 10:00:00と2025-03-05 10:02:35は、タイムスタンプの秒数の位置ですでに異なっています。同一prefixの長さはほぼゼロです。 その後の50,000 tokenが同一でも意味はありません。prefixの一致が最初の段階で途切れたため、prompt全体を通常料金で再計算する必要があります。
コスト最適化の提案で、promptの冒頭に動的に変化する内容を絶対に置かない ことが繰り返し強調される理由はここにあります。
例3:中央の一語を変えると、前半だけがヒットし、後半は無効になる
最初のリクエスト:
[System] あなたは専門的な文書分析アシスタントです。
[文書A](20,000 tokens)
[文書B](30,000 tokens)
[User] 文書Bの要点をまとめてください。
二回目のリクエスト:
[System] あなたは専門的な文書分析アシスタントです。
[文書A](20,000 tokens、完全に同一)
[文書C](30,000 tokens、文書Bとは異なる)
[User] 文書Cの要点をまとめてください。
先頭からtokenごとに比較すると、Systemも文書Aも同じで、文書Bと文書Cから差が生じます。Systemと文書Aに相当するおよそ20,000を超えるtokenが同一prefixとなり、cache hitします。しかし文書B/C以降の30,000を超えるtokenは、すべて再度prefillしなければなりません。
例4:few-shot examplesの順序が異なると、ほぼすべて無効になる
最初のリクエスト:
[System] あなたは感情分析アシスタントです。
[Example 1] 入力: "この製品は素晴らしい" → 出力: ポジティブ
[Example 2] 入力: "サービスがひどい" → 出力: ネガティブ
[Example 3] 入力: "まあまあかな" → 出力: ニュートラル
[User] 分析してください: "今日は気分が良い"
二回目のリクエスト:
[System] あなたは感情分析アシスタントです。
[Example 2] 入力: "サービスがひどい" → 出力: ネガティブ
[Example 1] 入力: "この製品は素晴らしい" → 出力: ポジティブ
[Example 3] 入力: "まあまあかな" → 出力: ニュートラル
[User] 分析してください: "価格が高すぎる"
Systemは同じですが、その直後のExample 1とExample 2が異なります。同一prefixはSystemの短い部分だけです。 examplesの内容が完全に同じで、順序を変えただけでも、キャッシュはほぼすべて無効になります。
エンジニアリング上の示唆:few-shot examplesの順序を一度決めたら、ランダムに入れ替えてはいけません。キャッシュを壊すことになります。
一文でまとめると、次のようになります。
prefix matchingは、二本のロープを先端から重ねて比べることに似ています。最初に分岐するまでの部分がキャッシュヒット領域であり、分岐後の内容は、どれほど似ていてもすべて再計算しなければなりません。 したがってprompt設計の原則は、変わらないものをできるだけ前へ置き、変わるものをできるだけ後ろへ置くことです。
4. 主要三社のPrompt Caching料金比較(2025年時点)
4.1 Anthropic(Claude)——明示的な制御と高いヒット率
- 仕組み: リクエストで
cache_control引数を使い、キャッシュの境界を明示する必要があります。 - 料金: 5分間キャッシュへのcache writeは、基本input token料金の 1.25倍 です。cache readは基本料金の 0.1倍、つまり10%です。write料金がより高い代わりに保持時間が長い、1時間のキャッシュもあります。
- 最小キャッシュtoken数: Claude Sonnet 4.5、Opus 4、Sonnet 4などは 1,024 tokens を必要とします。
- キャッシュ無効化の仕組み: リクエスト構成要素をTools → System Message → Message Historyの固定順序で処理します。前方の要素が変わると、後続のキャッシュが無効になります。
- 実測ヒット率: 能動的にキャッシュを指定すると、ヒット率は 100%近く になり、性能を予測できます。
- break-even point: cache writeに1.25倍の割増があるため、元を取るには少なくとも 2回のリクエスト が必要です。
4.2 OpenAI(GPT)——完全自動、設定不要
- 仕組み: gpt-4o以降のモデルで自動的に有効となり、コード変更も追加料金も不要です。
- 料金: cache hit時には、最新モデルで最大 90% の割引を受けられ、cache writeの追加料金はありません。
- 最小キャッシュtoken数: 1,024 tokens で、128 token単位で一致を判定します。
- キャッシュ有効期間: 通常は5〜10分間利用がないと削除され、最長1時間です。
prompt_cache_key引数をroutingの補助に利用できます。 - 実測ヒット率: 自動routingのためヒット率はおよそ 50% で、性能が十分に安定しない場合があります。
4.3 Google(Gemini)——二つの仕組みと時間課金
- 仕組み: 二つのモードがあります。Implicit caching はデフォルトで有効となり、重複prefixを自動検出して割引します。Explicit caching では、APIから手動でキャッシュを作成し、TTLを制御します。
- 料金: Gemini 2.5以降のモデルでは、cached tokenの読み取り料金は標準input料金の 10%、つまり90%割引です。Explicit cachingには、約$4.50/100万token/時の時間制ストレージ料金が別途かかります。
- TTL: Explicit cachingはデフォルト60分で、変更できます。Implicit cachingにはストレージ料金がありません。
- 適した場面: Explicit cachingは、同じ大きな文書へ長時間にわたって繰り返し質問する場合に適しています。
4.4 主な違いのまとめ
| 項目 | Anthropic | OpenAI | Google Gemini |
|---|---|---|---|
| 制御方法 | 明示的に指定 | 完全自動 | 暗黙的な自動 + 明示的な手動 |
| Cache Writeの割増 | あり(1.25倍) | なし | なし(ただし明示キャッシュにはストレージ料金あり) |
| Cache Readの割引 | 90% off | 最大90% off | 90% off |
| ヒット率の制御 | 高い(100%近く) | 低い(約50%) | 中程度 |
| 最小token数 | 1,024 | 1,024 | モデルによる(最小1,024) |
| ストレージ料金 | なし | なし | Explicitは時間課金あり |
5. AI Application Engineerのためのコスト最適化の思考法
5.1 第1層:Prompt構造を設計する思考
promptを「安定したprefix + 動的なsuffix」という階層構造として考えます。
- 安定したprefix(最初に配置): システム指示、ツール定義、few-shot examples、長い文書のcontextなど、複数のリクエストで変わらない内容。
- 動的なsuffix(最後に配置): ユーザーの具体的な質問、現在の会話turnなど、リクエストごとに異なる部分。
よくあるアンチパターンは、promptの冒頭にタイムスタンプ、リクエストIDなど、毎回変化するメタデータを入れることです。最初のtokenからprefixが一致しなくなり、キャッシュ全体が無効になります。
5.2 第2層:リクエストパターンを対応づける思考
アプリケーションの場面に応じて、最も適した提供者とキャッシュ戦略を選びます。
- 多数のユーザーが同じsystem promptを共有する高頻度の場面(カスタマーサービスbotなど):設定なしで効果を得られるOpenAIの自動キャッシュが適しています。
- 一人のユーザーが同じ長い文書について繰り返し質問する場面: 高いヒット率を確保できるAnthropicまたはGeminiの明示キャッシュが適しています。
- 非同期バッチ処理: Batch APIの割引(通常50%)とPrompt Cachingの割引を重ね、二重に節約できます。
5.3 第3層:処理全体のToken経済性を考える
一度のリクエスト料金だけに注目せず、タスク全体の処理を計算します。
- 一人のユーザーが一つのタスクを完了するまでに、input/output tokenをどれだけ消費するか。
- そのうち、リクエストをまたいで繰り返される部分はどのくらいか。
- キャッシュヒット率をどこまで高められるか。
- break-even pointはどこか。Anthropicのcache writeには1.25倍の割増があり、回収には少なくとも2回のリクエストが必要です。
5.4 第4層:アーキテクチャレベルのコストを考える
- Model Routing: 単純なタスクは安価なモデルで処理し、複雑なタスクだけに高価なモデルを使います。
- Context Compaction: summarizationで長い会話履歴を圧縮し、context windowの際限ない膨張を防ぎます。
- 監視と検証: 実際のキャッシュ指標を監視し、最適化戦略が機能していることを確認します。Anthropicは
cache_read_input_tokensとcache_creation_input_tokens、OpenAIはcached_tokens、GeminiはcachedContentTokenCountを返します。
5.5 第5層:基盤となる制約へのエンジニアリング上の直感
提供者が90%の割引を提供できる根本的な理由を理解します。cache hitではprefill段階の大量の行列乗算を省略し、GPUメモリにある既存のKVベクトルを読み出すだけで済みます。ただし、これらのベクトルは依然としてGPUメモリを占有します。そのため提供者は、メモリコストと割引率の間でバランスを取らなければなりません。キャッシュにTTL制限や最小token数の要件があり、Googleのexplicit cachingが保存時間に応じて課金される理由はここにあります。
6. 全体像を示す知識マップ
Transformer Self-Attention(Q、K、Vの計算)
│
├─→ 学習段階:すべてのtokenを並列計算し、KV Cacheの概念はない
│
└─→ 推論段階:自己回帰生成により、tokenを一つずつ出力
│
├─→ Prefill段階(promptを処理、計算律速、TTFTを決定)
│ └─ すべてのprompt tokenのK/Vを一括計算し、キャッシュへ保存
│
└─→ Decode段階(tokenごとに生成、メモリ帯域律速)
└─ 各ステップで新しいtokenのQ/K/Vだけを計算し、Qですべてのキャッシュ済みK/Vを検索
│
└─→ [第1層] 単一リクエスト内のKV Cache
(推論エンジンが自動処理し、APIユーザーには見えない)
│
└─→ リクエスト終了 → 通常はKV Cacheを破棄
│
└─→ [第2層] リクエストをまたぐPrompt Caching
(API提供者がKV Cacheを保持し、後続で再利用)
│
├─ Anthropic: 明示的制御、cache write 1.25倍、read 0.1倍
├─ OpenAI: 完全自動、最大90% off、ヒット率を制御不可
└─ Google: 暗黙 + 明示、90% off、explicitはストレージ料金あり
│
└─→ AI Application Engineerのコスト最適化
├─ Prompt構造:安定したprefix + 動的なsuffix
├─ リクエストパターン:場面に合わせて戦略を選択
├─ Token経済性:処理全体のコストを計算
├─ アーキテクチャ:routing + compaction + monitoring
└─ 基盤の直感:割引の背後にあるリソース制約を理解