AI Engineering Academy · Leçon

Construire un banc d’évaluation automatisé

Créez un pipeline d’évaluation reproductible qui exécute votre système RAG complet sur un jeu de test, calcule toutes les mesures et génère un rapport afin de suivre les progrès au fil du temps.

Leçon 4 sur 413 étapes

Construire un banc d’évaluation automatisé est une leçon AI Engineering Academy 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 Engineering Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Engineering Academy comprend 4 leçons au total.

Qu’est-ce qu’un dispositif d’évaluation ?

Un dispositif d’évaluation est un pipeline automatisé et reproductible qui exécute l’ensemble de votre système RAG sur un jeu de tests standardisé, calcule toutes les métriques et produit un rapport. Le mot essentiel est reproductible : chaque fois que vous modifiez votre stratégie de découpage, votre modèle de plongements, votre prompt ou votre LLM, vous exécutez le même dispositif et comparez les résultats à une référence. Le développement de RAG passe ainsi d’un tâtonnement subjectif à une démarche d’ingénierie fondée sur les données.

Architecture du dispositif

Un dispositif d’évaluation bien conçu comporte quatre couches : la gestion des données de test (charger et versionner le jeu de données de référence), l’exécution du pipeline (faire passer chaque question de test dans l’ensemble du pipeline RAG), le calcul des métriques (calculer toutes les métriques de récupération et de génération) et la génération du rapport (enregistrer les résultats avec les informations de version et produire une comparaison avec la référence précédente). Chaque couche doit pouvoir être testée et configurée indépendamment.

class RAGEvaluationHarness:
    def __init__(self, retriever, llm_client, config):
        self.retriever = retriever
        self.llm_client = llm_client
        self.config = config  # chunk_size, top_k, model, threshold, etc.
        self.results = []

    def run(self, golden_dataset):
        for item in golden_dataset:
            result = self._evaluate_single(item)
            self.results.append(result)
        metrics = self._compute_metrics()
        self._save_report(metrics)
        return metrics

Exécuter chaque cas de test

Pour chaque question du jeu de données de référence, exécutez l’ensemble du pipeline RAG et capturez toutes les sorties intermédiaires : les identifiants et les scores des segments récupérés, le contexte mis en forme, la réponse générée et le nombre de jetons. L’enregistrement de ces valeurs intermédiaires est essentiel pour déboguer les échecs : lorsqu’une question obtient un score faible, vous pouvez examiner exactement quels segments ont été récupérés et pourquoi la réponse était incorrecte, sans réexécuter le pipeline coûteux.

import time

def _evaluate_single(self, item):
    start = time.perf_counter()
    query_vector = embed_query(item['question'])
    chunks = self.retriever.retrieve(query_vector, top_k=self.config['top_k'])
    filtered_chunks = filter_by_score(chunks, self.config['threshold'])
    context = format_context(filtered_chunks)
    answer_result = generate_answer(item['question'], context, self.llm_client)
    latency_ms = (time.perf_counter() - start) * 1000

    return {
        'question': item['question'],
        'expected_answer': item['answer'],
        'generated_answer': answer_result['answer'],
        'retrieved_chunk_ids': [c['id'] for c in filtered_chunks],
        'retrieved_scores': [c['score'] for c in filtered_chunks],
        'relevant_chunk_ids': item['relevant_chunk_ids'],
        'context_texts': [c['text'] for c in filtered_chunks],
        'tokens_used': answer_result['tokens_used'],
        'latency_ms': round(latency_ms)
    }

Calculer toutes les métriques en un seul passage

Après avoir recueilli les sorties de tous les cas de test, calculez l’ensemble des métriques en un seul passage sur les résultats. Séparez les métriques de récupération (calculées à partir des identifiants de segments) des métriques de génération (calculées en appelant le LLM évaluateur). Regroupez les appels au LLM évaluateur pour maximiser l’efficacité — regroupez les évaluations de fidélité et envoyez-les en parallèle avec asyncio plutôt que séquentiellement. Consignez la progression, car le calcul des métriques de génération peut prendre plusieurs minutes pour plus de 100 cas de test.

