mcpField
L
llm-eval-harness

llm-eval-harness

github.com/daymadeテスト・品質1.5k377 回インストール今日更新
リポジトリ

llm-eval-harness について

モデルエンドポイントの可用性、リクエストの伝達、速度、同時実行時の安定性、プロトコル準拠、ユースケース品質を測定する評価スキルです。モデル導入やゲートウェイの診断に使え、API キーはコマンド引数ではなく環境変数名で指定します。

主な機能

  1. 01モデル ID の可用性を測定
  2. 02プロンプト、ツール、複数ターンの伝達を確認
  3. 03初回トークン時間と継続速度を測定
  4. 04同時実行時の安定性とプロトコル準拠を検証
  5. 05ユースケース回帰を実行し構造化結果を読み取り

活用シーン

  1. 01モデルやゲートウェイ採用前のエンドポイント検証
  2. 02システムプロンプトやツール呼び出しの未伝達調査
  3. 03モデル速度の比較と同時実行上限の確認
  4. 04Anthropic の thinking と履歴再送の互換性確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/daymade/claude-code-skills --skill llm-eval-harness

インストール後、Claude Code で/llm-eval-harnessを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/
example_usecases.json
references/
evaluation_disciplines.md
machine-results.md
production_testing_patterns.md
quality_blind_judge.md
vendor_evidence_protocol.md
scripts/
availability_probe.py
concurrency_probe.py
eval_result.py
fidelity_probe.py
protocol_probe.py
speed_probe.py
usecase_runner.py
tests/
test_results.py

詳細

LLM Eval Harness

概要

このスキルにエンドポイント(base_url + model +環境変数に設定した API キー)を渡すと、エンドポイントが実際に動作するか、またモデルが十分に高速で安定し、プロトコルに準拠していて、十分な品質を備えているかを測定します。ベンダーが公表している数値を鵜呑みにするのではありません。通常は場当たり的なスクリプトに分散しており、毎回同じバグを含んだまま書き直される、次の 6 つの観点を評価します。

観点スクリプト確認できること
可用性scripts/availability_probe.pyここで動作するモデル ID と、3 状態のエラー分類
リクエストの忠実性scripts/fidelity_probe.pyシステムプロンプト/ツール/履歴が実際にモデルへ届いているか
速度scripts/speed_probe.pyTTFT +持続的なデコード tok/s、思考を考慮
並行性/安定性scripts/concurrency_probe.py成功率、p50/p90 レイテンシ、どこで破綻するか
プロトコル準拠scripts/protocol_probe.py要求時に Anthropic の thinking ブロックが実際に動作するか、さらに、後続ターンで履歴にすでに含まれているブロックもエンドポイントが受け入れるか(N≥10。この 2 つは別々のチェックです。観点 3 を参照)
品質/ユースケースの回帰scripts/usecase_runner.py +ブラインド評価者蓄積した独自のテストケースに合格するか

実行する観点

ユーザーが実際に行っていることに応じて選んでください。

  • 「接入新模型/新网关先测一下」/プロバイダーの導入 → 可用性 → 忠実性。導入する可能性が高そうなら、その後に速度/並行性も確認します
  • 「この ID は使えるか/対応リストを作成したい」 → 可用性(「利用不可」と判定する前に、§9 の評価規律を確認してください)
  • 「モデルが system prompt に従わない/エラーはないのにエージェントの挙動がおかしい」 → 忠実性(システムカナリア)
  • 「速いか/tok/s は本当か」 → 速度。「負荷に耐えられるか」 → 並行性
  • 「この互換エンドポイントは本当に互換か」 → プロトコル +忠実性のツール/認証チェック
  • 「このモデル/チャネルを使っていると 400 になる/セッションが切れる/続行できない」(拡張思考クライアント、マルチターンセッション) → プロトコルの --check history-replay を指定してください。これは、生成チェックには合格する一方で、履歴の再送は気づかれないまま機能していない場合の症状です。生成チェックだけで終わらせないでください
  • 「モデルを変えると品質が落ちるか」 → 品質回帰
  • 「ベンダーにバグを報告したい」 → 関連するプローブを --output 付きで実行し、続いて references/vendor_evidence_protocol.md に従ってください
  • 「デプロイのゲート/常時監視/アラートの誤検知が多い」 → references/production_testing_patterns.md

