画面が消えた時刻を処理終了と呼ばない
利用者がタブを閉じると、ブラウザー上のストリームは直ちに消えます。しかし、その切断がWordPressのPHP処理、上流HTTP接続、モデル生成へ順番に伝わるとは限りません。請求の有無を判断するには『画面が閉じた』ではなく、各層がいつ終了を認識したかを調べます。
課金が残った一要求だけを選ぶ
対象は平均値ではなく、費用が残った1要求です。開始時の相関IDを軸に、ブラウザーの中止、WordPressが接続断を検知した時刻、上流要求を閉じた時刻、プロバイダーが完了または中止を記録した時刻をUTCと経過秒の両方でそろえます。
0秒・8秒・41秒を四層の出来事へ戻す
例として0秒に送信、8秒にタブを閉じ、41秒にサーバー側が完了したなら、少なくとも33秒間は利用者へ届かない処理が続いています。ただし、この33秒すべてが課金対象だったとは限らないため、完了時の入力・出力トークンとプロバイダー状態を同じ行へ置きます。
WordPressのログへ『成功』だけを残すと、利用者が受け取った回答と混同します。client_disconnected、upstream_cancel_sent、provider_completed、response_deliveredのように事実を分け、存在しない時刻は空欄のままにします。空欄を推測で補わないことが原因診断の精度を上げます。
切断タイムラインの空欄を可視化する
以下のタイムラインに実測値を入れると、キャンセル信号がどこで途切れたかが分かります。ブラウザーのAbortControllerを呼んだ記録があっても、WordPressから上流要求へ中止が渡っていなければ、プロバイダー完了は自然な結果です。
| 時計 | 記録する出来事 | 例 | 証拠 |
|---|---|---|---|
| ブラウザー | 要求開始/切断 | 0秒/8秒 | 開発者ツール、AbortController記録 |
| WordPress | 要求受領/接続断認識 | 1秒/9秒 | 相関ID付きアプリログ |
| 上流通信 | プロバイダー送信/キャンセル | 2秒/空欄 | HTTPクライアント記録 |
| プロバイダー | 受理/完了/課金量 | 3秒/41秒/4,820 token | 要求ID、利用明細 |
| 利用者 | 受信した最終断片 | 8秒 | ストリーム受信記録 |
キャンセルの到達点から対策を選ぶ
反対に上流キャンセルが送信済みでもプロバイダーが完了した場合は、使用しているAPIが中止をどの時点まで受け付けるかを公式仕様と実測で確認します。『キャンセルを送ったから無料』とは置かず、請求明細で結果を確定します。
検索補助や文章候補のように、利用者が離れた後の結果を使わない処理では、切断を上流へ伝播し、短い出力上限も置く価値があります。注文後メールや索引作成のように完了させる業務なら、ブラウザー接続から切り離し、課金されるバックグラウンド処理としてキュー、再試行、予算を明示します。
ガードレールで補える範囲を限定する
Proの1要求サイズ、異常な再試行、実行コンテキストのガードレールは、切断伝播そのものの代用品ではありません。アプリ側の終了処理を直したうえで、長すぎる出力や切断後の再試行が費用を増幅しない境界として使います。
同じ切断操作で完了試験を行う
修正後は同じ入力を10回実行し、最初の断片受信後に同じ秒数で切断します。中止が期待される処理では、上流キャンセル記録、プロバイダー側の終了状態、切断後の出力トークンが設定した許容範囲に収まることを確認します。
タイムラインの最終欄には、どの処理は中止し、どの処理は継続するかという運用上の約束を書きます。費用をゼロにすることではなく、利用者が捨てた生成が無制限に続かず、継続する処理には所有者と上限があることが復旧の条件です。
「タブを閉じた8秒後も課金が続いた――4つの時計でストリーミング中止を復元する」の「ストリーミング切断タイムライン」を、最初の実環境導入に使ってください。AI Cost Circuit Breakerを無料でダウンロードし、Monitoringから始め、期待するシグナルと戻し方を確認してから制御を有効にしましょう。