1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIエージェントの「できました」を疑うための実装 — 15日間気づかなかった配信失敗と、報告が嘘をつく4つの型

1
Last updated at Posted at 2026-09-23

AIエージェントの「できました」を疑うための実装

AIエージェントに実装を任せ、記録が残っている7月1日から12週間が経ちました。番号をつけて台帳に残した決定は707件あります(2026-09-23 時点)。そこから「機械が成功を名乗ったのに、実際には効いていなかった」ケースだけを抜き出すと、きれいに4つの型に分かれました。

コードの品質の話ではありません。報告と現物のあいだに検査が無い、という話です。

この記事の数字の出どころは、当社サイトに置いた元データです(配信の通し201回を全部数えた記録・期間と数え方つき)。引用するときは、こちらを出典にしてください。
https://braincommunity.jp/jissoku/mihari.html

型1:「配った」と名乗ったが、配れていなかった

顧客向けの資料を、公開期限が来たら自動で消す仕組みを組みました。毎朝9時に起動し、期限切れを探し、ファイルごと消し、コミットし、本番へ配る。

初めて実物が動いた日の記録がこれです。

2026-09-07 09:00:55 下見=21本 / 消した=21本 / git=凍結して上げた
                    / 突き合わせ=配ってよい / 配信=失敗 rc=2 / 見張り rc=0 OK
2026-09-12 09:00:54 下見=15本 / 消した=15本 / git=凍結して上げた
                    / 突き合わせ=配ってよい / 配信=失敗 rc=2 / 見張り rc=0 OK

2回とも配信が失敗しています。 それなのに行の最後は 見張り rc=0 OK です。気づいたのは15日後でした。

なぜ気づけなかったか。見張りが見ていたのは「ローカルのファイルが消えたか」で、「本番に反映されたか」ではなかったからです。消す処理と配る処理を1本のパイプラインに並べたのに、1行の要約の最後に来るのはローカル側の判定だった。

コードを読み直すと、もう1つ穴がありました。配信が失敗した回は、赤の知らせを置く作りにはなっていました。ところが翌朝、何も消さなかった回が「今日は異常なし」と判断して、その知らせを片づけていたのです。赤は1日で消え、人の目に届きませんでした。

本番を後から測ったら、期限を過ぎた47本は全部「公開期間は終了しました」の1枚を返しました。読めるものは0件(2026-09-23 に測り直しても47本とも同じ)。実害はありませんでした。ただしそれは、別件の配信が後日たまたま巻き取ったからであって、この仕組みが働いたからではありません。

# 悪い: 段ごとの rc を集めずに、最後の見張りだけで名乗る
run_delete()            # rc を見ていない
run_commit()            # rc を見ていない
run_deploy()            # rc=2 だが握りつぶし
if guard() == 0:
    log("OK")           # ← ここが嘘をつく

# 良い: 段ごとの rc を積み、1つでも非0なら全体を赤にする
rcs = {"delete": run_delete(), "commit": run_commit(), "deploy": run_deploy()}
bad = {k: v for k, v in rcs.items() if v != 0}
log(" / ".join(f"{k}={'できた' if v==0 else f'失敗 rc={v}'}" for k, v in rcs.items()))
if bad:
    notify_red(bad)     # 人が必ず見る場所へ置く
    sys.exit(1)

「消した」と「消えたものが配られた」は別の事実で、別々に数える必要があります。 そして、赤を消す条件を「その回が何もしなかった」にしてはいけません。赤は、直ったと測れたときだけ消します(この穴は記事を書く途中で見つけ、公開と同じ日に直しました。いまは毎朝、消した鍵を本番に1件ずつ訊き、1件でも残っていれば赤を片づけません)。

型2:「書けた」と返ってきたが、中身は古かった

同じ日に、別の形で踏みました。計測用のスクリプトを1行直して端末へ置き直し、「書き込み成功」の応答を受け取って、そのまま実行しました。出たのは、直したはずの行のエラーです。

Traceback (most recent call last):
  File "...\check.py", line 20, in <module>
    out('1件目のキー', list(recs[0].keys()))
KeyError: 0

recs という変数は、新しい版には1文字も存在しません。つまり端末では古い版が走っていました。書き込みAPIは成功を返し、更新時刻も進んでいました。それでも中身は置き換わっていなかった。