6 つすべてを習慣的に実行する必要はありません。ユーザーの質問に答えられるものを選んでください。

キーの取り扱い(必須): すべてのスクリプトでは、API キーを値ではなく環境変数名(--key-env MY_KEY)で指定します。これにより、ps、シェル履歴、保存されたレポートにキーが残りません。ユースケースファイルやラッパーにキーをハードコードしないでください。この規律やほかの評価規律の背景については、references/evaluation_disciplines.md を参照してください。

独自データはこのバンドルの外部に保存してください。 ユースケースライブラリ、モデル一覧、キーは ~/.llm-eval/(または秘密情報を保管している場所)に置き、このスキルのディレクトリには保存しないでください。このスキルは汎用の公開物であり、テストスイートは各自のものです。詳しくは、後述の「ユースケースライブラリ」を参照してください。

マシン用ランチャーと中断された実行: --result-json と --run-id の組み合わせのインターフェイス、および同梱の scripts/eval_result.py リーダーを使い、完了、非準拠、一部完了、未測定、失敗、キャンセルされた実行を区別してください。ランチャーに統合する前に、マシン結果を読んでください。このリファレンスでは、レシートの鮮度、型付きエビデンス、終了コード、認証情報の転送、オフライン回帰コマンドを扱っています。従来の --output と品質評価の回答アーティファクトも維持してください。

クイックスタート

利用可能なものを確認し、該当する次元のテストを実行します。OpenAI 互換モデルの場合:

export MY_KEY=sk-...                       # the key never appears in a command below

# Speed: real-task throughput + sustained decode ceiling
uv run --with openai python scripts/speed_probe.py \
  --base-url https://api.example.com/v1 --model some-model --key-env MY_KEY --mode both

# Concurrency: ramp until it breaks
uv run --with aiohttp python scripts/concurrency_probe.py \
  --url https://api.example.com/v1/chat/completions --model some-model --key-env MY_KEY \
  --format openai --concurrency 10 20 40 60

エンドポイントが Anthropic Messages 形式(/v1/messages)の場合は、プロトコルプローブも実行してください(以下を参照)。 ユーザーが実際に求めている内容に応じて次元を選んでください。「速いかどうか」だけを尋ねられたのであれば、6 つすべてを実行する必要はありません。

ルートで x-api-key ではなく Authorization が必要な場合は、Anthropic のプロトコル/同時実行プローブに --auth bearer を渡してください。デフォルトは引き続き x-api-key で、プロトコル生成と履歴リプレイの両方に適用されます。OpenAI の同時実行では Bearer 認証が維持されます。

次元 0 — 利用可否(実際に動作するモデル ID)

