大規模言語モデルの推論エンジンはどう動くのか:1つのTokenからAgentキャッシュまで
1回のForward Passが生み出すのは、わずか1つのTokenにすぎない。この物理的事実を軸に、Prefill、Decode、KV Cache、Continuous Batching、PagedAttention、そしてAgentがなぜPrefix Cachingに強く依存するのかを読み解く。
大半の開発者の日常的な直感において、大規模言語モデル(LLM)の呼び出しはシンプルなブラックボックスのネットワークリクエストにすぎない。プロンプト(Prompt)を送信すると、クラウド上のどこかにあるGPUがモデルを実行し、テキストが1行ずつストリーミングで返ってくる。しかし、視点をサーバーラックの内部へと移してみると、現実の物理世界はまったく異なる様相を呈している。高価なGPUのVRAM(ビデオメモリ)は常にパンクの危機に瀕しており、計算コアは入力不足によってアイドリングを強いられることもあり、低レベルのシステムはCPUとGPUの間でタスクのバトンリレーを絶え間なく調整し続けなければならない。
Together AIのエンジニアであるZain Hasan氏は、Open Source AI Summitにおいて、推論エンジンの基礎的なメカニズムに関する技術的な解説を行った。このプレゼンテーションはブラックボックス化されたあらゆるラッパーを剥ぎ取り、LLM推論の物理的本質を完全に再現してみせた。すなわち、推論エンジンとは単なる数学的演算器ではなく、3万ドルのGPUの計算能力を極限まで搾り取るために設計された「連続飽和マシン」であるということだ。
素朴な幻想と物理的真実:モデルは1回に1つの整数しか出力しない
原稿の3ページ目には、LLMの呼び出しに対する人々の素朴な認識が描かれている。ユーザーが質問を投げかけると、リクエストはどこかにあるGPUまたはモデルへと送られ、そのまま回答が直接返ってくるというものだ。

この図は、トークナイズ、スケジューリング、KVキャッシュのVRAM管理、そしてTokenごとの自己回帰ループをあえて省略しており、この後に真のブラックボックスを解体するための布石となっている。このようなインタラクションは、LLMがコンパイラによるコード処理のように、構文全体を解析したうえで文章全体を一気に吐き出しているかのような誤解を極めて生みやすい。
しかし、GPUはアルファベットや漢字などの文字を一切目にしたことがない。リクエストがGPUに届く前に、CPU上で動作するトークナイザー(Tokenizer)がテキストを細かく分割し、コンピュータが認識可能な整数のToken IDへとマッピングしなければならない。

原稿のサンプル語彙集(ボキャブラリ)には10万個以上のエントリが含まれており、大まかな換算基準として「1つのTokenはおよそ4分の3個の英単語に相当する」と示されている。モデルに入力されるのは文ではなく、純粋な整数のテンソル列である。
GPUに入った後のLLMの生成ルールは、一言で要約できる。すなわち、「1回のForward Pass(前向き計算)で、予測されるTokenは1つだけ」ということだ。

モデルは現在の入力シーケンスに基づいて次に最も可能性の高い整数を計算し、それをシーケンスの末尾に追加する。そして、その新たなシーケンス全体を次回の計算の入力として、再びForward Passを実行する。このシングルステップのループは、モデルが終了記号(Stop Token)を出力するか、最大長制限に達するまで繰り返される。500単語の英語の回答を1つ書き上げるために、モデルは基盤ハードウェア上で約700回の完全なForward Passを連続して実行しなければならない。
推論の二重人格:PrefillとDecodeの根本的な乖離
これら700回のループを駆動するのは同一のモデルの重み(Weight)であるが、推論ライフサイクル全体は物理的特性が正反対の2つのフェーズによって分断されている。

