簡潔な回答
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. ストリーミングを使えばタイムアウトを無視できる
1. ストリーミングを使えばタイムアウトを無視できる
**2つの事柄を区別して考える必要があります。**ストリーミングによって全体の生成時間が短縮されるわけではありません。最初のtokenから最後のtokenまでの合計時間は以前と変わらず、その部分に変更はありません。しかし、ストリーミングは「何も返ってこない」という問題を解決します。クライアントの読み取りタイムアウトをイベント間の間隔をカバーするように設定している限り(推論モデルの思考フェーズ中の測定された最大の無音ギャップは約42秒で、合間にキープアライブpingがあるため、90〜120秒で機能します)、誤ってタイムアウトがトリガーされることはありません。1つのタイムアウト値で10分以上の生成全体を最初から最後までカバーしようとする純粋な非ストリーミングこそが、実際には耐えられない構成です。したがって、正しいアプローチは次のとおりです。長い出力にはストリーミングを使用し、イベント間のギャップに応じた読み取りタイムアウトを設定します。シナリオごとの推奨値はAPIタイムアウトを回避する方法に記載されています。完全なレシピについては長文出力の実践ガイドを参照してください。
2. ストリーミングの方が高速である
2. ストリーミングの方が高速である
**最初の1バイトは速くなりますが、合計時間は変わりません。**同じモデルと同じpromptの場合、ストリーミングと非ストリーミングはおおよそ同じ時間で完了します。ストリーミングによって得られるのは体感速度です。ユーザーはスピナーを30秒間見つめ続ける代わりに、1秒以内に何らかの動きを目にすることができます。誰も画面を見ていない場合、その価値はゼロです。
3. ストリーミングの方が安価である、または受信した分のみが課金される
3. ストリーミングの方が安価である、または受信した分のみが課金される
**そうではありません。**上記の「課金と使用量」を参照してください。課金は同一であり、途中で切断した場合でも料金が発生します。
4. すべてのモデルとエンドポイントがストリーミングをサポートしている
4. すべてのモデルとエンドポイントがストリーミングをサポートしている
**そうではありません。**テキストチャットモデルは通常サポートしていますが、画像生成、エンベディング、リランクのエンドポイントにはストリーミングの概念がなく、
streamを無視するか拒否します。一部のモデルでは、ストリーミング時の特定のパラメータの組み合わせに対して追加の制限があります。確信が持てない場合は、まず非ストリーミングで呼び出しを成功させてから、stream: trueを追加してください。5. 非ストリーミングの方が信頼性が高い
5. 非ストリーミングの方が信頼性が高い
どちらにも固有の障害モードがあります。
- 非ストリーミングのリスク:生成中ずっと接続が無音状態になるため、プロキシ、CDN、企業のゲートウェイがアイドルタイムアウトによって接続を切断する可能性があります。また、非常に大きなレスポンスボディ(base64の画像出力は容易に数十MBに達します)の場合、終端処理がスタックする問題に遭遇することもあります — 転送は完了したものの応答が返らないリクエストおよびログでは完了と表示されるがクライアント側には何も届かないを参照してください。
- ストリーミングのリスク:SSEをサポートしていない、またはバッファリングを強制する中間ボックス(middlebox)との相性が悪く、クライアント側の解析が複雑になるため、気づきにくい不具合が発生しやすくなります。
api-cf.apiyi.com(CDNエンドポイント)には約100秒のリクエスト上限があり、両方のモードに影響します。長時間の処理を伴うリクエストにはapi.apiyi.comまたはb.apiyi.comを使用してください — Base URL設定ガイドを参照してください。6. ストリームから完全な回答を取得することはできない
6. ストリームから完全な回答を取得することはできない
**取得できます — 単にご自身で組み立てるだけです。**各チャンクの
delta.contentを順番に連結すれば、非ストリーミングのmessage.contentとまったく同じものが得られます。組み立てたテキストが不完全に見える場合は、次の3点を確認してください。finish_reasonを無視していないか、data: [DONE]を受信する前にループを抜けていないか、中間ボックスがレスポンスを切り詰めていないかです。ストリーミングが動作しない? 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 を含む、各コンソールログフィールドの意味