同じことは、この記事を直していた 2026-09-24 にも起きました。書き込みの応答が成功を返した直後に、端末のファイルの大きさを測った記録です。

バイト
送った側 4,908
端末に在ったもの 4,579

(最初の件は大きさの記録を残していなかったので、2026-09-24 の件の数を載せています。)

直し方は単純で、置き直し、走らせる前にサイズを突き合わせる。

# 置いたあと、走らせる前に必ず突き合わせる
assert remote_size(path) == local_size(path), \
    f"反映されていない: remote={remote_size(path)} local={local_size(path)}"

「成功を返したAPI」と「実際に置き換わったファイル」は、別の事実です。

型3:「数えた」が、数え方が違っていた

決定の記録から統治番号の最大値を取ろうとして、こう書きました。

nums = re.findall(r'C-(\d{3,4})', text)
max(int(n) for n in nums)   # → 2026(これに1を足して「次は2027番」と名乗った)

答えは2026。これに1を足して、次の番号を2027番と名乗りました。採番の機械は、最初から373番と答えていました(2026-08-21 の件)。同じ文書には別系統の採番も219件書かれていて、そちらは年を含む長い形をしています。語境界を入れていなかったので、その年の部分を統治番号として拾っていました。

# 悪い: 語境界が無いので、もっと長い別の採番の一部にも当たる
re.findall(r'C-(\d{3,4})', text)

# 良い: 前後が英数字・ハイフンでないことを確かめる
re.findall(r'(?<![A-Za-z0-9-])C-(\d{3,4})(?![0-9-])', text)

この種の間違いは自分では見つけられません。見つけたのは、同じ数を出す既存の機械が別の答えを返していたからです。

自前の印より、既に在る見張りの判定を正とする。

以前も踏んでいて、そのときは「死んだ絶対パスが62件ある」と自分の走査が言い、正式な見張りの判定は3件でした。残り59件は全部ノイズです。その場で書いた走査は広すぎて、正しいものまで拾います。

この記事の材料を集めるときも、この教訓を使いました。記事の材料として「決定に紐づくファイル」を自動で拾う機械を書いたところ、ドライブ名・フォルダ名・版番号・ブラウザの名乗り文字列まで「ファイルが見つからない」と報告してきました。印を「既知の拡張子で終わるものだけ」に狭めたら、見つからない割合は74%から8%に落ちました(2026-09-24、同じ決定24件を入力にして、広い印と狭い印の両方で数え直した値)。

印の広さ 拾ったパス 実在 見つからない
広い 192 49 143(74%)
狭い 49 45 4(8%)

広い印は、次に本当に見つけたいものを隠します。

型4:引き継いだ数字が、全部ずれていた

前の作業で書かれ、そのまま当社の別の記事に載っていた数字を、出どころまで辿ったらこうでした(2026-09-23)。

引き継いだ数字 辿った結果
AIのクローラが読みに来た回数「1,214回」 出どころを辿れない。記録から数え直すと267回
アクセス解析の期間「90日」 記録は7月9日からで、75日
運用の長さ(年で丸めた書き方) 記録が残っているのは7月からの12週間

3つともずれていました。原因は、数字が前の作業の文章から写され、一度も記録と突き合わせていなかったことです。3つとも、見つけた日に記事を直しました。

引き継いだ数字は、引き継いだ時点で仮説です。

なぜ4つとも同じ形なのか

並べると、全部これです。

成功を名乗る経路と、実際に効いた経路が、別のところを通っている。

  • 型1:ローカルの削除は成功した。配信は失敗した。判定はローカルだけを見た
  • 型2:APIは成功を返した。ファイルは置き換わらなかった。判定はAPIの応答だけを見た
  • 型3:走査は数を返した。数え方が違った。判定は自分の走査だけを見た
  • 型4:前の作業の文章は数を書いた。記録と突き合わせていなかった。判定は文章だけを見た

人が手で作業していた頃は、この差がここまで開きませんでした。手で消せば消えたのが見えるし、手で書けば書けたのが見えるからです。AIエージェントに任せた瞬間、「見える」が「報告を読む」に置き換わります。 そして報告は、書いた本人が書いています。

置いた対策は3つ

1. 段ごとに成否を持たせ、1つでも非0なら全体を赤にする

