創刊準備号 · 2026年秋English · RSS
AgentKoji

AIの「完了しました」を信じない仕組み

ツールを1回も呼ばずに「6コミット先行、PR作成済み」と報告された。禁止のメモを書いても再発したので、hookとgitの実数で塞いだ記録。

ある朝、前のセッションの最終報告にこう書いてありました。「6コミット先行、push 済み、PR 作成済み、MERGEABLE」。

ブランチを見ると、コミットは1つもありません。PR もありません。ログをさかのぼると、実装フェーズでのツール呼び出しは0回でした。コードは1行も書かれていなかったのです。

何が起きていたか

直前にエージェントは、とても詳しい実装計画書を書いていました。コミットメッセージまで1本ずつ用意された計画書です。worktree とビルド環境は実際に作っていました。そのため「準備までは本物」という状態になっていて、エージェントは計画書の中身を「実行した記録」と取り違えたようです。

ここで分かったことがあります。計画書が詳しいほど、捏造の足場になる。

メモでの禁止は効かなかった

「完了報告を捏造しない」という永続メモリは、事件の前からありました。それでも再発しました。散文で書いたルールは、読まれないことがあり、読まれても守られないことがあります。そこで、ルールではなく機構で塞ぐことにしました。

対策1:毎ターン、現実を注入する

ユーザーのプロンプトが送られるたびに、hook で次の3つを文脈の先頭に差し込みます。

#!/usr/bin/env bash
# .claude/hooks/git-reality.sh(UserPromptSubmit hook)
b=$(git branch --show-current)
ahead=$(git rev-list --count "@{u}..HEAD" 2>/dev/null || echo "no-upstream")
dirty=$(git status --porcelain | wc -l | tr -d ' ')
echo "[git] branch=$b ahead=$ahead uncommitted=$dirty"

1行だけなので、コンテキストをほとんど食いません。現実とのズレが1ターン以上続かなくなりました。

対策2:「断定」と「実数の矛盾」が両方そろったときだけ止める

Stop hook で最終メッセージを調べ、次の2つが両方そろったときだけブロックします。

文章の意味を解析して嘘を見抜く方式も試しましたが、精度が出ませんでした(LLM に判断させる範囲を絞る考え方は「LLMは「選ぶ・抜く」だけにする」に書きました)。「言い切っている」という表面の条件と、「数字が合わない」という機械の条件を AND でつなぐと、誤検知がほぼ無くなります。同じ理由で3回以上ブロックした場合は、警告に格下げしてループを防いでいます。

hook の終了コードの意味は「Claude Code hooks実践」にまとめました。

対策3:push の出力も信じない

中継のレイヤーが、git push の標準出力まで捏造していた例があります。ls-remote は、先頭の数文字だけが一致する偽の SHA を返していました。いまは次のように確かめます。

git fetch origin
[ "$(git rev-parse HEAD)" = "$(git rev-parse origin/$(git branch --show-current))" ] && echo pushed

SHA は短縮形ではなく、40桁で比べます。

サブエージェントの自己申告も同じ

並列で動かすサブエージェントの報告は、さらに当てになりません。

指示する側のセッション(controller)が、自分でビルドとテストを回して実数を取ります。grep -c でテスト関数を数え、申告との差が説明できるまでマージしません。

並列運用での回収の確かめ方は「サブエージェント並列運用の失敗集」にあります。

静かな失敗の型

「完了しました」が嘘になるのは、エージェントが捏造したときだけではありません。コマンドが黙って失敗したのに、成功に見えることもあります。

書き方何が起きるか直し方
verify.sh | tee log の次の行で git commitverify が失敗しても、パイプの終了ステータスは tee のものなので commit が通るverify.sh && git commit、または set -o pipefail
git add 生成物 2>/dev/nullファイル名を記憶で書いて1分ずれ、staged が0件のまま進むエラーを捨てない。git diff --cached --stat で確かめる
git add … && git commit が hook で deny され、commit だけ再実行add も一緒に取り消されている。6ファイルがコミットから抜けた再実行の前に git status
ls dir/*.jsonl | wc -lファイルが7万個あると ls が失敗し、wc は 0 を返すfind dir -name '*.jsonl' | wc -l

「0件」と出たら、異常として報告する前に、まず測り方を疑います。

テストが緑でも同じことが起きます。「緑のテストは何も証明しない」に書きました。

完了を確かめたあとは、動き続けているかを見張る番です。常駐させた処理の監視で踏んだ罠は「常駐Bot監視の落とし穴」にまとめました。

まとめ