LLMアプリの負荷試験とは?リクエスト数ではなくトークンと処理経路で限界を測る
少人数では快適だったLLMアプリも、同時利用が増えると突然遅くなる。モデルAPIのレート制限だけでなく、検索、道具、接続数、キュー、データベース、ログ送信のどこかが先に詰まるからである。
負荷試験は、単に大量の要求を送る作業ではない。想定する利用者の仕事を再現し、SLOを守れる量、劣化が始まる点、限界を超えたときの止まり方を確認する。LLMアプリでは、一秒当たりの要求数だけでなく、入力と出力のトークン数、道具の回数、ストリームの長さ、再試行を含めて設計する必要がある。
まず何を守る試験か決める
同じ負荷でも、チャットと夜間バッチでは合格条件が違う。試験の前に、前の記事で決めたSLOと業務上の制約を試験条件へ落とす。
例えば対話型RAGなら、次を定める。
- 最初の意味ある表示までの時間
- 最終回答までの時間
- 正常に完了した利用者タスクの割合
- 検索と根拠の品質
- 一件当たりの費用上限
- 取消後に不要な処理が残らないこと
- 過負荷時に安全な縮退または明確なエラーになること
「毎秒100件を送れた」だけでは合格にならない。SLOを満たした利用者タスクがどれだけ完了したかを測る。
QPSだけではLLMの負荷を表せない
短い分類要求と、長い会話履歴から長文を生成する要求は、一件でも必要な資源が大きく異なる。画像、ファイル、多数の道具定義、推論量、検索結果も処理量を変える。
OpenAIの現行Rate limits資料では、要求数だけでなく、一定時間内の入力と出力に関わるトークン量、画像、音声、Batchの待機トークンなど複数の上限がある。モデルごと、組織やプロジェクトごと、共有モデル群ごとに条件が異なる場合もある。
したがって、試験結果には少なくとも次を併記する。
- 一分当たりの利用者タスク数
- 同時実行数と同時ストリーム数
- 入力、出力、キャッシュ済み入力のトークン量
- 一タスク当たりのモデル呼び出し数
- 検索、再ランキング、道具の回数
- 再試行を含む実API要求数
- キューへ積まれた未処理仕事の量
一つの数字へ換算しきれない場合は、代表的な仕事の組み合わせとして容量を表す。
本番の仕事分布を小さく再現する
負荷試験用の要求をすべて短い固定文にすると、キャッシュが効きすぎ、実際より軽い結果になる。反対に、最大長の要求だけを連続して送ると、通常運用の容量を過小評価する。
本番または想定利用から、次の仕事区分を作る。
- 短い単発質問
- 長い会話履歴を持つ質問
- RAG検索を使う質問
- 一つまたは複数の道具を呼ぶ仕事
- 画像やファイルを含む入力
- 長い出力を求める仕事
- 安全に拒否すべき入力
- 利用者が途中で取り消す仕事
区分ごとの割合と入力長、出力長、道具回数の分布を再現する。本文は本番データをそのまま使わず、個人情報と機密情報を含まない代表データを用意する。
固定プロンプトはキャッシュの試験にしかならない
同じ接頭部分と同じ質問を繰り返す試験では、プロンプトキャッシュ、アプリの出力キャッシュ、検索キャッシュ、ネットワーク接続の再利用が通常より高くなる。
キャッシュのある状態とない状態を分けて測る。
- 冷えた状態: 新しい構成版、空のキャッシュ、起動直後
- 温まった状態: 通常の共通接頭部分と再利用率
- キャッシュが途切れる状態: 道具Schemaや共通指示を変更した直後
本番の再利用率に近い比率で、共有する接頭部分と変化する後半を組み合わせる。キャッシュ命中率も結果に残す。
到着の仕方を四種類に分ける
一つの一定負荷だけでは、開始直後と長時間運転の問題を見つけられない。
段階的な増加
低い負荷から少しずつ上げ、遅延、エラー、キュー長、レート制限が非線形に悪化する点を探す。提供者の急増制限に触れないよう、公式の Retry-After と増加条件を確認する。
想定ピーク
通常の最大利用を一定時間保ち、SLOを満たせるか確認する。平均ではなく、遅延の上位百分位と失敗区分を見る。
突発的なスパイク
通知、公開、障害復旧後の再送などで同時に要求が来る状態を再現する。どこで受付を制限し、どの利用者へ待機や拒否を返すかを見る。
長時間の連続負荷
数時間以上動かし、メモリ、接続、キュー、ログ、費用が徐々に増える問題を探す。短い試験で通っても、未回収のストリームや履歴の肥大化は長時間後に現れる。
閉じた試験と開いた試験を区別する
固定数の仮想利用者が、応答を受け取ってから次の要求を送る試験では、サービスが遅くなると送信量も自然に下がる。この閉じた方式だけでは、実際の到着率が一定のサービスで過負荷を隠すことがある。
外部から一定の割合で要求が到着する開いた方式も試す。処理が追い付かなければキューが伸び、期限切れや負荷制御が働くか確認できる。
どちらが本番に近いかは機能で異なる。対話画面では利用者が応答を待つが、Webhook、予約送信、バッチ投入では到着率が処理速度と独立している。
本番と試験のプロジェクトを分ける
負荷試験を本番のAPIキーとプロジェクトで行うと、本番利用者と同じレート枠や支出上限を消費し、履歴や監視データも混ざる。
OpenAIのProduction best practicesは、ステージングと本番を別プロジェクトにし、アクセス、レート上限、支出上限を個別に管理する方法を案内している。試験用の認証情報、予算警告、停止条件を用意する。
ただし、試験プロジェクトの上限が本番より低ければ、本番容量をそのまま推定できない。確認したいのが自分のアプリの限界か、提供者枠を含む全体の限界かを明示する。
外部サービスに大量要求を送る前に、利用規約、契約、上限、必要な事前連絡を確認する。負荷試験を許可なく第三者の本番環境へ向けない。
費用の停止条件を先に置く
LLMの負荷試験は、実行するほどトークンと道具の費用が発生する。送信件数だけの上限では、長い出力や再試行ループで予算を超える。
開始前に次の停止条件を設定する。
- 累積費用またはトークン量
- 時間当たりの費用
- 失敗率とタイムアウト率
- キューの最古時間
- 再試行率
- 本番への影響を示す指標
- 監視データの欠落
提供者側の支出上限だけに頼らず、試験実行側でも停止する。上限の反映には遅延があり、少し超過する可能性がある。
レート制限は一つずつ使い切るとは限らない
要求数の上限に余裕があっても、長い入力でトークン上限へ達することがある。短い要求を大量に送れば、逆に要求数の上限が先に来る。複数モデルが同じ共有枠を使う場合、別機能の負荷が影響する。
API応答のレート制限ヘッダーが利用できる場合は、上限、残量、リセットまでの時間を記録する。ただし、自分のキュー制御をヘッダーだけへ依存させない。応答が届かない障害時にも安全に制限できるよう、クライアント側の同時実行数とトークン予算を持つ。
429 も原因を分ける。短期的な要求やトークンの上限、急激な増加、月間支出上限、クレジット不足では対応が異なる。エラーコードと Retry-After を記録する。
再試行の嵐を試す
負荷時に一部要求が失敗すると、すべてのクライアントが同時に再試行し、元の負荷より大きな二波目を作ることがある。
試験では、モデルAPI、検索、道具、データベースの遅延や失敗を安全な試験環境で注入する。次を確認する。
Retry-Afterを尊重する- 指数バックオフに小さな揺らぎを加える
- 一仕事当たりの再試行回数に上限がある
- 期限を過ぎた仕事を再試行しない
- 再試行と新規要求を合わせた総量を制限する
- 同じ外部操作を二重実行しない
成功したAPI要求数ではなく、再試行を含む増幅率を見る。利用者タスク100件が内部要求300件になれば、余裕は想定より小さい。
キューは過負荷を消さず後ろへ移す
非同期キューは一時的なスパイクを吸収するが、到着量が処理量を上回り続ければ待ち時間が増える。キュー長だけでなく、最古の仕事が何秒待っているかを見る。
各仕事に期限と優先度を持たせ、期限切れを安い段階で終了する。対話要求、重要な更新、低優先度の再計算を同じキューへ入れない。
復旧後に古い要求が一斉に流れる状態も試す。キューを空にする速さではなく、本番のオンライン処理を守りながら安全に回復できる速度を選ぶ。
縮退と負荷遮断を実際に動かす
Google SREは、限界を超えたときに低品質でも安い結果を返す縮退や、早い段階で要求を拒否して全体停止を避ける負荷遮断を重視している。
LLMアプリの縮退例には次がある。
- 高価な再ランキングを省く
- 検索候補数を減らす
- 出力長を短くする
- 低優先度の補助評価を止める
- 待てる仕事をバッチへ移す
- 安全な定型回答と人への引き継ぎを返す
小さいモデルへ替えるだけで安全とは限らない。品質と道具選択を評価済みの経路だけを使う。金銭、削除、権限変更などでは、低品質な自動処理より停止を選ぶ。
負荷遮断では、重い処理を始めてからタイムアウトするより、入口で早く拒否する。誰を優先するか、どの状態コードと再試行時刻を返すかも試験する。
利用者の遅い接続も再現する
ストリーミングでは、モデルが生成できても、クライアントがゆっくりしか受け取れない場合がある。接続が長く残り、サーバーのメモリと同時接続枠を使う。
低速回線、途中切断、タブを閉じる操作を再現し、次を確認する。
- バッファが無制限に増えない
- 取消がモデル側まで伝わる
- 未完了回答を完成として保存しない
- 接続終了後に不要な道具を実行し続けない
- 再接続でイベントを重複適用しない
モデルAPIだけを直接たたく試験では、ブラウザ、CDN、プロキシ、アプリサーバーの接続限界は分からない。利用者から保存先までの経路で試す。
品質が負荷で変わらないか確認する
過負荷時に検索件数を減らす、モデルを切り替える、タイムアウトを短くするなら、回答品質も変わる。技術的な成功率だけでは縮退が役立つか判断できない。
負荷段階ごとに同じ評価例を混ぜ、根拠一致、必須項目、道具選択、安全性を測る。通常時と縮退時の品質差を事前に確認する。
非決定的な出力を一件ずつ比較せず、同じ構成と負荷条件で複数回実行し、合格率と失敗分類を見る。負荷試験中の全回答を人が読む必要はなく、代表例と重大失敗を標本監査する。
限界点ではなく安全な運用点を決める
一度だけ処理できた最大値を本番容量にしない。モデル提供者、入力分布、キャッシュ、ネットワーク、コード版で限界は変わる。更新中の台数減少や一つの依存障害も考慮する。
合格した負荷の中から、SLO、費用、品質を満たし、想定外の増加を吸収できる余裕を残した運用点を選ぶ。次を文書化する。
- 試験した構成版と日時
- 仕事分布と到着パターン
- 各依存の上限と設定
- SLOを満たした最大負荷
- 劣化が始まった負荷と最初のボトルネック
- 縮退、遮断、回復の挙動
- 安全な運用上限と見直し条件
モデル、プロンプト、道具、検索、インフラ、レート枠を変更したら再試験する。三か月前の結果を現在の容量として扱わない。
LLMアプリの負荷試験は、最大QPSを競うものではない。実際のトークン量と処理経路を持つ利用者タスクを流し、SLOを守れる運用点と、超えたときの縮退・拒否・回復を確かめる作業である。費用と第三者サービスへの影響を制限しながら、どこが最初に詰まり、再試行で何倍に増幅するかまで測れば、公開後の急増を安全に受け止めやすくなる。