uv run --with aiohttp python scripts/availability_probe.py \
  --base-url <base> --key-env <ENV> --format both \
  --models model-a vendor/model-b @models.txt --output /tmp/avail.json
  • 判定は成功/失敗ではなく、失敗ごとに 3 種類です:no-channel(この ID へのルートがない)、upstream-error(ルートはあるが、上流で障害が発生している — 後で再試行してください)、empty-content(多くの場合、推論モデルでの max_tokens によるものです。モデルが壊れているわけではありません)。思考モデルが停止しているように判定されないよう、プローブの --max-tokens はデフォルトで 8192 に設定されています。値を下げて「最適化」しないでください。
  • 「利用不可」リストを書く前に:失敗した ID が公式ドキュメントに存在することを一つずつ確認してください。現行世代のモデル名は学習データより新しいため、推測を並べるのではなく、WebSearch でベンダー/モデル開発元のモデルページを確認してください。サフィックス(-preview、日付付きのバリアント、ティア名)によってルーティングの可否が決まります。よくある落とし穴は disciplines §9 を参照してください。
  • クライアント側のマーカーを実際のモデル ID としてプローブしないでください。 some-model[1m] のように角括弧でサフィックスが付いた文字列は、CLI クライアント独自のローカルな規約であることがよくあります(Claude Code はリクエストが作られる前に [1m] を解析して取り除きます。仕組みの詳細は claude-switch-models-setup の「コンテキストウィンドウサイズの設定」セクションを参照してください)。この文字列が通信上に現れることはありません。プローブはこのような文字列を検出すると警告しますが、確認するのはあなたの責任です。Claude Code の ANTHROPIC_MODEL 環境変数からコピーした文字列ではなく、ベンダーのドキュメントに記載されたサフィックスなしのモデル ID をプローブしてください。
  • ゲートウェイの /v1/models の一覧は入力の一つにすぎず、判定そのものではありません。実際のゲートウェイでは、一覧にないモデルにもルーティングされる場合があります。

次元 0.5 — リクエストの忠実性(ペイロードはモデルに届いているか)

uv run --with aiohttp python scripts/fidelity_probe.py \
  --base-url <base> --model <model> --key-env <ENV> \
  --format anthropic --check all --repeat 10 --output /tmp/fidelity.json
  • 4 つのチェックがあります:system(システムプロンプトに埋め込んだカナリアコードをモデルが復唱できるか)、tools(ツール結果を返す処理を含む一連の往復を行い、最終回答がその結果を使用していることを検証する)、multiturn(複数のターンにわたって事実を埋め込み、すべてを想起できるか)、auth(両方のプロトコルエンドポイントで x-api-key と Bearer を比較する — ゲートウェイは一方のパスでは両方を受け付け、もう一方では片方だけを受け付けることがよくあります。誤ったヘッダーによる 401 は、無効なキーの場合とまったく同じように見えます)。
  • この次元で検出できるのは、ゲートウェイで最も厄介な障害です:すべてのリクエストが 200 を返し、会話も流暢に進むのに、システムプロンプトがモデルに届いていない — そのため、ルールをシステムプロンプトに記述したツールは、どこにもエラーが表示されないまま、ひそかに誤動作します。実際のあるゲートウェイでは、あるモデルエイリアスは、許可されたあらゆる送信方法を試した 100 件以上のサンプルでシステムプロンプトを一度も届けませんでした。一方、別のエイリアスでは断続的に届き、その割合は時間帯によって変化していました。
  • ルーティングされたゲートウェイ経由の配信は確率的で、時間とともに変化します。そのため、--repeat(デフォルトは 10)と 3 種類の判定(配信済み/断続的に配信 k-of-N/未配信)が用意されています。ある時間帯で良好な結果が得られても、それはその時間帯についての結果にすぎません。数値を公表する前に、別の時間帯にも再サンプリングしてください(disciplines §12)。
  • プローブはトークン計数による裏付けも報告しますが、それだけを信頼することはありません。判断の基準となるのは動作上の証拠です。disciplines §10(計測値の調整)と§11(カナリアが本人確認や従順さのテストではなく、中立的な外部事実である理由)を参照してください。

ディメンション 1 — 速度(思考を考慮)

