AI 支援開発

AIが書いたコードをレビューすべきは別のモデルである理由

自身の出力をレビューするモデルは、自身の盲点も共有してしまいます。差分を他のプロバイダーの独立したモデルにルーティングし、結果をクロスチェックしてください。

この記事は英語の原文をAIモデルが翻訳したものです。表現が原文と異なる場合があります。 英語の原文を読む

1つのAIモデルがパッチを書き、同じモデルがそれをレビューする場合、そのレビューは重要なバグに関してはほとんど価値がありません。コードを生成したモデルは、その背後にある推論も生成しているため、その推論をレビューに持ち込み、自身の作業を肯定します。存在しないAPIを幻覚した場合、その呼び出しを正しいものとして読み取ります。仕様を誤読した場合、同じ誤読に基づいてコードを評価します。これは、人が自分の書いた文章を校正し、10回読んだ誤字を読み飛ばしてしまうことの機械版です。

解決策は、より良いプロンプトではありません。それは、コードを書いていないモデル、理想的には異なるデータでトレーニングされた別のプロバイダーのモデルからのセカンドオピニオンです。同じ差分を2つか3つの独立したモデルに送り、それぞれにバグとリスクを尋ね、その判断を固定のルールで組み合わせます。彼らの死角が重ならない場合、クロスチェックは単一のレビューアが見逃すものを捉えます。この投稿では、それをどのように実行するか、そのコスト、そして単一のレビューで既に十分な場合はいつかについて説明します。

セルフレビューは作者の盲点を共有する

コードレビューは、レビュー担当者が作者には見えなかった何かを発見できた場合にのみ役立ちます。2人の人間がうまく機能するのは、彼らが異なるメンタルモデル、過去の異なるバグから得た異なる傷跡、そしてコードが何をすべきかについての異なる仮定を持っているからです。レビュー担当者は、作者が想像もしなかったケースを指摘します。

自身の出力をレビューするモデルには、そのような距離感が全くありません。コードに対するその「意見」は、そもそもコードを生成したのと同じ重み、同じトレーニングデータ、そして多くの場合同じコンテキストウィンドウの延長線上にあります。関数を書いた直後に「ここにバグはありますか?」と尋ねると、作者に作者自身を否定するように求めていることになります。表面的な問題、nilチェックの欠落、明らかなタイプミスは見つけるでしょうが、間違った仮定から生じた微妙なロジックエラーは、レビューがその仮定を継承するため、生き残る傾向があります。

経験的に、失敗はいくつかの箇所に集中します。幻覚によって生成されたメソッドやフィールドは、モデルがそのメソッドが存在すると信じ続けているため、セルフレビューを通過します。境界条件におけるoff-by-oneは、モデルの境界に対する考えが両方のパスで同じように間違っているため、通過します。セキュリティギャップ、例えば認証チェックの欠落は、モデルが両方の時間でハッピーパスに焦点を当てていたために通過します。これらはまさに後で発見するのが高コストになる欠陥であり、セルフレビューが最も苦手とする欠陥でもあります。

モデルによって失敗する箇所は異なる

2つ目のモデルが役立つ理由は、モデルが単一の死角のセットを共有しないからです。各モデルは、異なるデータの組み合わせでトレーニングされ、異なる目的で調整され、慎重さと確信度のトレードオフにおいて異なる位置に落ち着きます。あるモデルは並行処理の危険性の発見に強いがSQLには弱い。別のモデルはデータフローをうまく読み取りますが、競合状態は見逃します。それらのエラーは同一ではなく、それがまさに重要な点なのです。

これは、アンサンブルが単一の分類器に勝るのと同じ考え方です。3人のレビュー担当者がそれぞれ実際の欠陥の30%を見逃したとしても、彼らが見逃すのが異なる30%であれば、3人全員が同じ特定のバグを見逃す確率は30%よりもはるかに低くなります。独立したエラーは相殺されます。落とし穴は、そしてそれは現実的なものですが、「独立」という言葉です。同じモデルファミリーの2つのモデル、または同じモデルに2回プロンプトを出しても、独立したエラーは得られません。それらは、より高い確信度で同じエラーを2回返すだけです。その利点を得るには、ソースの真の多様性が必要です。つまり、異なるプロバイダー、異なるモデルファミリーであり、1つのモデルの2つのtemperatureではありません。