第1のフェーズは事前充填(Prefill)である。このとき、GPUはユーザーから渡されたプロンプト全体を一度に読み込み、すべての入力位置のアテンション・テンソルを完全に並列計算できる。行列乗算の規模は極めて大きく、GPUが備える数万個の計算コアを容易にフル稼働させることができる。このフェーズは純粋なコンピュートバウンド(Compute-bound:計算律速)のタスクであり、ここで消費される時間が、ユーザーが最初の文字が表示されるまで待つ遅延(Time To First Token:TTFT)を直接左右する。
第2のフェーズは自己回帰デコード(Decode)である。ひとたび生成段階に入ると、モデルは1ステップずつのシリアル処理へと退化せざるを得ない。Forward Passを1回行うごとに、出力されるTokenは1つだけである。各ステップにおいて、ハードウェアはそれまでのコンテキストキャッシュ全体を読み出したうえで、この1つの新しいTokenを計算しなければならない。1ステップあたりの並列計算量は極めて少ないのに対し、メモリの読み出し量は非常に大きい。このときボトルネックはメモリ帯域律速(Memory-bound)へと移行し、これが1秒間に何個のTokenを生成できるか(Tokens Per Second:TPS)を直接規定する。
この計算特性こそが、この後に登場するあらゆる推論エンジンのスケジューリング設計を理解するための基礎となる。
VRAMキラー「KV Cache」:空間で二次関数の再計算を置き換える
Decodeフェーズにおいて、自己注意機構(Self-Attention)は新しいTokenとすべての過去のTokenとの間の相関を計算する必要がある。もしキャッシュが一切存在しなければ、1ステップ生成を進めるたびに、ハードウェアは第1のTokenからシーケンス全体のすべてのKey行列およびValue行列を最初から再計算しなければならない。その計算量はテキスト長に伴って、受け入れがたい二次関数的な爆発的増加を示すことになる。
このような膨大な無駄骨を避けるため、推論エンジンはKV Cacheを導入した。すべての過去のTokenのKeyテンソルおよびValueテンソルを計算後にGPUのVRAMに保存しておき、以後の各ステップでは新たに追加されたTokenの1列分の新規ベクトルのみを計算し、過去の列はVRAMから直接読み出すようにするのである。

原稿の図示によると、KV Cacheは各ステップにおける重複したK/V計算を「新しいTokenの1列分のみを計算し、過去の列はキャッシュから読み出す」形へと変換する。読み出し量はコンテキストが長くなるにつれて増加し続け、さらにこの「空間で時間を買う」設計は、KV Cacheを同時接続ユーザー数やコンテキスト長とともに膨張させる。そのため、このプレゼンテーションでは、単に計算量を見るのではなくVRAMの容量こそを推論バッチの日常的な上限と見なしている。
推論エンジンの3層パイプラインアーキテクチャ
原稿の8ページ目では、実際の推論サービスが同時に直面しなければならない4つの核心的制約が指摘されている。

- ハードウェアコストが高額:GPU1枚のコストは約3万ドルであり、アイドル(空転)状態にしておくべきではない。
- 並行ステータスが不揃い:システムは同時に数百人のユーザーを処理し、各人がリクエストの異なるフェーズにいる。
- 1ステップあたりの産出量が極めて低い:各ユーザーは1回のForward Passにおいて1つのTokenしか生成しない。
- VRAMボトルネックが先行する:計算能力よりも先に枯渇するのは、往々にしてVRAM容量である。
これら4つの物理的制約は、その後のシステムエンジニアリングを理解するための因果の要となる。高価なGPUを極力空転させず、計算力よりも先にVRAMが枯渇するという現実のもとで異なるフェーズにあるユーザーリクエストを協調動作させるために、推論エンジンにはCPUスケジューラ、連続バッチ処理(Continuous Batching)、そしてページ式KV VRAM(PagedAttention)が必要となるのだ。
したがって、最新の推論エンジンの本質は、GPUを中心に構築された「連続供給パイプライン」である。システム全体は明確に3つの層に分割されており、モデルの重みが存在するのは最後の層だけである。

APIサーバー(CPU上で動作):HTTPの入出力を処理し、プロンプトを整数のTokenシーケンスに分割し、出力側ではGPUが算出した整数IDを可読な文字へとデトークナイズ(逆トークン化)してクライアントへストリーミング返却する役割を担う。
スケジューラ(CPU上で動作):エンジン全体の頭脳である。各実行ステップ(Pass)が開始される前に、現在のバッチにどのリクエストを含めるか、各リクエストで何個のTokenを処理するか、そしてどのKV VRAMブロックを使用するかを決定する。
実行ワーカーノード(GPU上で動作):GPU1枚ごとに独立したワーカーが対応し、高強度のテンソル並列計算に専念する。スケジューラからパッケージ化されたバッチを受け取り、1回のForward Passを実行し、語彙空間でサンプリングを完了させ、選択されたToken IDをCPUへと返す。
これら3者は時間軸上でオーバーラップしながら進行する。GPUが第NステップのForward Passを全速力で実行している間に、CPU上のAPIプロセスは第N-1ステップで生成されたTokenのデトークナイズを並行して実行し、ネットワーク経由で送信している。
GPUが最終的な結果を吐き出す瞬間、低レベルでは数十万次元から単一の整数への確率の収縮(コラプス)が行われている。

