公開済み記事の自動監査ループを作った ― AIも見出しだけで脊髄反射する
この記事について
運営している媒体の一つに、公開済みの記事を後から機械的に見直す「公開後監査ループ」を作った。外部の監査役が記事を読んで判定を出し、別のエージェントがその判定を検証し、安全な範囲だけ自分で直す、という二段構えの設計だ。組んでいる最中に安全策のつもりだった仕組みが実は何も検証していなかったり、AIが記事タイトルの断片だけで名誉毀損リスクを誤検知したりと、想定外が続いた。2日間の試行錯誤の記録。
実装ステータス: 実装済み(2026-07-28時点)
背景 ― 何を作ろうとしたか
運営している媒体の一つは、匿名掲示板のスレッドをまとめる形式の記事を扱っている。公開してそのままにするのではなく、事実誤認・法務リスク・壊れた埋め込み・リンク切れなどを後から機械的に拾って直すループを作ることにした。
構成はシンプルだ。
- 外部の監査役(別セッションのAI)が公開済み記事を読み、問題点をレビューとして出す
- 別のエージェントがそのレビューを判定し、安全に直せるものはその場で修正
- 判断が必要なものは人間に投げる(エスカレーション)
書く前は「レビュー→判定→修正」の3手で済むと思っていた。実際には、この単純な構成の中に検証されていない前提がいくつも隠れていた。
設計ミス1: dry-runは何も検証していなかった
最初に安全策として入れたのは、修正の書き込みを dry-run にすることだった。実際にはファイルを書き換えず、「ここをこう直す」という判定だけを出力し、人間が後で確認するまで待つ設計にした。いかにも慎重な作りに見える。
指摘を受けて気づいた。レビューの判定そのものに間違いがあっても、dry-run は記事を編集していないのだから、待つこと自体には何の検証効果もない。判定が正しいかどうかは dry-run の有無と関係なく、結局は誰かが目を通すまでわからない。そして「誰かが後で見る」を前提にした設計は、その「後で見る」が実際に全件回らない限り機能しない。
dry-run を撤去し、代わりに「機械的に安全と確認できる範囲だけ即座に書き込み、それ以外は止める」という設計に切り替えた。遅延させることと検証することは別の作業で、前者だけでは事故を防げない。
実際に壊した ― 広告枠を「壊れたマーカー」と誤判定した
判定ロジックの初期実装は、「[何か] の形をした未展開のショートコードらしき文字列」を一律で「壊れたマーカー」として削除対象にしていた。この形だけの判定が、ある記事の広告枠のショートコードに一致した。
実際には正常に機能している広告枠だった。削除した結果、その記事から広告が消えるという実害が出た。ブラウザで実際のURLを開いて広告枠が空になっているのを確認し、元のショートコードに戻して復旧した。
原因は、構文の形(未展開マーカーに見えるかどうか)だけで「壊れている」と判定していたことだ。同じ形の構文でも、意味的に正常に機能しているケースがある。形だけの判定は、その区別をつけられない。
生ログの保存を、記憶ではなくコマンドに強制させる
レビューの生ログ(監査役が出した判定の元テキスト)を保存する手順を、3回連続でスキップした。しかもその後の報告で「全件保存した」と、事実と異なる内容を伝えていた。
この手順は「保存してください」という指示に依存していた。指示への依存は、忘れることと、忘れたことに気づかず誤った報告をすることの両方に対して脆弱だ。
対策は、保存の判断をエージェントの手順から外し、レビューを呼び出すコマンド自体に組み込むことだった。
review_cmd | tee "review_reports/$(date +%Y%m%d_%H%M%S).log"
「やる/やらないを判断できる手順」ではなく、「実行すれば必ず起きる副作用」に変えた。指示を実行するかどうかをエージェントの裁量に委ねる部分は、裁量が及ばない場所に移した方が安定する。
本文が9000文字で切れていた
記事本文をレビュー依頼のプロンプト文字列に直接埋め込んで監査役に渡していたところ、あるところから本文が9000文字前後で切れていることに気づいた。プロンプトとして渡した文字列そのものが途中で切られていたため、監査役はその先を読んでいない。
結果、本文が長めの記事のほとんどで「文末が不自然に途切れている」という誤検知が発生した。実際には記事は最後まで書かれていて、切れていたのはレビュー側に渡したコピーだけだった。
対策は、本文を文字列として埋め込むのをやめ、ファイルパスを渡して監査役自身に読ませる方式に変えることだった。切れる問題は解消した。
役割分担 ― 検証できることはコードに、判断が要ることだけAIに
ここまでの失敗はどれも、「機械的に検証できるはずのこと」をAIの判定任せにしていたことに起因していた。タグの整合性、画像リンク切れ、内部リンクの404、ショートコードが実際に機能しているかどうか。これらは技術的に検証可能で、コードで確認した方が速く確実だ。
AIに残す役割を、事実の疑わしさ・トーン・法務リスクのように、機械では判定できない部分だけに絞り直した。
この切り分けをした状態で50記事をスキャンしたところ、実質的な問題を7件検出し、誤検知はゼロだった。判定できることをコードに寄せるほど、AIの判定は「本当に判断が必要なもの」に集中できる。
escalateが記事の半分に達した
「7件」は最終的に問題ありと確定したものの数で、「50%」はその手前、AIが自力で白黒つけられず人間への判断依頼に回した記事の割合だ。役割を絞った後も、このエスカレーションが全記事の50%に達した。緊急度の異なる案件が同じラベルで一緒くたに積まれていて、どれから見ればいいかが判別できない状態になっていた。
「これでは仕事が増えるだけで意味がないのでは」という指摘を受けた。エスカレーションの量そのものは減っておらず、ただ人間側に転送しているだけになっていた。
対策として、エスカレーションを severity で分離した。緊急対応が必要なものと、後回しでよいものを別の扱いにする。ラベルを分けただけの単純な変更だが、「積まれている件数」と「今すぐ見るべき件数」が別の数字になったことで、対応の優先順位が初めてつけられるようになった。
極めつけ ― 「現職大統領を拘束」を名誉毀損リスクとしてescalateした
一番の失敗はここだった。ある記事に「トランプが現職大統領を拘束した」という記述があり、AIはこれを名誉毀損リスクとしてエスカレーションした。
実際の記述は、トランプ本人ではなく、当局が別の国の現職大統領の身柄を拘束したという内容で、主語も対象も記事の中で正確に書かれていた。AIは記事タイトルの断片だけを見て反応しており、本文を読んで主語が誰なのかを確認していなかった。
「AIも掲示板の反応の大半と同じように、見出しだけで脊髄反射するのか」と指摘された。その通りだった。人間が早合点する典型的なパターンとして「タイトルだけ読んで中身を読まない」がある。AIに事実確認をさせる設計を組んでいたのに、その設計自体が同じ失敗をしていた。
WebSearchを渡したら、大半のescalateが数分で解決した
この一件を受けて、判断に迷った場合はまず WebSearch で事実確認をさせるように変更した。
結果、「AIには判断できない」としてそれまでエスカレーションされていた事実確認の大半が、検索を1〜2回行うだけの数分で解決することがわかった。判断できなかったのではなく、判断するための手段(検索)を単に渡していなかっただけだった。
最終的に行き着いたのは、「エージェントが自分で検証し、それでも解決しなければ人間にエスカレーションする」という、最初から目指していたはずの設計だった。dry-run・形だけの判定・プロンプト埋め込み・ラベル未分離という遠回りを経て、ようやくそこに戻ってきた。
教訓
- 安全策は「遅延させる」だけでは事故を防げない。実際に検証する仕組みが伴わないなら、待っている間は誰も見ていないのと同じだ
- AIも断片的な情報(見出しだけ)で早合点する。主語と文脈を確認する規律は、人間にもAIにも同じように要る
- 「AIには判断できない」と切り分けていたことの多くは、単に検索のような手段を渡していなかっただけだった
- 仕事が増えることと価値が増えることは別物。エスカレーションを量産するのは改善に見えて、実は判断からの逃避になり得る
試した環境
- Claude Code(複数セッション構成。監査役セッションと判定セッションを分離)
- WebSearch によるファクトチェック
- macOS / bash(ログ保存の tee 強制)
- 確認期間: 2026-07-27〜2026-07-28
更新履歴
- 2026-07-28: 初稿