🤖
ChatGPTストリーミングはどう動くのか?
SSEによるトークン単位のリアルタイム応答配信
OpenAIのChat Completions APIにstream: trueを設定すると、レスポンスが一度に返されずSSE(Server-Sent Events)形式でトークンが生成されるたびに送信されます。クライアントはEventSourceやfetch + ReadableStreamでストリームを受信し、各chunkはdata: {"choices":[{"delta":{"content":"あ"}}]}形式です。最後にdata: [DONE]が来るとストリーム終了。ユーザーはLLMが生成中にリアルタイムで結果を確認できます。
構造ダイアグラム
🌐
ChatGPT UI
fetch + ReadableStream
① POST stream:true
→
text/event-stream
←
data: {"delta":{"content":"안"}}
←
data: {"delta":{"content":"녕"}}
←
data: {"delta":{"content":"하"}}
←
data: {"delta":{"content":"세"}}
←
data: [DONE]
🧠
OpenAI API
LLMトークン生成
トークンを1つずつ生成 → 即時送信
画面に表示される様子:
안녕하세
ポイント: LLMが全回答を完成するまで待たず、<strong>トークン生成即座にSSEで送信</strong>
なぜSSEか?(WebSocketではない理由)
- リクエストは1回(POST)、レスポンスのみストリーミング → 単方向で十分
- HTTPベースでCDN/プロキシ互換性が良い
- 接続切断時は新しいリクエストでリトライ(stateless)
- WebSocketに比べてサーバー実装がシンプル
動作フロー
1
クライアントがPOST /v1/chat/completionsにstream: trueでリクエスト
2
サーバーがContent-Type: text/event-streamで応答開始
3
LLMがトークン生成 → data: {"delta":{"content":"あ"}} 即座に送信
4
クライアントが各chunkを受け取りUIにappend
5
全トークン生成完了 → data: [DONE] 送信
6
クライアントがストリーム終了処理
メリット
- ✓ 体感応答速度が大幅に向上(TTFT最小化)
- ✓ LLMの全生成完了を待つ必要なし
- ✓ HTTPベースなので実装がシンプル
- ✓ 途中キャンセル可能(AbortController)
デメリット
- ✗ トークン単位の処理ロジックが必要
- ✗ エラー処理が複雑(途中切断)
- ✗ 総トークン数を事前に把握できない
- ✗ クライアントのバッファリング管理が必要
ユースケース
ChatGPT / Claude Web UI
AIコーディングアシスタント
AIチャットボットインターフェース
文書要約/翻訳のリアルタイム表示