上原正吉(EarthLink Network Co., Ltd.)。Claude Codeを開発の主体に据え、20を超えるプロダクトを1人で同時に開発・運用しています。これは、その現場の実測記です。
使っていたLLMモデルが廃止され、生成機能が止まった — 2つのプロダクトの原因と再発防止
2026 年 7 月 10 日の朝、マルチ LLM(大規模言語モデル)ワークフローの SaaS(サービスとして提供されるソフトウェア)、promptflow のデザイン生成機能が全滅しました。原因はこちらのコードではありません。Google が gemini-2.5 系のモデルを事前告知なしで即日廃止したためです。そしてこの「モデルが消える」事故は初めてではありませんでした。同じ月の 7 月 5 日には画像生成の dall-e-3 が OpenAI の API(外部から機能を呼び出す窓口)から消え、その 1 ヶ月前の 6 月 3 日には、ニュースキュレーション基盤の curation で gemini-2.0-flash-lite の廃止対応に追われたばかりだったのです。
この記事では、別々のプロダクトで続けて起きた「プロバイダによるモデル廃止」を突き合わせ、(1) それがなぜ即座に障害になるのか、(2) 何を監視し、何を fallback として用意すれば被害を抑えられるのか、を当時のログとコードで残します。このリスクは、事前に十分想定できていませんでした。モデル廃止は、この 1 か月で 3 回。最初の curation(6/3)は後継モデルへの名前の差し替えで済みましたが、promptflow では 7 月に 2 回(7/5・7/10)続けて起き、そこで「起きたら直す」のをやめて仕組みで防ぐようにしました。個別のインシデントの詳細は、promptflow 編(PF-002)と curation 編(MISC-002)に分けて書いています。この記事は、その 2 つを突き合わせた横断の教訓に絞ります。
結論を先に置きます(本文の読了は約 6 分)。
- 原因は自分たちのコードではなく、プロバイダによるモデルの廃止だった — 昨日まで動いていたモデルが、今日はもう存在しない。コードレビューでは捕まえられない種類の変化です
- モデル ID をコードやプロンプトに直書きしていたのが、障害を全体へ広げた — 版が消えた瞬間に生成経路ごと落ちる。版はエイリアスに寄せ、廃止時の代替候補を宣言で持つ
- 応答の形式も変わり、パースが二次被害を出した — Gemini 3 は thinking(思考)パートを別で返すようになり、生成した HTML から doctype が欠落した。特定モデルの出力形式に暗黙依存しない
- 同じ事故が、1 か月で 3 回・2 つのプロダクトにわたって起きた — その場しのぎで終わらせず、モデルカタログのガバナンスと、廃止時の自動切り替えに昇格させた
症状: 「does not exist」と、応答形式ごと変わる罠
最初の兆候は curation の方でした。6 月 3 日、記事分析に使っていた gemini-2.0-flash-lite が廃止予告に入り、応答が不安定になりました。ここは後継の gemini-2.5-flash-lite へ移すだけで済み、ついでに analyze と long_insight の 2 コールを 1 コールに統合して 1 記事あたり約 16% のトークンを削りました。このときは「モデル名を差し替える定型作業」くらいの認識でした。
本番で本当に問題になったのは翌月の promptflow 側です。7 月 5 日、画像生成の E2E(端から端まで通しの)テストが次のように落ちました。
POST /v1/images/generations model=gpt-image-1 -> HTTP 504 Gateway Timeout
POST /v1/images/generations model=dall-e-3
-> 400 { "error": { "code": "model_not_found",
"message": "The model `dall-e-3` does not exist" } }
本命の gpt-image-1 は 504 を返し、代替候補の dall-e-3 は「存在しない」と言われました。ここは gpt-image-1-mini への切替で応急復旧しました。ところが 5 日後の 7 月 10 日、今度は Google が gemini-2.5 系を予告なく即日廃止し、デザイン生成が全滅します。厄介だったのは、単に名前が消えただけではなかった点です。移行先の Gemini 3 系は応答の形式そのものが変わっていて、思考パート (thinking) を本文と誤読し、生成された HTML から <!doctype html> が丸ごと欠落する二次被害まで出ました。
最初の仮説と、その外し方
7 月 10 日、私が最初に疑ったのは自分たちのプロンプトの作り込みが壊れたことでした。デザイン生成はこの時期ずっと品質チューニングの最中で、「またプロンプトをいじって壊したか」と思ったのです。しかしプロンプトを直前の安定版へロールバックしても生成物は空のまま。次に API キーの失効を疑いましたが、他モデルは正常に応答する。
切り分けがはっきりしたのは、モデル ID を固定して素の呼び出しを投げたときでした。
curl -s "$GEN_ENDPOINT/models/gemini-2.5-flash:generateContent" \
-H "x-goog-api-key: <REDACTED>" \
-d '{"contents":[{"parts":[{"text":"ping"}]}]}' | jq '.error // .candidates[0]'
返ってきたのは私たちのバグではなく、プロバイダ側の「そのモデルは提供終了」を意味するエラーでした。ここで仮説がプロンプトからプロバイダへ切り替わります。つまり、昨日まで動いていたものが、今日はもう存在しない。コードレビューでは絶対に捕まえられない種類の変化でした。
仕組み: なぜモデル廃止はすぐに全体障害になるのか
落ち着いて整理すると、被害が一気に全体へ広がる理由は 3 つありました。
- モデル ID をコードやプロンプト契約に直書きしていたこと。生成経路が特定の版に固定されていると、その版が消えた瞬間に経路ごと落ちます。
- 応答パースが特定モデルの出力形式に暗黙依存していたこと。Gemini 3 が thinking パートを別フィールドで返すようになると、旧パーサはそれを本文と混ぜてしまい、HTML として壊れる。
- 廃止を事前に知る手段がなかったこと。プロバイダの告知はメールや変更履歴に埋もれ、人間が毎日追える前提にはできません。
要するに、外部の都合でモデルが消えることに対して、システム側に代替へ切り替える余地が何も用意されていなかったのです。
修正: 版固定をやめ、壊れた応答に耐えるパースにし、廃止を検知する
まず即応として、モデル参照を固定 ID から latest 相当のエイリアスへ寄せ、応答パースに thinking パート除外と HTML サルベージを入れました。
// Gemini 3 は thinking パートを混ぜてくる。本文だけ取り出し、
// doctype が無ければ補って「壊れた HTML」を救出する
function extractHtml(parts: Part[]): string {
const body = parts
.filter((p) => p.kind !== "thought") // 思考パートを本文と誤読しない
.map((p) => p.text ?? "")
.join("");
const html = body.trim();
if (!/^<!doctype/i.test(html) && /<html[\s>]/i.test(html)) {
return `<!doctype html>\n${html}`; // doctype が無ければ補う
}
return html;
}
そのうえで再発防止を仕組みにしました。ひとつはモデルカタログのガバナンスで、使ってよいモデルを版付きの集合として持ち、新モデルの自動検出スキャナーを cron(定期実行)で回して、追加・廃止を通知経由で人に上げる。もうひとつは廃止時の graceful fallback で、モデルごとに代替候補を宣言しておき、呼び出しが model_not_found で落ちたら次の候補へ自動で切り替える、という設計です。イメージとしては次のようなマップを持ちます。
# 生成モデルの版と、廃止時の切り替え先を宣言しておく
image_generation:
primary: gpt-image-1
fallback: [gpt-image-1-mini] # 廃止された dall-e-3 は候補から外す
design_generation:
primary: gemini-flash-latest # 版を固定しない
fallback: [gemini-flash-stable, sonnet-html-fill]
parser: defensive-v2 # thinking 除外 + doctype サルベージ
curation 側でも、モデル移行を「名前の差し替え」で済ませず、想定外の応答を終端ステータスに落として無限リトライを防ぐ処理と組で入れるようにしました。curation での教訓が、promptflow の設計に効いた形です。
その後と、限界
この 7 月の 2 連発を経て、モデル廃止は「起きたら直す事故」から「起きる前提で代替へ切り替える運用」に変わりました。スキャナー cron は以後、新モデルの登場を先に知らせてくれるようになり、少なくとも「ある朝いきなり全体が止まる」頻度は下げられたと考えています。ただし限界もはっきりしています。fallback 先の品質は primary と同じではなく、デザイン生成のように出力品質が売りの機能では「落ちないが劣化する」状態が残る。応答形式の変更はスキャナーでは検知できず、結局は実応答を投げてみないと分からない。そして根本的に、自分が所有していないモデルに依存している以上、供給停止のリスクはゼロにはできません。できるのは、その日が来たときに全体が止まるのではなく、一部の劣化で済ませる備えだけです。
まとめ — 転用できる教訓
- モデル ID を直書きしない。版はエイリアスに寄せ、廃止時に切り替える fallback 候補を宣言で持つ。過去に動いた版が、今も提供されているとは限りません
- 応答パースは特定モデルの形式に暗黙依存させない。thinking パート混入や doctype 欠落のような「形式変更」は、壊れた応答からでも本文を取り出せる作りにしておく前提で書く
- 廃止を人手で追わない。使用可能モデルを版付き集合で管理し、スキャナー cron で追加・廃止を検知して人に上げる
- 1 度目を仕組みに変える。同じ事故は別プロダクトで必ずまた起きる。その場しのぎで終わらせず、fallback とガバナンスに昇格させておくと、2 度目の被害を大きく減らせる
筆者について
上原正吉。EarthLink Network Co., Ltd. でAI開発をしています。2025年からClaude Codeを開発の主体に据え、今は20を超えるプロダクトを1人で同時に開発・運用しています。この連載では、その現場で実際に起きたこと(うまくいったことも、失敗も)を、数字と一緒に書いていきます。
また、AIで業務や開発を組み替えたい会社・チーム向けに、AI活用のコンサルティングも受け付けています。ご相談は www.eln.ne.jp からどうぞ。
