Évaluer les pipelines DSPy
Métriques, jeux de développement et fonction evaluate() pour une évaluation automatisée.
Évaluer les pipelines DSPy est une leçon AI Prompt Engineering gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Prompt Engineering, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Prompt Engineering comprend 4 leçons au total.
Pourquoi l'évaluation est importante dans DSPy
L'optimisation de DSPy n'est jamais meilleure que votre évaluation. Une métrique faible produit un programme compilé qui obtient de bons résultats selon cette métrique, mais qui échoue en production. Un dispositif d'évaluation approprié vous permet de comparer les programmes non optimisés et optimisés, ainsi que de détecter les régressions lorsque vous mettez à jour votre chaîne de traitement.
La classe dspy.Evaluate
dspy.Evaluate exécute votre programme sur un jeu de données, applique une métrique et fournit des scores agrégés. Il prend en charge le traitement parallèle via num_threads pour évaluer rapidement de grands jeux de données.
import dspy
# Build a devset of labeled examples
devset = [
dspy.Example(question='What is 7 * 8?', answer='56').with_inputs('question'),
dspy.Example(question='Name the largest planet.', answer='Jupiter').with_inputs('question'),
# ... more examples
]
# Create evaluator
evaluate = dspy.Evaluate(
devset=devset,
metric=exact_match_metric, # Your metric function
num_threads=4, # Parallel evaluation
display_progress=True, # Show progress bar
display_table=True, # Show per-example results
)
# Run
score = evaluate(my_program)
print(f'Overall score: {score:.1%}')Écrire des fonctions métriques
Les fonctions métriques ont la signature (example, prediction, trace=None) -> float. Elles comparent la prédiction du programme à la vérité terrain présente dans l'exemple.
Le paramètre trace est différent de None pendant l'optimisation, mais pas pendant l'évaluation — vous pouvez l'utiliser pour appliquer une logique différente lors de la compilation et de l'évaluation.
import dspy
def exact_match_metric(example, prediction, trace=None):
return float(
example.answer.strip().lower() == prediction.answer.strip().lower()
)
def contains_metric(example, prediction, trace=None):
"""Check if expected answer appears anywhere in prediction."""
return float(example.answer.lower() in prediction.answer.lower())
def length_penalized_metric(example, prediction, trace=None):
"""Reward correct answers, penalize overly long ones."""
correct = float(example.answer.lower() in prediction.answer.lower())
length_ok = float(len(prediction.answer.split()) <= 20)
return correct * (0.8 + 0.2 * length_ok)
# Use any of these as the metric parameter
evaluate = dspy.Evaluate(devset=devset, metric=contains_metric)Modèles de seuil réussite-échec
Pour les métriques binaires, vous pouvez définir un seuil : une prédiction est considérée comme réussie si elle atteint un niveau de qualité minimal. Cela est utile pour filtrer les démonstrations à partir de quelques exemples pendant l'optimisation par initialisation.
import dspy
def quality_metric(example, prediction, trace=None):
"""
Multi-factor metric with pass/fail threshold.
Returns float 0.0 to 1.0.
During compilation (trace is not None), DSPy uses this to decide
which traces to bootstrap as demos.
"""
score = 0.0
# Factor 1: Factual correctness (0.6 weight)
if example.answer.lower() in prediction.answer.lower():
score += 0.6
# Factor 2: Conciseness (0.4 weight)
word_count = len(prediction.answer.split())
if word_count <= 15:
score += 0.4
elif word_count <= 30:
score += 0.2
# During optimization: only use examples scoring >= 0.6
if trace is not None:
return score >= 0.6
return scoreRépartir les données : entraînement, développement, test
Suivez les pratiques standard de répartition des données en apprentissage automatique avec DSPy :
- Jeu d'entraînement : utilisé par l'optimiseur pour initialiser les démonstrations (20 à 200 exemples)
- Jeu de développement : utilisé par l'optimiseur pour la validation pendant la recherche
- Jeu de test : entièrement mis de côté — utilisé uniquement pour l'évaluation finale
import random
# All labeled examples
all_examples = load_examples() # Returns list of dspy.Example
random.shuffle(all_examples)
total = len(all_examples)
train_end = int(total * 0.6)
dev_end = int(total * 0.8)
trainset = all_examples[:train_end] # 60% for optimization
devset = all_examples[train_end:dev_end] # 20% for validation
testset = all_examples[dev_end:] # 20% held out
print(f'Train: {len(trainset)}, Dev: {len(devset)}, Test: {len(testset)}')Comparer les programmes optimisés et non optimisés
Évaluez toujours votre programme compilé par rapport au programme de référence, non compilé, sur le même jeu de test. Cela prouve que l'optimisation a réellement été utile et permet de quantifier l'amélioration.
import dspy
evaluate = dspy.Evaluate(
devset=testset,
metric=exact_match_metric,
num_threads=4,
display_progress=True,
)
# Baseline: unoptimized program
baseline_score = evaluate(unoptimized_program)
print(f'Baseline (no optimization): {baseline_score:.1%}')
# BootstrapFewShot compiled
bs_score = evaluate(bootstrap_compiled_program)
print(f'BootstrapFewShot compiled: {bs_score:.1%}')
# MIPRO compiled
mipro_score = evaluate(mipro_compiled_program)
print(f'MIPRO compiled: {mipro_score:.1%}')
# Pick the winner
print(f'Best improvement: +{max(bs_score, mipro_score) - baseline_score:.1%}')Traitement parallèle avec num_threads
Évaluer de grands jeux de données séquentiellement prendrait des heures. num_threads dans dspy.Evaluate exécute les prédictions en parallèle, ce qui réduit proportionnellement le temps réel écoulé.
Adaptez num_threads aux limites de débit de votre API — un nombre excessif de threads déclenche des erreurs de dépassement de limite de débit.
import dspy
import time
devset = [...] # 200 examples
# Sequential evaluation
start = time.time()
evaluate_seq = dspy.Evaluate(devset=devset, metric=metric, num_threads=1)
score_seq = evaluate_seq(program)
print(f'Sequential: {time.time()-start:.0f}s')
# Parallel evaluation (4 threads)
start = time.time()
evaluate_par = dspy.Evaluate(devset=devset, metric=metric, num_threads=4)
score_par = evaluate_par(program)
print(f'Parallel (4 threads): {time.time()-start:.0f}s')
# Typically ~4x faster — same score, less wait timeInterpréter les résultats de l'évaluation
Lorsque display_table=True est utilisé, DSPy affiche un tableau détaillé contenant chaque exemple, la prédiction et l'indication de réussite ou d'échec de la métrique. Cela est extrêmement utile pour diagnostiquer les schémas d'échec.
Recherchez notamment : des échecs systématiques pour un type de question, des cas limites de la métrique ou des exemples que votre jeu d'entraînement ne couvre pas.
import dspy
evaluate = dspy.Evaluate(
devset=devset,
metric=exact_match_metric,
num_threads=2,
display_progress=True,
display_table=10, # Show first 10 rows of results table
return_outputs=True, # Return (score, outputs) tuple
)
score, outputs = evaluate(program, return_all_scores=True)
# Find failing examples
failures = [
(ex, pred, s)
for ex, pred, s in outputs
if s == 0.0
]
print(f'Failures: {len(failures)}/{len(devset)}')
for ex, pred, _ in failures[:3]:
print(f'Q: {ex.question}')
print(f'Expected: {ex.answer}')
print(f'Got: {pred.answer}')Utiliser des métriques évaluées par un LLM
Pour les sorties ouvertes pour lesquelles la correspondance exacte échoue, utilisez un LLM pour évaluer la qualité. DSPy rend cette opération simple : votre fonction métrique peut elle-même appeler un prédicteur DSPy.
import dspy
class GradeAnswer(dspy.Signature):
"""Grade whether the predicted answer is correct given the reference."""
question: str = dspy.InputField()
reference_answer: str = dspy.InputField()
predicted_answer: str = dspy.InputField()
is_correct: bool = dspy.OutputField(
desc='True if the predicted answer is semantically correct'
)
grader = dspy.Predict(GradeAnswer)
def llm_graded_metric(example, prediction, trace=None):
result = grader(
question=example.question,
reference_answer=example.answer,
predicted_answer=prediction.answer,
)
return float(result.is_correct)
# Use this metric when answers can vary in phrasing
evaluate = dspy.Evaluate(devset=devset, metric=llm_graded_metric)Tests de régression avec l'évaluation
Considérez votre suite d'évaluation DSPy comme une suite de tests. Chaque fois que vous mettez à jour votre signature, l'architecture de votre module ou vos données d'entraînement, relancez l'évaluation et comparez les scores afin de détecter les régressions.
import json
import dspy
def run_and_save_evaluation(program, program_name, testset, metric):
evaluate = dspy.Evaluate(
devset=testset,
metric=metric,
num_threads=4,
)
score = evaluate(program)
# Save score to history file
history_file = 'eval_history.json'
try:
with open(history_file) as f:
history = json.load(f)
except FileNotFoundError:
history = []
history.append({'program': program_name, 'score': score})
with open(history_file, 'w') as f:
json.dump(history, f, indent=2)
print(f'{program_name}: {score:.1%}')
return scoreBonnes pratiques d'évaluation
Principes essentiels pour évaluer les chaînes de traitement DSPy :
- Conservez votre jeu de test strictement à l'écart — ne l'utilisez jamais pour l'optimisation
- Utilisez au moins 50 à 100 exemples de test pour obtenir des scores fiables
- Adaptez votre métrique à votre objectif réel en production
- Comparez plusieurs optimiseurs — les résultats varient selon la tâche
- Suivez les scores au fil du temps pour détecter les régressions
- Examinez manuellement les échecs pour améliorer vos données d'entraînement
Vérification des connaissances : paramètre trace de la fonction métrique
Dans une fonction métrique DSPy, qu'indique un paramètre trace différent de None ?
Récapitulatif : évaluer les chaînes de traitement DSPy
dspy.Evaluate exécute votre programme sur un jeu de développement annoté, applique une fonction métrique et fournit des scores agrégés. Les fonctions métriques suivent le modèle (example, prediction, trace=None) -> float. Utilisez num_threads pour l'évaluation parallèle et display_table=True pour diagnostiquer les échecs. Comparez toujours les programmes optimisés et non optimisés sur un jeu de test mis de côté. Pour les sorties ouvertes, les métriques évaluées par un LLM sont plus performantes que la comparaison exacte de chaînes.
Questions Fréquemment Posées
La leçon « Évaluer les pipelines DSPy » est-elle gratuite ?
Oui — le texte complet de « Évaluer les pipelines DSPy » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Prompt Engineering, passe à CoddyKit PRO. Le cours AI Prompt Engineering comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Évaluer les pipelines DSPy » ?
Métriques, jeux de développement et fonction evaluate() pour une évaluation automatisée. Tu pratiques AI Prompt Engineering avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer AI Prompt Engineering ?
Aucune expérience préalable n'est requise. AI Prompt Engineering sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.
Combien de temps prend la leçon « Évaluer les pipelines DSPy » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon AI Prompt Engineering ?
Oui. Chaque leçon AI Prompt Engineering inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Introduction au framework DSPy
- Définir des signatures et des modules
- Compiler et optimiser les prompts
- Évaluer les pipelines DSPy