本文へスキップ
AI活用

AIエージェントの公開前チェックは2層に分ける|決定的ルールとLLM審査の設計

Hirokuma
13分で読める
AIエージェントの公開前チェックは2層に分ける|決定的ルールとLLM審査の設計

AIエージェントに公開作業を任せるときの品質チェックは、1つの賢いAIレビュアーに全部任せるのではなく、「決定的に落とす層」と「LLMに判断させる層」の2層に分ける。私が毎日動かしているSNS・ブログの自動運用は、この分け方で公開前チェックを組んでいます。そして先日、GitHubがalt textの自動チェックについて公開した設計解説が、理由まで含めてまったく同じ分け方をしていました。この記事は、自分の2層構成の実装記録と、GitHubの設計から持ち帰った点の整理です。

対象読者は、AIエージェントの出力をそのまま外に出す運用(公開、送信、デプロイ)を組もうとしている実務者です。結論を先に置きます。客観的に証明できる違反は決定的なルールで機械的に落とし、主観的な品質はLLMに判定させる。そしてLLM側には「誤検知でも運用が止まらない逃げ道」を先に設計しておく。 この2つを混ぜると、チェック機構そのものが信用を失っていきます。

全部LLMに任せたくなる誘惑

エージェントの出力チェックを作るとき、最初に来る誘惑は「レビューもLLMにやらせれば、ルールを書かなくていい」です。禁止事項も文体も事実確認も、まとめてプロンプトに書いて、賢いモデルに判定させる。一見きれいに見えます。

ただ、この一枚岩のレビュアーには2つの問題があります。1つは、絶対に通してはいけない違反(たとえば本番ブランチへの直接push)まで確率的な判定に乗ってしまうこと。LLMの判定は揺れるので、「100回に1回通る」禁止事項は禁止になっていません。もう1つは逆で、判定が厳しすぎたときに誤検知の苦情がレビュアー全体に向かうこと。「またAIが変なところで止めた」が積み重なると、チェックそのものが外される圧力になります。

言い換えると、一枚岩のレビュアーは「見逃してはいけないもの」と「見逃しても工数損で済むもの」を同じ確率的な判定に乗せてしまいます。前者に必要なのは賢さではなく確実さで、後者に必要なのは確実さではなく判断力です。要求が逆方向なので、1つの仕組みで両方を満たそうとするとどちらも中途半端になる。ここが分離の出発点でした。

そこで私の運用では、チェックを性質の違う2層に分けました。

自分の2層構成

第1層: hooksで決定的に落とす

エージェントが破ってはいけない操作は、Claude Codeのhooksでコマンド実行前に検査して機械的にブロックしています。mainへの直接pushや自己マージのような「戻れない操作」が対象で、正規表現ベースのガードスクリプトが判定し、引っかかればその場で実行を止めます。判定に迷うケースは通す側ではなくブロック側へ倒す、という方針も固定です。

Claude Codeのhooksで自動運用に禁止ラインを引く|Bashガード実装記録Claude Codeのhooksで、エージェントに破らせたくない操作をコードとして強制する実装記録。PreToolUseフックの設定、Bashコマンドを検査するガードスクリプト、exit code 2でブロックする仕組み、正規表現ルールの設計と限界まで、実運用中の構成をそのまま解説します。www.tentspace.net

この層の特徴は、誤検知がほぼなく、なぜ止まったかを1行で説明できることです。「このコマンドはこのパターンに一致したから止めた」。判定は毎回同じで、揺れません。その代わり、この層に置けるのは機械的に判定できるルールだけです。

決定的な層はhooksだけではありません。ブログ記事の公開フローでは、frontmatterの必須フィールド、内部リンクの参照先、ローカル画像の存在をスクリプトで検証していて、これもLLMには聞かない側です。「必須フィールドが欠けている」「存在しないslugへリンクしている」は証明できる違反なので、スキーマ検証として決定的に落とす。CIでも同じ検証が走るので、すり抜けてもマージ前に止まります。決定的な層は1枚である必要はなく、コマンド実行前・コミット前・マージ前と、確実に判定できる場所に薄く重ねる形になっています。

