Skip to main content
Glama

evalmine

リーダーボードの数字は、モデルの変更が実際に実行している40数個のタスクにどう影響するかを予測したことは一度もありません。あるベンチマークでトップになったモデルに切り替えたら、依存している仕事では静かに悪化していた、ということがあります。

evalmineは、モデル変更について、あなたのタスク上で一つの問いに答えます:それは役に立ったのか、害になったのか、それとも同じ結果に対してコストが増えたのか?あなたはタスクのYAMLスイートを書きます。それを2つ以上のモデルで実行し、各回答をスキーマ検証し、時間を計測し、LLMジャッジが回答を両方向の順序でペア比較します。これにより、ジャッジが最初に見た方を好むという偏りは相殺されます。そのジャッジを、あなたの選好ラベルに対してコーエンのκ係数で評価し、ジャッジがあなたと一致していることを示せない場合、勝率を見出しとして掲げることを拒否します。コストは日付に固定された価格表から来ます。未知のモデルは$0として扱うのではなく、実行を失敗させます。レポートはスイートのハッシュでバージョン管理されます。3つのツールを持つMCPサーバーにより、エージェントがタスクの途中で評価を実行できます。

ここにある例のスイートをフェイクアダプターに対して実行した結果:12ラベルでのκ係数0.25は0.40の下限を下回るため、0.463の勝率はフラグ付きで表示され、見出しにはなりません。その拒否こそがツールの機能です:

$ evalmine run examples/everyday-eight.yaml \
    --models anthropic/claude-haiku-4-5,google/gemini-2.5-flash --fake

run 20260823T210009Z_c4545e4e_dbc76614  (everyday-eight)
  report: reports/everyday-eight/20260823T210009Z_c4545e4e_dbc76614/report.md
  calibration: below_floor - kappa 0.25 (fair) over 12 labels - headline eligible: false
  google/gemini-2.5-flash vs anthropic/claude-haiku-4-5: win-rate 0.463 (UNCALIBRATED) [0.325-0.613] over schema-passing pairs only, n=20 - flips 3 - excluded 0
  cost: $0.0658 this run (answers $0.0081, judge $0.0578); if uncached $0.0658

フェイクアダプターは決定的なので、これらの数値はクリーンなチェックアウトで正確に再現されます。上記の何もプロバイダーに接続せず、1セントも費やしていません。

evalmine: スイートを検証し、フェイクアダプターで実行し、レポートのキャリブレーションと勝率セクションを読む

そのすべてのフレームは実際の実行です。vhs docs/demo.tape(vhs、brew install vhs)で再録画してください。

ステータス。 v0.1.0、プレリリース。コア、3つのプロバイダーアダプター、実行チェック、MCPサーフェスは構築・テスト済みです。価格表は、各プロバイダーの公開価格ページを固定日付で検証済みです。決定ログのエントリはまだありません — 未実装を参照。

仕様書:docs/spec.md。これはコードが準拠する契約であり、両者が矛盾する場合はこのREADMEよりも優先されます。詳細な仕組み:docs/learning/how-it-works.md(スタイル付きHTMLレンダリング)。

クイックスタート

git clone https://github.com/hishamalward/evalmine.git && cd evalmine
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"          # add ,mcp -> ".[dev,mcp]" for the MCP server

Python 3.10以降。実行時依存関係は3つ:PyYAML、jsonschema、httpx。

費用をかけずにスイートを検証する。 validateはファイルを解析し、JSON Schemaを適用し、すべてのプロンプトをレンダリングします(一致しない{{placeholder}}はハードエラーです)。また、すべてのモデル文字列を価格表に対して解決します。ネットワーク呼び出しはゼロです。

evalmine validate examples/everyday-eight.yaml
# ok: examples/everyday-eight.yaml - 8 tasks, 20 cases, 12 labels; every prompt
# rendered; 3 model strings resolved against prices-2026-08-23.yaml

フェイクアダプターで実行する。 --fakeはすべてのモデル文字列を組み込みの決定的アダプターにルーティングします:キーなし、ネットワークなし、費用なし。以下の2つのモデル文字列は、例のスイートの12の人間ラベルが参照するものです。したがって、この実行はキャリブレーションパスをエンドツーエンドで実行します。

evalmine run examples/everyday-eight.yaml \
  --models anthropic/claude-haiku-4-5,google/gemini-2.5-flash --fake

実際に実行する。 キーは環境変数からのみ取得され、他の場所からは取得されません。.env.exampleをコピーし、リポジトリの外で記入し、必要なものをエクスポートします。

export ANTHROPIC_API_KEY=...
export GOOGLE_API_KEY=...

