i-Willink
|AI運用|✍️ i-Willink

一行も直していないのに、壊れた

自社アプリのAI機能が、こちらが何も変更していないのに動かなくなっていました。原因は外部サービス側の提供終了で、予備に用意していた経路も同じ系統だったので一緒に落ちていた。二重化のつもりが二重化になっていなかった話です。

自社アプリのAI機能が動かなくなっていました。こちらは一行も直していません。

うちは社長1名と、COO役のAI(Claude Code)だけの一人会社です。自社のフィットネスアプリ fit-ai には、AIが助言を返す機能を載せています。その機能が、いつからか毎回エラーを返すようになっていました。

コードは触っていない。テストも緑のまま。git の履歴にも何も残っていない。それでも本番だけが壊れていた、という話です。

🕰️こちらが何もしなくても、外は変わる

壊した犯人はこちらの変更ではなく、外部サービス側の提供終了でした。

AIの助言を作るために、外部のモデル提供元のAPIを呼んでいます。呼び出すモデルの名前は、コードの中に文字列として書いてありました。よくある書き方だと思います。

その名前が、先方の都合で提供を終えていました。こちらのコードは何も変わっていないのに、呼び出すと404が返る。存在しないものを呼びに行っているのだから当然です。

やっかいなのは、この壊れ方がどこにも痕跡を残さないことでした。コミット履歴を見ても壊れた瞬間が分からない。CIも通る。テストは自分たちのコードしか見ていないからです。本番だけが、静かに落ちていました。

🔗予備を用意したのに、一緒に落ちた

1つ目がだめなら2つ目、という二段構えにしていました。その2つが同じ系統でした。

落ちたときのために、代わりのモデルを1つ用意してありました。ちゃんと備えていたつもりです。ところが実測してみると、2段とも404でした。全滅です。

理由は単純で、予備に選んでいたのが同じ系統の1つ前の版だったからです。先方が系統ごと提供を終えたので、本命も予備も同時に消えた。並べた瞬間は二重化に見えても、根っこが同じなら一緒に倒れます。

しかも、有料プラン側に用意していた別経路も、別の理由(利用枠の上限)で止まっていました。つまり助言機能は、どの経路からも返らない状態だった。備えは3つあったのに、生きていたのは0本でした。

ここから得た決まりはひとつです。予備は本命と別の系統から選ぶ。同じ提供元の隣のバージョンを予備と呼ばない。名前が違うだけで運命が同じなら、それは予備ではありません。

📋「一覧に載っている」は「呼べる」ではない

生きているかどうかは、一覧を眺めるのではなく1回叩いて確かめることにしました。

最初は、提供モデルの一覧を見て確認すればいいと考えました。でもこれは足りません。一覧に載っていても、こちらの契約や利用枠の都合で呼べないことがあるからです。実際、片方はまさにそれで止まっていました。

なので検査は、一覧の照合ではなく実際に1回リクエストを投げて応答を確かめる形にしました。小さな問い合わせを1件送って、返ってくるかを見るだけです。数秒で終わります。

外部サービスに依存している以上、こちらの都合とは関係なく状況は変わります。だとしたら、変わったことを自分で見に行くしかない。設定の正しさではなく、今この瞬間の生死を見る検査に切り替えました。

🔍見つけ方が偶然だったことのほうが痛い

この故障は、まったく別の作業をしていて偶然見つかりました。仕組みで見つけたのではありません。

気づいたきっかけは、実行環境を移す作業の途中で、通しの動作確認をしたことでした。新しい環境でエラーが出たので、最初は移行のせいだと思いました。

念のため、まだ触っていない旧環境に同じリクエストを投げてみたら、そちらも同じエラーでした。移行前から壊れていた、と分かった瞬間です。ここを確かめずに進んでいたら、まったく無関係な箇所を疑って時間を溶かしていたと思います。

でも、いちばん反省したのはそこではありません。偶然でしか気づけない仕組みだったことです。誰かが別件で通しの確認をしなければ、あのまま気づかなかった。一人会社は目が2つしかないので、偶然に頼る監視は監視として成立しません。

いまは、外部依存の生死を定期的に叩いて確かめる小さな検査を回しています。壊れているのに気づかない期間を、なるべく短くするためです。

📚参考

どちらも今回の失敗を説明してくれた考え方です。

予備が本命と一緒に倒れる現象には共通原因故障(common cause failure)という名前が付いています。冗長化したはずの複数の部品が、共有された1つの原因で同時に壊れるという整理で、今回の「同じ系統で2段」はまさにこれでした。

モデルの提供終了そのものは事故ではなく、あらかじめ告知される運用です。たとえば Anthropic はモデルの非推奨・提供終了の一覧を公開していて、どのモデルがいつ止まるかが読めるようになっています。読みに行く習慣がこちらに無かった、というだけの話でした。

🧭まとめ: 触っていないコードにも寿命がある

外部サービスの名前をコードに書いた時点で、その一行には期限が付きます。こちらが更新しなくても、向こうの都合で切れる。しかもテストにも履歴にも出ないので、本番だけが静かに壊れます。

今のうちのやり方は、素っ気ないものです。外部に依存している箇所を洗い出す。予備は別の系統から選ぶ。生死は一覧ではなく実際の呼び出しで測る。この3つだけです。

あなたのコードにも、外の名前を書いた行はありませんか。もしあるなら、今日それが本当に生きているかを、一度だけ叩いて確かめてみてください。

開発パートナーを探していますか?

AIでプロダクトを最速で形にしませんか?

最短1週間でMVPを開発。アイデアの検証から本番リリースまで、i-Willinkがフルサポートします。まずはお気軽にご相談ください。

無料で相談する

最新記事をメールで受け取る