第2層: criticサブエージェントにrubricで判定させる

一方、原稿の品質——事実と出典の整合、文体、禁止表現、検索意図とのずれ——は機械的なルールでは書き切れません。ここはLLMの仕事です。私の構成では、執筆エージェントとは別の読み取り専用サブエージェント(critic)が、ファイルに固定した審査基準(rubric)に照らして PASS/FAIL を判定します。PASSしたものだけが公開工程へ進めます。

Claude Codeのサブエージェントで公開前レビューを分離する|critic実装記録Claude Codeのサブエージェントで、公開前レビューを執筆エージェントから分離した実装記録。読み取り専用のcritic定義、判定基準のファイル固定、PASS/FAILの機械判定、修正2回上限と隔離まで、実際に運用している構成を解説します。www.tentspace.net

criticを書き手と別のエージェントにしているのは、自分の原稿に自分でFAILを出すことがまず起きないからです。審査役は読み取り専用にして、原稿を直す権限を持たせない。直すのは書き手側で、criticは指摘を返すだけ。この分離があるので、判定基準のファイルを差し替えれば審査の目線だけを変えられます。

そしてこの層には、最初から「誤検知でも止まらない逃げ道」を組み込んであります。FAILした原稿の自動修正は2回まで。2回直しても通らなければ、その原稿は公開せずに隔離して人間に通知し、システムは次の新規作成へ進む。隔離は「あとで自動で再挑戦する」状態ではなく「人間に引き渡した」状態で、エージェントが勝手に列へ戻すことはありません。良い原稿を誤ってFAILにする損失は工数で済みますが、審査待ちで運用全体が止まる形にはしない、という設計です。

どちらの層に置くか迷ったら

実際に組んでいると、どちらの層に置くべきか迷うルールが出てきます。私の判定基準は「そのルールの違反を、実行ログの1行で第三者に証明できるか」です。

  • 文字数の上限、禁止コマンドのパターン、必須フィールドの有無、リンク切れ → 証明できる。決定的な層へ
  • 「経歴の自己言及をしない」「煽りを書かない」「出典が主張を支えているか」 → 表現の解釈が要る。LLM層へ
  • 迷う例: 「CTA文言を本文に書かない」。特定の定型句なら正規表現で書けますが、言い回しを変えた誘導は無限にあります。私は決定的な層に代表パターンだけ置き、残りはLLM層のrubricに書く、という二重掛けにしています

境界のルールを片方の層だけで完璧にしようとすると、正規表現が肥大化するか、LLMに確実性を求めて失望するかのどちらかになります。両方に薄く置くほうが運用は安定しました。

GitHubが同じ分け方を、理由つきで公開していた

この分け方を自分の運用の工夫だと思っていたのですが、2026年8月24日にGitHubのエンジニアリングブログが公開したalt textチェックの解説が、同じ構造を採っていました(Your alt text passes automated checks — that doesn't mean it's any good、確認日2026-08-26)。

GitHub Accessibility Scanner向けのalt textプラグインは、チェックを明確に2段に分けています。altの欠落、ファイル名がそのまま入っている、プレースホルダ、汎用語、重複——という決定的な5ルールはデフォルトで有効。その上に、GitHub Models経由のVisionモデルで「altが画像を意味ある形で説明できているか」を判定する品質チェックをオプトインとして置く。

品質チェック側の作りも参考になります。画像単体を見せて「良いaltか」を聞くのではなく、見出しやキャプション、周辺テキストという文脈を一緒に渡して、その文書の中で意味の通る説明になっているかを判定させる。「正しいalt」が文脈依存で決まる以上、判定材料も文脈ごと渡すしかない、という設計です。私のcriticがrubricだけでなくfactsファイルや発注書(題材の指示書)まで読んでから判定するのと同じ理屈で、LLM審査の精度は指示文よりも渡す材料で決まる、というのが運用しての実感です。

