OpenAI互換APIでは、model の値を変更するだけで別のモデルを呼び出せることがあります。実装上は簡単ですが、障害調査まで簡単になるとは限りません。
モデルを切り替える前に、最低限次の5種類の情報を構造化ログとして残しておくと、比較とロールバックがかなり楽になります。
1. 実際に送信したモデルID
表示名ではなく、リクエストに入れた完全なモデルIDを記録します。設定ファイルの値と実行時の値がずれていないか確認できるようにします。
2. クライアント側のrequest ID
APIを呼ぶ直前にUUIDを生成し、アプリケーションログ、ジョブ、エラー画面で同じIDを使います。複数のサービスをまたぐリクエストでも、一つの手がかりで追跡できます。
3. 試行回数
初回を 1 とし、再試行するたびに増やします。成功率だけでなく、「成功するまでに何回必要だったか」を確認できます。
4. HTTPステータスとエラー分類
401 や 422 を無条件に再試行しても直りません。一方、429 や一部のサービス障害は、上限付きのbackoffで回復する可能性があります。
HTTPステータスと、レスポンスに含まれるエラー種別を分けて記録します。
5. 結果メタデータ
少なくともレイテンシ、finish_reason、利用可能な場合のtoken使用量を記録します。モデル変更の前後で、成功/失敗だけでなく挙動の違いも比較できます。
以下はNode.jsの最小例です。
const model = "google/gemini-2.5-flash";
const requestId = crypto.randomUUID();
const startedAt = Date.now();
const response = await fetch(
"https://routerbase.com/v1/chat/completions",
{
method: "POST",
headers: {
Authorization: "Bearer " + process.env.ROUTERBASE_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
model,
messages: [
{ role: "user", content: "Return only the word: ok" },
],
}),
},
);
const payload = await response.json().catch(() => ({}));
console.log(JSON.stringify({
request_id: requestId,
model,
attempt: 1,
outcome: {
http_status: response.status,
error_type: payload.error?.type ?? null,
},
result: {
latency_ms: Date.now() - startedAt,
finish_reason: payload.choices?.[0]?.finish_reason ?? null,
total_tokens: payload.usage?.total_tokens ?? null,
},
}));
if (!response.ok) {
throw new Error(payload.error?.message ?? "AI API request failed");
}
raw promptは通常ログに残さない
調査に便利だからという理由だけで、ユーザー入力をすべて一般ログへ保存するのは危険です。promptの保存が必要な場合は、明示的に有効化し、アクセス制御、保持期間、マスキングを決めます。
再試行ポリシーも先に決める
catch したら全部再試行するのではなく、ステータスとエラー分類ごとに判断します。最大試行回数を設定し、指数backoffとjitterを使い、最終的に停止した理由も記録します。
tool callのように副作用がある処理では、再送前に冪等性と承認境界も必要です。二回目の成功が、二重実行になることがあるためです。
モデル切り替えは一行でも、運用上の安全性はログとポリシーで決まります。
この記事の例では、OpenAI互換endpointを提供する RouterBase を使用しました。
参考資料
- RouterBase Chat Completions
- RouterBase Error Codes
- RouterBase Rate Limits
- Qiita コミュニティガイドライン