evalmine run examples/everyday-eight.yaml \
  --models anthropic/claude-haiku-4-5,google/gemini-2.5-flash \
  --max-cost 0.50

最初のライブ呼び出しの前に、事前見積もりが実行されます。それが--max-costを超える場合、実行は拒否され(終了コード4)、何も費やされません。どこにも上限がない場合、CLIのデフォルトは$2.00です。すべての呼び出しはコンテンツハッシュでディスクにキャッシュされるため、再実行は無料で、レポートは再現可能です。--no-cacheは新しい呼び出しを強制し、それでも書き込みます。

他のコマンド:evalmine prices [--for suite.yaml]、evalmine last suite.yaml、evalmine report <run-id>、evalmine compare <report_a> <report_b>。

Related MCP server: AgentOps EvalBench MCP

スイートファイル

1つのYAMLファイルに、タスク、ジャッジ設定、ラベルが含まれます。同梱の例はexamples/everyday-eight.yamlです:20ケースにわたる8つの架空のタスク(書き換え、抽出、分類、説明、小さなコード変更)で、そのうち3つは出力スキーマを持ち、12の選好ラベルがあります。完全なスキーマは仕様§5にあります。形状は次のとおりです:

suite: everyday-eight
version: 1

defaults: { temperature: 0, max_tokens: 700, timeout_s: 60 }
limits:   { max_cost_usd: 1.50 }

judge:
  model: anthropic/claude-sonnet-4-6
  rubric: |
    Prefer the answer that a competent colleague would ship without editing.
    ...
  calibration: { min_kappa: 0.40, min_labels: 10, on_below_floor: flag }

tasks:
  - id: ticket-triage
    kind: classify                # a free label, used only to group report rows
    prompt: |
      Classify this support ticket. Return JSON only.

      Ticket:
      {{ticket}}
    schema: { type: object, required: [category, severity], ... }
    rubric: |                     # appended to the suite rubric for this task
      In addition to the suite rubric: ...
    cases:
      - id: charged-twice
        vars: { ticket: "I was charged twice this month..." }

labels:
  - { task: ticket-triage, case: charged-twice,
      baseline: anthropic/claude-haiku-4-5,
      candidate: google/gemini-2.5-flash,
      prefer: candidate, note: "team-wide lockout is high, not medium" }

このファイルについて意図的な3つの点:

  • テンプレートはJinjaではありません。 正確に{{name}}、一度だけ置換され、式やフィルターはありません。一致する変数がないプレースホルダーは、読み込み時にハードエラーになります。なぜなら、静かに空の変数は、評価を静かに無意味にする最も簡単な方法だからです。

  • 未知のキーはエラーです。 すべてのレベルで。タイプミスしたrubrik:が無視されると、見た目は良くても意味のないレポートが生成されます。

  • labelsはツールの信頼性の源です。 それらはあなたの判断であり、勝率を見る前に記録され、ジャッジはそれらに対して評価されます。ラベルのないスイートも実行できますが、見出しの数字を生成することはできません。

例を自分のタスクに置き換えてください。それがこのツールの目的です。

コードタスクの実行チェック

散文は、実行されるコードの代用としては不十分です。ケースはcheckを宣言できます:回答のコードを取得するbashスニペット($ANSWERはファイル、$ANSWER_TEXTはテキスト)で、正常に動作すれば終了コード0を返します。これは新しい一時ディレクトリで、タイムアウト付きで、環境からシークレットを除去して実行され、キャッシュされることはありません。回答内のすべてのフェンス付きブロックが順番に実行され、それぞれが独自のフィクスチャで実行されます。最後のブロックが判定であり、それ以前のブロックはその隣に記録されます。したがって、間違ったブロックを撤回して2番目のブロックを書いた回答は、2番目のブロックで評価され、撤回が表示されます。

- id: jq-remote
  vars: { task: "Write a jq filter ... the JSON is in postings.json" }
  check:
    setup: 'printf "[{\"t\":\"a\",\"remote\":true}]" > postings.json'
    run: 'jq -r "$(cat "$ANSWER")" postings.json | grep -q a'

結果(合格/不合格、終了コード、出力)は、answers.jsonl、スコアカード、HTMLペアビューで回答の隣に表示され、ジャッジには1つの固定ルールで示されます:チェックに失敗した回答は、合格した回答に勝つことはできません。仕様§6.6。

レポートの読み方

reports/<suite>/<run-id>/report.mdは、report.json、report.html、answers.jsonl、pairs.jsonlと並んでいます。この順序で読んでください。

