mcpField
D
dspy-evaluation-suite

dspy-evaluation-suite

github.com/omidzamani测试与质量12440 次安装18 周前更新
代码仓库

关于 dspy-evaluation-suite

用于评估 DSPy 程序的技能,支持内置或自定义指标及并行执行。它提供平均分和逐样本结果,可用于比较程序版本、建立基准和检查上线准备情况。

主要功能

  1. 01使用内置及自定义指标
  2. 02并行评估样本
  3. 03返回平均分和逐样本结果
  4. 04支持版本比较与基准测试

适用场景

  1. 01衡量优化前后的表现
  2. 02比较不同程序版本
  3. 03建立评估基准
  4. 04检查生产就绪情况

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/omidzamani/dspy-skills --skill dspy-evaluation-suite

安装后在 Claude Code 中输入/dspy-evaluation-suite确认已加载。

附带文件example.py

详细介绍

DSPy 评估套件

目标

使用内置和自定义指标,通过并行执行系统地评估 DSPy 程序。

适用场景

  • 衡量优化前后程序的性能
  • 比较不同的程序变体
  • 建立基线
  • 验证是否已做好生产部署准备

相关技能

输入

输入类型描述
programdspy.Module要评估的程序
devsetlist[dspy.Example]评估示例
metriccallable评分函数
num_threadsint并行线程数

输出

输出类型描述
scorefloat平均指标分数
resultslist每个示例的结果

工作流

阶段 1:设置评估器
from dspy.evaluate import Evaluate

evaluator = Evaluate(
    devset=devset,
    metric=my_metric,
    num_threads=8,
    display_progress=True
)
阶段 2:运行评估
result = evaluator(my_program)
print(f"Score: {result.score:.2f}%")
# Access individual results: (example, prediction, score) tuples
for example, pred, score in result.results[:3]:
    print(f"Example: {example.question[:50]}... Score: {score}")

内置指标

answer_exact_match
import dspy

# Normalized, case-insensitive comparison
metric = dspy.evaluate.answer_exact_match
SemanticF1

基于 LLM 的语义评估:

from dspy.evaluate import SemanticF1

semantic = SemanticF1()
score = semantic(example, prediction)

自定义指标

基本指标
def exact_match(example, pred, trace=None):
    """Returns bool, int, or float."""
    return example.answer.lower().strip() == pred.answer.lower().strip()
多因素指标
def quality_metric(example, pred, trace=None):
    """Score based on multiple factors."""
    score = 0.0
    
    # Correctness (50%)
    if example.answer.lower() in pred.answer.lower():
        score += 0.5
    
    # Conciseness (25%)
    if len(pred.answer.split()) <= 20:
        score += 0.25
    
    # Has reasoning (25%)
    if hasattr(pred, 'reasoning') and pred.reasoning:
        score += 0.25
    
    return score
与 GEPA 兼容的指标
def feedback_metric(example, pred, trace=None, pred_name=None, pred_trace=None):
    """Return a GEPA-compatible score and textual feedback."""
    correct = example.answer.lower() in pred.answer.lower()
    
    if correct:
        return dspy.Prediction(score=1.0, feedback="Correct answer provided.")
    else:
        return dspy.Prediction(
            score=0.0,
            feedback=f"Expected '{example.answer}', got '{pred.answer}'"
        )

生产示例

import dspy
from dspy.evaluate import Evaluate, SemanticF1
import json
import logging
from typing import Optional
from dataclasses import dataclass

logger = logging.getLogger(__name__)

@dataclass
class EvaluationResult:
    score: float
    num_examples: int
    correct: int
    incorrect: int
    errors: int

def comprehensive_metric(example, pred, trace=None) -> float:
    """Multi-dimensional evaluation metric."""
    scores = []
    
    # 1. Correctness
    if hasattr(example, 'answer') and hasattr(pred, 'answer'):
        correct = example.answer.lower().strip() in pred.answer.lower().strip()
        scores.append(1.0 if correct else 0.0)
    
    # 2. Completeness (answer not empty or error)
    if hasattr(pred, 'answer'):
        complete = len(pred.answer.strip()) > 0 and "error" not in pred.answer.lower()
        scores.append(1.0 if complete else 0.0)
    
    # 3. Reasoning quality (if available)
    if hasattr(pred, 'reasoning'):
        has_reasoning = len(str(pred.reasoning)) > 20
        scores.append(1.0 if has_reasoning else 0.5)
    
    return sum(scores) / len(scores) if scores else 0.0

class EvaluationSuite:
    def __init__(self, devset, num_threads=8):
        self.devset = devset
        self.num_threads = num_threads
    
    def evaluate(self, program, metric=None) -> EvaluationResult:
        """Run full evaluation with detailed results."""
        metric = metric or comprehensive_metric

        evaluator = Evaluate(
            devset=self.devset,
            metric=metric,
            num_threads=self.num_threads,
            display_progress=True
        )

        eval_result = evaluator(program)

        # Extract individual scores from results
        scores = [score for example, pred, score in eval_result.results]
        correct = sum(1 for s in scores if s >= 0.5)
        errors = sum(1 for s in scores if s == 0)

        return EvaluationResult(
            score=eval_result.score,
            num_examples=len(self.devset),
            correct=correct,
            incorrect=len(self.devset) - correct - errors,
            errors=errors
        )
    
    def compare(self, programs: dict, metric=None) -> dict:
        """Compare multiple programs."""
        results = {}
        
        for name, program in programs.items():
            logger.info(f"Evaluating: {name}")
            results[name] = self.evaluate(program, metric)
        
        # Rank by score
        ranked = sorted(results.items(), key=lambda x: x[1].score, reverse=True)
        
        print("\n=== Comparison Results ===")
        for rank, (name, result) in enumerate(ranked, 1):
            print(f"{rank}. {name}: {result.score:.2%}")
        
        return results
    
    def export_report(self, program, output_path: str, metric=None):
        """Export detailed evaluation report."""
        result = self.evaluate(program, metric)
        
        report = {
            "summary": {
                "score": result.score,
                "total": result.num_examples,
                "correct": result.correct,
                "accuracy": result.correct / result.num_examples
            },
            "config": {
                "num_threads": self.num_threads,
                "num_examples": len(self.devset)
            }
        }
        
        with open(output_path, 'w') as f:
            json.dump(report, f, indent=2)
        
        logger.info(f"Report saved to {output_path}")
        return report

# Usage
suite = EvaluationSuite(devset, num_threads=8)

# Single evaluation
result = suite.evaluate(my_program)
print(f"Score: {result.score:.2%}")

# Compare variants
results = suite.compare({
    "baseline": baseline_program,
    "optimized": optimized_program,
    "finetuned": finetuned_program
})

最佳实践

  1. 留出测试数据 — 切勿在评估集上进行优化
  2. 使用多个指标 — 结合正确性、质量和效率
  3. 统计显著性 — 使用足够多的示例(100+)
  4. 持续跟踪 — 对评估结果进行版本控制

局限性

  • 指标因任务而异;没有通用的衡量标准
  • SemanticF1 需要调用 LLM(有成本)
  • 并行评估可能触发速率限制
  • 可能无法涵盖边缘情况

官方文档

评论

支持 Markdown