ストリーミングとエッジの100秒
ストリーミングしない長いリクエストが、なぜ約100秒でエッジに切られるのか。stream を true にするだけで何が変わるのか。ストリームの最後のフレームが運ぶ使用量で計量がどう精算されるのか、途中で接続が切れたらそれがどうなるのか。ポータルの三つの時計(アップロード、最初のトークンまで、合計)の読み方まで。
ストリーミングしないリクエストは、回答が全部できあがるまで、あなたに一バイトも送りません。一行で終わる返事なら気づきもしませんが、長い生成では、それが問題のすべてです。リクエストがモデルに届いてから、回答の最初の一バイトが返ってくるはずの瞬間までに、数分かかることがあります。そしてこのサイトの前に立つCDNのエッジが許す沈黙は、約100秒。それを超えると、エッジはその応答を見限ります。その線を越えた時点で、モデルはまだ書いているのに、リクエストのほうがエッジで死にます。これを防ぐのはフィールド一つで、しかも有効にしたところで課金額は一切変わりません。
なぜ長いバッファ型のリクエストは死ぬのか
バッファ型のリクエストは、真ん中に長い間(ま)を挟んだ一往復です。クライアントがbodyを送り、モデルが回答を最後まで書き、最後のトークンが決まってはじめて、最初のバイトが接続を戻ってきます。その二つの瞬間のあいだ、接続には何も流れていません。そしてエッジが見張っているのは、まさにその「何もない」ところです。約100秒の沈黙、それが「応答はもう来ない」と判断するまでにエッジが許す全部です。あなたに見えるのは失敗したリクエストです。実際に起きていたのは、回答がまだ書かれている最中だった、ということです。
ストリーミングするリクエストには、そもそも尽きるべき間がありません。最初のトークンとともにバイトがクライアントへ向かって出ていき、それ以降のトークンも生成されたそばから届くので、接続がエッジに見限られるほど長く沈黙することはありません。覚えておく価値があるのはここです。これはタイムアウトを長くした話ではなく、決して静かにならない応答の話です。四分かかる生成は四分ストリーミングし、そして届きます。
直し方はフィールド一つ
ここでのストリーミングは、別のendpointでも、別のキーでも、別の価格でもありません。あなたがすでに送っているbodyの中のフィールド一つ、stream: true です。/v1/chat/completions でも /v1/responses でも、まったく同じように効きます。
POST /v1/chat/completions
{
"model": "astra",
"stream": true,
"messages": [
{"role": "user", "content": "Write the report."}
]
}
公式SDKでも同じフィールドを、それぞれの書き方で持っています。Pythonなら stream=True、Nodeなら stream: true。あなたの側で変わるのは回答の形だけ — 待って受け取る一つのオブジェクトではなく、届いた順に回していくチャンクになります — そしてそのループは、どのクライアントライブラリにも最初から書かれています。
ストリームの周りで計量が何をしているか
ストリーミングにしても、計量の外に出るわけではありませんし、確かめようのない請求書が来るわけでもありません。使用量はちゃんと届きます。ストリームの最後のフレームがトークン数を運んできますし、chat completionsではゲートウェイが stream_options.include_usage を自分で付けるので、頼み忘れられる類のものではありません。回答がストリーミングだろうとそうでなかろうと、計量が精算に使う数字は同じです。
その周りの手順は、ここのどのリクエストもたどるものと同じです。リクエストが始まった時点で予約が取られ — ポータルの残高が動くのはそのときで、最後のトークンが着地したときではありません — 精算は、ストリームの最後のフレームが報告した使用量に対して、予約を取った時点で記録した価格で行われます。途中で接続が切れた場合、その予約は請求されるのではなく、照合に回されて解放されます。精算まで届かなかったトークンが課金されることは、決してありません。
時計は一つではなく三つ
リクエストが遅く感じられたとき、それがどの種類の遅さなのかはポータルが教えてくれます。リクエストごとに三つの時計が記録されています — アップロード、最初のトークンまでの時間、そして合計 — 一つの言葉の下に三つの別々の問題が隠れているからです。アップロードが長いのは、あなた自身の上り回線がbodyをこちらへ渡している時間で、こちら側で何をしても短くはなりません。最初のトークンまでが短くて合計が長いのは、長い生成がやるべきことをやっているだけで、それこそストリーミングが存在する理由です。最初のトークンまでが長い場合が、こちらに連絡する値打ちのある一つです。
クライアントが読んでおくべき数字がもう一つあります。リクエストが429で拒否されたとき、Retry-Afterヘッダーはそのまま手を加えずに通します。公式SDKが再試行の間隔を決めるのに使っているのがこのヘッダーなので、これを尊重するクライアントは、自分で当て推量をするのではなく、こちらが実際に送った間隔だけ待ちます。
途中で接続が切れた場合、その予約は請求されるのではなく、照合に回されて解放されます。精算まで届かなかったトークンが課金されることは、決してありません。
本記事は機械翻訳です。ネイティブによる確認は未了で、英語版が正本です。