はじめに
LLMにコードレビューをさせると、もっともらしい評価を返してくれます。しかし、その評価がどれだけ信頼できるかを検証したことはあるでしょうか。
本記事では、LLMのコードレビュー出力の信頼性を70〜85%まで引き上げる3ステップ手法を紹介します。この手法は、3つのLLM・30ファイルの実検証を通じて設計したものです。
手法概要:検証駆動プロンプト設計
核心は**「証拠を要求する前に、証拠を集める手段を提供する」**ことです。
従来のプロンプト(v2)では「file:行を記載せよ」と指示するだけでした。v3では、評価の前に3つの検証ステップを必須とします。
Step 0: ファイルスコアリング(何を読むか決める)
↓
Step 1: 行数の外部検証(数字が合っているか確認)
↓
Step 2: Readツールによる必須読み込み(実際に読む)
↓
Step 3: 読み込んだファイルのみ評価
Step 0: ファイルスコアリング
大規模コードベース(数百ファイル)を全て読むことは不可能です。まず、重要度に基づいて評価対象を選定します。
ネットワーク中心性(NetworkX)を使ったスコアリングアルゴリズムで、各ファイルにスコアを付けます。
- 拡張子重み(
.py>.md>.json等) - パス重み(
core/>tests/等) - Import中心性(多くのファイルからimportされているほど高スコア)
- 行数(適度な大きさのファイルを優先)
上位20件を評価対象とし、未選定ファイルは評価不可とします。
Step 1: 行数の外部検証
スコアリング結果の行数をwc -lで実測し、照合します。
wc -l <file1> <file2> <file3> ...
不一致率が10%を超えるファイルは検証不通過とし、評価の信頼性フラグに反映します。
Step 2: Readツールによる必須読み込み
スコア上位ファイルをReadツールで実際に読み込みます。これが最も重要なステップです。
- 読み込んだファイルのみ評価カードを出力
- 未読ファイルの評価カードは出力禁止
- 評価カードに
file_read: true/falseを必須項目として追加
評価カードの拡張フィールド
| フィールド | 意味 | 検証方法 |
|---|---|---|
loc_verified |
行数検証結果 | Step 1のwc -lとの照合 |
file_read |
ファイル読込完了 | Step 2のRead実行記録 |
score |
スコアリング順位 | Step 0の結果 |
centrality |
Import中心性 | NetworkX計算 |
検証結果
この手法を3つのPythonプロジェクト(計30ファイル)で検証しました。
信頼性の向上
| 指標 | 従来手法(v2) | 検証駆動(v3) |
|---|---|---|
| 行数乖離 | 最大10.7倍 | ゼロ(10/10ファイル一致) |
| 未読ファイルの評価 | 全ファイル | ゼロ(未読は評価不可) |
| 信頼性(3LLM平均) | 15〜55% | 70〜85% |
3LLM検証の結果
3つの異なるLLMで自己検証を実施しました。
| LLM | 修正前の誤り数 | 信頼性(修正前→後) |
|---|---|---|
| GLM | 8箇所 | 35% → 80% |
| MiniMax | 14箇所 | 15% → 70% |
| Claude Opus | 14箇所 | 55% → 85% |
「一番賢いLLMなら大丈夫」という前提は崩れました。しかし、3LLM全員が「コードを実際にコンテキストに載せれば評価可能」と一致しており、問題はLLMの能力ではなく読み込む手順の欠如でした。
正確な評価がもたらした発見
検証駆動の評価により、3プロジェクト全てで「テスタビリティ(テスト容易性)が最弱」という共通弱点が判明しました。この発見をもとに、DI注入やエラー処理の改善を含む20項目のリファクタリングを実行しました。
正確な評価手法は、単に「スコアを出す」だけでなく、「どこを改善すべきか」を示す実用的なツールになります。
なぜこの手法が必要なのか(背景)
LLMは「読んだことにして」評価する
従来のプロンプト設計では、以下の4つの構造的欠陥がありました。
- 証拠要求の実効性がない: 「file:行を記載せよ」と指示しても、LLMがファイルを読む手段がなければ、もっともらしいfile:行をハルシネーションする
- ファイルアクセス手順の不在: パスを渡すだけで、LLMがどうやってファイルを読むかを指示していない
- 行数検証の不在: LLMが自己申告する行数を検証する外部ステップがない
- サンプリング戦略の不在: どのファイルを優先すべきかの指針がないため、LLMは「全部読んだことにして」全ファイルに評価をつける
具体例:行数の乖離
実際に確認された乖離の例です。
| ファイル | LLM申告行数 | 実測行数 | 乖離 |
|---|---|---|---|
| browser_use_agent.py | 1,592 | 329 | 4.8倍 |
| __init__.py | 717 | 67 | 10.7倍 |
| product_extractor.py | 982 | 466 | 2.1倍 |
この行数が★評価の基礎データとして使われていたため、評価全体の信頼性が崩壊していました。
プロンプト設計の5つの教訓
1. 証拠を要求する前に、証拠を集める手段を提供する
「file:行を記載せよ」だけでは不十分。LLMがファイルを読む手順をプロンプトに組み込む必要があります。
2. サンプリング戦略は必須
大規模コードベースを全て読むことは不可能。「どのファイルを読むか」を評価前に決めるステップが不可欠です。
3. 外部検証を組み込む
LLMの出力を行数やファイル数などの客観的指標で検証するステップを入れます。ハルシネーションを構造的に防げます。
4. 「未読=評価不可」を明示する
ファイルを読んでいないのに評価を出力することを明示的に禁止します。「全部読んだことにする」問題を防ぎます。
5. 正確な評価は常に低く出る
正しい評価手法に変えると、スコアは下がるのが普通です。不当に高いスコアは、検証不足の兆候と考えた方がよい場合があります。
この手法の限界と今後
- 工数が増える: 従来手法は1回のプロンプトで完結しますが、本手法は3ステップが必要です
- LLMの文脈窓に依存: 大きなファイルは1つのコンテキストに収まらない場合があります
- スコアリングアルゴリズムの妥当性: Import中心性が本当に「重要度」と等価かは議論の余地があります
今後の改善として、スコアリング部分をMCPサーバー化し、プロンプト実行中に自動でスコアリング→読み込み→評価まで完結する仕組みを検討しています。
おわりに
LLMにコードレビューをさせることは有効です。問題は「LLMが嘘をつくこと」ではなく、「嘘をつけない構造になっていなかったこと」でした。
プロンプトの設計一つで、LLMの出力の信頼性は劇的に変わります。「出力を信じる」のではなく、「信じられる出力を構造的に強制する」設計が重要です。
関連記事
- Claude Code CLIをGLM(Z.AI)で代替した話(コスト大幅削減の実測)
- Claude Code CLIにMiniMaxをフォールバックとして組み込んだ話
- 月間27億トークンを処理したLLMルーティングの実運用レポート
本記事は個人Zennでも公開しています。