元のプレゼンテーションでは具体的なサンプリングの事例が示されている。GPUの最終線形層は、10万個以上の語彙エントリのそれぞれに対して未加工のスコア(Logit)を出力する。温度係数(Temperature)、Top-p、Top-k、および反復ペナルティなどのルールによって確率分布を再成形した後、GPUは現在の候補セット(例えば語彙内でスコアがそれぞれ0.52の「mat」、0.21の「rug」、0.14の「floor」など)の中からランダムサンプリングを行い、離散IDである4352を選択し、CPUに引き渡してテキストへと変換したうえで出力する。
ハードウェアを搾り尽くす3大古典的スケジューリングの柱
理想的な状態において、スケジューラは数百人のユーザーリクエストを同時に並行処理したいと考える。しかし現実には、ユーザーごとに入力長は異なり、生成の進み具合もまちまちである。従来のバッチ処理の考え方を適用してしまうと、ハードウェアの利用率はたちまち崩壊してしまう。GPUの飽和稼働を維持するために、最新の推論エンジンは3つのコアメカニズムを確立した。
Continuous Batching(連続バッチ処理)
初期の静的バッチ処理モデルでは、システムは複数のリクエストを1つのバッチとしてまとめてGPUに投入していた。リクエストによって生成される長さの差異が極めて大きいため、短いリクエストは10ステップ目で出力が完了するかもしれない一方で、長いリクエストは500ステップを要する場合がある。残りの490ステップの間、すでに終了した計算スロットは空席のまま待機を強いられ、バッチ内で最も遅いリクエストが終了して初めてバッチ全体が入れ替えられることになっていた。

Continuous Batchingはスケジューリングの粒度を変更し、生成ステップごとに現在のバッチを再確認して再構成する。あるリクエストが終了記号を出力してVRAMスロットから抜けると、スケジューラは次のForward Passステップですぐに空いたスロットへ新しいリクエストを投入する。すべてのスロットが常にフル稼働状態に保たれ、受動的な待機によって生じる計算の空泡(コンピュートバブル)が完全に解消される。
PagedAttention(ページ式アテンション)
Continuous Batchingが計算スロットの浪費を減らしたとすれば、PagedAttentionはオンデマンドのブロック分割によってVRAMの事前確保や断片化による浪費を削減する。
かつては、リクエストが到達しうる最大長(例えば4Kや8Kなど)に合わせて、ユーザーごとに連続した物理VRAM空間をあらかじめ割り当てておく必要があった。元のプレゼンテーションが指摘するように、この静的確保メカニズムのもとでは、わずか3つの長いリクエストだけでグラフィックカード全体のVRAM空間が完全にロックされてしまう。それらのリクエストがその時点で数文字しか生成しておらず、確保されたVRAMの大部分が空っぽであったとしても、4つ目のリクエストはキューの外で順番待ちを強いられるしかなかった。

PagedAttentionは、現代のオペレーティングシステムの仮想メモリページングの設計思想を導入した。すなわち、KV Cacheを固定サイズの物理ブロック(Block)に分割し、必要に応じてオンデマンドで確保し、動的にマウントするのである。各物理ブロックはVRAM内で物理的に連続している必要はまったくなく、仮想マッピングテーブルを通じて論理的にアドレッシングされる。共有された動的な物理ブロックプールを通じて、本来なら3つのリクエストしか収容できなかったVRAM空間で4つのリクエストを実行できるようになり、それでもなおVRAMプールの半分は空き領域として利用可能な状態を保てる。原稿が総括しているように、KVキャッシュの容量は、通常、計算能力よりも先にバッチ処理規模を制限する要因となる。
Chunked Prefill(チャンク化されたPrefill)
システム内に「テキストを生成中の短いリクエスト」と「送信されたばかりの極めて長いプロンプト」が同時に存在するとき、もう一つの衝突が発生する。
もしスケジューラが非常に長いプロンプトに1回のForward Passステップ全体を丸ごと独占することを許してしまうと、デコード段階にある他のアクティブユーザーはそのステップで新しいデコードTokenを受け取ることができなくなってしまう。