したがって、設計目標は「より多くのレビュー」ではありません。それは「間違いが相関していないレビュー」です。真に異なるモデルからの単一のレビューは、作成者モデルを5回再実行するよりも価値があります。

ワークフローのステップバイステップ

パイプラインは小規模です。1つのモデルが書き込み、複数の独立したモデルが判断し、ルールがその判断を統合し、人間が意見の対立を解消します。

ステップ 担当 出力
1. 実装 作成者モデル diffまたはパッチ
2. 並行レビュー N個の独立したモデル(作成者モデルは含まない) モデルごとの判定:GOまたはNO-GO、および指摘事項
3. コンセンサスルールの適用 自動化 Pass、fail、またはescalate
4. 意見の不一致の解決 人間 分かれた判定に対する最終決定

2つの特性により、これが機能します。レビュー担当者は独立して実行されるため、互いにアンカリングすることはできません。そして、各レビュー担当者は同じ中立的なプロンプトを受け取るため、NO-GOはモデル間で同じ意味を持ちます。作成者モデルはレビュープールから完全に除外してください。その投票は、あなたがすでに持っているものであり、最も信頼できないものです。

これを疑似コードで表すと次のようになります。

def multi_model_review(diff, author_model, reviewers, rule):
    verdicts = []
    for model in reviewers:            # reviewers excludes author_model
        v = model.review(
            diff=diff,
            prompt="List concrete bugs, security risks, and correctness "
                   "issues in this change. Then answer GO or NO-GO.",
        )
        verdicts.append(v)

    passed = rule(verdicts)            # unanimous or majority, see below
    if passed is UNDECIDED:
        return escalate_to_human(diff, verdicts)
    return passed, verdicts

review呼び出しは、具体的な指摘事項のリストと、単一のGOまたはNO-GOの2つのものを返します。指摘事項は、レビューが失敗したときに人間が読むものです。GOまたはNO-GOは、ルールが使用するものです。

リスクに見合ったコンセンサスルールを選択する

複数の判定を1つの決定に変えるルールは、厳格さを調整する場所です。2つの賢明なデフォルトがあり、その間にはスペクトラムが存在します。

全員一致のGOは厳格なルールです。変更は、すべてのレビュー担当者がGOと判定した場合にのみ承認され、1つのNO-GOでブロックされます。これにより、バグの再現率が最大化され、どのモデルが見つけられるものでもキャッチできますが、より多くの誤検知を犠牲にします。なぜなら、1人の過度に慎重なレビュー担当者が変更を止めてしまうからです。元に戻すのが難しいコードに使用します。

多数決のGOは寛容なルールです。変更は、ほとんどのレビュー担当者がGOと判定した場合に承認されます。オオカミ少年になりがちなモデルからの単独のNO-GOはマージをブロックしませんが、3人中2人が問題に同意した場合はブロックします。これにより、バグ検出を少し犠牲にして、摩擦を大幅に減らします。見逃された問題を次のコミットで安価に修正できるような、通常の変更に使用します。

def unanimous(verdicts):
    if all(v.decision == "GO" for v in verdicts):
        return PASS
    if all(v.decision == "NO_GO" for v in verdicts):
        return FAIL
    return UNDECIDED          # split -> human decides

def majority(verdicts):
    gos = sum(v.decision == "GO" for v in verdicts)
    if gos > len(verdicts) / 2:
        return PASS
    return FAIL

全員一致のルールが意見の不一致に対して何をするかに注目してください。それは、暗黙のうちにどちらかの側につくことはありません。エスカレーションするのです。有能で独立したレビュー担当者間の意見の不一致は、その変更が本当に曖昧であるという強力なシグナルであり、それこそが人間が時間をかける価値のあるケースなのです。意見の不一致を「まあ、多数派の勝ちで」と扱うことは、この取り組み全体から得られる最も価値のある成果を捨て去ることになります。

敵対的なプロンプトは「これで問題なさそうですか?」に勝る

誰に尋ねるかと同じくらい、どのように尋ねるかが重要です。デフォルトのレビュープロンプトである「これは正しいですか?」は、同意することが簡単な補完であるため、モデルに同意を促します。あなたが得るのは、精査ではなく、肯定です。2つの調整がその傾向を押し返します。

