dspy-evaluation-harness について
GEPA などで言語モデルプログラムを最適化する DSPy 開発者向けの評価ハーネス作成ガイドです。文章によるフィードバック、独立した検証データ、再現可能な評価を重視します。
主な機能
- 01Prediction によるスコアとフィードバック
- 02学習データと検証データの分離
- 03多軸評価指標の設計
- 04CI 対応の評価設定
- 05トレースと結果のバージョン記録
活用シーン
- 01学習データへの評価リークを避けた DSPy 評価
- 02GEPA に文章による指標フィードバックを提供
- 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 つのルール
- 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 にはスコアとフィードバックの組み合わせが必要です。 - 検証セットを分けてください。 同じ例で最適化と評価を行ってはいけません。オプティマイザーはすぐに過学習します。
リッチフィードバックを使う標準メトリクス
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。
コメント
Markdown 対応