Chunked Prefill(チャンク化Prefill)は、1ステップあたりの計算予算を設定する(原稿の例ではステップあたり2K Token)。8Kの長いプロンプトに直面した際、エンジンはそれを一度にすべて計算するのではなく、4つの連続する2Kのスライス(0〜2047、2048〜4095、4096〜6143、6144〜8191)へと分割し、4回のForward Passに分散させて段階的に処理する。各2Kのスライスを実行すると同時に、スケジューラは既存のアクティブユーザーの単一のDecode Tokenを混合してパッケージ化する。これにより、超長文プロンプトのPrefillを着実に進行させつつ、アクティブユーザーが毎ステップ途切れることなくDecode Tokenを受け取り続けられるように保証する。
Agent時代の奔流:膨張する三角形とPrefix Caching
LLMのユースケースが通常の対話から自律型Agentへと移行するにつれて、低レベルにおけるトラフィックパターンは劇的な変化を遂げた。
典型的なAgentの実行ループでは、人間が目標を提示すると、モデルはまず思考してツール呼び出しコマンドを出力する。外部システム(検索API、Pythonインタプリタ、内部APIなど)がそのツールを実行し、長大な実行結果を対話ログに追加する。続いてモデルは再びすべての履歴を読み込み、次のアクションを決定する。このプロセスが数十回にわたって繰り返される。
推論エンジンから見れば、Agentの各イテレーションは増分更新ではなく、すべての履歴ログを再送信するまったく新しいリクエストである。

エンジンが低レベルで直面するのは、奇妙な形をした「膨張する三角形」である。入力プロンプトはターンごとに激しく膨張していく(システムプロンプト+数十個のツール定義記述+長大化する対話履歴トレース)のに対し、モデルが1回に出力する内容は極めて短い(多くの場合、ツール呼び出しのためのわずか数十TokenのJSONにすぎない)。
もしキャッシュのない従来のロジックに従うなら、Agentのループが回るたびに、過去数十ターンで生成されたすべてのテキストをまったく新しいプロンプトとして扱い、最初から完全なPrefill計算を実行し直さなければならない。
このオーバーヘッドを定量化するため、原稿の17ページ目にはPrefix Caching(プレフィックスキャッシュ)の効果を示す図解計算モデルが提示されている。

読者にあらかじめ注意を促しておかなければならないのは、このスライドのタイトルには「across a 100-turn loop」と記載されているものの、図の横軸の終点は明確に「TURN 10」と記されている点である。図中の「55」と「10」という数値は、実際には図に示された10ターンにおいて、各ターンで追加される末尾が等長(1図解単位)であると仮定したモデルに対応している。キャッシュがない場合、10ターンの累積は 1 + 2 + … + 10 = 55 計算単位となり、キャッシュがある場合は10ターンそれぞれで追加された末尾1回分のみを処理するため、計10単位となる。したがって、55と10はあくまで図中の10ターンかつ各ターンの追加末尾が等長であるという前提から推計された図解上の単位にすぎず、100ターンの実測値や総量ではない。Prefix Cachingがない場合、各ターンで前のターンの全履歴を重複して処理し続けることになる。
インタラクティブ · 図解モデル
ターン数をスライドして、重複Prefillの蓄積を確認
各ターンのAgentが等長(1単位)の新しい末尾のみを追加すると仮定します。キャッシュなしの場合、ターンごとに履歴全体を再計算します。Prefix Cachingにヒットした場合、新規追加された末尾のみを処理します。
Prefix Caching なし
55 Prefill単位
1 + 2 + … + 10 = 55
Prefix Caching あり
10 Prefill単位
10 × 1 = 10
この図解モデルでは、10ターン時点でキャッシュなしの場合に 45 単位多く処理されます。
基準の説明:原稿の該当スライドのタイトルには「100-turn」と記載されていますが、図の軸および55/10の数学的関係は10ターンに対応しています。本インタラクティブは図中の10ターンに基づく図解の数式に沿って表示しており、実測の速度、コスト、キャッシュヒット率を示すものではありません。
この悪循環を打ち破る武器が、プレフィックスキャッシュ(Prefix Caching)である。