1. まずキャリブレーション。 これは意図的に勝率の上に表示されます。ジャッジの判定とあなたのラベルの間のコーエンのκ係数(Landis-Kochの帯域名付き)と、その下に3x3の混同行列が必要です。単純な一致率ではなくκ係数を使うのは、あるカテゴリが支配的になると一致率が膨張するからです。そして、それは起こります:ジャッジは引き分けが安全であることを学習します。行列はジャッジがどのように間違っているかを示します。これは重要です — あなたが「引き分け」と言うときに決して言わないジャッジは、新しいものを体系的に好むジャッジとは異なる問題です。その下のタスクごとの内訳は、どこで間違っているかを示します:1つのκ係数は、書き換えタスクでは優れているがトリアージタスクでは役に立たないジャッジを隠すことができ、平均はあなたが失う発見です。

2. 信頼すべきでない勝率。 3つの条件があり、いずれか1つで十分です:

  • headline_eligible: false — κ係数が下限を下回る、ラベルが少なすぎる、または両方の評価者が全体を通して1つのカテゴリを使用したためκ係数が未定義。レポートはその数値を見出しにすることを禁止し、すべての数値にダガーを付け、JSONとすべてのMCP応答にも同じフラグが付くため、要約を読むエージェントは注意書きなしにその数値を引用できません。

  • フリップ率が0.30を超える。 フリップとは、2つの回答の順序が入れ替わったときにジャッジが回答を変更したペアです。約3分の1を超えると、勝率は品質ではなく表示順序を測定しています。レポートは同じ表でその旨を述べます。

  • nが小さい、または減少している。 勝率はスキーマ合格ペアのみで計算されます:どちらかの側が解析に失敗した、またはスキーマに失敗したペアは、敗北としてスコアリングされるのではなく除外されます。これにより、JSON出力が苦手なモデルがフォーマットの失敗で品質比較に負けることはありません。代償としてnが減少します。そのため、セクションのタイトルは「スキーマ合格ペアのみ、n=…」であり、nは同じ画面にスキーマ合格率なしで表示されることはありません。

数値を公開する前に、min_kappaを0.60に引き上げてください。 同梱のデフォルトは0.40です — 公平から中程度の一致の慣例的な下限で、12個のラベルを持つ最初のスイートが妥当にクリアできる低さです。これは自分で数値を使うための下限であり、ラベリングがどう進んだかについての自分の記憶があります。0.60 — 「実質的」 — は他の人に数値を伝えるための下限であり、その記憶は伝わりません。ツールは寛容に同梱されているため、最初のスイートは2回実行する価値があります。この推奨は、最初のスイートがブログ記事に載らないようにするためのものです。

3. 次にスコアカード。コストは品質と一緒に読み、後回しにしない。 スキーマ合格率(nativeまたはpromptedとラベル付け。あなたのためにスキーマを強制するプロバイダーと、単に丁寧に頼まれたプロバイダーは同じ測定ではないため)、実行チェックを宣言するタスクの実行合格率とそのn、p50およびp95レイテンシとそのn、今回の実行コストとキャッシュなしの場合のコスト。3倍の費用で0.55勝つ候補は、半分の費用で0.55勝つ候補とは異なる決定です。

4. タスクごとの表、悪い順にソート。 見出しの勝率が動かなかったのに、3つのタスクが反対方向に0.4動いた場合、それは見逃す発見です。evalmine compare A Bは、2つの実行間のまさにその動きを表示します。

5. report.htmlとラベリングフロー。 すべての実行は、自己完結型のページも1つ書き出します — サーバーなし、依存関係なし、file://パスから開きます。同じセクションに加えて、各判定ペアがモデル名を隠し、ジャッジの判定を折りたたんだ状態で並べて表示されるため、ジャッジが読んだのとまったく同じように回答を読むことができます。各ペアの下にPrefer A · Tie · Prefer B、次にcopy labels YAMLがlabels:エントリを提供し、スイートに貼り付けることができます:手動編集の30分ではなくクリック10分。これが、成長するキャリブレーションセットと成長しないセットの違いです。

レポートには形容詞は含まれず、推奨もありません。判断はDECISIONS.mdに、あなたの判定に基づいて記述されます — レポートは各実行の最後にテンプレートを事前入力します。

MCP

evalmine-mcpはstdio MCPサーバーで、CLIが呼び出すのと同じcore.py関数を呼び出す、正確に3つのツールを公開します:

ツール

機能

費用

run_suite(suite_path, models, max_cost, baseline, no_cache)

スイートを実行し、要約とレポートパスを返す

上限まで

compare(report_a, report_b)

2つのレポート間の差分

なし

last_report(suite_path)

スイートの最新レポート

なし

.mcp.json.exampleを.mcp.jsonにコピーして登録します。最初にエクストラをインストール:pip install -e ".[mcp]"。

ポイントは、エージェントがタスクの途中で評価を実行できることです — 「このファイルのモデルを交換する前に、スイートを実行して勝率を教えて」 — 後で人がレポートを読むのではなく。

