AI Streaming

AIストリーミング

AI Streaming を分かりやすく

AI Streaming(ストリーミング)は、AI の応答を少しずつリアルタイムで受け取る技術です。

例え話をしましょう。あなたが友人に長い質問をして、その答えを聞くとします。

Streaming なし(従来の方法)

  • あなた「昨日の旅行はどうだった?詳しく教えて」
  • 友人「...(5分間沈黙)...」
  • 友人「はい、では話します。朝6時に起きて、7時に出発して...(一気に全部話す)」
  • あなたは5分間ずっと待たされる

Streaming あり

  • あなた「昨日の旅行はどうだった?詳しく教えて」
  • 友人「朝6時に起きて...」(話しながら考える)
  • 友人「7時に出発して...」(続けて話す)
  • 友人「最初に向かったのは...」(途切れることなく話す)
  • あなたはすぐに応答が始まり、待ち時間を感じない

AI Streaming も同じです。AI が全ての文章を生成し終わるまで待つのではなく、生成された部分から順次表示することで、ユーザーは即座にフィードバックを得られます。

なぜ Streaming が重要なのか

AI モデル、特に大規模言語モデル(LLM)は、長い応答を生成するのに数秒から数十秒かかることがあります。Streaming を使わない場合、ユーザーは真っ白な画面を見つめて待つことになります。

Streaming なしの問題点

ユーザー: 「Pythonの基礎を教えて」(送信)
          ↓
       (30秒待機...)
          ↓
AI: 「Pythonは...(一気に2000文字の応答)」

この30秒間、ユーザーは以下のように感じます。

  • 「本当に処理されているのか?」
  • 「フリーズしたのでは?」
  • 「もう一度送信すべきか?」

Streaming ありの改善

ユーザー: 「Pythonの基礎を教えて」(送信)
          ↓(0.5秒)
AI: 「Python」
          ↓(0.1秒)
AI: 「Pythonは」
          ↓(0.1秒)
AI: 「Pythonはプログラミング言語で...」

このように、即座に応答が始まることで、ユーザーは安心感を得られます。

Streaming の仕組み

AI Streaming は、Server-Sent Events(SSE)や WebSocket などの技術を使って実現されます。

基本的な流れ

  1. クライアントが AI にリクエストを送信
  2. サーバーが AI モデルにプロンプトを送る
  3. AI モデルが1トークンずつ生成
  4. 生成されたトークンを即座にクライアントに送信
  5. クライアントが受け取ったトークンを画面に追加表示
  6. 生成完了まで 3〜5 を繰り返し

Streaming の実装パターン

Streaming を実装するには、いくつかのアプローチがあります。

パターン 1: Fetch API と ReadableStream

async function streamAIResponse(prompt: string) {
  const response = await fetch('/api/chat', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ prompt }),
  })

  const reader = response.body?.getReader()
  const decoder = new TextDecoder()

  while (true) {
    const { done, value } = await reader.read()
    if (done) break

    const chunk = decoder.decode(value)
    // 画面に表示
    displayChunk(chunk)
  }
}

このパターンでは、ReadableStream を使ってサーバーからのデータを少しずつ読み取ります。

パターン 2: EventSource(Server-Sent Events)

function streamWithSSE(prompt: string) {
  const eventSource = new EventSource(
    `/api/chat?prompt=${encodeURIComponent(prompt)}`
  )

  eventSource.onmessage = (event) => {
    const chunk = event.data
    displayChunk(chunk)
  }

  eventSource.onerror = () => {
    eventSource.close()
  }
}

EventSource は、サーバーからクライアントへの一方向の通信に特化した API です。実装がシンプルで、自動的に再接続も行われます。

パターン 3: OpenAI SDK の Streaming

import OpenAI from 'openai'

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
})

async function streamChatCompletion(messages: any[]) {
  const stream = await openai.chat.completions.create({
    model: 'gpt-4',
    messages,
    stream: true, // Streaming を有効化
  })

  for await (const chunk of stream) {
    const content = chunk.choices[0]?.delta?.content || ''
    process.stdout.write(content)
  }
}

OpenAI の公式 SDK は、Streaming を簡単に実装できる API を提供しています。stream: true を指定するだけで、非同期イテレータとして応答を受け取れます。

