Eight months inside a GitHub Actions merge queue teaches you something that feature comparison matrices never will. A framework can ship fifty metrics, gorgeous dashboards, and citations from respected research labs. If it blocks your deploy because a "vibe check" score drifted from 0.72 to 0.68 against identical code, it is worse than useless. It becomes an active threat to your shipping velocity.
これこそが、ほとんどのLLM評価まとめ記事が見落としているフィルターです。それらは「何ができるか」を数えます。しかし、マージキューにおいて唯一重要な問い、「このチェックは実行されるたびに、全く同じようにパスまたは失敗するか?」を問うことは滅多にありません。
私は、気が進まない泥臭い作業を通じてこれを学びました。6つのオープンソースLLM評価フレームワークを、実際のCIパイプラインに組み込んだのです。それらは8ヶ月間、本番環境のプルリクエストに対して実行されました。そのうち2つは、ゲートキーパー(門番)として残る権利を得ました。残りは、アドバイザリー(助言用)ダッシュボードへと格下げされるか、ナイトリージョブへと移動されるか、あるいは完全に削除されました。その教訓は痛烈で、高くつきました。メインブランチを守る際、決定論的な構造(deterministic structure)は、確率的な品質(probabilistic quality)に勝るということです。
マージゲートの真の役割
CIゲートは研究環境ではありません。それは「用心棒(bouncer)」です。その唯一の目的は、特定の変更を確認し、「Yes」か「No」で答えることです。「はい、このPRはメインブランチに統合できます」「いいえ、できません」。その回答は数秒以内に、わずかなコストで、そして後から覆ることがあってはなりません。静かな火曜日と、慌ただしい金曜日に、同じコミットに対して同じパイプラインを再実行したとしても、結果は同一でなければなりません。
ここで、ほとんどのLLM評価フレームワークがつまずきます。それらはデータサイエンティストによって、データサイエンティストのために作られているからです。それらは洞察、探索、そして微細なスコアリングを最適化します。一方で、マージキューが最適化するのは、バイナリな決定(二値判定)、スピード、そして「フラッキーさ(flakiness)」の排除です。これら2つの目標が重なる部分は、ごくわずかです。
なぜ LLM-as-Judge がキューを壊すのか
私のテストで失敗したツールには、共通の設計上の罪がありました。それは、主要なゲートメカニズムとして「LLM-as-judge(判定役としてのLLM)」の呼び出しに過度に依存していたことです。
LLM-as-judgeのプロンプトは、モデルに1から10のスケールで出力をスコアリングさせたり、2つの回答のうち優れた方を選ばせたり、事実の正確性を評価させたりします。このアプローチは品質の傾向を把握するには強力ですが、ブロッキング(実行を停止させる)CIチェックにとっては毒となります。Temperature(温度パラメータ)、モデルのバージョン、プロンプトのフォーマットなどがノイズとなるため、同じ入力であっても日によって異なるスコアが出ることがあります。そのスコアが厳格な閾値や終了コード(exit code)に紐付けられていると、キューは「幽霊(実体のないもの)」によってブロックされることになります。
失敗は瞬く間に連鎖します。非決定論的なチェックは、キューの滞留を引き起こします。エンジニアは、スコアが好ましい数値になるまでリトライすることを覚え、チームは「赤色のビルド(失敗したビルド)」を無視するように訓練されてしまいます。リトライのたびにAPIクレジットを消費するため、トークンコストも膨れ上がります。最悪なのは、シグナルが意味をなさなくなることです。ビルドが赤くなるのは「バグを混入させた」という意味であるべきです。もしそれが「判定モデルが今日は気難しい気分なようだ」という意味になってしまえば、信頼は失墜します。
生き残ったツールが異なる点
PromptfooとDeepEvalが生き残ったのは、決定論的なチェックを「第一級市民(first-class citizens)」として扱い、LLMによる判定スコアを「二次的で、ブロッキングしないシグナル」として扱っているからです。彼らは、ゲートに必要なのは「意見を持った浮動小数点数」ではなく、「終了コード」であることを理解しています。
MITライセンスでリリースされている Promptfoo は、コマンドライン向けに構築されています。正規表現の一致、JSONスキーマの検証、包含チェック、完全な文字列比較といったアサーションを実行します。これらは派手なものではありません。いわば、強化版の grep や jq コマンドです。だからこそ、CIで機能するのです。正規表現は一致するか、しないかのどちらかです。JSONスキーマは検証が通るか、エラーを投げるかのどちらかです。Promptfooは標準的なUnix終了コードを返すため、GitHub Actionsはいつマージを停止すべきかをネイティブに理解できます。CLIツールとして動作するため、言語に依存しません。出力を検証するためだけに、Node.jsのサービスリポジトリ内にPythonのエコシステムをインストールする必要はありません。
Apache 2.0ライセンスの DeepEval は、Pythonチーム向けの選択肢です。pytestのように統合できます。使い慣れた構文でテストを記述でき、失敗すれば自然にテストスイートがブロックされます。DeepEvalは膨大な指標のカタログを提供していますが、重要なのは、それらを慎重に使用しなければならないという点です。ゲートには決定論的またはヒューリスティックな指標に頼ってください。もしG-Evalやその他の判定ベースのスコアラーを導入する場合は、ハードなアサート(hard asserts)としてではなく、ブロッキングしないレポート生成器の中にラップして使用してください。このように使用すれば、DeepEvalは研究用ノートブックのような不安定さを排除しつつ、テストフレームワークとしての使いやすさ(ergonomics)を提供してくれます。
残りの4つの位置づけ
ゲートとして生き残れなかった4つのフレームワークにも、依然として価値はあります。それらは単に、ツールチェーン内の別の場所に適しているだけなのです。
Future AGI (Apache 2.0) は50以上のメトリクスを提供しており、カスタムSDKを構築するチームを対象としています。メトリクスは徹底していますが、問題は、CIキューで実行するために独自のハーネス(実行環境)を記述することを前提としている点です。研究の文脈であれば、それは妥当なトレードオフです。しかし、マージキューにおいては、カスタムの配線(ワイヤリング)が重なるたびに、新たな不安定さの要因となります。有能な評価エンジンではありますが、そのままゲートキーパー(判定役)として使えるものではありません。
RAGAS (Apache 2.0) は、RAG(検索拡張生成)の品質測定に長けています。その faithfulness(忠実性)や answer relevance(回答の関連性)といったメトリクスは、ナレッジベースの経時的なパフォーマンスを理解する上で非常に有用です。残念ながら、これらのメトリクスはLLMジャッジに大きく依存しています。Slackにトレンドを投稿するような、夜間の品質チェックジョブには最適ですが、プルリクエストの門番(バウンサー)としては不向きです。RAGASはマージをブロックする仕組みではなく、定期的な分析パイプラインに組み込むべきです。
Arize Phoenix は Elastic License 2.0 を採用しており、全く異なる領域に位置しています。これは分散トレーシングと評価を連携させることで、モデルがなぜそのような挙動をしたのかという観測性(observability)を提供します。本番環境でのインシデントのデバッグや、ハルシネーションの原因となった不適切な検索チャンクを特定したいときには、これが求められます。しかし、ジュニア開発者のフィーチャーブランチをリリースしてよいかどうかを、トレーシングツールに判断させたいわけではありません。そのアーキテクチャは、インサイトを得るためのものであり、バイナリな判定(ゲート)のためのものではありません。
MLflow Evaluate (Apache 2.0) は、実験トラッキングの系譜を継いでいます。そのため、非常に重厚です。軽量なCIイメージにこれを取り込むと、起動時間と依存関係が増え、すべてのジョブが遅くなります。どうしてもパイプライン内で使用する必要がある場合は、構造チェックのためにヒューリスティックなメトリクスに留めておきましょう。そうしても、フレームワークの根本的な設計思想と戦うことになります。MLflowは実行結果をログに記録し、数週間にわたって実験を比較することを目的としています。一方、マージキューが求めているのは、1分以内に出される判定です。
ゲート管理のための実践的なルール
この実験から他の何を得られなかったとしても、この3つのルールだけは覚えておいてください。
第一に、雰囲気(バイブス)ではなく、構造をゲートにすること。出力が有効なJSONであることを強制できます。必要なキーが含まれていることを強制できます。分類ラベルが許可された列挙型(enum)に属していることを強制できます。これらのチェックは高速で、コストが低く、決定論的です。要約が「フレンドリー」であることや、書き換えが「クリエイティブ」であることを、確実に強制することはできません。そうした性質は、自動化されたゲートではなく、人間によるレビューや定期的なバッチ評価に委ねるべきものです。
第二に、入力が変わっていないのにスコアが変動した場合は、直ちにそのメトリクスの格付けを下げてください。まったく同じアーティファクトに対して、評価スイートを2回実行してください。もし、いずれかのメトリクスが「合格」から「不合格」に変わったなら、それはマージをブロックする権利を失ったことになります。そのメトリクスは、ばらつきが想定され、許容される「アドバイザリー(助言用)ダッシュボード」へと格下げしてください。
第三に、終了コード(exit code)を尊重すること。赤いバナーが付いた綺麗なHTMLレポートは、マージを止めません。ゼロではない終了コードが、マージを止めます。評価ツールは、CIプラットフォームのネイティブ言語で話さなければなりません。標準出力(Standard out)は人間のためのものであり、終了コードは機械のためのものです。
まとめ
LLMを活用したアプリケーションをどのようにテストすべきか、私たちはまだその初期段階にいます。評価を、ニュアンスを含み、文脈に依存し、多少主観的な「人間の採点基準」のように扱いたくなる誘惑に駆られます。それは研究論文では通用しますが、マージキューでは破綻します。
8ヶ月間の本番トラフィックを経て、私のパイプラインは現在、サービスを横断した構造およびスキーマの検証には Promptfoo を、合格・不合格の条件に明確にマッピングできる Python 側の挙動チェックには DeepEval を使用しています。それ以外のすべては夜間のダッシュボードに報告されます。キューは安定し、シグナルはクリアです。チームは再び「ビルド失敗(red build)」を信頼できるようになりました。
ゲートに必要なのは、より多くのメトリクスではありません。毎回必ず真実を伝える、より少ないメトリクスなのです。
Dev.to で共有されたオリジナルのテストと記事に基づいています。信頼性の高いAIシステムの構築に関するさらなる議論については、Telegram の GyaanSetu コミュニティに参加してください。