def _compute_metrics(self):
    # Retrieval metrics (no LLM calls needed)
    hit_rates = []
    mrr_scores = []
    for r in self.results:
        retrieved = r['retrieved_chunk_ids']
        relevant = set(r['relevant_chunk_ids'])
        hit = any(rid in relevant for rid in retrieved)
        hit_rates.append(1.0 if hit else 0.0)
        for rank, rid in enumerate(retrieved, 1):
            if rid in relevant:
                mrr_scores.append(1.0 / rank)
                break
        else:
            mrr_scores.append(0.0)

    metrics = {
        'hit_rate_at_5': sum(hit_rates) / len(hit_rates),
        'mrr': sum(mrr_scores) / len(mrr_scores),
        'mean_latency_ms': sum(r['latency_ms'] for r in self.results) / len(self.results),
        'mean_tokens': sum(r['tokens_used'] for r in self.results) / len(self.results)
    }
    return metrics

Enregistrer les résultats avec les informations de version

Chaque exécution d’évaluation doit être enregistrée avec des métadonnées de version afin que vous puissiez comparer les résultats entre les configurations. Incluez le hachage de validation git du code, les paramètres de configuration (modèle de plongements, taille des segments, K, seuil, modèle de LLM), l’horodatage et une description lisible de l’exécution. Stockez les résultats dans un fichier JSONL ou dans une table de base de données. Vous conserverez ainsi un historique permanent de l’évolution de votre système.

import json
import subprocess
from datetime import datetime

def _save_report(self, metrics):
    git_hash = subprocess.check_output(
        ['git', 'rev-parse', '--short', 'HEAD']
    ).decode().strip()

    report = {
        'run_id': datetime.utcnow().strftime('%Y%m%d_%H%M%S'),
        'git_commit': git_hash,
        'config': self.config,
        'metrics': metrics,
        'n_test_cases': len(self.results),
        'timestamp': datetime.utcnow().isoformat()
    }

    with open('eval_history.jsonl', 'a') as f:
        f.write(json.dumps(report) + '\n')
    print(f'Saved evaluation run: {report["run_id"]}')
    print(json.dumps(metrics, indent=2))

Comparer à la référence

Après chaque exécution, comparez automatiquement les résultats à la référence précédente et signalez les régressions. Une régression correspond à toute métrique qui diminue de plus d’un seuil donné (par exemple, 2 points de pourcentage). Affichez un tableau de comparaison présentant l’évolution des métriques. Si une métrique régresse de manière importante, l’exécution d’évaluation doit échouer avec un code de sortie différent de zéro, ce qui amènera le pipeline CI/CD à bloquer le déploiement de cette modification.

def compare_to_baseline(current_metrics, baseline_file='best_eval.json'):
    import json
    from pathlib import Path
    if not Path(baseline_file).exists():
        print('No baseline yet. Saving current as baseline.')
        Path(baseline_file).write_text(json.dumps(current_metrics, indent=2))
        return True

    baseline = json.loads(Path(baseline_file).read_text())
    regressions = []
    print('\nMetric comparison (current vs baseline):')
    for metric, current_val in current_metrics.items():
        baseline_val = baseline.get(metric, 0)
        delta = current_val - baseline_val
        status = 'OK' if delta >= -0.02 else 'REGRESSION'
        print(f'  {metric}: {current_val:.3f} vs {baseline_val:.3f} ({delta:+.3f}) {status}')
        if status == 'REGRESSION':
            regressions.append(metric)
    return len(regressions) == 0

Intégrer à CI/CD

Le dispositif d’évaluation est particulièrement puissant lorsqu’il est intégré à votre pipeline CI/CD. Configurez-le pour qu’il s’exécute automatiquement à chaque demande de fusion qui modifie la logique de découpage, la configuration du modèle de plongements, les modèles de prompt ou les paramètres de récupération. Le pipeline ne réussit que si toutes les métriques atteignent les seuils minimaux et qu’aucune métrique ne régresse par rapport à la référence de la branche principale. Cela empêche la mise en production de régressions accidentelles de la qualité.