低レベルのVRAMはすでにPagedAttentionによってページ分割して格納されているため、エンジンは計算済みの各物理ブロックの内容に対してハッシュ値を算出できる。同一ユーザーによる複数ターンのAgentのイテレーションであれ、異なるユーザー間で共有される同一のシステムプロンプトやツール定義であれ、プレフィックスのテキスト内容が一致していれば、新しいリクエストはマッピングテーブルを通じてVRAM内にすでに存在する物理ブロック0〜3を直接指し示す。原稿では、この機能は低レベルで完全に透過的であり、ユーザーがAPIを呼び出す際に特別なフラグを立てる必要は一切ないと明確に強調されている。
Prefix Cachingの導入後、各ターンのループではそのターンで新たに追加されたごく短い末尾に対してのみPrefillを実行すればよく、過去の長大なテキストはすべてキャッシュにヒットする。図中の10ターン、各ターンの新規追加末尾が等長であるとした図解単位に従えば、10ターンのPrefill総計算量は10単位となり、キャッシュなしの55単位と比べて大幅に削減される。さらに原稿は、各ターンの最初のTokenの到着もより高速になると指摘している。
トラフィック特性とシステム制御の低レベルマッピング
読者がAgentのアプリケーション層の振る舞いと推論エンジンのエンジニアリング手法との間の対応関係を完全に把握できるよう、原稿の18ページ目では一般的なAgentのトラフィック特性とシステムの対応メカニズムが明快なマッピング体系として整理されている。

ターン間およびユーザー間での高度に共有されたプレフィックス:Agentの重厚なシステムプロンプト(ペルソナ)、数十個のツール定義、初期の対話履歴は複数回の呼び出しにおいて完全に一致するため、エンジンはPrefix Cachingによってブロック単位の再利用を実現し、重複計算を排除する。
並行ツール呼び出しとサブAgentのバーストパルス:Agentが複数のサブタスクを並行して生成したり、複数の検索リクエストを同時に発行したりすると、瞬間的な並行数が急増する。エンジンはContinuous Batchingによって空いたバッチスロットを動的に埋める。
極めて長い入力コンテキストと極めて短い生成出力:数万Tokenを入力しながらツール呼び出しのための数十Tokenしか出力しないという極端に非対称な負荷に対し、エンジンはChunked Prefillによって長い入力を細分化する。さらに物理クラスタレベルにおいて、大規模な計算スループットを専門に担うPrefillノードと、広帯域なVRAMからのストリーミング読み出しを専門に担うDecodeノードを分離・疎結合化する。
ツール実行期間中の長時間のアイドル(沈黙):Agentがコード実行や外部検索を呼び出す際、数秒から数分間の待ち時間が発生することがあり、その間、対応する対話チャネルでは生成動作が一切行われない。そのKV Cacheを高価なGPUのVRAM内にロックしたまま放置すれば、システムのキャパシティはたちまち枯渇してしまう。エンジンはKV Cacheのスワップアウト(Offloading)機構により、待機中の沈黙期間にはこのデータを比較的安価で大容量なCPUメモリへと一時退避させ、ツールが結果を返して次のターンのインタラクションがトリガーされた際に迅速にスワップイン(書き戻し)して復元する。
まとめ:システムの常識に立ち返る4つの基本認識
シングルカードのハードウェアから分散推論クラスタに至るまで、複雑な最適化技術は最終的に4つの素朴な物理的事実へと収束する。
第1に、1回のForward Passで出力されるのは1つのTokenのみである。モデルが受け取る計算表現は生のテキストではなくToken IDであり、物理的な低レベルで実行されているのは語彙空間における確率計算とシングルステップのサンプリングである。
第2に、PrefillとDecodeは性質がまったく異なる2種類の計算ワークロードである。前者は計算コアのボトルネック(Compute-bound)に制約され、最初の文字の待ち時間を決定する。後者はVRAM帯域のボトルネック(Memory-bound)に制約され、1秒あたりの出力速度を決定する。あらゆるスループット最適化の第一歩は、現在直面している物理的ボトルネックを見極めることにある。
第3に、推論エンジンとはハードウェアを限界まで飽和稼働させるマシンである。Continuous Batchingは動的な穴埋めによって各計算ステップの空泡(バブル)を埋め尽くし、PagedAttentionは仮想メモリのメカニズムによってVRAM事前確保の浪費を打破する。システムエンジニアリングのすべての目標は、高価なコアを決して停止させないことにある。
第4に、Agentの実行効率はキャッシュ戦略に深く依存している。極めて長い入力と複数回の再送が行われるマルチターンループにおいて、プロンプトのプレフィックス構造の決定性と安定性を維持することこそ、アプリケーション層の開発者が低レベルの推論エンジンに提供できる最大のエンジニアリング上の思いやりである。
