— Insights
AI コードレビューを多層で組む — hook × サブエージェント × 第 2 モデル
1 つの LLM に全レビューを任せると、誤検知で信頼が壊れ、権限不足で全 push が止まります。決定論ゲート・Claude サブエージェント・Codex 第 2 モデルの 3 層に役割を分けると、AI レビューは実運用に耐える強度を持てます。
「AI に PR をレビューさせよう」と決めたその日から、選択肢は膨らみ続けます。GitHub Copilot Review、CodeRabbit、cubic、Graphite の Diamond、あるいは自前 subagent と Codex CLI の組み合わせ。どれもそれっぽく動くのですが、少しの期間運用すると同じ結論に辿り着くはずです — 1 つの LLM に全レビューを任せると、誤検知が積もって使われなくなる。
この記事は、その先で「多層に役割を分ける」設計を採用した実例のまとめです。決定論的な commit ゲート、Claude サブエージェントによる push 前レビュー、Codex 第 2 モデルによる区切りの相互チェック。3 つのレイヤーに違う責務を持たせることで、AI レビューは実運用に耐える強度を持ち始めます。前提として Claude Code hooks で品質ゲートを作る も併読すると、Tier 0 の実装がつながって読めます。
単層 AI レビューが崩れる 2 つの理由
まず「なぜ 1 つのモデルに全部任せるとダメか」を短く整理します。この理解を挟まないと多層設計の意味が薄れます。
誤検知が信頼を先に壊します。 指摘の内容が spurious に見えた瞬間、レビュー全体が「ノイズ」として扱われるようになります。誤検知が続くと、開発者はレビュー結果を眺めもしないで通過するようになり、レビュー機能が事実上死にます。誤検知率の絶対値はツールと設定で大きくぶれるので断定しにくいのですが、Graphite の解説記事は業界推計として 5〜15% のレンジを挙げています(一次研究へのリンクはないので、あくまで肌感の裏付け程度に読むべき数字です)。
より参考になるのは Cloudflare が公開した実運用データです。同社は AI コードレビューを全社展開した最初の 30 日間で、48,095 件の MR に対して 131,246 回のレビューを走らせています。注目したいのは指摘の量で、平均 1.2 findings/review まで絞り込んだうえで、開発者が結果を手動でオーバーライドする「break glass」は 0.6% に留まっています。同社は “We biased hard for signal over noise” と明言していて、指摘を減らす方向に倒すこと自体が設計判断だったことが分かります。
モデルの死角は 1 モデルでは埋まりません。 どの LLM にも系統的な穴があります。Claude は長文の因果整理に強く、Codex(GPT 系)はコードのパターン推論に細やか、といった得意領域の違いがあります。1 モデルで全部を拾おうとすると、そのモデルが苦手とする種類のバグはずっと見逃されます。別系統のモデルで回した review が Claude の見落としを拾い、その逆も起こる、という「モデル多様性」を仕組みとして担保する必要があります。
答えは、責務ごとに層を分けることです。以下の 3 層構成が最小構成として機能しています。
Tier 0 決定論ゲート — 毎コミット必ず、機械的判定のみ
まず typecheck / lint / format check / secret scan は commit hook で決定論的に止めます。ここは 絶対に LLM を挟まない のが原則です。LLM を挟むとレイテンシが跳ね、コストがかさみ、コミットのリズムそのものが崩れます。次のようなイメージです。
# .claude/hooks/pre-commit-check.sh (抜粋)
set -euo pipefail
# secret スキャン (全リポで共通)
if git diff --cached | grep -E "(sk_live|api_key=|PRIVATE KEY|ghp_[A-Za-z0-9]{36})" >&2; then
echo "❌ secret パターンを検出" >&2
exit 2 # Claude Code の hook 仕様では exit 2 でブロック
fi
# stack 別ゲート (TS/Node の例)
if [[ -f package.json ]] && command -v pnpm >/dev/null; then
jq -e '.scripts.typecheck' package.json >/dev/null && \
CI=true perl -e 'alarm 120; exec @ARGV' pnpm typecheck || {
echo "❌ typecheck 失敗" >&2
exit 2
}
jq -e '.scripts.lint' package.json >/dev/null && \
CI=true perl -e 'alarm 120; exec @ARGV' pnpm lint || {
echo "❌ lint 失敗" >&2
exit 2
}
fi
exit 0
Tier 0 のポイントは 速さと確実さ です。フルビルド(astro build / next build 等)は commit ごとに走らせるには重すぎるので Tier 0 には含めず、push 前や CI に降ろします。テストも「差分に近い部分だけ」に絞る運用が現実的です。テストランナーの watch モードで無限ハングする話や、cwd が git root と一致しない罠は 前の記事 に詳しく書きました。ここでは「Tier 0 は commit ごとに 30 秒以内で終わる薄い層」と割り切ります。
Tier 1 Claude サブエージェント — push 前・「重大のみ block」
次に、push 直前に Claude Code の agent hook で subagent を起動し、差分を LLM でレビューさせます。これが Tier 1 です。Tier 0 と違い、LLM を挟むので不確実性が入りますが、「block するのは高信頼度の重大指摘のみ、それ以外は advisory」 の運用にすれば信頼を壊さずに済みます。設定は ~/.claude/settings.json に直接書きます。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"if": "Bash(git push*)",
"hooks": [
{
"type": "agent",
"prompt": "現在のブランチと origin の差分を code-reviewer 観点でレビューし、以下の重大な問題のみ block してください。secrets / 認証欠落 / SQL injection / XSS / SSRF / 明白なクラッシュ。それ以外は advisory として扱い、必ず {\"ok\": true} を返してください。信頼度 75 未満の指摘は返さないでください。最新の commit message に [skip-review] が含まれる場合は無条件に {\"ok\": true} を返してください。"
}
]
}
]
}
}
この仕組みで最初に踏むのが 「subagent が git を読めない」問題 です。差分を確認するには subagent が git diff / git log / git show を叩く必要があります。permissions.allow にこれらが入っていないと、subagent は「評価不能」で ok: false を返し、全 push が止まります。これは fail-closed の設計で正しい振る舞いなのですが、user global の permissions に読み取り系 git を通しておかないと本当に何もできなくなります。
{
"permissions": {
"allow": [
"Bash(git log:*)",
"Bash(git diff:*)",
"Bash(git show:*)",
"Bash(git status:*)",
"Bash(git rev-parse:*)"
]
}
}
もう一つ、experimental な if: "Bash(git push*)" マッチャは 複合コマンドで誤発火することがあります。「git check-ignore && git status」のようなチェーンで、なぜか push レビューが動いた事例がありました。単体の git push では再現せず、パーサの複合コマンド扱いの緩さが原因のようです。実運用では commit message パターンの [skip-review] を素早く使えるようにしておくことで、この手の突発発火をかわしています。
Tier 2 Codex 第 2 モデル — 節目に別モデルで相互チェック
Tier 1 まででも実用にはなりますが、Claude だけに見張らせている状態には残る不安があります。ここで Codex(OpenAI 系)を第 2 モデル として、区切りのタイミング(PR 作成前 / 大きな設計変更後 / リリース直前など)に手動で回します。Codex CLI には codex exec review サブコマンドがあり、差分スコープと review ロジックが内蔵されています。自前で prompt を書く必要はほぼありません(以下は 0.142.5 で確認した挙動です)。
# 未コミット差分をレビュー
codex exec review --uncommitted -m gpt-5.6-sol --json
# push 前 (base 比較)
codex exec review --base origin/main -m gpt-5.6-sol --json
# 特定コミットだけ
codex exec review --commit <sha> -m gpt-5.6-sol --json
モデルは -m で明示指定します。上の例で使っている gpt-5.6-sol は 2026 年 7 月時点で Codex のカタログ上「最新の frontier agentic coding model」と位置づけられているもので、エイリアスの gpt-5.6 でも同じものを指せます。--json で JSONL イベントを吐かせて、confidence 0.8 以上の findings だけを拾う運用にしています。
第 2 モデルの意義はレビューの網目を細かくすることではなく、「同じモデルで見落とすタイプのバグ」を潰すことです。Codex が拾って Claude が拾わなかった指摘、あるいはその逆が出た時こそ、レビューが健全に多層化している証拠になります。片方が全部拾えているなら、単に片方が過剰に鳴っているだけの可能性が高いです。
Codex を回すコストは軽くないので、毎 push で走らせるのはやり過ぎです。私は /ready という自前スラッシュコマンドの中に Tier 2 を任意で組み込む形にしていて、/ready full や /ready codex と引数を付けたときにだけ Codex が呼ばれます。「重い層は手動起動、軽い層は毎回」のメリハリが実運用の肝になります。
3 層をまとめる /ready コマンド
上の 3 層は、それぞれ別のツールで動きますが、開発者の視点では 1 つのゲート としてまとめて見せた方が使いやすくなります。私は /ready というスラッシュコマンドで層をラップしていて、実行時に次のような 1 枚表を出します。
| チェック | 種別 | 状態 | 指摘 | ブロック |
|---|---|---|---|---|
| astro check | 決定論 | ✅ pass | 0 errors / 0 warnings | 必須 |
| pnpm typecheck | 決定論 | ✅ pass | — | 必須 |
| pnpm lint | 決定論 | ✅ pass | — | 必須 |
| code-reviewer | LLM | ✅ pass | 🟡 1 件 (docstring rot) | 任意 |
| secret-detector | LLM | ✅ pass | — | 任意 |
| codex | LLM(外部) | ➖ 未実施 | /ready full で実行 | 任意 |
この表の一番の効き目は、「決定論」と「LLM」が視覚的に区別されていることです。決定論に ❌ があれば commit 不可、LLM 側の指摘は基本 advisory で参考程度、というルールが実行結果を見ただけで直感的に伝わります。信頼度は「決定論 ≥ LLM 高信頼度 ≫ LLM 一般所見」の順で暗黙に扱われます。
ブロック方針の成熟:何を止めるべきか
多層構成の落とし穴は、レイヤーごとに ブロック方針を混乱させる ことです。以下の分類が現状の運用解です。
- block: 決定論の failure(type エラー、lint 崩壊、secret 検出、危険コマンドなど)
- block: LLM 高信頼度の重大(明白な認証欠落、平文 credentials、明確な SQL injection ・XSS ・SSRF、明白なクラッシュを招くバグ)
- advisory: それ以外の LLM 指摘(設計提案、可読性、リファクタ、パフォーマンス最適化、テスト網羅の指摘など)
「block: LLM 高信頼度の重大」の閾値設計は保守的にしておきます。運用初期は confidence 0.8 以上 & 「secret / 認証 / injection / crash」のみ block、他はすべて advisory、と厳しめにスタートし、advisory で 4〜6 週間の運用実績が溜まったカテゴリだけを段階的に block に昇格 させます。前述の Cloudflare が指摘の量を意図的に絞ったのと同じ発想です。いきなり全部 block にすると開発者が bypass を条件反射で覚え、その後どんな重大指摘が来ても素通しされる状態になります。
hook と on-demand の使い分け
もう一つ多層設計に組み込みたいのが、hook でやること / /ready でやること の役割分担です。次の性質を持つチェックは on-demand の /ready に寄せます。
- サブエージェントを呼ぶ必要があるもの(hook から subagent は直接呼べません)
- 数秒以上かかるもの(毎回の tool 実行で待たされると生産性が壊れます)
- 開発者が「今のタイミングで確認したい」と自覚しているもの(区切り前・PR 作成前)
逆に、毎コミットで数百ミリ秒以下で確定するチェック(secret パターン検出、危険コマンドブロック、format)は hook に寄せます。hook は決定論の高速チェック専用、on-demand は LLM を含む重い総合レビュー専用、と役割を切ると設計が単純化します。両方に load-balanced に載せようとすると、hook のタイムアウトと /ready の粒度がどちらも中途半端になりがちです。
リポ規約は Markdown で共有する
多層で走らせるレビュアたち — Claude subagent と Codex — に、それぞれ別々の prompt をメンテすると規約がぶれます。ここは CLAUDE.md チェーン(ないし Codex の AGENTS.md)を 共通の規約ソース として使うのが実務解です。
Codex は root から作業ディレクトリまでの AGENTS.md を順に連結して読み込み、階層が近いものほど後方に置かれるので上書きが効きます。レビュー規約を code_review.md のような別ファイルに切り出す場合、自動では読まれない点に注意が必要です。AGENTS.md から明示的に参照して初めてレビュー時に反映されます。Claude 側は subagent の prompt に CLAUDE.md の関連 section を明示的に注入します。
規約が Markdown に集約されていれば、「あるチームでは snake_case 禁止、あるリポでは any 使用禁止」といったローカルルールも 1 か所で管理できて、レビュアの一貫性が保てます。規約更新の PR は Markdown だけ触れば済むので、LLM レビュアの品質を上げるコストが劇的に下がります。
まとめ
AI コードレビューを「1 モデル任せ」で運用しようとすると、遅かれ早かれ信頼が壊れます。誤検知率が数 % に載る前提と、モデルごとの死角がある前提を受け入れると、多層で役割を分ける 設計が自然な帰結になります。commit 前の決定論ゲート、push 前の Claude サブエージェント、節目の Codex 第 2 モデル。各層に「何を block し、何を advisory とするか」を明確に持たせ、開発者には 1 枚の表で見せる。この形なら、AI レビューは長期間運用に耐えます。
現状の運用は前述の /ready コマンドに集約されていて、その仕様の正本は vault の knowledge/tech/ai-code-review-practices.md に置いています。ADR 側でも「多層コードレビュー」を意思決定として残しているので、時間が経った後でも「なぜこの設計を採ったか」を追えるようにしてあります。まずは Tier 0 の hook を 記事 #1 の要領で組み、次に Tier 1 の subagent を薄く導入するのが実装順として自然です。Tier 2 は「余裕が出てから」でよく、いきなり 3 層すべてを揃える必要はありません。