# GitHub Actions workflow (eval.yml)
# on:
#   pull_request:
#     paths:
#       - 'rag/**'
#       - 'prompts/**'
#       - 'config/**'
# jobs:
#   evaluate:
#     runs-on: ubuntu-latest
#     steps:
#       - uses: actions/checkout@v3
#       - name: Install dependencies
#         run: pip install -r requirements.txt
#       - name: Run evaluation harness
#         run: |
#           python eval/run_harness.py \
#             --test-set eval/golden_dataset.json \
#             --config config/rag_config.yaml \
#             --fail-on-regression
#         env:
#           OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

Générer des rapports lisibles par l’être humain

En plus des fichiers de métriques brutes, générez un rapport HTML ou textuel lisible par l’être humain que votre équipe pourra examiner dans les commentaires des demandes de fusion. Incluez un tableau récapitulatif de toutes les métriques, une liste des cas de test échoués avec la question, la réponse attendue, la réponse générée et les segments récupérés, ainsi qu’un graphique de tendance montrant les métriques des 10 dernières exécutions. Les rapports visuels aident les parties prenantes non techniques à comprendre si le système s’améliore.

def generate_markdown_report(metrics, failed_cases, run_id):
    lines = [
        f'# RAG Evaluation Report — {run_id}\n',
        '## Summary Metrics',
        '| Metric | Score | Target |',
        '|--------|-------|--------|',
        f'| Hit Rate@5 | {metrics["hit_rate_at_5"]:.1%} | > 80% |',
        f'| MRR | {metrics["mrr"]:.3f} | > 0.70 |',
        f'| Mean Latency | {metrics["mean_latency_ms"]:.0f}ms | < 500ms |',
        '',
        f'## Failed Cases ({len(failed_cases)} failures)'
    ]
    for case in failed_cases[:10]:  # show first 10
        lines += [
            f'**Q:** {case["question"]}',
            f'**Expected:** {case["expected_answer"]}',
            f'**Generated:** {case["generated_answer"]}\n'
        ]
    return '\n'.join(lines)

Suivre le coût de chaque exécution d’évaluation

Les exécutions d’évaluation ont un coût : elles appellent l’API de plongements, l’API du LLM et le LLM évaluateur. Suivez le coût de chaque exécution d’évaluation en parallèle des métriques de qualité. Une évaluation complète de 100 cas de test coûte généralement de 0,50 $ à 2,00 $, selon les modèles utilisés. Utilisez des modèles moins coûteux pour les appels d’évaluation (GPT-4o-mini pour évaluer la fidélité) et réservez les modèles coûteux à la génération. Incluez l’estimation du coût de l’exécution dans le rapport enregistré afin de pouvoir intégrer le coût des évaluations à votre cycle de développement.

def estimate_run_cost(results, config):
    # Embedding cost
    embed_tokens = sum(len(r['question'].split()) * 1.3 for r in results)
    embed_cost = (embed_tokens / 1_000_000) * 0.02  # $0.02/1M tokens

    # Generation cost
    total_gen_tokens = sum(r['tokens_used'] for r in results)
    gen_cost = (total_gen_tokens / 1_000_000) * 5.0  # gpt-4o approx

    # Judge cost (faithfulness evals)
    judge_cost = len(results) * 0.001  # ~$0.001 per eval with gpt-4o-mini

    total = embed_cost + gen_cost + judge_cost
    print(f'Evaluation cost estimate: ${total:.2f}')
    print(f'  Embedding: ${embed_cost:.3f}')
    print(f'  Generation: ${gen_cost:.3f}')
    print(f'  Judgment: ${judge_cost:.3f}')
    return total

Évaluation planifiée pour surveiller la production

Au-delà de l’évaluation CI/CD déclenchée par les modifications du code, exécutez le dispositif selon un planning en production — quotidien ou hebdomadaire — en effectuant des tests sur de vraies requêtes d’utilisateurs échantillonnées dans vos journaux. Cela permet de détecter la dérive des données : à mesure que le corpus documentaire évolue et que les habitudes de requête des utilisateurs changent, la qualité du système peut se dégrader sans aucune modification du code. Planifiez des évaluations hebdomadaires qui échantillonnent 50 requêtes récentes d’utilisateurs, les évaluent et envoient automatiquement un récapitulatif de la qualité au canal de discussion de votre équipe.