分けた理由の説明が明快でした。「客観的に証明できること(altの有無、ファイル名パターン)と、主観的な品質判断は違う」。そして誤検知については、こう言い切っています。「誤検知を出す品質ルールは、チームにオフにされるルールだ」。だから確実に判定できるものだけをデフォルトONにし、判定が揺れうる品質チェックは選んだチームだけが使う。チェック機構への信頼を守る側に倒した設計です。

デフォルトONにしない彼らと、必須にした私

面白いのは、LLM判定層の扱いが自分と逆なことです。GitHubは品質チェックをオプトインにしました。私はcriticを全原稿に必須にしています。無人で毎日公開する運用では、品質チェックを「任意」にした瞬間に誰も掛けなくなるからです。

ただ、必須にするなら誤検知コストをどこかで吸収する必要があります。GitHubはオプトインという入口で吸収し、私は「修正2回まで・超えたら隔離して先へ進む」という出口で吸収している。層の分け方は同じで、誤検知の逃がし場所が違うだけ、と整理できます。どちらを選ぶかは、そのチェックを外されたときの損失と、誤検知で止まる頻度の見積もりで決まる話だと思います。

「PASSは適合ではない。床である」

同じ記事でもう1つ持ち帰ったのが、この一文です。GitHubは「自動チェックのPASSは適合ではない。自動チェックは床であって、目標は支援技術の利用者とのテストだ」としています。

これは自分の運用にそのまま刺さります。criticがPASSを出しても、それは「公開してよい最低ラインを割っていない」という意味でしかなく、読まれるかどうかは別の問題です。だから私は、PASSの先の物差しを審査とは別に持つようにしています。公開後の初速の閲覧数や検索流入を週次で振り返り、審査基準ではなく題材と書き方の側を直す。審査は床を保証し、床の上の出来は実測で測る。 この分担を混ぜて「criticの基準を上げれば記事が良くなる」方向に行くと、誤検知が増えて第2層の信用が削れるだけ、というのが今のところの結論です。

なお1つ、推測として書いておきます。私のcriticも、FAILと隔離が積み上がっていけば「またcriticが落とした」となって、審査自体が信用を失うリスクを抱えています。GitHubの言う「オフにされるルール」と同じ力学です。隔離が増え始めたら、それは原稿の質の問題ではなくrubricの側の点検サインだと見るようにしています(ここはまだ数値の裏付けがない運用感覚です)。

2層に分けるときの設計指針

自分の運用とGitHubの設計を突き合わせて、指針を4つにまとめます。

  1. 客観的に証明できる違反は、決定的な層へ。正規表現やスキーマ検証で書けるルールはLLMに聞かない。この層は常時有効にし、判定に迷うものはブロック側へ倒す
  2. 主観的な品質判断は、LLM層へ。基準はプロンプトに埋め込まず、ファイルに固定して判定だけさせる。書き手と審査役は分ける
  3. LLM層には誤検知の逃げ道を先に設計する。オプトインにするか、修正回数の上限と隔離を置くか。どちらもないLLM審査は、いずれ外される
  4. PASSは床として扱う。床の上の出来は、審査基準ではなく公開後の実測で測り、基準の側をむやみに厚くしない

チェックを増やすことと、チェックが信用され続けることは、別の問題です。エージェントの自動運用を長く回すなら、後者から設計するのが結局近道だと思います。

まとめ

  • エージェントの公開前チェックは「決定的に落とす層(hooks等)」と「LLMに判断させる層(rubric審査)」の2層に分ける
  • GitHubのalt textプラグインも同じ層分けで、決定的な5ルールはデフォルトON、Visionモデルの品質判定はオプトイン。「誤検知を出す品質ルールはオフにされる」が分離の理由
  • LLM審査を必須にするなら、修正回数上限と隔離のような「誤検知でも運用が止まらない逃げ道」を出口に置く
  • 「PASSは適合ではなく床」。床の上の品質は審査ではなく公開後の実測で測る