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文字も存在しません。つまり端末では古い版が走っていました。サイズを突き合わせたらこうでした。

バイト
送った側 2,691
端末に在ったもの 3,234

書き込みAPIは成功を返し、更新時刻も進んでいました。それでも中身は置き換わっていなかった。 直し方は単純で、名前を変えて置き直し、走らせる前にサイズを突き合わせる

# 置いたあと、走らせる前に必ず突き合わせる
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)   # → 2027

答えは2027。実際の最大は788です。同じ文書には別系統の採番も書かれていて、そちらは年を含む長い形をしています。語境界を入れていなかったので、その年の部分を統治番号として拾っていました。

# 悪い: 語境界が無いので、もっと長い別の採番の一部にも当たる
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件は全部ノイズです。その場で書いた走査は広すぎて、正しいものまで拾います。

同じ日に、この教訓をもう一度使いました。記事の材料として「決定に紐づくファイル」を自動で拾う機械を書いたところ、ドライブ名・フォルダ名・版番号・ブラウザの名乗り文字列まで「ファイルが見つからない」と報告してきました。印を「既知の拡張子で終わるものだけ」に狭めたら、雑音は38%から7%に落ちました。

印の広さ 拾ったパス 実在 見つからない
広い(最初) 21 13 8(38%)
狭い(修正後) 30 28 2(7%)

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

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

前の作業から引き継いだ資産の規模が、実際に数えたらこうでした。

引き継ぎ書 実測
見張り(ガード) 13本 48本(+自己検査41本)
作業スクリプト 490本 576本
YAMLパイプライン 442本 1,439本

範囲=3リポジトリ、node_modules・退避フォルダ・ビルド生成物を除く、2026-09-22時点。

3つとも少なく出ていました。原因は、引き継ぎ書がファイル名の印象と会話の記憶から起こされていて、一度も全数を数えていなかったことです。少なく出たほうは、1つのフォルダしか見ていませんでした。

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

なぜ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週間で、パイプライン1,439本と見張り48本が動くようになりました(2026-09-22 時点・3リポジトリ)。

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

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

元データと引用のしかた

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

出典:ブレインコミュニティ合同会社「出荷前の見張りは、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?