AIにPRを承認させる4層設計 — newmo「riskjudge」に学ぶLLMコードレビュー自動化の勘所
はじめに
AI が書くコードが増えると、ボトルネックは「書く」から「レビューする」へ移ります。全 PR を人が読む運用は続かない一方で、LLM に「この PR は安全か」と聞いて承認させるのは怖い。その間をどう埋めるかについて、具体的な答えの一つが出てきました。
newmo の技術ブログが公開した「riskjudge」は、PR ごとに approve(bot が承認)か needs_human(人がレビュー)を返す仕組みです。2 か月ほど運用した結果も載っています。この記事では、4 層の設計がそれぞれ何を担っているかを整理し、LLM に任せる部分とコードで決める部分をどこで分けたかに注目して解説します。
riskjudge の 4 層構成
riskjudge は 1 回の LLM 判定で決めません。性質の異なる 4 つの判定を順に通し、1 つでも引っかかれば needs_human にします。安い判定から順に評価し、止まった時点で後ろは実行しません。
① 機械ゲート: 変更パスと差分の形だけを見ます。認証・認可・個人情報・本番 DB マイグレーション・決済・インフラ・CI 設定などに触れたら、LLM に回さず人へ送ります。ただしパスだけで判定すると止めすぎるため、例外があります。GraphQL / Protocol Buffers のスキーマなら、出力型へのフィールド追加や enum 値の追加は通し、意味のある行の削除や input 型へのデフォルトなし非 null フィールドの追加は止めます。CI の YAML は base と head を全文パースし、コメントを除いた AST 同士を比べます。run: | の中の # はシェルのコメントなので、行単位の diff では区別できないからです。生成物の判定には git check-attr で .gitattributes の linguist-generated を引きます。
② LLM ゲート: 差分の中身を読み、JSON を返します。主な項目は、業務ロジックが変わるか(businessLogicChanged とその根拠)、確信度(0〜1)、観点別のリスク(設計・性能・エラー処理・並行性・モバイルの後方互換など。それぞれ severity 付き)です。
③ 複数回判定: 配車や決済などの重要領域と、1,000 行を超える PR は、同じ判定を 3 回行います。集約は多数決ではなく OR で、1 票でも懸念があれば懸念ありとします。確信度は 3 回の最小値を使います。
④ 決定式: 承認するかどうかを決めるのは LLM ではなく Go の純粋関数です。確信度の閾値は既定 0.80、重要領域は 0.95 で、作成者がコードオーナーでなければ 0.10 上乗せします。suggestion 以外のリスクが 1 件でもあれば止めます(想定外の severity も止まります)。
なぜこの形になるのか — 設計判断を読み解く
承認は LLM の「出力」ではなくコードの「判定」
一番大事な点は、LLM の役割を所見を返すところまでに限っていることです。記事は「agent proposes, policy authorizes, infrastructure enforces」という考え方に沿うと書いています。エージェントは提案し、ポリシーが許可を決め、インフラが強制する、という分担です。
この分担には実務上の利点が 3 つあります。
- テストできる: 決定式は純粋関数なので、LLM を呼ばずに「warning があれば止まる」「NaN の確信度は止まる」といった規則をユニットテストで固定できます。
- モデルを替えても前提が残る: 実際に newmo は Gemini 2.5 Flash の提供終了に合わせてモデルを移しましたが、比較が必要だったのは LLM ゲートの出力だけでした。「重要領域は人へ」という規則はモデルと関係なく守られます。
- 注入に強い: 承認の可否は PR コメントの文面ではなく、プロセスの終了コードで CI に渡します。ファイル名や LLM の出力に「承認」に似た文字列が混ざっても、承認にはなりません。
多数決ではなく OR で集める理由
LLM の判定はぶれます。3 回のうち 1 回だけが危険に気づいた場合、多数決ではその気づきが消えます。記事には「LLM が読めていない変更を approve するのが一番怖い」という趣旨の記述があり、承認の誤り(危険な PR を通す)を止めすぎ(安全な PR を人へ回す)より重く見ていると言えます。それなら OR で集めるのが筋です。確信度に最小値を使うのも同じ考え方です。
1,000 行超を複数回判定の対象にしたのにも根拠があります。人がコメントする率は、200 行未満の PR で 13〜27% だったのに対し、1,000 行超では 60% でした。大きい PR ほど人がコメントする率が高く、記事によれば長い差分は LLM の判定もぶれやすいとのことです。
「見えないものは人へ」を徹底する
生成物を除いた差分が 512KB を超えたら LLM に回しません。コンテキストに入りきらず、暗黙に切り捨てられるのを避けるためです。GitHub API が patch を返さない巨大ファイルやバイナリ、LLM 呼び出しの失敗も needs_human になります。迷ったら人へ倒す(fail closed)設計が全体で一貫しています。
実行環境は、判定そのものを守る構成になっています。pull_request_target で動かしますが、PR のコードは実行せず、main ブランチの判定プログラムで GitHub API から差分を取得します。PR 自身が判定プログラムを書き換えて自分を承認する経路をふさいでいます。
運用結果から分かること
2 か月の運用で、approve した PR がリリース後の不具合の原因になった割合は、needs_human の PR の半分ほどでした。ただし著者自身が書いているとおり、needs_human には大きな PR や重要領域の PR が集まるので、単純な比較はできません。
数字より参考になるのは、うまくいかなかった点です。
- 見逃しは差分の外にある: 見逃した不具合の多くは、呼び出し先のコードや別ファイルの SQL を読まないと分からないものでした。差分だけを入力にする方式の限界と言えます。
- 業務ロジック判定は止めすぎる: 「業務ロジックが変わる」で止めた PR は、approve した PR と不具合の指摘率があまり変わらず、テスト付きの普通の機能追加まで止めていました。一方、LLM がリスクで止めた PR は、approve した PR の 2 倍強の割合でレビュー時に不具合を指摘されていました。リスク判定は効いていて、業務ロジック判定は効きが弱い、と切り分けられています。
- 同じモデルでも 85% しか一致しない: 過去の PR を同じモデルで判定し直しても、本番の判定と一致したのは 85% ほどでした。そのため一致率は品質指標にせず、判定が分かれた PR は人が読んで正解を確かめています。
著者が「一番効いた」と挙げているのは、層を分けたことでどこで止まったかが分かる点です。止めすぎの原因が機械ゲートの誤検知なのか、LLM の癖なのかを切り分けられます。
エンジニアへの影響 — 自分のチームで試すなら
riskjudge をそのまま導入しなくても、設計の考え方は小さく取り入れられます。
- まず機械ゲートから作る: 「認証・決済・CI 設定など、決めた領域に触れたら必ず人」というルールは LLM なしで作れて、それだけで AI レビューの対象を安全な範囲に絞れます。CODEOWNERS とパス判定の組み合わせで始められます。
- LLM には JSON で所見だけを返させる: 「承認してよいか」を自然文で聞かず、確信度とリスクを構造化して返させ、承認はコードで決めます。閾値を後から変えやすくなります。
- 判定にラベルを付けて記録する: newmo は
needs_humanラベルをデータ基盤に取り込み、「人に回したのに指摘なしでマージされた PR」をダッシュボードで追っています。止めすぎの調整にはこのデータが要ります。 - 差分の外をどう渡すかが次の課題: 見逃しの多くが差分の外にある以上、呼び出し元・呼び出し先をコンテキストに入れる工夫が精度を上げる鍵になります。
関連して、Zenn の「コードを読まずにマージするために必要なこと」は、仕組みだけでなく組織の合意が要ると指摘しています。手順どおりに進めて見逃しが起きたときに個人の責任を問えば、結局みんなコードを読む運用に戻ります。riskjudge のような仕組みを入れるときは、見逃しを仕組みの改善で受け止めるという取り決めも一緒に作る必要があります。
まとめ
- riskjudge は、機械ゲート・LLM 判定・OR 集約の複数回判定・Go の決定式の 4 層で PR を
approveかneeds_humanに振り分ける - LLM は所見を返すだけで、承認はテスト可能な純粋関数が決める。モデルを替えても前提が崩れない
- 巨大差分や API 失敗は人へ倒し、実行環境は PR のコードを実行しない構成で自己承認の経路をふさいでいる
- 見逃しの多くは差分の外にあり、業務ロジック判定は止めすぎるという課題も数字で示されている
newmo は今後、差分を見てすぐ答える速い判定を手前に置くことを検討しています。「コードで決められることはコードで決め、LLM には読む判断だけを任せる」という分担は、PR 承認以外のエージェント設計にもそのまま使える考え方です。