トークンストリーミング(トークンストリーミング)
LLMが生成するトークンを1つずつまたは小さなチャンクごとにクライアントへ逐次送信する技術。autoregressive生成の性質を利用し、完全な応答を待たずにリアルタイムでテキストを表示する。
トークンストリーミングとは
トークンストリーミングは、LLMのautoregressive生成プロセスで産出されるトークンを、生成されるたびにクライアントへ即座に送信する出力方式です。GPT-4oが1,000トークンの回答を生成する場合、非ストリーミングでは全トークン生成完了まで約3〜8秒の待ち時間が発生しますが、トークンストリーミングでは最初のトークンが200〜400msで表示され始めるため、ユーザー体験が劇的に向上します。
トークン生成の流れ
Autoregressive デコーディングの各ステップ
LLMのテキスト生成は以下のサイクルを繰り返します。
| ステップ | 処理内容 | 所要時間(GPU依存) |
|---|---|---|
| 1. Forward Pass | モデル全層を通して次トークンの確率分布を計算 | 5〜30ms |
| 2. サンプリング | Temperature/Top-p/Top-kに基づいてトークンを選択 | < 1ms |
| 3. KVキャッシュ更新 | 選択されたトークンのKey/Valueを追加 | < 1ms |
| 4. トークン送信 | 選択されたトークンをクライアントへSSEで送信 | < 1ms |
| 5. 次ステップ | 新トークンを入力に追加してステップ1へ | - |
1トークンあたりの生成時間(ITL: Inter-Token Latency)はモデルサイズとハードウェアに依存します。
モデルサイズ別のITL目安
| モデル | パラメータ数 | ITL(A100 80GB) | ITL(RTX 4090) |
|---|---|---|---|
| Llama 3.1 8B | 8B | 8〜12ms | 15〜25ms |
| Llama 3.1 70B | 70B | 25〜40ms | 非対応 |
| GPT-4o | 非公開 | 15〜25ms(推定) | - |
| Claude 3.5 Sonnet | 非公開 | 20〜35ms(推定) | - |
| Gemini 1.5 Pro | 非公開 | 15〜30ms(推定) | - |
チャンキング戦略
トークンストリーミングの実装では、1トークンずつ送信する「純粋ストリーミング」と、複数トークンをまとめて送信する「チャンクストリーミング」があります。
1トークン送信
最小レイテンシーを実現しますが、SSEイベント数が多くなりネットワークオーバーヘッドが増加します。日本語では1文字が複数トークンに分割されるケースがあり、文字の途中で分断されると文字化けのような表示になる場合があります。
Nトークンチャンク送信
4〜8トークンをバッファリングしてまとめて送信します。ネットワーク効率が向上し、日本語の文字分断問題も軽減されます。ただし、チャンクが溜まるまでの追加レイテンシーが発生するため、ITL × Nの遅延が加わります。
単語/文境界チャンク
トークンではなく単語や文の区切りで送信する方式です。自然な表示が得られますが、長い単語や文の生成中はバッファが溜まり続けるため、体感レイテンシーが不安定になります。
フロントエンド実装のベストプラクティス
スムーズなテキスト表示
トークンが不規則な間隔で到着する場合、テキストがカクカクと表示されることがあります。到着したトークンをキューに溜め、一定間隔(16ms = 60fps)でキューからテキストを描画するアニメーションバッファリング技法が効果的です。
自動スクロール
ストリーミング中にテキストが画面外に伸びる場合、自動スクロールが必要です。ただし、ユーザーが上方向にスクロールして過去のテキストを読んでいる場合は自動スクロールを一時停止し、ユーザーが最下部に戻ったら再開する実装が望ましいです。
Markdownレンダリング
LLMの出力がMarkdownの場合、ストリーミング中のパーシャルMarkdownをリアルタイムレンダリングする必要があります。不完全なMarkdown(閉じていないコードブロックや表など)の処理が課題で、部分的なMarkdownを安全にレンダリングするライブラリ(例: marked のストリーミングモード)を使用します。
推論サーバーの対応状況
| 推論サーバー | ストリーミング方式 | 対応プロトコル | Continuous Batching |
|---|---|---|---|
| vLLM | トークン単位 | SSE, gRPC | あり |
| TGI (Text Generation Inference) | トークン単位 | SSE | あり |
| NVIDIA Triton | チャンク単位 | gRPC, HTTP | あり |
| llama.cpp server | トークン単位 | SSE | 限定的 |
| Ollama | トークン単位 | NDJSON | なし |
| LM Studio | トークン単位 | SSE (OpenAI互換) | なし |
FAQ
Q1: トークンストリーミングで生成を途中で止められる?
A1: はい。クライアント側でSSEコネクションを切断(abort)すれば生成が停止します。多くの推論サーバーはコネクション切断を検知して生成処理を中止し、GPUリソースを解放します。ただし、APIプロバイダによっては切断時点までの出力トークン料金が発生します。
Q2: 日本語でトークンが文字途中で分割される問題は?
A2: BPEトークナイザーでは漢字やカタカナが複数トークンに分割されることがあり、UTF-8バイト列の途中で区切られると文字化けします。対策として、受信バッファにUTF-8デコーダーを配置し、不完全なバイト列は次のチャンク到着まで保持する実装が一般的です。
Q3: ストリーミング中にサーバーがダウンしたらどうなる?
A3: SSEコネクションが切断され、クライアントには途中までのテキストのみが残ります。堅牢な実装では、受信済みテキストを保存し、再接続後にプレフィックスとして送信して続きから生成させるリカバリー戦略を採用します。