6か月以上稼働しているエージェントシステムには、ほぼ必ず同じパターンが見られます。evals/ フォルダがあり、2週目に意気込んで書いた40のシナリオが並び、last_run.json は最新のプロンプト変更よりも前の日付になっています。
これは eval スイートではありません。3月のミーティングのスクリーンショットです。
2つの仕事の問題
eval スイートが CI の外にあると、相反する2つの仕事を背負わされます。
- システムが動いていることを自分に納得させる(一度走らせて、デモして、アーカイブ)
- ユーザーが気づく前にリグレッションを捕まえる(変更ごとに走らせ、リリースをゲートする)
1つ目の仕事は徹底性を評価します。あらゆるエッジをカバーする200ケースを書け、という方向です。2つ目はスピードを評価します。5分以内に終わらなければ誰も待ってくれません。
CI の外では、スイートは1つ目の仕事に流されがちです。なぜなら、2つ目には強制力がないからです。結果として、デモには映えるが実務では役に立たないスイートが残ります。
「CI に入れる」とは具体的に何か
コストの低い順に、具体的には3つです。
- PR ごとにサブセットが走る。 高速・高カバレッジな20〜40ケースを選び、90秒以内に終わるように。失敗したらマージをブロックします。
- メインブランチに対して毎晩フルスイートが走る。 失敗時には誰かをページする。on-call ローテーションではなく、そのプロンプトのオーナーを。
- スコアの差分を時系列で追う。 単なる pass/fail ではなく。引用精度が4%落ちても hard gate を通過するならそれもリグレッションです。
真ん中の項目、つまり「失敗時に オーナー をページする」部分は、チームが省略しがちで、その結果ドリフトが累積する原因になります。
我々がすべてのシステムに同梱するもの
pnpm eval(または相当物)ターゲットで、高速サブセットをローカルで2分以内に走らせられること。同じものを PR で走らせる GitHub Action。本番プロンプトに対してフルセットを夜間に走らせる仕組み。そして、タグ別 pass 率と時系列 pass 率の2つのチャートを載せた1つのダッシュボード。
それだけです。このリストに目新しいものはありません。新しいのは、初日から配線され、我々が去った後も配線され続けるという点です。
なぜこれが効くのか
強制されないシステムに対して、エンジニアは evals を書きません。eval スイートは道徳的な成果物ではなく、テストピラミッドを支える構造部材です。他のテストが住んでいる場所に置きましょう。