uv run --with openai python scripts/speed_probe.py \
  --base-url <…/v1> --model <model> --key-env <ENV> --mode both --output /tmp/speed.json
  • mixed は実際の使用感に近い代表的なタスクを実行します。decode は長い出力を 1 つ生成し、持続可能な上限(ベンダーの公称値と比較する数値)を調べます。both は両方を実行します。
  • このスクリプトが回避するための落とし穴: 推論モデルは思考を別の reasoning_content フィールドにストリーミングしますが、completion_tokens にはそのトークン数も含まれます。content だけを収集し、completion_tokens で割ると、数値が大幅に水増しされます。実際、約 750 tok/s のモデルが、この方法では 4700 tok/s と測定されたことがあります。このスクリプトは両方のフィールドを取得し、どちらかの種類の最初のトークンを TTFT として、completion_tokens / (total − TTFT) を報告します。
  • 出力の正しい読み方: 実タスクでのスループットは、デコードの上限よりも低くなります。短い出力は定常状態に達しないためで、これは想定どおりであり、バグではありません。両方の数値を報告し、モデルが思考を出力する場合はその旨を記載してください(エンドツーエンドのレイテンシには、文字入力の時間だけでなく推論時間も含まれます)。

ディメンション 2 — 同時実行性/安定性

uv run --with aiohttp python scripts/concurrency_probe.py \
  --url <full endpoint URL> --model <model> --key-env <ENV> \
  --format openai|anthropic --concurrency 10 20 40 60 --output /tmp/conc.json
  • 複数の --concurrency レベルを指定して負荷を上げ、成功率が低下する、またはレイテンシが急増するレベルを上限として見つけます。シングルスレッドでは高速なモデルでも、控えめな同時実行数で破綻することがあります(実例:あるプロバイダーは同時実行 50 件を 0.4s で処理できた一方、別のプロバイダーは同時実行わずか 5 件でリクエストを処理できなくなりました)。
  • このスクリプトは環境変数によるプロキシの影響を受けないよう trust_env=False にし、キープアライブのプーリングを無効にするため force_close を指定します。そうしないと、モデルではなくプロキシの上限や、固定された 1 つのアップストリームレプリカを測定してしまいます。また、リクエストが実際に並列実行されたことを確認できるよう、重複するリクエストのペアを示す「同時実行性の証明」も出力します。
  • 出力を見て失敗の種類を区別してください。HTTP 429(正常なスロットリングで、再試行可能)、タイムアウトまで停止する TCP 切断(UX が大きく損なわれます)、5xx では、それぞれ必要な対処が大きく異なります。

ディメンション 3 — プロトコル準拠(Anthropic の thinking ブロック:生成と履歴の再送)

uv run python scripts/protocol_probe.py \
  --url <…/v1/messages> --model <model> --key-env <ENV> --check all --repeat 10 --output /tmp/proto.json
  • Anthropic の /v1/messages 互換をうたうエンドポイントにのみ関連します。--check all (デフォルト)では、異なるコードパスをテストする両方のサブチェックを実行します。一方に合格しても、もう一方では完全に失敗することがあります。
    • generation:リクエストしたときに、thinking: {type: enabled} によって実際に thinking_delta / signature_delta の SSE イベントが生成されますか?(元のチェック)
    • history-replay:以前のアシスタントターンにすでに含まれていた type: "thinking" ブロックが、後続ターンでその履歴を再送したときにエンドポイントで受け入れられますか?Claude Code をはじめ、ほかのすべてのエージェント型クライアントが継続のたびに行う処理とまったく同じです。 (このチェックだけを実行するには --check history-replay を指定します)
  • history-replay チェックのきっかけとなった実際のインシデント(2026-07-21):中国のリセラー経由で利用した Kimi/Moonshot モデルは、generation には問題なく合格しました(要求すると正しく thinking を出力します)が、history-replay は完全に拒否されました。以前の thinking ブロックが入力として戻った途端に 400「invalid part type: thinking」を返し、後続ターンのたびにセッションが終了しました。generation に合格しても、この問題はわかりませんでした。これは互いに独立した障害モード(応答の生成とリクエストの検証)です。
  • 単一の軸をまたいだ比較だけで「ベンダー/リセラー X は壊れている」と結論付けないでください。また、ベンダーが文書化しているパラメーターを裏付ける単一軸の比較が得られたからといって、そこで調査を止めないでください。 正しく判断できるまでに 3 回の検証が必要でした。この事例は、典型的な注意例として分野別資料 §§17-19 に記載されています。1 回目はモデルとリセラーを同時に変えて比較したため、(誤って)リセラーのせいだと判断しました。2 回目は条件を修正し、同じリセラーのままモデルだけを変えました。その結果は、文書化されているベンダー固有の名前付きパラメーター(Moonshot 独自の preserve_thinking)と一致し、確認が取れたように見えました。しかし、2 回目も間違いでした。プローブはその 1 社のリセラーから出なかったため、ベンダー自身の直接/ネイティブエンドポイントでは「拒否された」モデルが問題なく動作することを確認できませんでした。実際の本番トラフィックがそれを証明しました。単一のリセラーで確認できた結果を確定と見なす前に §19 を読み、何かに「X は thinking をサポートしていない」と結論を書き込む前に、該当する実際のチャネルについてベンダー自身のエンドポイントまたは実トラフィックを確認してください。
  • 両方のチェックにおいて、準拠は二択ではなく、確率的なことがよくあります。 ある実在のベンダーでは、thinking ブロックが尊重されたのは generation リクエストのわずか約 13%でした(競合 2 社は 100%)。そのため --repeat のデフォルトは 10 です。generation の判定には 3 つの状態があります(fully-implemented、intermittent (k/N)、not-implemented)。history-replay にも独自の 3 つ以上の状態があります(accepts-thinking-in-history、rejects-thinking-in-history、inconsistent、またはエラーが thinking と無関係に見える場合の inconclusive)。単一のサンプルから結論を出さないでください。
  • リクエストごとに Connection: close を強制するため、ロードバランサーがすべてのサンプルを 1 つのレプリカに割り当てて実際の分布を隠すことはありません(実際のプローブでは、同じエンドポイントで keep-alive 使用時は 0/10、close 使用時は 17/90 でした)。

