FrontierCodeの評価設計から学ぶ、コーディングエージェントに任せるときの心がけ
2026年6月15日 公開
Cognitionが公開したFrontierCodeは「このPRを、メンテナはmergeするだろうか」という観点でコーディングエージェントのアウトプットを評価する方法を実現しました。
OSSメンテナが実際のリポジトリからタスクを作り、挙動の正しさだけでなく、変更範囲、テストの意味、既存の設計に沿っているかまで評価します。さらに、作った評価基準が雑な解法を通してしまわないか、逆に妥当な別解を落としてしまわないかも検証します。
以前、AIによってコードが速く生成されるようになったとき、システムの理解やレビューのあり方も一緒に変わっていく、という話を書きました。1 そのとき考えていたのは、生成されたコードが入っていくシステム全体のことでした。
今回FrontierCodeを読んで考えたのは、もう少し手前の話です。エージェントが出してきた一つの差分を、私たちは何を根拠に受け入れるのか。
これは、コーディングエージェントのベンチマーク手法として筋が良さそうですし、それだけでなく、私たちがコーディングエージェントに実装を任せるときにも、かなり持ち帰れるものがあるように思いました。
テストが通ることの、その先
これまでコーディングエージェントの評価としてよく参照されてきたものに、SWE-benchがあります。SWE-benchは、GitHub上の実際のissueとコードベースをもとに、モデルが問題を解決するpatchを生成できるかを見るベンチマークです。2
OpenAIが公開したSWE-bench Verifiedの記事では、SWE-benchの各サンプルには、修正前には失敗し修正後には通るFAIL_TO_PASS testと、既存機能を壊していないかを見るPASS_TO_PASS testがあると説明されています。3
これはとても重要な評価です。
実際のコードベースを読み、issueを理解し、必要な変更を入れ、テストを通す。HumanEvalのような小さな関数を書く評価から比べると、かなり実務に近い場所へ踏み込んでいます。
ただ、実務のコードレビューでは、テストが通っただけでは終わりません。
変更範囲は妥当か。既存の抽象に沿っているか。テストは本当に今回の振る舞いを守っているか。名前や責務はこのリポジトリの文脈に合っているか。将来ここを変更するとき、次の人が追える形になっているか。
こういう問いは、テストの緑色だけでは拾いきれません。
FrontierCodeが面白いのは、ここを正面から評価しようとしているところです。記事では、これから問うべきなのは「モデルは正しいコードを書けるか」だけではなく、「モデルはよいコードを書けるか」だとされています。冒頭で書いたmerge可能性の問いは、このあたりにあります。
「良いコード」の条件を考える
FrontierCodeでは、20人以上のOSSメンテナが、36の主要OSSリポジトリからタスクを作っています。各タスクには40時間以上をかけ、メンテナ自身がそのリポジトリにおける「mergeできる変更」の基準を評価項目に落とし込んだ、と説明されています。
評価軸には、behavioral correctness、regression safety、mechanical cleanliness、test correctness、scope、code qualityが並びます。挙動が正しいか。既存の挙動を壊していないか。追加したテストが意味を持っているか。必要な範囲だけを触っているか。コードが既存の設計や慣習に合っているか。
この並びを見たとき、これはベンチマークの設計であると同時に、かなりそのままコードレビューの観点だと思いました。
エージェントの差分を見ていると、たまにレビューコメントを書く手が止まることがあります。動いている。テストも通っている。説明も筋が通っている。けれど、ここにこのhelperを足すのか、この責務をこの層に置くのか、というところで手が止まる。
その違和感は、単に「好み」と片づけるには少し大きいことがあります。
FrontierCodeでは、criteriaをblockerとnon-blockerに分けます。blockerは、メンテナがコードレビューでhard stopにするような条件です。たとえば、仕様を満たしていない、重要な性能要件を壊している、変更範囲が大きすぎる、といったものです。non-blockerは、style、type safety、readabilityのように、品質スコアには効くけれど、それ単独では必ずしもmergeを止めないものとして扱われます。
この分け方は、エージェントと作業するときにもかなり効きそうです。
人間がレビューコメントを書くとき、すべての指摘が同じ重さになってしまうことがあります。「これは絶対に直してほしい」と「ここは好みだけれど、できれば整えたい」が同じ口調で並ぶ。人間同士でも少し大変ですが、エージェントに対してはなおさらです。
どれが成功条件なのか。
どれが品質の好みなのか。
どれは今回触らなくてよいのか。
そこを分けて渡すだけで、作業の進み方はかなり変わる気がします。
もう一つ、自分の実感に近かったのはscopeの評価です。
エージェントは、ときどき親切です。渡した修正のついでに、近くの命名を整えたり、少し広めの抽象化をしたり、関連しそうなファイルまで触ったりします。それが本当に助かることもあります。
ただ、小さなバグ修正のつもりだったのに、差分が広がることもあります。何が今回の目的に必要な変更で、何がついでの整理なのかが混ざる。すると、テストが通っていても受け入れにくくなります。
FrontierCodeのscope criterionは、file boundary、diff size、semantic localityのような観点で、変更が必要な範囲に収まっているかを見ます。これは、エージェントと作業するときにもほとんどそのまま使えます。
public APIは変えない。formattingだけの差分を混ぜない。必要なら先に相談してから範囲を広げる。
こういう制約は、エージェントを縛るためだけのものではありません。むしろ、レビューしやすく、受け入れやすく、あとから思い出せる変更にするための足場です。
「良いコード」の条件を考えるなら、テストについても同じです。
FrontierCodeの記事で特に印象に残ったのは、reverse-classicalという評価方法でした。
これは、エージェントが追加したテストを、修正前の壊れたコードベースに対して実行し、ちゃんと失敗するかを見るものです。修正前でも通ってしまうテストなら、そのテストは今回の問題を捕まえていないかもしれません。
言われてみれば当たり前です。
でも、エージェントと実装を進めていると、「テストも追加しました」という報告に少し安心してしまうことがあります。差分を見るとテストファイルが増えている。CIも通っている。説明ももっともらしい。それだけで、なんとなく守られた気持ちになる。
けれど、そのテストが本当に壊れていた振る舞いを固定しているかは、別の問いです。
これは自分の作業にもそのまま持ち込めます。
エージェントにバグ修正を任せるなら、最初から「このテストは修正前には落ち、修正後に通ることを確認してください」と伝える。あるいは、こちらがレビューするときに、追加テストが何を捕まえているのかを一緒に確認する。
テストを書くことと、安心できることの間には距離があります。
その距離を、エージェント任せにしすぎない。ここはかなり大事だと思いました。
ループするエージェントの外側で、判断する感覚を育て続ける
FrontierCodeの記事には、LOG_WARNING()という警告ログ用のhelperを追加する例が出てきます。
あるモデルの解法は、挙動としては正しく見えます。複数行のwarningも標準エラーに出ます。ただし、1行目だけLOG_WARNING()を使い、続く行ではstd::cerrを直接使っていました。今はどちらも同じstreamに出るので動きます。けれど、将来helperの実装が変わったとき、call site側に危うい前提が残ってしまう。
これは、テストでは通るけれど、メンテナとしては気になる変更です。
抽象を作ったなら、その抽象を使い切ってほしい。今たまたま動くことだけではなく、次に触る人がどの前提で読めばよいのかを残してほしい。
この感覚は、コーディングエージェントと作業するときにも必要になります。
エージェントに「作って」と渡すだけなら、こちらは利用者に近い立場です。けれど、出てきた差分を受け入れ、将来もそのコードを育てるなら、こちらは少しメンテナになります。
大きなOSSのメンテナでなくても、自分のリポジトリには自分なりの文脈があります。
どこまでを今の変更に含めるのか。どの抽象を使うのか。どのテストで守るのか。どの違和感は今直し、どの違和感は別のissueに分けるのか。
こうした判断は、エージェントが完全には肩代わりできません。むしろ、エージェントが実装できる範囲が広がるほど、人間の側に残る判断が見えやすくなります。
ここで、単に「任せ方を丁寧にしましょう」とまとめてしまうと、少し足りない気がします。
目的を書く。成功条件を書く。触ってよい範囲を書く。そういう心がけは大事ですし、Harness Engineeringの文脈でもよく語られています。実際、エージェントに渡す情報が曖昧なら、出てくる差分も曖昧になります。
でも、FrontierCodeの記事でおもしろかったのは、最初に何を書くかよりも、評価を作ったあとにもう一度それを問い直しているところでした。
タスク作者は、rubricを書いて終わりにはしていません。あえて雑な解法やズルい解法を作り、その評価で通ってしまわないかを見る。逆に、正しい別解が不当に落ちないかも見る。記事では、そうした検証のためにhack reportを作ると説明されています。
最近のCoding Agentには、/loopや/goalのように、実行を一回きりで終わらせないための仕組みがあります。計画し、実装し、検証し、失敗したら戻る。そういうループをどう設計するかが、エージェントを使う側にも求められています。
ただ、そのループの外側にも、もう一つのループがあります。
エージェントが出した差分を見て、自分はなぜこれを受け入れたいのか、あるいは受け入れたくないのかを考える。その違和感を言葉にし、次の条件やレビュー基準に戻していく。
テストは通っているけれど、期待した抽象には乗っていない。変更範囲は小さいけれど、将来の変更点を増やしている。仕様は満たしているけれど、レビューで説明しづらい形になっている。そういう差分を見たときに、「プロンプトが悪かった」で終わらせない。
この差分を受け入れたくない理由は何だったのか。最初の時点で書けるほど明確だったのか。それとも、差分を見て初めて自分の中の判断が見えたのか。
そこを次のループに戻していく。
任せる側が小さなメンテナになる、というのは、最初から完璧な条件を書くことではありません。
ループするエージェントの外側で、自分の判断する感覚を育て続けることです。
私はしばらく、この感覚を手元に置いておきたいです。エージェントに渡す部分を増やしながら、それでも何をmergeしたいのか、どんな変更なら育てていけるのかは、自分の側にも残しておく。
速く作れることと、受け入れられる形で作れることは、少し違います。
その違いを忘れないための手がかりとして、FrontierCodeの評価設計はとてもよいものに見えました。
Footnotes
-
OpenAI, "Introducing SWE-bench Verified," 2024. ↩