LLMの精度を上げる:判定させず「証拠」を渡す設計パターン
LLMに「このコードは危険か」を直接判定させるのではなく、決定的な解析で見つけた事実を証拠として提示し、判断の材料だけを渡す設計に切り替えたところ、根拠つきの修正が初めて成立しました。判定と証拠収集を分離する、という考え方を実例で紹介します。
LLMに「このコードにバグがあるか判定して」と丸ごと投げると、根拠のない結論が混ざりやすくなります。もっともらしい理由をつけて誤検知したり、逆に本当のバグを見逃したりする——これはプロンプトの書き方の問題というより、役割分担の設計の問題です。今回は「LLMに判定させる」から「LLMには証拠を渡すだけにする」へ設計を切り替えたことで、根拠つきの修正が初めて成立した事例を紹介します。
背景:LLMに判定させることの限界
「決定的に判定できることは全部プログラムでやり、LLMは最後の手段にする」というのが基本方針です。しかし修理フェーズでは、LLMに「このコードにバグがあるか」を直接判断させる構成になっており、根拠のない誤検知や見落としが目立っていました。LLMは自然言語としてもっともらしい説明を作るのが得意なため、判定を任せると「理由はそれっぽいが実際には成立していない」指摘が紛れ込みやすくなります。
切り替えた設計:証拠は集める、決定はしない
そこで、LLMに判定させるのをやめ、決定的な静的解析(型の不整合、ゼロ除算の可能性など)で見つけた事実を証拠(evidence)として整形し、プロンプトに注入する方式に切り替えました。具体的には、契約ヒント(関数がどんな入力を前提にしているか)と実行トレース(実際にどう呼ばれ、どんな値が渡っているか)を証拠として渡します。LLMの役割は「証拠を読んで、それに基づいた修正案を出す」ことに限定し、判断の主体はあくまでプログラム側の解析結果に置きます。
呼び出し関係の情報を補う
この証拠注入パターンを機能させるには、「この関数はどこから呼ばれ、どこを呼んでいるか」という呼び出し関係の情報が要ります。これが欠けていると、証拠自体は正しくても文脈が足りず、LLMが誤った前提で修正案を出してしまうことがあります。呼び出し関係を解析結果に含める仕組みを新設したことで、この不足を補いました。
効果:ゼロ除算バグで初の確証つき修理
この構成に切り替えてから、ゼロ除算関連の既知バグに対して、初めて「確証つきの修理(修理の成立が検証で裏付けられる状態)」が成立しました。それまでは修理の成功率がほぼ0件という実測もあった中で、根拠を伴う成功例が出たのは設計転換の効果を裏付ける結果です。
実装チェックリスト / 調査順
- LLMに「判定」そのものを丸投げしていないか確認する。判定できる部分は決定的なコードに寄せられないか検討する。
- 決定的に出せる証拠(型情報、実行トレース、呼び出し関係など)と、LLMに委ねる部分を明確に線引きする。
- 証拠を渡す際は、判定結果ではなく「事実」の形で渡す。判定を混ぜると誤りが証拠として固定化されてしまう。
- 証拠を機能させるための前提情報(呼び出し関係など)は後から不足に気づくことが多い。証拠の質を上げる投資は継続的に行う。
- 効果があったかは印象でなく、実際に修理が成立した件数の変化で確認する。
「LLMに判断させない」というのは一見遠回りに思えますが、決定的に判定できる部分をきちんと切り出すほど、LLMに残された仕事は「証拠を読んで言語化する」というLLMが得意な作業に絞られます。結果として、根拠のある出力が増えるという構図です。
次の一歩として、自分のパイプラインでLLMに「判定」を丸投げしている箇所がないか、そこに決定的な証拠を差し込めないか、を洗い出してみてください。