Dimension 4 — 品質/ユースケースの回帰(ブラインド判定)

意図的に二段階に分けています。まず収集し、その後、独立して判定します。

ステップ 1 — 収集:ユースケースライブラリに対するモデルの回答を収集します。

uv run --with openai python scripts/usecase_runner.py \
  --base-url <…/v1> --model <model> --key-env <ENV> \
  --usecases ~/.llm-eval/usecases.json --output-dir ~/.llm-eval/runs/<model>

ステップ 2 — 独立したブラインド判定者で判定(インラインでオーケストレーションします。モデル自身に採点させないでください)。 実行ディレクトリ内の各回答について、独立した Task エージェントを 3 つ(簡易チェックの場合はそれより少なくても構いません)起動します。各判定者に渡すのは、プロンプト、回答、ケースのrubricだけです。また、他の判定者のスコアや過去の評価を知らずに、単独で判定するよう明示します(これにより、アンカリングを防ぎます)。その後、集計します。

  • ケースが合格となるのは、判定者の多数が同意した場合のみです。
  • tagsを使用してカテゴリごとの適合率を計算します。判定者がルーブリックと体系的に食い違うカテゴリは、実際の弱点です。ある実際の評価では、カテゴリ全体の適合率が 12.5%となり、単独の採点者では見逃していた体系的な誤分類が明らかになりました。
  • 明示的な判定のみをカウントします。 判定を返さなかった判定者は合格扱いになりません。無回答は同意を意味しません。これは自動化バイアスを防ぎます。

ルーブリック採点の仕組み(LLM-as-judge のしきい値、llm-rubric)については、promptfoo-evaluation スキルと組み合わせることもできます。providersを同じエンドポイントに設定してください。このハーネスのブラインド判定方式と promptfoo のルーブリックアサーションは相互補完的です。ケースごとの合否判定をすばやく行うには promptfoo を、弱いと思われるカテゴリの適合率を検証するにはブラインド判定者を使います。手法の全容は、references/quality_blind_judge.mdを参照してください。

ユースケースライブラリ