途中の段が黙って失敗して、最後の段だけ緑になる構造を作らない。外部コマンドは終了コードだけでなく、[FAIL] を含む行を全部と出力の末尾を添えて記録します。失敗の理由はたいてい末尾に出ます。

2. 見張り自身に、3つ揃いの自己検査を付ける

# 素で通る / 故意に壊したら止まる / 戻したらまた通る
assert guard(clean_input)  == 0   # 素 = 0
assert guard(broken_input) == 1   # 故意の違反 = 1
assert guard(clean_input)  == 0   # 戻して = 0

3つ目がないと、何にでも落ちる見張りが「全違反を検出できている」ように見えます。違反の種は既存データに頼らず自分で作ります。データの状態に依存させると、データが変わった日に検査が黙ります。

この仕組みで配信が実際に何回止まったかは、記録から全部数えて当社サイトに置いています。止めた段20種の内訳と、数え方・「この数字で言えないこと」は元データの方にだけ載せています。人の指示でAIが起動した配信 201回(2026-07-21〜09-22)のうち48回が本番の前に止まり、そのうち14回は見張り自身の自己検査が止めました。
→ https://braincommunity.jp/jissoku/mihari.html

3. 「やった」と名乗る前に、現物を数える

「消した」「置いた」「配った」「無い」は、測ってから名乗る。特に在ることと戻せることは別です。配ったものの中身が一致していても、バージョン管理に載っていなければ届いていません。実際に、中身の照合だけを見ていた見張りが、13本すべて「未コミット」の状態を「13本届いている」と報告したことがあります。

おまけ:この記事も、自分の見張りに止められた

対策2で書いた「3つ揃いの自己検査」を、記事そのものにも当てています。顧客名・内部の固有名・実機の絶対パス・署名の有無・数に範囲が添えてあるかを、出す前に機械が見る検査です。

その検査に、この原稿の初稿を通したらこうなりました。

顧客名の名簿 = 48 件(値は出さない)
[FAIL] 案件ID が本文に在る(BC-…)
[FAIL] 実機の絶対パス が本文に在る(C:/…)
[FAIL] 改修番号 が本文に在る(C-…)
KIJI_GUARD_FAIL 件数=3

3件のうち2件は本物でした。上の型3を説明するために、実物の採番と実機のパスをそのまま書いていたのです。「出してはいけないものを書かない」と決めていた本人が、それを説明する文章の中で書いていました。 残る1件は誤検知で、検査が見出し部を本文として読んでいたのが原因です。そちらは検査のほうを直しました。

人の注意力は、自分が今いちばん集中している対象をすり抜けます。だから検査は、書いた本人ではなく機械が持ちます。

まとめ

AIエージェントに実装させること自体は、うまくいっています。記録が残っている7月からの12週間で、見張り49本と、その見張りを試す自己検査41本が動くようになりました(2026-09-22 の終わりの時点・4リポジトリの版管理に載っている物を数えた)。

壊れるのは実装ではなく、実装が自分について語る言葉のほうです。だから私たちは、AIに任せる範囲を広げるのと同じ速さで、AIの報告を信じない仕組みを増やしています。自己検査41本は、そのためにあります。

「AIに全部やらせる」の反対側にあるのは「AIを使わない」ではなく、AIの出力を機械が検算するという設計です。

(2026-09-24 訂正:型2・型3・型4と、まとめの数を、出どころと突き合わせて直しました。型3の件は、元の記録どおり「答えは2026、名乗ったのは2027番、正しくは373番」に直しています。記録が残っていなかった数は、測り直した値か、辿れる別の件に差し替えました。)

元データと引用のしかた

見張りが配信を止めた回数・止めた段の全部・数え方・この数字で言えないことは、当社サイトに置いています。数字を引用するときは、記事ではなく元データを出典にしてください。そのまま貼れる形はこうです。

出典:ブレインコミュニティ合同会社「出荷前の見張りは、201回の配信のうち48回を止めた」
https://braincommunity.jp/jissoku/mihari.html (2026-09-23 測定)

当社が自分で測ったほかの数字も、同じ置き場にまとめています。
→ https://braincommunity.jp/jissoku/


業務システムの内製化と、AI活用の受託開発をしています。
ブレインコミュニティ合同会社 — https://braincommunity.jp/

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?