コーディングAIに「これは間違ってる」と伝えるのは簡単だ。難しいのはその先である。AIがその言葉を受けて本当に直したのか、それとも直したふりをしてやり過ごしただけなのかを見極める作業だ。
Operaはまさにその一点を狙ったコーディング批評家(critic、エージェントが作業する様子を見守り、修正フィードバックを与える役割)だ。一度指摘して手を引くのではなく、指摘した問題が実際に消えるまで食らいついて離さない。リポジトリ全体を何時間もかけて手直しする長丁場のコーディング作業に付けてみると、批評家なしの基本設定より成功率が明確に高かった。最も差が開いたケースでは15ポイントの開きがあった。
長丁場の作業を任されたコーディングエージェントは、コードを掘り、直し、走らせて課題を仕上げる。息が長くなるほど事故が増える。同じ失敗したコマンドを何度も繰り返し、すでに見たファイルをまた掘り返し、検証もないまま「全部できた」と宣言する。
途中で指摘してくれる批評家が自然な処方だ。ところが罠がある。批評家はまだ終わっていない作業を、証拠が半分しか積み上がっていない状態で判断しなければならない。この段階では「本物のミス」と「ありうる途中経過」の境界が曖昧だ。見立てを誤れば、放っておけば成功していた作業を批評家がかえって脱線させてしまう。
だから良い批評家の条件は、賢い指摘ではない。いつ割り込むか、その指摘が正当か、そして指摘が効いたかを最後まで見届ける規律である。既存の批評家たちは前の二つまではやる。進行中の作業を評価したり、完成した成果物を採点したり。肝心のフィードバックを渡したあとに何が起きたかはほとんど見ない。指摘はしておいて、それが通ったかを確認しない――ちょうどその空白だ。
Operaが掘り下げた隙間がここにある。フィードバックを渡したあとを二つの問いに切り分ける。言われたことをやったのか。そして元の問題が消えたのか。この二つを一緒くたにしないことがOperaのすべてだと言っても言い過ぎではない。
言葉では抽象的なので、一場面を追ってみよう。
エージェントがテストを通そうとして失敗する。Operaが割り込んで診断する。「特定の入力を弾くガード(不正な値が入ってこないよう防ぐ防御用の条件文)が抜けている」。そしてこの問題を一枚のメモに書き留める。メモには診断だけが書かれるわけではない。「この問題が解決したと見なすには何が見えていなければならないか」まで一緒に打ち込まれる。今回は「もともと要求されたテストが通ること」。
メモがエージェントに渡され、エージェントはガードをコードに追加する。ここで素朴な批評家なら「言ったらやったな」とメモを閉じる。Operaは二段階に分けて見る。まず指示に従ったか――ガードを入れたので、ひとまず合格だ。だがもっと重要なことを問う。それで元のテストは通るのか――いや、まだ赤信号のままだ。最初に打ち込んでおいた基準を越えていないので、メモは閉じられない。開いたまま次のレビューまで追いかける。
「直すふり」と「本物の解決」が分かれるのが、まさにここだ。報告書だけ見れば、二つは同じく「処理完了」である。
このメモがOperaの心臓だ。論文はこれを持続メモと呼ぶ。診断された問題一つ、いま渡す指針、そして最初に決めたら変わらない解決基準を一つに束ねたものだ。コードレビューで誰かが付けたコメントが「直した」チェックが入るまで閉じられずに残っているのとまったく同じである。コメントが消えないので批評家は複数回のレビューにわたって同じ問題を追え、基準が固定されているので「たった今何を言いつけたか」ではなく「元の問題が消えたか」で判定する。開いているメモは一度に一つだけ置く。このメモを三つの規律が動かす。
いつ見るか。固定間隔でだけレビューすると反応が遅く(次のレビューまで失敗を繰り返す)、事件が起きたときだけ見ると静かな失敗(確信を持って見当違いのファイルを直すケース)を見逃す。Operaは両方を混ぜる。一定ターンごとに回しつつ、停止・反復・エラー・完了宣言・最終提出の五つの信号のいずれか一つでも立てば即座に見る。重要なのは、見るからといって必ず割り込むわけではないという点だ。うまく進んでいれば批評家は何も言わずに通り過ぎる。不要なおせっかいが成功するはずの作業を台無しにするので、沈黙を堂々とした選択肢として設計してある。
何を指摘するか。自由に書かせておくと「エッジケースを考慮しろ」のような漠然とした物言いになりがちだ。エージェントからすれば、すぐ手をつける取っ掛かりがない。そこでOperaはすべての診断を、あらかじめ決めておいたタグ一つに必ず収めさせる。タグはコードを直す五段階――探索・レビュー・修正・テスト・提出――に結びついている。肝心なのは、タグごとにルールが付いてくるという点だ。こういう証拠が見えたときだけこの指摘を出せる、解決したかはこういう基準で判定する、そんな約束が最初から付いて回る。おかげで指摘どうしが重ならず、あとで「解決したか」を測る物差しも揺るがない。さらに一度見るたびに、根拠が明確なイシューをきっかり一つ、それに対する最小の修正だけを選ぶ。指示を浴びせないことで互いにぶつからず、何が効いたのかも辿り直せるからだ。(タグは全部で九つあり、全リストは記事末にまとめておいた。)
間違った指摘をどう濾すか。批評家も結局は言語モデルなので、もっともらしいが間違った診断を出す。そこでメモの一生に二度の確認を置く。送り出す前に一度――根拠が診断を支えているか、必ず直さねばならないか、提案した確認方法で本当に解決を証明できるか。課題と無関係な掃除や、新しい証拠もなく言う小言がここで濾される。閉じる前にもう一度――解決したという証拠が実際にあるか。動作を要求したなら実行結果を、コードの状態を要求したなら実際の編集を確認してから閉じる。この二度の確認がOperaのフィルターだ。
Operaはエージェントと実行記録・言葉によるフィードバックだけをやり取りし、モデルの重みには手をつけない。だから既存のハーネス(エージェントを課題環境につないで走らせる実行枠)にそのまま差し込める。
性格の異なる三つのベンチマーク(性能を測る標準試験)で測った。ターミナルでコマンドを扱うTerminal-Bench、実際のソフトウェアリポジトリを直すSWE-Bench Pro、そして基本成功率が三つの中で最も低い難しい課題群のDeepSWE。あえて毛色の違う三つを選んだ。一つの環境でだけ通じる小手先ではなく、広く通じる枠組みなのかを見るためだ。
同じエージェント(コードを書く主体モデルは固定)に批評家だけ付け替えて比べたのが核心だ。上げ幅はポイント(pp)で記す。
| 方法 | Terminal-Bench | SWE-Bench Pro | DeepSWE |
|---|---|---|---|
| 批評家なし | 65.9 | 75.3 | 32.4 |
| SWE-PRM | 68.9 | 77.7 | 36.6 |
| SWE-Search | 70.0 | 77.3 | 38.6 |
| LLM-as-verifier | 71.2 | 78.7 | 38.9 |
| Agentic Rubrics | 71.9 | 78.3 | 39.5 |
| Opera | 73.8 | 79.3 | 41.3 |
三つすべてでOperaが一位だ。ただし二位との距離はまちまちである。ターミナルとDeepSWEでは1.9pp、1.8pp先行したが、SWE-Bench Proでは0.6ppにとどまる。理由は同じ表の一番上の行にある。SWE-Bench Proは批評家なしの基本がすでに75.3と強い。うまくいく土俵では、どんな批評家を付けても上げる余地がほとんどない。
この「上げる余地」が結果全体を貫く鍵だ。得られる上乗せの大きさは、モデルの絶対的な実力よりも、矯正する余地がどれだけ残っているかで分かれた。すでに満点近い生徒より、ぎりぎりで間違える生徒にこそ、ピンポイントの個別指導が最も効くのと同じ理屈である。四つのモデルにまたがる最大の上昇幅は12.4・15.0・8.9ppで、ターミナルとSWE-Bench Proでは最も弱いモデルが最も大きく上がった。逆に、基礎がまるでなく問題そのものを解けない生徒には、個別指導も役に立たない。その弱いモデルはDeepSWEを批評家の有無にかかわらず一問も解けず、そのためDeepSWEの最大上昇はむしろもっと強いモデルの取り分になった。底から出発して成功率がほぼ三倍になったモデルもあったが、絶対的な上乗せ自体は大きくなかった。
ここまでは「批評家を付けたら上がった」という話だ。もっと見るべきは、何がその上昇を生むのかである。
一つ目の答えはフィルターだ。先に述べた二度の確認をどちらも外し、すべての指摘がそのまま伝わり、すべての閉じる要求が受け入れられるようにすると、上乗せが崩れ落ちる。ターミナルで+7.9ppが+2.6ppへ(三分の二が蒸発)、DeepSWEで+8.9ppが+4.5ppへ(半分)落ちた。たくさん指摘することより、正確な指摘だけを選んで解決するまで抱え続ける仕掛けが、上乗せの半分以上を担っているという意味だ。間違って直したときの代償が大きい土俵ほど、フィルターの取り分が大きかった。
二つ目の答えがもっと意外だ。批評家をエージェント自身にしても上がった。新しい知識を足せないにもかかわらずである。DeepSWEで二つのモデルが自分自身を批評家にしてそれぞれ6.2pp上がり、これはもっと強い外部の批評家を付けたのと肩を並べるか、上回った。批評家を付け替えた33通りの組み合わせのうち、基本より劣ったのはたった一つだけだった。ここまでくると、上乗せの大きな部分は批評家がどれだけ賢いかではなく、適時に見て、種類を定めて診断し、閉じるまで追う枠組みそのものから来ている、と読める。
ただしここで一度立ち止まるべきだ。自己批評も推論を余分に使うのに、この比較はその計算量を揃えていない。だから向上分をすべて枠組みの手柄にするのは早計だ。さらに、エージェントに課題を解く元手そのものがなければ、自己批評は力を発揮しない。最も弱いモデルはターミナルで外部の批評家を付けたとき最低12.4pp上がったが、自己批評では多くても2.7ppだった。
Operaが回って残した作業記録には、使い道がもう一つある。あとで批評家なしでも使える学習データになるということだ。研究陣は小さなモデルを二種類の記録でそれぞれ教えてみた。一つはOperaのコーチを受けながらその小さなモデルが自分で解いた記録、もう一つはもっと強いモデルが解いた模範解答だ。
あるベンチマークでは両方とも同じくらい上がった。勝負が分かれたのは別のベンチマークだった。もっと強いモデルの模範解答をそのまま写させたら、ターミナル成功率が23.6%から7.9%へかえって転落した。一方、Operaのコーチを受けた自分の記録で学んだ側は25.8%で踏みとどまった。すべての行動が生徒自身から出ており、批評家の手は脇のメモとしてだけ入ったデータなので、自分のペースを失わなかったのだ。生徒が自分で解いた問題集に先生が赤ペンで添削だけしてくれたのと、先生が解いた答案を丸ごと暗記したのとの違いである。前者のほうが揺らがない。
研究陣が自ら釘を刺す。批評家はない能力を作り出してはくれない。先に見た「一問も解けなかった」ケースがその証拠だ。
もっと微妙な罠もある。強いモデルはフィードバックをよく受け入れて救済されるケースが増えるが、同時に、もともと解けていた課題をかえって台無しにするケースも一緒に増える。回復と撹乱が一つの身体にある、というわけだ。不要なおせっかいの代償が、強いモデルほど大きいという意味でもある。
根本的な限界も挙げておく価値がある。批評家が言語モデルである以上、間違った診断は完全には消えず、それを濾してくれる二度の確認もまた別の言語モデルの判断に頼る。フィルターがフィルターを完全には保証しない。そしてSWE-Bench Proで見たように、基礎がすでに盤石な土俵では、批評家設計の違いがなかなか表れない。
ONTLEでは、AI PMが顧客の要望一つを受けて、内部のワーカーが何時間もかけて処理する過程を管理する。顧客が「注文キャンセル時の返金額の計算を変えてほしい」と言ったとしよう。
PMがワーカーに作業を渡す。しばらくしてワーカーが「処理しました」と報告する。ここで終えると、ONTLEが長く患ってきた病が再発する。偽の完了だ。ワーカーは返金計算のコードに手をつけはしたが、顧客が言ったその条件で金額が正しく出るかは誰も見ていない。
Opera流にやれば、この報告は「順応」にすぎない。PMは顧客の要望を受け取った瞬間、「返金額がこの条件でこう出なければならない」という解決基準をメモに打ち込んでおく。ワーカーが「やった」と言っても、メモはその基準を実際の実行結果で越えるまで閉じられない。完了宣言と成果物の提出をレビュー信号にすれば、偽の完了が顧客へ「完了」として渡る直前に基準を突きつけられる。
メモをワーカーに送り出す前には確認を一つ置く。同僚のPMが「この指摘に根拠があるか、本当に必要な修正か」を交差で見る。もっともらしいが根拠の薄いおせっかいが、まともに回っていた作業を脱線させる事故を、顧客に影響が及ぶ前に防ぐ。Operaの実験でこの確認を外したとき上乗せの半分以上が吹き飛んだことは、外部の顧客が絡むONTLEのような環境では、この手続きが選択ではなく必須だという根拠として読める。
資源配分のヒントもある。自己批評が有能なモデルでよく効いたという結果は、元手が足りている作業には重い外部検証を控えめにしてよく、元手が足りない作業に力を集めればよいという意味に読める。PM運用にそのまま移せる教訓だ。
はい。Operaはモデルを追加で学習させるのではなく、実行時に診断・修正へ計算を多く割いて性能を引き上げる方式です。そのため成功率が上がった分だけ追加の計算コストが伴います。この記事の成功率の数字自体はそのコストを示していないので、導入を天秤にかけるときは二つを一緒に見るべきです。
だからこそ閉じるを開くより先に処理します。一度のレビューで、済んだイシュー一つを閉じ、すぐ次のイシュー一つを開くという具合です。開いているのは常に一つですが、流れが詰まることはありません。
三つのベンチマークをそれぞれ別の実行枠で回しつつ、課題は共通のツールで管理し、ネットワークを隔離して外部から答えを引いてくるチートを防ぎました。環境が互いに違ってもOperaが一貫して先行したという点が、特定の設定に頼った結果ではないことを裏づけます。
| 段階 | タグ | 何を捉えるか |
|---|---|---|
| 探索 | repo_localization_search | 直す対象をまだ見つけられていない |
| 探索 | implementation_target_shift | 見当違いの対象を直している |
| レビュー | implementation_readiness_review | 直す箇所が明確なのにコードをまた眺めている |
| レビュー | failure_signature_triage | ツール出力を読み違えている |
| 修正 | requirement_contract_review | 要件違反 |
| 修正 | diff_scope_review | 抜けている、または無関係な変更 |
| 修正 | state_transition_review | 実行ロジックの欠陥 |
| テスト | minimal_repro_or_focused_verifier | 次の判断を下す証拠がない |
| 提出 | submission_readiness_review | 証拠なしに完了を主張している |