この記事は、AIエージェントの運用に関する事故録シリーズの1本である。自社が運用する、探索型ゲームを遊ぶAIキャラクターの運用記録から、進行を担う処理が停止していたにもかかわらず外部からの監視では「正常」に見え続けた事故と、その診断中に自分が立てた誤った仮説を検証する。
この記事で分かること
- なぜ外部監視が「正常」と応答し続けたのか
- 誤診断がどのように組み立てられ、何が真実だったか
- 復旧までの具体的な手順
この記事で分からないこと(正直に書く)
- 生存確認に使う値を別スレッドが書き続けるという同じ構造が、他のどの処理に残っているか
何をした/何が起きた
未知の状況を検知したら詳細を調べに行く処理を追加したが、必要なモジュールの読み込みが漏れており、該当する種類の未知の状況が最初に発生した瞬間に処理そのものが停止する不具合を仕込んでいた。この不具合は、その種類の未知の状況が初めて発生したときにしか発火しないため、追加直後の動作確認では踏まずに通過していた(出所: 自社の運用記録、確認日2026-09-06)。
停止したのは、進行そのものを担う唯一の処理だった。この処理が止まると、状態を示す値は一切更新されなくなる。ところが外部からの問い合わせに応答する処理と、判断を行う処理は別のまま動き続けていたため、外部から見た稼働時間はそのまま伸び続け、応答も返り続けた。外形の監視だけを見れば「サービスは正常」に見える状態だった。判断を行う処理も、止まった画面に向けて判断を出し続けていた(20秒で2回、出所: 自社の運用記録、確認日2026-09-06)。ログも新たに出力されなくなり、手がかりはログファイルの更新時刻だけだった。
実際の停止は2回発生し、いずれも同じ1行が原因だった。1回目は2時間46分、2回目は64分続き、2回目は再起動から38秒後に発生していた。当初、この2回は合わせて「3時間の停止」と誤って記録していた(出所: 自社の運用記録、確認日2026-09-06)。
なぜ気づけなかったか(誤った仮説を立てた経緯)
状態を示す値の1つが、上限より低い値のまま変化していないことに気づき、「誰かがこの値を意図的に0付近へ戻し続けている」という仮説を立てた。値を戻しうる複数の処理を洗い出し、コードを引用しながら筋の通った説明を組み立てたが、これは誤りだった。真実は単純で、その値は処理が停止した瞬間の値がそのまま残っていただけであり、誰も書き込んでいなかった。修正後、同じ値は上限を超えて伸び続けた。
外形監視に使っていた稼働時間や判断の呼び出し回数は、停止した処理とは別のスレッドが進めている値であり、これらが動いていることは、肝心の処理が生きている証明にはならない。むしろ両方が正常に見えることで、誤った安心につながっていた。
どう直したか
読み込みが漏れていたモジュールを追加したうえで、判断を下す処理全体を例外処理で囲み、単一の例外で処理全体が停止しないようにした。判断が決まらない場合は安全側(操作しない)に倒し、その旨を種類ごとに60秒に1回はログへ残すようにした。復旧の確認は、数分間座標のような状態値を連続で取得し、値の「種類数」を数える方法に切り替えた。対策直後の18サンプルでは10種類の異なる値が確認でき、これをもって復旧と判断した(出所: 自社の運用記録、確認日2026-09-06)。
なお、「同じ座標が続いたら停止とみなす」という追加の検知案も検討したが、これは効果がなかった。値を書き込む処理そのものが止まっている以上、停止を数える側の処理も同じスレッドに置けば一緒に止まってしまうためである。
再発防止の具体的な手順
- 値を解釈する前に、その値が今も更新され続けているかを確認する。同じ値を時間を空けて2回読み、変化がなければ「詰まっている」ではなく「書かれていない」と判断する
- 稼働時間や呼び出し回数など、別のスレッド・別の処理が進める値を、肝心の処理の生存確認に使わない
- 判断・実行を担う処理は例外を1か所で吸収し、決められない場合は操作をしないという安全側に倒す
- 例外や異常は黙って握りつぶさず、種類ごとに間隔を空けて1行のログを残す
- 監視・見張りの処理は、見張る対象と異なるスレッド・プロセスに置く
目印(フラグ)を立てても誰も見ていなかった別の事故(旗を立てても誰も見ていなかった記事)と同じ運用記録からは、一時的な理由による学習が通り道を塞いだ事例(通れなかった学習が通り道を消していく記事)も見つかっている。批評だけでは判断の誤りを防げないという点は、判断批評がすべて失敗した記事とも通じる。
自社が運用するAIエージェントの障害対応・運用監視について相談したい場合は、お問い合わせから編集部まで連絡してほしい。
よくある質問
Q1. なぜ処理が停止しているのに外部からは「正常」に見えたのか
進行を担う処理は停止していたが、外部への応答や稼働時間を管理する処理は別のまま動き続けていたため。稼働時間や呼び出し回数は、停止とは無関係に増え続けていた。
Q2. 診断の過程でどんな誤りを犯したのか
状態値が上限より低い値のまま変化しないことから「誰かが値を戻し続けている」という仮説を立てたが、実際は処理が止まった瞬間の値がそのまま残っていただけで、誰も書き込んでいなかった。
Q3. 同じ停止は何回発生し、どれくらい続いたのか
同じ1行が原因で2回発生し、1回目は2時間46分、2回目は64分続いた。2回目は再起動のわずか38秒後に発生していた。
Q4. 復旧をどう確認したのか
数分間、状態値を連続で取得し「種類数」を数える方法に切り替えた。対策直後の18サンプルで10種類の異なる値が確認でき、これをもって復旧と判断した。
Q5. 「同じ座標が続いたら停止とみなす」という追加策は効いたのか
効かなかった。値を書き込む処理そのものが停止している以上、停止を数える処理も同じスレッドに置けば一緒に止まってしまうため。