🤖

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チャットボットインターフェース 文書要約/翻訳のリアルタイム表示