mcpField
D
dspy-evaluation-harness

dspy-evaluation-harness

github.com/intertwineテスト・品質27877 回インストール8 週間前に更新
リポジトリ

dspy-evaluation-harness について

GEPA などで言語モデルプログラムを最適化する DSPy 開発者向けの評価ハーネス作成ガイドです。文章によるフィードバック、独立した検証データ、再現可能な評価を重視します。

主な機能

  1. 01Prediction によるスコアとフィードバック
  2. 02学習データと検証データの分離
  3. 03多軸評価指標の設計
  4. 04CI 対応の評価設定
  5. 05トレースと結果のバージョン記録

活用シーン

  1. 01学習データへの評価リークを避けた DSPy 評価
  2. 02GEPA に文章による指標フィードバックを提供
  3. 03バージョン間の評価結果を比較

インストール

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

$npx skills add https://github.com/intertwine/dspy-agent-skills --skill dspy-evaluation-harness

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

同梱ファイルexample_metric.pyreference.md

詳細

DSPy 評価ハーネス(3.2.x)

多くの場合、メトリクスはプログラムより重要です。特に dspy.GEPA では、メトリクスのテキスト形式のフィードバックの品質によって、最適化が収束するかどうかが決まります。

2 つのルール

  1. dict ではなく、dspy.Prediction(score=..., feedback=...) を返してください。 dspy.Evaluate の並列エグゼキューターはスコアを合計で集計するため、dict の出力ではエラーになります(TypeError: unsupported operand type(s) for +: 'int' and 'dict')。dspy.Prediction は __float__/__add__ をサポートしており、GEPA のアダプターがネイティブにアンラップする型です。純粋な dspy.Evaluate によるスコアリングでは float のみを返しても機能しますが、GEPA にはスコアとフィードバックの組み合わせが必要です。
  2. 検証セットを分けてください。 同じ例で最適化と評価を行ってはいけません。オプティマイザーはすぐに過学習します。

リッチフィードバックを使う標準メトリクス

import dspy

def rich_metric(gold: dspy.Example, pred: dspy.Prediction, trace=None,
                pred_name: str | None = None, pred_trace=None):
    # 1. Compute sub-scores — multi-axis beats scalar
    correctness = 1.0 if _normalize(pred.answer) == _normalize(gold.answer) else 0.0
    cited = _has_citation(pred.answer, gold.sources) if hasattr(gold, "sources") else 1.0
    concise = 1.0 if len(pred.answer.split()) <= 50 else 0.5
    score = 0.6 * correctness + 0.25 * cited + 0.15 * concise

    # 2. Write feedback that teaches the optimizer
    parts = []
    if correctness < 1.0:
        parts.append(
            f"Answer mismatch. Predicted: {pred.answer!r}. Expected: {gold.answer!r}. "
            f"Likely cause: reasoning skipped the units/quantity in the question."
        )
    if cited < 1.0:
        parts.append("Did not ground the claim in the provided sources. Quote a source fragment.")
    if concise < 1.0:
        parts.append("Answer exceeded 50 words — tighten to one sentence.")
    if not parts:
        parts.append("Correct, grounded, and concise.")
    feedback = " ".join(parts)

    return dspy.Prediction(score=score, feedback=feedback)

標準ハーネス

evaluator = dspy.Evaluate(
    devset=valset,
    metric=rich_metric,
    num_threads=8,
    display_progress=True,
    display_table=10,          # pretty-print first 10 rows
    provide_traceback=True,    # surface exceptions, don't swallow them
    max_errors=5,
    failure_score=0.0,
    save_as_json="eval_runs/baseline.json",
)
result = evaluator(program)
print("Overall:", result.score)
for example_result in result.results[:3]:
    print(example_result)

dspy.Evaluate は .score(集計された float)と .results((example, pred, score) タプルのリスト)を持つ EvaluationResult を返します。

データセットの衛生管理

  • サイズ:GEPA の反復的な内省処理には 20~50 個の例、MIPROv2 形式のブートストラップには 100~500 個の例で十分です。
  • 分割:互いに重複しない 2 つのセット、trainset(最適化用)と valset(最適化後のプログラムをメトリクスで評価するためのもの)を手作業で厳選してください。開発中に決して確認しないテストセットを用意するのが理想です。
  • サイズよりも代表性が重要です。エッジケース、曖昧さ、敵対的な入力を含めてください。
  • dspy.Example(...).with_inputs("question", "context") を作成してください。with_inputs の呼び出しによって、どのフィールドが入力で、どのフィールドが正解出力かが示されます。
trainset = [
    dspy.Example(question="…", answer="…").with_inputs("question"),
    ...
]

複数軸のメトリクス(推奨)

正確性、忠実性、フォーマットへの準拠、レイテンシー、コストを組み合わせます。各軸は、定義を明記した 0~1 の float にしてください。重みは明示的に設定してください。マジックナンバーの中に重みを隠さず、定数にして、トレードオフをオプティマイザーに指定できるようにします。

CI 対応の評価スイート

# tests/test_dspy_eval.py
import dspy, pytest
from my_program import program, valset, rich_metric

@pytest.fixture(scope="module")
def evaluator():
    return dspy.Evaluate(devset=valset, metric=rich_metric, num_threads=8,
                         display_progress=False, provide_traceback=True)

def test_program_meets_threshold(evaluator):
    result = evaluator(program)
    assert result.score >= 0.75, f"Regression: {result.score:.3f}"

キャッシュ済みの LM(dspy.LM(..., cache=True))と事前にデータを格納した DSPY_CACHEDIR を使い、CI でオフライン実行します。

トレーシングと可観測性

  • dspy.configure で track_usage=True を設定すると、予測結果にトークン数が蓄積されます(pred.get_lm_usage())。
  • MLflow:import mlflow; mlflow.dspy.autolog() → すべての予測をトレースします。
  • W&B:dspy.GEPA に use_wandb=True を渡すと、パレートフロントを記録します。
  • 実行間で差分を比較できるよう、評価結果は必ずバージョン管理された JSON ファイル(save_as_json=...)に記録してください。

アンチパターン

  • GEPA の使用時に、フィードバックのないスカラー値のみのメトリクス(float のみ)を使うこと。シグナルを無駄にしています。
  • return {"score": s, "feedback": f}(dict):dspy.Evaluate の並列アグリゲーターがクラッシュします。dspy.Prediction(score=s, feedback=f) を使用してください。
  • オープンエンド生成タスクで完全一致メトリクスを使うこと。意味的なスコアリングか、LM を審査員として使うスコアリングを使用してください。
  • trainset で評価すること。実際より 10~30 ポイント高くなります。
  • 例外を黙って無視すること(provide_traceback=False)。KeyError の原因を LM のせいにすることになります。
  • 再ベースライン化せず、実験の途中でメトリクスを変更すること。それまでの数値を比較できなくなります。

次のステップ

  • このメトリクスを dspy-gepa-optimizer に渡します。
  • ハーネスの全体的なパターン → reference.md。
  • 実行可能な例 → example_metric.py。

コメント