RAGシステムの出力が、与えられていない価格主張を断言し、文脈では答えられない質問に対して応答している例です。この出力を最も人気のある2つのLLM-as-judge faithfulness指標にかけ、5回ずつ評価しました。ジャッジはgpt-4o、temperature 0 — ジャッジの安定性に最も有利な設定です。

RAGASは0.000と評価しました。

DeepEvalは1.000 — 5回すべてで — と評価し、以下のように説明しました:「スコアが1.00である理由は、実際の出力と検索文脈の間に矛盾がないためです。」 ある繰り返しではさらに「精度と一貫性を維持する素晴らしい仕事です!」と付け加えました。

どちらの指標もfaithfulnessと呼ばれています。どちらも内部的には一貫しています。しかし、捏造に気づくのはそのうちの1つだけです。

なぜそうなるのか、適切に測定した際に何が見つかったのか、そして私の結論が両方を使う — LLMジャッジと決定論的チェック — である理由を説明したいと思います。むしろLLM-as-judgeの否定を期待するかもしれませんが。

なぜこれを測定する必要があったのか

私は医療向けのAIツールを構築しています — 医療ライター向けの主張検証、登録医療機器である患者向けテキスト簡素化ツール、臨床的な匿名化処理。この領域では、回答はソース資料から正当化できる場合にのみ有用であり、問題となる障害モードは静かなものです:簡素化された退院サマリーから薬剤投与量が静かに削除される;モデルが親切に発明した基準範囲;文脈に答えがないのに自信たっぷりに回答するシステム。

これらの特性でリリースを制御する必要がありました — プロンプト変更がシステムの信頼性を低下させた場合にビルドを失敗させる。そしてLLM-as-judge指標はビルド制御には使いにくいものです:実行ごとにコストがかかり、非決定的であり、(判明したところ)「同じ」指標の2つの実装が同じものを測定していないことがわかりました。

そこでOpenGATEを構築しました。すべてのチェックは出力と手動ラベル付きゴールドケースの純粋な関数です:必要な事実は存在しなければならない(受け入れられた言い換えを含む)、回答内のすべての数値は文脈まで遡及可能で、文脈が答えられない場合はシステムは棄権しなければならない。どこにもグレーダーモデルはありません。そしてRAGASとDeepEvalに対して制御された比較を行い、それぞれのアプローチが実際に何を見ているかを調べました。

実験

27の出力からなる凍結コーパス:6つのキャプチャされたシステム出力(3つは本番環境から)と、それぞれ1つの既知の欠陥を注入したミュータント — 欠落した事実、捏造された数値、棄権の失敗、追加された矛盾、またはその場での意味の反転。5回繰り返し。各アームで実施。ジャッジはgpt-4o、temperature 0。

公平性のために3つの設計上の選択が重要です。欠陥クラスはスコープごとにグループ化され、決して合計されません — 決定論的チェックは意味の反転を見ることはできないため、すべてを1つの精度数値にまとめることはどちらの方向にも操作になります。検出は、同じケースの未変異ベース出力に対する相対的な注入欠陥への反応として測定されるため、既存の癖が誤ってカウントされることはありません。そして出力はすべての繰り返しでフラグが立てられた場合にのみ検出されたとカウントされます — 断続的に失敗するゲートはゲートではありません。すべての入力、スクリプト、および繰り返しごとのスコアは論文アーティファクトにコミットされています。

結果は二面性を持つ

ジャッジが決定的に勝つところ。 意味の反転(「食事と一緒に服用する」が「空腹時に服用する」になり、すべてのアンカーと数値が逐語的に残る)で、RAGASは4/5、DeepEvalは5/5をキャッチしました。決定論的チェックは0/5 — そして常にそうなる。文字列チェックには意味のモデルがありません。これはジャッジのカテゴリカルな優位性であり、限界的なものではなく、これがこの記事が否定論でない理由です。

ジャッジが負けるところ。

省略。 欠落した抗生物質投与量は何もサポートされていないことを主張しない — したがってfaithfulness指標は構造上それに盲目的です。ジャッジは0/5と1/5の欠落した事実をキャッチしました。決定論的アンカーチェックは5/5をキャッチしました。500 mgの投与量を削除した退院サマリーで、DeepEvalは「矛盾がない」という根拠で1.00を返しました。抗生物質投与量が欠落している出力に対して、合格スコアの自信たっぷりの説明です。

コストと速度。 1,000評価あたり$11.49(RAGAS)と$8.29(DeepEval)に対して$0.00。繰り返しあたり194秒と119秒に対して3.8ミリ秒。この差は、すべてのコミットで — またはすべての回答で — チェックを実行できるかどうかを決定します。

ローカライゼーション。 決定論的チェックが失敗すると、失敗を命名します:missing fact "500 mg"。RAGASはスカラー値を返します。DeepEvalは時々良い根拠を返し、時には上記の「矛盾がない」という文を返します。