# Example scheduled evaluation (cron job or scheduled cloud function)
import random

def sample_production_queries(query_log_file, n=50):
    with open(query_log_file) as f:
        all_queries = [json.loads(line) for line in f]
    sample = random.sample(all_queries, min(n, len(all_queries)))
    # Convert to golden dataset format (without expected answers — use LLM judge)
    return [
        {'question': q['user_question'], 'relevant_chunk_ids': []}
        for q in sample
    ]

# Run weekly evaluation against production queries
if __name__ == '__main__':
    prod_queries = sample_production_queries('/var/log/rag_queries.jsonl')
    harness = RAGEvaluationHarness(retriever, llm_client, config)
    metrics = harness.run(prod_queries)
    send_slack_digest(metrics)

Visualiser l’évolution des métriques au fil du temps

Les nombres bruts dans un fichier JSONL sont difficiles à interpréter en un coup d’œil. Créez une visualisation simple des tendances qui représente chaque métrique sur les 20 dernières exécutions d’évaluation dans un graphique linéaire. Utilisez l’horodatage de l’exécution comme axe des abscisses et le score de la métrique comme axe des ordonnées. Tracez une ligne horizontale au niveau du seuil minimal acceptable. Lorsqu’une métrique passe sous cette ligne, le problème devient immédiatement visible, sans avoir à parcourir les données brutes. Des outils de visualisation comme une bibliothèque de graphiques ou un tableau de bord web simple conviennent très bien.

import json
import matplotlib.pyplot as plt
from pathlib import Path

def plot_metric_trends(history_file='eval_history.jsonl', metric='hit_rate_at_5'):
    records = [
        json.loads(line)
        for line in Path(history_file).read_text().strip().split('\n')
    ]
    timestamps = [r['timestamp'][:10] for r in records[-20:]]
    scores = [r['metrics'].get(metric, 0) for r in records[-20:]]
    plt.figure(figsize=(10, 4))
    plt.plot(timestamps, scores, marker='o', label=metric)
    plt.axhline(y=0.80, color='r', linestyle='--', label='Min threshold')
    plt.title(f'{metric} over last 20 evaluations')
    plt.xticks(rotation=45)
    plt.tight_layout()
    plt.savefig(f'eval_trend_{metric}.png')
    print(f'Saved trend chart for {metric}')

Vérification rapide

Vérifiez votre compréhension des concepts d’ingénierie de l’IA présentés dans cette leçon.

Récapitulatif de la leçon

Dans cette leçon, vous avez appris à structurer un dispositif d’évaluation complet avec la gestion des données de test, l’exécution du pipeline, le calcul des métriques et la génération de rapports ; à comparer les exécutions à une référence et à faire échouer CI/CD en cas de régression ; à générer des rapports lisibles par l’être humain pour l’examen par l’équipe ; et à planifier la surveillance de la production afin de détecter la dérive des données sans modifier le code. Vous disposez maintenant d’une base complète pour créer et évaluer des systèmes RAG en production.

Gratuit pour commencer

Apprends Python avec un tuteur IA — gratuit

Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.

Cours
30
Leçons
120

Questions Fréquemment Posées

La leçon « Construire un banc d’évaluation automatisé » est-elle gratuite ?

Oui — le texte complet de « Construire un banc d’évaluation automatisé » 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 Engineering Academy, passe à CoddyKit PRO. Le cours AI Engineering Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Construire un banc d’évaluation automatisé » ?

Créez un pipeline d’évaluation reproductible qui exécute votre système RAG complet sur un jeu de test, calcule toutes les mesures et génère un rapport afin de suivre les progrès au fil du temps. Tu pratiques AI Engineering Academy 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 Engineering Academy ?

Aucune expérience préalable n'est requise. AI Engineering Academy 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 « Construire un banc d’évaluation automatisé » ?

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 Engineering Academy ?

Oui. Chaque leçon AI Engineering Academy 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

  1. Pourquoi l’évaluation est importante dans RAG
  2. Mesures de récupération : taux de réussite, MRR et NDCG
  3. Mesures de génération : fidélité et pertinence des réponses
  4. Construire un banc d’évaluation automatisé
← Retour à AI Engineering Academy