Streaming のユースケース

AI Streaming は、様々な場面で活用されています。

チャットボット

ChatGPT のような対話型 AI では、Streaming が標準です。ユーザーの質問に対して、即座に応答が始まることで、自然な会話のリズムが生まれます。

長文生成

ブログ記事、レポート、コードなど、長いテキストを生成する場合、Streaming なしでは数十秒待つことになります。Streaming により、生成中の内容を確認しながら待てます。

リアルタイムの文章校正

ユーザーが文章を入力すると同時に、AI が校正結果をストリーミングで返すことで、タイピング中にリアルタイムでフィードバックが得られます。

コード補完

GitHub Copilot のようなコード補完ツールでは、ユーザーがコードを書いている最中に、AI が次の行を予測してストリーミングで提案します。

Streaming の課題と対策

Streaming にはいくつかの技術的な課題があります。

課題 1: エラーハンドリング

Streaming の途中でエラーが発生した場合、すでに表示されている部分をどう扱うかが問題になります。

対策

  • エラーが発生したことを明確に表示
  • 部分的な応答も保存しておき、再試行時に活用
  • タイムアウトを設定して、無限に待たせない

課題 2: ネットワークの不安定性

モバイルネットワークなど、不安定な環境では接続が切れることがあります。

対策

  • 自動再接続の実装
  • 受信済みのトークンを保持
  • プログレスインジケーターで状況を可視化

課題 3: トークンの分割

日本語などのマルチバイト文字は、トークンの途中で分割されると文字化けする可能性があります。

対策

  • UTF-8 のデコードを適切に処理
  • TextDecoder で stream: true オプションを使用
  • バッファリングで完全な文字単位で処理

Streaming のパフォーマンス最適化

Streaming を効率的に実装するためのベストプラクティスです。

最適化 1: バッファリング

トークンを1つずつ送るのではなく、数トークンごとにまとめて送信することで、ネットワークのオーバーヘッドを削減できます。

let buffer = ''
const BUFFER_SIZE = 5 // 5トークンごとに送信

for await (const token of tokens) {
  buffer += token

  if (buffer.length >= BUFFER_SIZE) {
    sendToClient(buffer)
    buffer = ''
  }
}

// 残りを送信
if (buffer.length > 0) {
  sendToClient(buffer)
}

最適化 2: デバウンス

クライアント側で、受信したトークンを即座に DOM に反映するのではなく、少し遅延させることで、再レンダリングの回数を減らせます。

最適化 3: キャンセル処理

ユーザーが応答の途中で「停止」ボタンを押した場合、Streaming を適切に中断できるようにします。

const controller = new AbortController()

fetch('/api/chat', {
  signal: controller.signal,
  // ...
})

// ユーザーが停止ボタンを押したら
stopButton.onclick = () => {
  controller.abort()
}

まとめ

AI Streaming は、ユーザー体験を大幅に向上させる重要な技術です。待ち時間を感じさせないインタラクティブな対話を実現し、AI アプリケーションの実用性を高めます。

重要なポイント

  1. 即時フィードバック - ユーザーは即座に応答が始まることを確認できる
  2. 自然な対話 - 人間同士の会話のように、途切れることなく応答が続く
  3. 長文対応 - 長い応答でも、最初から読み始められるので待ち時間が短く感じる
  4. 技術選択 - ReadableStream、SSE、WebSocket など、用途に応じて適切な技術を選ぶ
  5. エラー対策 - ネットワークエラーや中断に適切に対処する

Streaming を使うべき場面

  • チャットボットや対話型 AI
  • 長文の生成タスク(記事、レポート、コードなど)
  • リアルタイムの文章校正や翻訳
  • コード補完やサジェスト機能

Streaming が不要な場面

  • 短い応答(数文字〜数十文字)
  • バッチ処理(ユーザーが待つことを想定している)
  • 応答全体を見てから処理が必要な場合

AI Streaming を適切に実装することで、ユーザーは待ち時間にストレスを感じることなく、AI との対話を楽しめるようになります。現代の AI アプリケーションでは、もはや必須の技術と言えるでしょう。