1つ目は、レビュアーにコードに対して反論させることです。「これをレビューしてください」の代わりに、「あなたの仕事は、この変更がなぜ間違っているかを見つけることです。バグがあるという最も強力な論拠を挙げてください」と尋ねます。タスクを反論として位置づけることで、同意への引力を減らします。モデルは今や、差分を承認することではなく、欠陥を見つけることで報酬を得るようになり、中立的なプロンプトの下では見過ごしていたであろう懸念点を表面化させます。あなたはモデルに欠陥について正しいことを求めているのではなく、熱心に探すことを求めているのであり、その後の誤報は人間がフィルタリングします。

2つ目は、各レビュアーに異なるレンズを与えることです。3つの一般的なレビューを行うのではなく、役割を割り当てます。あるレビュアーは仕様に対する正しさをチェックし、あるレビュアーはセキュリティと入力処理をチェックし、あるレビュアーはパフォーマンスとリソース使用量をチェックします。同じ差分を、3つの角度から見ます。これは意図的にカバレッジを広げ、2人のレビュアーが同じ明白な問題にすべての労力を費やし、3番目の領域が見過ごされるのを防ぎます。

Reviewer A: "Find correctness bugs. Where does this violate the stated behavior?"
Reviewer B: "Find security holes. Assume the input is hostile."
Reviewer C: "Find performance and resource problems under load."

反論と役割の多様性の積み重ね。作成者とは異なるモデルに基づいた敵対的なセキュリティレビュー担当者は、自動化のみで可能な限り、自己確認からかけ離れた存在です。

コストと、それをスキップすべき場合

これらはどれも無料ではありません。変更ごとにモデル呼び出しを2、3回余分に実行するため、トークンを2、3倍支払い、レイテンシーも増加します。並行して実行しても、最も遅いレビュー担当者がペースを決めるからです。小さな変更や元に戻せる変更では、そのオーバーヘッドはほとんど何も生み出しません。ログ行のタイポを修正するのに、審議会は必要ありません。

価値が発揮されるのは、重要で元に戻すのが難しい変更の場合です。

  • データをインプレースで書き換えるデータベースの移行。
  • ミスが本番環境をダウンさせる可能性がある、デプロイやインフラストラクチャのコード。
  • 微妙なバグがセキュリティや金銭的な問題になる、認可や課金のパス。
  • 一方通行の変更、つまり一度リリースするとクリーンに元に戻せないものすべて。

それらの場合、3回のモデル呼び出しのコストは、バグのコストに比べれば些細なものです。そして、厳格な全員一致ルールは、その誤検知に見合う価値があります。数秒でオフにできる機能フラグの背後にある日常的な変更については、レビューが1回、あるいはまったくないのが正しい判断です。手順の厳格さは、影響範囲に合わせて調整しましょう。

階層化されたポリシーは、毎回考えることなくこれを実現します。

変更の種類 レビュー担当者 ルール
軽微または元に戻せる変更 1名(または作成者による自己チェック) 助言のみ
通常の機能開発 独立した2名 多数決でGO
移行、デプロイ、認可、課金 独立した3名 全員一致でGO

失敗のモード: 意見が一致するモデルがすべて間違っている可能性

この手法の真の限界は、相関のある死角です。独立したエラーは、そのエラーが実際に独立している場合にのみ相殺されます。もし、プール内のすべてのモデルが、公開されているインターネット上の同じ人気だがバグのある例から同じ間違ったイディオムを学習した場合、それらのモデルはすべてそれを繰り返し、満場一致で自信を持ってそれを承認するでしょう。コンセンサスは証拠であって、証明ではありません。3つのモデルからのゴーサインは、3つのモデルが問題を見つけられなかったことを意味しますが、これは問題がないことと同じではありません。

これは、最新種類のバグや、最もドメイン固有のバグにとって特に重要です。最新のフレームワークにおける微妙な欠陥や、あなたの会社内にのみ存在しどのトレーニングセットにも含まれていないビジネスルールは、それらすべてのモデルにとって一度に見えなくなります。どれだけクロスチェックをしても、どのレビュー担当者も持っていない知識を生み出すことはできません。複数モデルによるレビューは、すり抜けるバグのクラスを縮小しますが、それを空にするわけではありません。

ですから、それが正当化されるほど重要な場面では人間をループに参加させ続け、単なるGO/NO-GOの集計だけでなく、実際の検出結果を読んでください。判定は、人間の注意をより有効に活用させるためのフィルターであり、それに取って代わるものではありません。AIが書いたコードをレビューするには別のモデルを使いましょう。なぜなら、2つ目の死角セットは1つよりも優れているからです。ただ、マシン間の合意を真実だと誤解しないでください。