簡潔な答え
3つの文:
- ストリーミングと非ストリーミングの違いは、完全にあなた自身のコードで決まります—リクエストボディ内の
streamフィールドです。同じキー、同じモデル、同じエンドポイントでも、前後で切り替わるなら、切り替えているのはクライアントコード(またはそれをラップしている SDK / フレームワーク)です。ゲートウェイが勝手に切り替えることはありません。 - どちらのモードでも最終的な内容は同一で、課金も同じです。 違いは、テキストをいつ受け取るかと、どうパースするかだけです。
- 選び方: 画面を人間が見ている → ストリーミング; プログラムが結果を消費する(JSON のパース、バッチジョブ、ツール呼び出し) → 非ストリーミング。
一目でわかる違い
なぜ私のリクエストはストリーミングと非ストリーミングの間で切り替わるのですか?
これは最もよくある質問で、答えは お客様側の何かがそれを変えています。 以下の一覧を順に確認してください — ほぼ必ずどれかが当てはまります:1. `stream` はコード内の変数または設定値です
1. `stream` はコード内の変数または設定値です
典型的なケースは、
stream=config.get("stream", False) または stream=is_web_request です。異なるエントリポイントから同じ関数に異なる値が渡されるため、ログ上はモードがランダムに切り替わっているように見えます。確認方法: 実際に送信しているリクエストボディを出力し、stream フィールドを確認してください。2. 異なる SDK とフレームワークではデフォルトが異なります
2. 異なる SDK とフレームワークではデフォルトが異なります
同じビジネスロジックでも、クライアントによって挙動が異なります:
- OpenAI SDK
chat.completions.create(): 既定では 非ストリーミング client.chat.completions.stream()またはwith_streaming_response: ストリーミング- LangChain / LlamaIndex のようなラッパー:
invokeとstreamのどちらを呼ぶか、またモデルオブジェクトを生成するときにstreaming=Trueを渡したかどうかによって異なります - デスクトップクライアント、エージェントツール、ワークフロープラットフォームでは、通常、設定に「ストリーミング出力」の切り替えがあり、既定値はさまざまです
3. 複数のアプリケーションで共有されている 1 つのキー
3. 複数のアプリケーションで共有されている 1 つのキー
Web チャット UI(ストリーミング)と夜間のバッチジョブ(非ストリーミング)の両方で同じキーを使うと、一緒に見るとランダムに見えるログが生成されます。確認方法: ユースケースごとに個別の token を作成してください — するとログも分かれて表示されます。token 管理を参照してください。
4. ミドルボックスがストリームを平坦化しました
4. ミドルボックスがストリームを平坦化しました
実際には
stream: true を送信しているのですが、Nginx、社内ゲートウェイ、または何らかのプロキシがレスポンスを バッファリング していました — サーバーはチャンクごとに送り、プロキシがそれを保持して一度に放出したため、非ストリーミングのように見えます。確認方法: 一度プロキシをバイパスしてテストし、Nginx のバッファリングをオフにしてください(proxy_buffering off;)。この場合でも、ゲートウェイが実際にストリーミングで出力しているため、コンソールログには引き続き is_stream = true と表示されます。シナリオ別の選び方
ストリーミングを使う
- チャット UI やサポートボット — ユーザーは即時フィードバックを必要とします
- IDE プラグイン / コーディングアシスタント(Claude Code, Cursor など)
- 長文生成(長い記事、長い翻訳、大きなコードブロック)
- 長時間の推論モデルタスク — 少なくとも進捗が見えます
- 生成の途中でユーザーが「停止」を押せる場所ならどこでも
非ストリーミングを使う
- 構造化出力:
json.loads()のために JSON 全体が必要です - function-calling / tool-call 引数のパース
- バッチ処理、オフラインジョブ、スケジュール済みタスク
- 最終結果だけが重要で、誰も待っていないバックエンドフロー
- 素早い検証、デバッグ、テストケースの作成
統合の手間: 同じタスクを、両方の方法で
- Python 非ストリーミング
- Python ストリーミング
- Node.js ストリーミング
- cURLを並べて比較
Claude のネイティブ形式(
/v1/messages)は別のストリーミングプロトコルを使用します: Anthropic の名前付きイベント SSE(message_start / content_block_delta / message_delta など)であり、OpenAI の一様な data: チャンクではなく、usage は message_start と message_delta のイベントに分割されます。完全なパースガイド: Claude ネイティブ形式: ストリーミングおよび非ストリーミングのレスポンス。課金と使用量: どちらでも同じ
usage をめぐる2つの落とし穴:
- ストリーミングではデフォルトで usage は返されません。 OpenAI互換のエンドポイントでは
stream_options: {"include_usage": true}を渡す必要があります。すると usage は最後のチャンクに届きます(そのchoices配列は空です — インデックスで参照する前に確認してください)。これは APIYI のいくつかのモデルで動作確認済みです。 - API が返す
usageと請求を突き合わせないでください。 特にキャッシュ関連のフィールドです。返された値は、実際に課金された内容と必ずしも一致しません。キャッシュヒットが発生したかどうかは、コンソールログ内の「cache billing details」 で判断されます。 キャッシュ課金の説明 を参照してください。
6つのよくある誤解
1. streaming は timeout を防ぐ
1. streaming は timeout を防ぐ
そうではありません。 streaming は 最初 の token を早く届かせるだけです。全体の生成時間は短くならず、安定したデータの流れも保証されません。推論モデル(
gemini-3.1-pro-preview、gpt-5.6-sol、gpt-5.5-pro など)は、思考フェーズ中にまったく何も出力しないことがあり、その場合でもクライアントの read timeout を同じように発生させます。正しい対処は、シナリオごとに timeout 値を設定することです。詳細は API timeout を回避する方法 をご覧ください。2. streaming のほうが速い
2. streaming のほうが速い
最初のバイトは速くなりますが、合計時間は変わりません。 同じモデルと prompt であれば、streaming と non-streaming の完了時間は概ね同じです。streaming がもたらすのは 体感上の 速さです。ユーザーは 30 秒のスピナーを見つめ続ける代わりに、1 秒以内に動きが見えます。画面を誰も見ていなければ、その価値はゼロです。
3. streaming は安くなる、または受信した分だけ課金される
3. streaming は安くなる、または受信した分だけ課金される
いいえ。 上の「課金と利用」をご覧ください。課金は同一で、途中で切断しても課金されます。
4. すべてのモデルと endpoint が streaming をサポートする
4. すべてのモデルと endpoint が streaming をサポートする
いいえ。 テキストチャットモデルは通常そうですが、画像生成、embedding、rerank の endpoint には streaming の概念がなく、
stream を無視するか拒否します。いくつかのモデルでは、streaming 下で特定の parameter の組み合わせに追加の制約があります。判断に迷う場合は、まず non-streaming で呼び出しを動かし、その後 stream: true を追加してください。5. non-streaming のほうが信頼性が高い
5. non-streaming のほうが信頼性が高い
どちらにも失敗モードがあります。
- non-streaming のリスク: 生成中ずっと接続にデータが流れないため、プロキシ、CDN、社内ゲートウェイが idle timeout で切断することがあります。レスポンス本文が非常に大きい場合(base64 の画像出力は簡単に数十 MB に達します)、終端処理が停止する問題にも遭遇します — 転送は完了したのに戻らないリクエスト と ログ上は完了なのにクライアントには何も届かない をご覧ください。
- streaming のリスク: SSE をサポートしない、またはバッファリングを強制するミドルボックスと相性が悪いことです。クライアント側のパースはより複雑で、微妙な誤りを起こしやすいです。
api-cf.apiyi.com(CDN エンドポイント)には約 100 秒のリクエスト上限があり、両方のモードに影響します。長いリクエストには api.apiyi.com または vip.apiyi.com を使用してください。詳細は Base URL 設定ガイド をご覧ください。6. streaming から完全な回答は得られない
6. streaming から完全な回答は得られない
できます。ただ、自分で組み立てる必要があります。 各 chunk の
delta.content を順番に連結すれば、non-streaming の message.content とまったく同じになります。組み立てたテキストが不完全に見える場合は、3 つ確認してください。finish_reason を無視していないか、data: [DONE] を受け取る前にループを抜けていないか、そして middlebox がレスポンスを途中で切っていないかです。ストリーミングが動作しない? 4つの手順
1
リクエストボディに本当に stream: true が含まれているか確認してください
実際に送信している JSON を出力してください。ラッパーライブラリでは、「渡したつもりだった」と「実際に渡っていた」は別物であることがよくあります。
2
curl -N で直接テストしてください
上の「cURL を並べて表示」タブにあるコマンドを使って、自分のコードやプロキシをバイパスしてください。curl でチャンクが段階的に到着するなら、サーバー側は正常で、問題はクライアントまたはミドルボックスにあります。
3
ミドルボックスのバッファリングを確認してください
Nginx では
proxy_buffering off; を追加してください。企業のゲートウェイやセキュリティアプライアンスは text/event-stream を全体のペイロードとしてスキャンする場合があります — ネットワーク管理者に通過を許可してもらってください。4
パースロジックを見直してください
SSE を 1 行ずつ読み取り、空行と
: で始まるコメント行をスキップし、data: [DONE] で停止してください。usage を含む最終チャンクには空の choices 配列があります — そこをインデックス参照しないでください。関連ドキュメント
API タイムアウトを回避する方法
シナリオ別のタイムアウト値と、ストリーミングでは回避できない理由
Base URL 設定ガイド
エンドポイントの違いと、CDN ノードの 100 秒上限
ログには完了と表示されるのに応答がない
セグメントごとのタイミングを含む、典型的な大きい非ストリーミング応答の問題
Claude のストリーミングと非ストリーミング
Anthropic のネイティブな名前付きイベント SSE プロトコルのパース
テキスト生成 API
完全なパラメータ一覧と呼び出し例
ログ課金の詳細を理解する
is_stream を含む、コンソールログの各フィールドが意味するもの