45395 - シコウサクゴ -

Agentの「該当なし」を鵜呑みにしない検証Trinity:未参照画像2枚→107枚の乖離

2026-05-17
AI駆動開発
AI駆動開発
Claude Code
検証
subagent
Astro
Last updated:2026-05-17
10 Minutes
1832 Words

ブログの未参照画像を削除したくて、Claude Code の Explore subagent に調査を依頼しました。返ってきた要約は 「未参照は2枚だけです」

念のため自分で直接 grep を打ったら、107枚が未参照でした。実際に削除したのは87枚。Agent の報告と桁が2つズレていたわけです。

これは Agent が悪いのではなく、AI agent に「該当なし」を判定させると構造的に起きる現象です。本記事は、この事故から導き出した「検証 Trinity(三層検証)」という運用ルールの記録です。

事の発端:「未参照画像を調査して削除コマンドを教えて」

ブログの public/images/ 配下には記事ごとのキャッチ画像が135枚溜まっていました。OGP 画像を代表画像1枚に統一する変更を入れたあと、過去記事ごとの個別画像が宙に浮いた状態でした。

リポジトリの掃除をしたくて、Claude Code に依頼しました。

1
ユーザー: ブログで参照していない画像を調査して削除コマンドを教えて

Claude Code は内部で Explore subagent を起動して調査を始めました。

Agent の第一報:「未参照は2枚」

Explore agent からの要約は、こんな趣旨でした。

1
Agent: 135枚を調査した結果、明らかに未参照と思われるのは2枚のみです。
2
- aidriven_prevention_of_error_recurrence.jpg
3
- aidriven_windows_task_schedule.jpg
4
5
その他の画像はファイル名と記事スラッグが一致しているため、参照されていると推定されます。

この瞬間、私は危うく「2枚だけなら手で消すか」と納得しかけました。しかし違和感がありました。

最近、私は OGP 画像を全記事 bg.jpg に統一する変更をコミットしたばかりです。記事ごとの個別 OGP を廃止したのに、なぜ「ファイル名と記事スラッグが一致 = 参照されている」と判定されるのか?

直接検証してみたら桁が違った

Agent の summary を信じず、自分で grep を打ち直しました。

Step 1: 全画像と「実際の参照」を抽出

Terminal window
1
ls public/images/ > /tmp/all_images.txt
2
3
# ソースコード内の /images/... 参照を全部拾う
4
grep -rhoE '/images/[a-zA-Z0-9_.-]+' src/ public/ astro.config.mjs 2>/dev/null \
5
| sed 's|/images/||' | sort -u > /tmp/referenced.txt

結果は 26件 しかありませんでした。

Step 2: 記事 frontmatter の image: 指定もチェック

Astro の記事は frontmatter で image: を指定すると個別 OGP 画像を持てます。

Terminal window
1
grep -c '^image:' src/content/blog/*.md

結果は 0件。誰も使っていませんでした。

Step 3: fallback を template から読む

src/components/BlogHead.astro
1
const defaultOgImage = 'bg.jpg'
2
const resolvedImage = image || defaultOgImage

つまり、全記事の OGP は bg.jpg にフォールバックしていました。「ファイル名 = 記事スラッグ」というルールは存在しなかったのです。

Step 4: 差集合で未参照を確定

Terminal window
1
sort -u /tmp/referenced.txt固定keepリスト > /tmp/keep.txt
2
comm -23 /tmp/all_images.txt /tmp/keep.txt > /tmp/unreferenced.txt
3
wc -l /tmp/unreferenced.txt
4
# → 107

107枚。Agent 報告の 53.5倍でした。

なぜ Agent は間違えたのか

Explore agent の判断ロジックを推測すると、こうだったはずです。

ファイル名 aidriven_xxx.jpg と記事スラッグ aidriven_xxx.md が対応しているから、おそらく参照されているだろう

これは ヒューリスティック(経験則による推定) です。一般的な Astro ブログではこのパターンが多いので、確率的には正しい推論です。

しかし、私のブログは 少し前に OGP 統一の変更を入れていたため、その経験則が成立しなくなっていました。Agent はその「最近の変更」を知らないまま、一般則で判断したのです。

検証Trinity:三層で「該当なし」を裏取りする

この事故から、AI agent の「網羅した/該当なし」報告に対して、3つの層で裏取りする運用を作りました。

層1: Agent summary(最速・最も誤りやすい)

Agent の要約。手軽だが、特に 「該当なし」「未参照」「網羅完了」 系の判定は信頼度が低い。手がかりとしては使うが、最終判断には使わない

層2: 直接 grep / 差集合(中速・確実)

ソースコードを自分で grep する。Agent と同じ場所を見ているように思えるが、全件突合のロジックを自分で書くことで、ヒューリスティック判定の余地を排除できる。

Terminal window
1
# 全件 - 参照 = 未参照、を機械的に計算
2
comm -23 <(sort 全件) <(sort 参照件) > 未参照

層3: ビルド後生成物の grep(最遅・最も確実)

Astro/Next.js のような静的サイトジェネレータなら、ビルド後の dist/ を見れば 実際に HTML に焼かれた参照 がわかります。

Terminal window
1
pnpm build
2
grep -rhoE '/images/[a-zA-Z0-9_.-]+' dist/ | sort -u > /tmp/dist_refs.txt
3
4
# 削除予定の画像が dist に出てこないか確認
5
while IFS= read -r ref; do
6
[ -f "public/images/${ref#/images/}" ] || echo "MISSING: $ref"
7
done < /tmp/dist_refs.txt

MISSING: 行が 0 なら、ビルド成果物として 404 にならないことが保証されます。

適用シーン:いつ Trinity を使うべきか

毎回これをやる必要はありません。コストが高くなる判定にだけ適用します。

判定タイプTrinity 必要?理由
「このバグの原因は X です」不要バグ修正は逆に検証可能(再現テスト)
「この関数の使い方は Y です」不要動かして確かめられる
「未参照ファイルは N 個です」必須削除後に取り戻せない
「該当する設定は見つかりません」必須「存在しない」の証明は本質的に難しい
「全ファイルをチェックしました」必須網羅性は自己申告では検証不能

ポイントは 「false negative(見落とし)が致命的になる操作」 で必須化することです。削除・マイグレーション・一括置換が典型例です。

結局、何枚削除したか

最終的な作業ログ。

  • 調査: 135枚中 107枚が未参照 と判明
  • 削除: そのうち 87枚をコミット(残り20枚は未追跡ファイルで別途整理)
  • 検証: pnpm build で 405ページ生成、画像404はゼロ
  • 残: 47枚(必要な固定アセット + 最新記事用)

Agent が言っていた「2枚」のままだったら、133枚のゴミが永遠に残るところでした。

まとめ:Agent の沈黙誤判定を消す

AI agent は 「ある」を見つけるのは得意ですが、「ない」を証明するのは苦手です。これは AI の限界というより、「網羅性は自己申告では検証できない」という論理的な性質です。

だから、削除・移行・棚卸しのような false negative が致命的な操作 では、Agent の要約を一次情報として使いつつ、自分で機械的な突合を書いて裏取りする。これが検証 Trinity の本質です。

Agent を使わない、ではなく、Agent の出力を「仮説」として扱い、自分で証拠を集める。AI 駆動開発で最も効くのは、この一段階の懐疑です。

Article title:Agentの「該当なし」を鵜呑みにしない検証Trinity:未参照画像2枚→107枚の乖離
Article author:45395
Release time:2026-05-17

記事へのご質問・ご感想をお聞かせください

フィードバックを送る