レート制限とリトライ:壊れにくいLLM呼び出しを作る
ホスティングされたLLMは、ありふれた形で失敗します。制限、タイムアウト、一時的なエラー。少しのリトライの規律が、脆弱な統合を頼れるものに変えます。
ほぼすべてのLLM統合の最初の版は、開発者のノートPCでは動き、本番の最初の忙しい午後に倒れます。その理由がモデルであることはめったにありません。理由は、ホスティングされたAPIが共有のネットワークサービスであり、共有のネットワークサービスはありふれた予測可能な形で失敗するということです。制限を課し、タイムアウトし、ときに「少し待ってもう一度試して」という以上の意味を持たないエラーを返します。脆弱な統合と頼れる統合の違いは、巧妙さではありません。少しのリトライの規律を一貫して適用することです。本ガイドは、何がうまくいかないか、そして各ケースをどう落ち着いて扱うかを扱います。
なぜ呼び出しは失敗するのか(そしてどの失敗が普通か)
失敗を二つの山に分けることから始めましょう。両者は正反対の対応を要求するからです。
一時的な失敗は一時的で、持続的な意味であなたの落ち度ではありません。サービスが一瞬過負荷になった、レート制限に当たった、リクエストがタイムアウトした、接続が切れた。決定的な特徴は、同じリクエストが、ただもう一度送れば成功するかもしれないことです。これがリトライの存在する理由となる失敗です。
恒久的な失敗は、繰り返しても改善しません。不正な形式のリクエスト、無効なキー、ポリシーに違反するプロンプト、モデルに対して大きすぎる入力。再送はただ時間とクォータを浪費し、さらに悪いことに、レート制限により深く沈み込ませることがあります。決定的な特徴は、リクエストが運が悪いのではなく、間違っていることです。
壊れにくいLLMコードにおいて唯一最も重要な習慣は、この二つを見分け、異なる対応をすることです。恒久的な失敗をリトライするのはバグです。一時的な失敗で強硬に失敗するのもバグです。脆弱な統合のほとんどは、この二つの誤りのどちらかをそこかしこで犯しています。
レート制限を理解する
レート制限は最も一般的な一時的失敗であり、罰ではありません。それは、共有サービスが、単一のクライアントに圧倒されることから自らと他の利用者を守る方法です。提供者はたいてい、いくつかの軸で一度に利用を制限します。ある時間枠で何回リクエストするか、そしてその枠でどれだけの総作業量(しばしばトークンで計測される)を押し通すか。一方の上限の下にとどまりながら、もう一方に当たることがありえます。
実用的な帰結は、リクエスト数を数えるだけではスループットを推し量れないということです。一握りの非常に大きなリクエストが、リクエスト上限にはまったく近づかないままトークン予算を使い果たしうるのです。レート制限が発動すると、サービスはそれを明示的に伝え、しばしばどれだけ待つべきかのヒントを添えます。正しい対応は、より激しく叩くことではありません。速度を落とし、後で戻ることです。
二つの習慣が、レート制限の苦痛のほとんどを起きる前に防ぎます。第一に、レスポンスヘッダーとエラー本文を読むこと。提供者はそこに現在の利用状況と制限を露出しており、その情報が賢く後退するための入力になります。第二に、自らのトラフィックをならすこと。作業のバーストがあるなら、一度に発射するのではなく分散させ、制限に一気にぶつかるのではなく徐々に近づくようにしましょう。
正しいリトライの仕方
リトライするとき、どうリトライするかが極めて重要です。素朴なやり方、すなわち即座に何度も繰り返すリトライは、積極的に有害です。サービスが過負荷なら、即時リトライの洪水はそれを悪化させ、あなたは生き延びようとしている問題の一部になります。規律あるやり方には三つの成分があります。
指数バックオフ。 最初のリトライの前に少し待ち、その後の各リトライの前に待ち時間をおよそ倍にしていきます。最初のしゃっくりには素早い二度目の機会が与えられ、持続する問題には次第に多くの余裕が与えられます。この一つのパターンが、一時的な失敗の大多数を解決します。
ジッター。 各待ち時間に小さなランダムな量を足します。それがないと、同じ瞬間に失敗した多くのクライアントが、すべて同じ瞬間にリトライし、同期した殺到を生んでサービスを再び過負荷にします。ジッターはリトライを散らします。規模が大きくなると効果が桁違いの小さな変更で、省くのは典型的な誤りです。
リトライの上限。 試行回数と費やす総時間に上限を設けます。永遠にリトライすると、短い障害が、リソースを縛り、待つ人を苛立たせるハングしたリクエストに変わります。上限を超えたら、きれいに諦め、本当の失敗を表に出しましょう。
まとめると、一時的なエラーでは、指数バックオフにジッターを加えて待ち、固定の上限までリトライし、どれだけ待つべきかのヒントが与えられていれば、自分で計算した遅延より優先してそれを尊重しましょう。
タイムアウトとリトライの限界
すべての呼び出しにタイムアウトが必要であり、それを選ぶのは本物のトレードオフです。短すぎると、成功したはずのリクエストを見捨て、遅いが問題のない応答を失敗に変えます。長すぎると、詰まったリクエストがシステムをハングさせ、ユーザーを縛ります。実際に期待する応答の長さに基づいてタイムアウトを選び、長い生成は正当に長くかかることを忘れないでください。一行の答えに合わせたタイムアウトは、長い答えへのリクエストを誤って殺します。
タイムアウトはリトライと、人を噛む形で相互作用します。サーバーがまだ作業している間に、あなたの側でリクエストがタイムアウトしうるのです。盲目的にリトライすると、同じ高価な作業を二度動かしてしまうかもしれません。読み取り専用の生成なら、それは単に無駄なだけです。効果を引き起こす呼び出し、すなわちメッセージの送信、レコードの書き込み、ツールの起動については、重複実行は本物のバグです。防御は冪等性です。効果を持つ操作を、二度行っても安全になるよう設計するのです。しばしば、サーバーが繰り返しを認識して重複排除できる一意なキーを付けることによって。
リトライが尽きたとき優雅に失敗する
レジリエンスは回復だけの話ではありません。回復が不可能なときにうまく失敗することでもあります。ときにサービスは本当に落ちており、どれだけバックオフしても助けにならないからです。優雅な失敗にはいくつかの性質があります。
- 境界がある。 ユーザーや呼び出し元のシステムが、無期限のハングではなく、妥当な時間内に明確な答えを得る。
- 崩壊するのではなく劣化する。 製品が許す場合、空白のエラーではなく、有用な何か、すなわちキャッシュされた結果、より単純な非モデルの経路、正直な「これは一時的に利用できません」へフォールバックする。
- 可視である。 後で理解できるだけの文脈とともに失敗を記録し、監視できるシグナルを表に出す。そうすれば上昇する失敗率が、ユーザーがエスカレートする前にあなたに届く。
成熟した統合の証は、決して失敗しないことではありません。失敗したとき、下流の何ものも驚かないことです。
痛む前に見る
見えないものは調整できません。最低限、種類別の失敗率、リトライが発動する頻度と最終的に成功する頻度、バックオフに費やした時間を含むレイテンシ、そしてレート制限にどれだけ近く動いているかを追跡しましょう。最後のものが早期警報システムです。利用が上限へじわじわ近づくのは、壁に当たった後ではなく前に、トラフィックを分散し、プロンプトを最適化し、より高い制限を要求する合図です。ほとんどのレート制限のインシデントは、見ている者には数時間前からトレンドとして見えます。
まとめ
壊れにくいLLM呼び出しは、短く退屈な規律に帰着します。一時的な失敗を恒久的なものと分け、それぞれに正しく対応すること。一時的な失敗を、指数バックオフ、ジッター、そして固い上限とともにリトライし、決してきつい即時ループでリトライしないこと。トラフィックをならし、サービスが伝えることを読むことで、レート制限を尊重すること。タイムアウトを意図的に設定し、効果を持つ呼び出しを冪等にして、リトライが決して二度実行しないようにすること。リトライが尽きたら、境界があり、可視で、劣化する形で失敗し、制限を見張って壁の前に行動すること。どれも華やかではなく、そしてそのすべてが、忙しい午後を生き延びる統合と、そうでない統合を分けるものです。