CLI全体ではなく3つのツールである理由は、エージェント向けのサーフェスは決定をサポートする最小の動詞セットであるべきであり、追加のツールはすべて、誰も承認していないお金を使う別の方法だからです。

上限、そしてエージェントのデフォルトがあなたのものより低い理由。 上限はcore.run_suite()のパラメータであり、MCPが再実装するCLIフラグではありません:お金を使える場所は正確に1つあり、そこで上限が設定されます。エージェントがmax_costを提供した場合はそれが使用されますが、EVALMINE_MCP_MAX_COST_CEILING(デフォルト$5.00)を超えるリクエストは、クランプして実行するのではなく、完全に拒否されます。エージェントが省略した場合、上限はmin(suite.limits.max_cost_usd, EVALMINE_MCP_MAX_COST)で、デフォルトは**$1.00** — CLIの$2.00の半分です。CLIで人間が数字を入力したのに対し、エージェントは入力していないからです。上限超過の実行は構造化された拒否を返し、何も費やさず、収まるように静かに切り詰められることはありません。切り詰められた実行は、完全なもののように見える小さな数字を生成します。

run_suite はサマリーとパスを返し、生のプロバイダー応答は決して返しません。それらはディスク上の answers.jsonl に残ります。すべての応答をエージェントのコンテキストにストリーミングするツールは、評価自体よりも多くのコストを呼び出し元に課し、評価ハーネスをプロンプト内のあらゆる情報の外部送信経路に変えてしまいます。suite_path も EVALMINE_MCP_SUITE_ROOT(デフォルト: サーバーの作業ディレクトリ)内で解決されなければなりません。

先行事例

promptfoo と Braintrust はこの分野の代表的なツールであり、どちらも本ツールより高機能です。

promptfoo ははるかに多くのアサーションタイプ、Web ビューア、レッドチーミング、そして3つではないプロバイダー対応を備えています。Braintrust はホスト型プラットフォームです。トレーシング、本番ログから構築されたデータセット、本格的な UI、コラボレーション、そして誰かの製品であることに伴う運用上の成熟度を備えています。幅広さを求める場合、または同じ数値を見るチームがいる場合は、それらのいずれかを使用してください。

evalmine は、より狭い3つの理由のために存在します。

  • 判定はあなたに対して較正されるか、その数値は出力されません。 上記の両ツールは LLM 判定でスコアリングできます。しかし、あなたのラベルに対する較正を、勝率が引用可能であるためのゲートにしているものはありません。その逆転——拒否がデフォルトであること——が全体の主張であり、これは数値を無条件に出力するツールに後付けできる機能ではありません。

  • 決定ログは第一級の成果物です。 評価の出力は数値ではなく、6ヶ月後に弁護しなければならない決定です。DECISIONS.md はレポートによって事前に埋められ、人間によって書かれ、その決定の対象となったコードの隣のリポジトリに置かれます。

  • 表面積は一読で把握できるほど小さいです。 4つのアダプタ、レポート、実行チェックを含めておよそ6,000行。LLM フレームワークも、プロバイダー SDK もありません——文書化された JSON エンドポイントへの手書きの POST が3つだけです。そのコストは現実的であり、明言する価値があります。プロバイダーが API を変更したとき、私たちはアップグレードではなく破損によってそれを知るのです。

これら3つが重要でない場合、正直な推奨は promptfoo です。

未実装

v0.1.0 の範囲外であり、README はそれをあなたが発見するままにせず明記しています。RAG または検索評価、エージェントまたはマルチターンの軌跡、何かのファインチューニング、Web UI、ホスト型の何か、3つを超えるプロバイダー、ルーブリックの自動生成、上記3つを超える MCP ツール。

この README のすべての数値は、架空のサンプルスイート上のフェイクアダプタから来ています。実際のスイートでのラベル付き実行は、まだ較正された数値や DECISIONS.md エントリを生成していません。それは v0.1.0 タグの前に行われます。

開発

pip install -e ".[dev,mcp]"
python -m pytest -q          # 310 tests, none of which make a network call
python -m ruff check src tests

CI は {ubuntu, macos, windows} x {3.10, 3.13} で実行され、各レグがすべてのテストを実行し、さらに作業ツリーと完全な git 履歴に対するシークレットスキャンを行います。このリポジトリに API キーは一切属さず、evalmine run はスイートファイルに既知のキープレフィックスに一致する文字列が含まれている場合、起動を拒否します。

CONTRIBUTING.md を参照してください。変更は docs/spec.md から始まります。

ライセンス

MIT。LICENSE を参照してください。

Related MCP Connectors

Related MCP Servers