弊社は自社の業務の一部を、複数のAIエージェントに無人で運転させています。人が見ていない時間に何が動いて何が止まったかを知るために、常駐サービスの死活を5分おきに測って記録し続けています。
無人運転でいちばん怖いのは、警報が鳴らないことではありません。鳴りすぎて、誰も読まなくなることです。今週、その手前まで来ていたことが実測で分かりました。そして直した翌朝、同じ直しを隣の警報に当てそうになって止めました。その2日分をそのまま出します。企業名・個人名は出しません。
同じ形に見える警報が、2つ並んでいた
画面に出る赤には、大きく2種類あります。ひとつは「サービス停止」で、常駐しているはずのものに繋がらなかったときに出ます。もうひとつは「タスク失敗」で、定期作業が0以外の終了コードを返したときに出ます。
どちらも直前の1回の結果だけを見て赤を出す作りでした。片方が誤報だと分かった以上、もう片方も同じ理由で誤報のはずだ——これが翌朝の入口でした。結論から言うと、この横展開は間違いでした。形は同じでも、測っているものの性質が違ったからです。
60回の失敗のうち58回は、前後を正常に挟まれた1点だった
まず1つ目の警報を数えました。記録が残っている2026-08-30から2026-09-20 11:17までの死活記録は、5サービス分で24,779サンプルあります(自社計測。以下すべて同じ記録からの実測)。
このうち失敗しているサンプルは60個でした。ここからが本題です。失敗が連続した固まりとして数え直すと59個の固まりになり、そのうち58個は長さ1、つまり前後を正常なサンプルに挟まれた単発のへこみでした。2回続けて失敗したことがあるのは、記録全体で1回(2026-09-08 13:12と13:17)だけです。
さらに、失敗した60サンプルが起きた瞬間は36通りしかありませんでした。そのうち8つの瞬間では、互いに独立した2つ以上のサービスがまったく同じ秒に揃って失敗しています。この8つの瞬間だけで32サンプル、全体の半分を超えます。別々のポートで動く無関係なプログラムが同じ秒に揃って落ちる確率より、測っている側が一瞬こけたと考えるほうが自然です。
失敗した瞬間の応答時間が、犯人を指していた
決め手は応答時間でした。管制画面の死活記録3,420サンプルのうち、成功したサンプルの応答時間は中央値5ミリ秒・最大222ミリ秒です。一方、失敗した30サンプルの応答時間は2,018〜2,101ミリ秒の範囲に全部収まっていました(実測)。
中央値5ミリ秒で返るものが、落ちるときだけ必ず約2秒かかる。これは「サービスが重くなって返せなくなった」形ではなく、「繋ぎに行った先に誰もいなかったときに、決まった手順で決まった時間だけ待たされた」形です。30回とも同じ形をしている以上、30回とも同じ原因だと見るのが妥当です。
原因は自分たちの設計でした。管制画面は「起動時より新しいプログラムファイルがあれば、落として起こし直す」設定になっています。エージェントが観測まわりのコードを触るたび、5分間隔のサンプルが1点だけ必ずへこみます。9月19日23時47分に出た赤も、同じ時刻に画面そのものは正常に応答していました。直前の23時42分は正常(23ミリ秒)、直後の23時52分も正常(4ミリ秒)です。自分で起こし直しておいて、自分で「停止」と叫んでいたわけです。
直したのは記録ではなく、人に見せる段だけ
候補は3つありました。2回連続の失敗を「停止」とみなす案、再起動直後の一定時間を除外する案、記録は一切触らず表示だけ絞る案です。
採ったのは3つ目です。理由は単純で、記録の意味を変えると「いつ落ちたか」を後から数える用途が壊れるからです。感度は1ミリも下げず、人に赤を見せる条件だけを「直近2サンプルとも失敗」に変えました。判定は1つの関数にだけ置き、画面も巡回もそこを呼びます。同じ数え方を2か所に書き写すと、画面と巡回が別の数を主張し始める——これは過去に実際にやらかしています。
結果は翌朝すぐ確かめられました。9月20日9時47分、同じ形のへこみ(2,030ミリ秒)がまた出ましたが、画面は赤を出しませんでした。単発のへこみは「1点だけ失敗(様子見)」と表示され、記録には生のまま残っています。
引き換えに、本物の停止の検知は最大5分遅れます。これは受け入れた代償です。
隣の警報に同じ直しを当てなかった理由
翌朝、同じ論法でタスク失敗の警報も鈍くしようとして、途中で止めました。
死活監視が測っているのは生存プローブ、つまり「いま繋がるか」という瞬間の状態です。瞬間の値は、測る側の都合で簡単にへこみます。だから連続で確認する意味がある。
一方、タスク失敗が見ているのは実際に失敗した結果の記録です。これは瞬間の観測ではなく、起きてしまった事実の記録です。ここに「2回連続でなければ赤を出さない」を当てると、1回だけ本当に失敗した仕事が画面から消えます。本物を隠す方向の変更です。
同じ「1点で騒ぐ」という形をしていても、瞬間を測った1点と、事実として残った1点は、まったく別のものでした。誤報の直し方を横展開するときに確認すべきは、警報の見た目ではなく、その1点が何を測った1点なのかです。
本当の誤報は「1点で騒ぐこと」ではなく「直った失敗が消えないこと」
では、タスク失敗の警報はうるさくなかったのかというと、別の欠点がありました。最終結果だけを見て、最後にいつ走ったかを見ていないのです。
このため、すでに直った失敗が、その仕事の次の実行時刻まで画面に赤として残り続けます。どれくらい残るかを実測しました。現在有効な定期作業は26本あり、前回の実行から次回の実行までの間隔は、13本が1時間より長く、4本は1日より長い。いちばん長いものは月1回の更新作業で、間隔は720時間です。つまり最悪の場合、とっくに直っている失敗が30日間赤のまま居座ります。
直すべきはこちらでした。うるさかったのは「1点で騒ぐこと」ではなく、「古い失敗がいつまでも消えないこと」です。この併記は現時点で未実装であり、担当を割り当てた状態です。
まだ埋まっていない穴
正直に書いておきます。
ひとつ。今回の「2点とも失敗のときだけ赤を出す」は、過去の記録に当てると58/59の警報を黙らせます。5分で自然復旧するような本物の短い停止があった場合、それは見逃します。その見逃しはまだ一度も実測できていません(そもそも記録上、2点続けて失敗した例が1回しかないため)。
ふたつ。鈍くしすぎることへの歯止めとして、社内では「以前およそ8.7時間の停止に人が先に気づいた」という過去の事故を根拠にしています。ただしその事故は、いま数えているこの死活記録には1行も残っていません(当該サービスの記録開始は2026-09-08 13:21で、事故はそれ以前)。判断の根拠にした出来事を、判断に使った記録そのものは見せられないという状態です。コード内の記述と当時の判断記録にしか残っていません。
みっつ。この停止判定には自動試験がありません。同じ「試験が無い」指摘は今月すでに別の箇所で出しており、直っていない型です。
ここから持ち帰れること
無人で回す仕組みに監視を足すとき、実感として効いたのは次の3点です。
1つ目は、警報を減らす前に、その1点が何を測った1点かを言語化すること。瞬間の観測なら連続確認は正しく、事実の記録なら連続確認は隠蔽になります。見た目の形は当てになりません。
2つ目は、感度を下げるのと、表示を絞るのを分けること。記録を曇らせると、後から「いつ落ちたか」を数える用途まで一緒に壊れます。人に見せる段だけを絞れば、うるささは消えて証拠は残ります。
3つ目は、誤報の原因が自分たちの設計であることを疑うこと。今回の30回はすべて、自分で落として自分で起こし直した瞬間でした。監視対象より先に、監視している側の都合を数えたほうが早い場合があります。
無人運転で本当に怖いのは、止まることではなく、止まったと言われても誰も驚かなくなることです。赤の数を減らす作業は、機能追加より優先度が高いと考えています。