最も気にする発見は定義的です。 RAGASは「すべての主張は文脈によってサポートされているか?」と尋ねます。DeepEvalのfaithfulnessは「何らかの主張が文脈と矛盾するか?」と尋ねます。捏造された数字は何とも矛盾しません — 文脈はそれについて沈黙しています。したがって、ソースに含まれていないことを主張する6つの出力で、RAGASは5つをキャッチし、DeepEvalは0をキャッチしました。明白な矛盾については密接に一致します(7/10対9/10)。発散は体系的であり、ノイズではありません。

不快な含意:実装を読まずに「LLM-as-judge faithfulness」を採用するチームは、厳密さのレベルを選択していません。どの失敗モードに盲目的になるかを無意識に選択しているのです。患者向け医療ツールでは、発明された投与量が最も重要な失敗であり、まさに矛盾ベースのジャッジがスルーするものです。

撤回する主張

私はこの実験を、ジャッジの不安定性を実証することを期待して設計しました。強力なジャッジモデルでは、その発見は弱く、それを埋もれさせるのではなく報告しています:gpt-4o/temp 0では、繰り返し間の平均出力ごとのスコア範囲はRAGASで0.026、DeepEvalで0.013でした。gpt-4o-miniでの同じコーパスは substantially ノイズが多く(27出力中11出力が繰り返し間で判定を変更)、したがって不安定性は判断自体の特性ではなく、ジャッジモデルの特性です。ビルドをジャッジで制御しない理由は、省略盲、定義の発散、およびコストに基づいており、より良いジャッジモデルでも修復されません。

決定論的ゲーティングが本番環境でキャッチしたもの

このフレームワークは4つの本番システムでCIで実行されます。初回実行結果、すべて以前は未知:複数主張の判定の~50%を「Not Supported」にデフォルトするサイレントパース失敗;非識別化エンジンの2つの名前キャプチャバグ(O'Brienのようなアポストロフィ付き姓がキャプチャパターンから外れる);そして簡素化ツールが退院サマリーからその抗生物質投与量を削除する。

数ヶ月後に来たお気に入りの失敗。投与量削除の修正後、同じシステムが捏造された数字を生成しました:「通常、ヘモグロビンレベルが12 g/dL以上であることを確認したい」 — 臨床的には正しく、ソースレターには存在しません。以前のプロンプト修正は数値を省略したり変更したりしないと言っていました;それは数字を失うことを禁じ、発明することについては沈黙していました。最後の欠陥に対して強化されたプロンプトは、次の欠陥に対しては強化されていません — これは、1回実行された監査ではなく、すべての変更で実行されるゲートのための全引数です。グリーンスコアカードは測定であり、特性ではありません。

正直な注意事項

ミュータントは合成であり、野生からサンプリングされたのではなく、観察した欠陥から導出されたものです。コーパスは27の出力です。1つのジャッジモデル。そして欠陥分類法とアームの1つを書いた — これがスコープが別々に報告される理由であり、私自身のアプローチがゼロをスコアする反転クラスがコーパスにある理由です。完全な論文には妥当性の脅威セクションがあり、事前登録したがまだ実行していない実験(ゴールドセットの独立した2番目のラベラー)を含みます。

実際に推奨すること

それぞれが見えるものに両方を使います。意味論のためのLLMジャッジ — 意味の反転、矛盾、オープンエンドの品質 — ここではカテゴリカルに優れています。決定論的チェックをゲートとして:必要な事実が存在し、数値が追跡可能で、棄権が尊重される — 再現可能、命名された失敗、無料で、すべてのコミットとすべての回答に十分な速さ。

決定論的側面を試したい場合、MITライセンスで、APIキーなしで1行で実行できます:

npx @pharmatools/opengate

Enter fullscreen mode Exit fullscreen mode

Python(pip install opengate-grounding):

from opengate_grounding import check_grounding

result = check_grounding(
    answer,
    context,                        # the retrieved evidence
    anchors=["500 mg", "twice daily"],  # facts the answer must contain
)
result.grounded   # deterministic — same evidence, same verdict, every run

Enter fullscreen mode Exit fullscreen mode

また、pytestヘルパー、DeepEvalのGroundingMetric(はい — 統合はジャッジ指標と並行して正常に動作します;それがポイントです)、ビルドを回帰で失敗させるGitHub Action、回答前にエージェントが自分の回答をチェックできるMCPサーバーもあります。

リポジトリ:github.com/nickjlamb/opengate · 論文:doi.org/10.5281/zenodo.21365095

私は医療ライターで、医療向けAIツールの構築に移りました。この投稿のすべてはコミットされたアーティファクトから再現可能です — 成立しないものを見つけたら、issueを開いてください;それがゲートの目的です。