このバンドルの外(例:~/.llm-eval/usecases.json)に置いてください。そうすれば、スキルの更新後も保持され、公開リポジトリに含まれることもありません。通常の JSON リストなので、プライベートリポジトリでバージョン管理し、時間をかけて回帰テストスイートを蓄積できます。

[
  {"id": "refund-window", "prompt": "A customer asks for a refund 20 days after purchase. Reply as support.",
   "rubric": "1.0 if it correctly cites the 30-day refund window; 0.0 if it refuses or invents a different window.",
   "tags": ["support", "policy"]},
  {"id": "lru-cache", "prompt": "Implement an LRU cache in Python with O(1) get/put.",
   "rubric": "1.0 if get and put are both O(1) via dict + doubly linked list and the self-test passes.",
   "tags": ["code"]}
]

assets/example_usecases.jsonはコピーして使えるスターターです。必須なのはidとpromptのみです。rubric、expected、tagsを加えると、より精度の高い判定ができます。

評価を一通り実行する

ユーザーが「このモデルを評価/ベンチマークして」と言った場合、通常の流れは次のとおりです。

  1. 形式を特定する — OpenAI 互換(/v1/chat/completions)ですか、それとも Anthropic-Messages (/v1/messages)ですか? GET /v1/models を呼び出すか、ベンダーのドキュメントを確認してください。思い込みで判断してはいけません。これによって、どのプローブを適用するかが決まります(プロトコルプローブは Anthropic 専用です)。どちらの場合も、一覧は不完全な証拠であることを忘れないでください(原則 §9)。
  2. 新しいエンドポイントでは、まず可用性と忠実度を確認する — システムプロンプトが一度も届かないモデルの速度を測っても、答えたい質問に答えることにはなりません。
  3. ユーザーが重視する軸を実行する — 「速くて安定しているか」を調べるなら速度と同時実行性を測定し、Anthropic 系ベンダーならプロトコルを追加し、ユースケースのテストスイートがある場合は品質も追加します。各プローブの --output JSON を実行ディレクトリに保存してください。
  4. 測定値と推測を分け、正直に報告する。 まず、ユーザーが尋ねた内容の要点を伝えてください(例:「持続的なデコード上限はベンダーが主張する tok/s を上回る一方、実タスクのスループットはそれより低い」)。 数値が不自然に見える場合(例:スループットがベンダーの主張を大幅に上回る、または単一サンプルに基づくプロトコル判定など)は、結果として扱うのではなく、調査すべき測定上のアーティファクトと見なしてください。この疑いを持つ姿勢こそが、このハーネスの要点です。ルーティングされたゲートウェイのレートはさらに時間とともに変化するため、数値を文書に確定して記載する前に、別の時間帯でもう一度サンプルを取得してください(原則 §12)。
  5. 2つのモデルを比較しますか? 同じフラグを指定して、それぞれに同一のプローブを実行し、2つの JSON 出力を並べてください。テスト条件(同じ同時実行レベル、同じユースケースなど)を同一にしないと、比較に意味はありません。
  6. 報告に値するベンダーのバグを見つけましたか? 生のプローブ出力をベンダーのサポート窓口に貼り付けてはいけません。 references/vendor_evidence_protocol.md に従って証拠パッケージを作成してください(まず自己監査を行い、観測内容に基づいて記述し、リクエスト ID の表、事前登録したしきい値、敵対的な観点からのレビューを含めます)。

デプロイゲート、常駐カナリア、障害注入モック、そして誤解を招く監視統計など、稼働中のシステムに対して繰り返し実行するテストについては、 references/production_testing_patterns.md を参照してください。

次のステップ

実行後は、自然な次のステップを提案してください。

Evaluation complete for <model>.

Options:
A) Render an HTML dashboard of the results — compose with a visualization skill (Recommended if sharing)
B) Compare against another model — same probes, side-by-side
C) Add the failing cases to ~/.llm-eval/usecases.json as a permanent regression guard
D) Done — the numbers answer the question

コメント