Aller au contenu principal

Le framework Evals d'OpenAI

Pourquoi évaluer systématiquement

Vous ne pouvez pas améliorer ce que vous ne mesurez pas. Quand vous changez un prompt, un modèle ou un paramètre, comment savez-vous que la qualité s’est améliorée ? Les évaluations (evals) transforment l’intuition en mesure objective. OpenAI fournit un framework d’évaluation intégré à la plateforme.

Le framework Evals

Accéder aux Evals

Les Evals sont accessibles sur platform.openai.com dans la section dédiée. Vous pouvez :

  • Créer des jeux de données de test
  • Définir des critères d’évaluation
  • Exécuter des évaluations automatiques
  • Comparer les résultats entre modèles et prompts

Concepts fondamentaux

  • Dataset : un ensemble de cas de test avec entrées et sorties attendues
  • Grader : un critère d’évaluation (correspondance exacte, LLM-as-judge, score)
  • Run : une exécution d’évaluation sur un dataset avec un modèle/prompt donné

Créer un dataset de test

Structure du dataset

import json

# Un dataset est une liste de cas de test
dataset = [
    {
        "input": "Quelle est la capitale de la France ?",
        "expected": "Paris",
        "metadata": {"categorie": "geographie", "difficulte": "facile"},
    },
    {
        "input": "Expliquez le théorème de Bayes en une phrase.",
        "expected": "Le théorème de Bayes permet de calculer la probabilité "
                    "d'un événement en fonction de probabilités conditionnelles "
                    "connues.",
        "metadata": {"categorie": "mathematiques", "difficulte": "moyen"},
    },
    {
        "input": "Écrivez une fonction Python qui inverse une liste.",
        "expected": "def inverser(lst): return lst[::-1]",
        "metadata": {"categorie": "code", "difficulte": "facile"},
    },
]

# Sauvegarder en JSONL
with open("eval_dataset.jsonl", "w") as f:
    for cas in dataset:
        f.write(json.dumps(cas, ensure_ascii=False) + "\n")

Générer un dataset à partir de la production

import openai
import random

client = openai.OpenAI()

def creer_dataset_depuis_logs(
    logs_production: list[dict],
    taille_echantillon: int = 100,
) -> list[dict]:
    """Crée un dataset d'évaluation à partir des logs de production."""
    # Échantillonner
    echantillon = random.sample(
        logs_production,
        min(taille_echantillon, len(logs_production)),
    )

    dataset = []
    for log in echantillon:
        dataset.append({
            "input": log["prompt"],
            "expected": log["reponse_validee"],  # Réponse validée par un humain
            "metadata": {
                "source": "production",
                "date": log["date"],
                "modele_original": log["modele"],
            },
        })

    return dataset

Types de graders

Grader à correspondance exacte

def grader_exact(reponse: str, attendu: str) -> dict:
    """Évalue si la réponse correspond exactement."""
    score = 1.0 if reponse.strip().lower() == attendu.strip().lower() else 0.0
    return {"score": score, "type": "exact"}

Grader par inclusion

def grader_inclusion(reponse: str, mots_cles: list[str]) -> dict:
    """Vérifie que la réponse contient les mots-clés attendus."""
    reponse_lower = reponse.lower()
    trouves = sum(1 for mot in mots_cles if mot.lower() in reponse_lower)
    score = trouves / len(mots_cles) if mots_cles else 0.0
    return {
        "score": score,
        "trouves": trouves,
        "total": len(mots_cles),
        "type": "inclusion",
    }

Grader LLM-as-Judge

Le plus puissant : un autre LLM évalue la qualité de la réponse :

def grader_llm_juge(
    question: str,
    reponse: str,
    attendu: str,
    criteres: str = "exactitude, complétude, clarté",
) -> dict:
    """Utilise un LLM pour évaluer la qualité de la réponse."""
    prompt_juge = f"""Évaluez la qualité de cette réponse sur une échelle de 1 à 5.

Question : {question}
Réponse attendue : {attendu}
Réponse à évaluer : {reponse}

Critères : {criteres}

Répondez UNIQUEMENT avec un JSON :
{{"score": <1-5>, "justification": "<explication courte>"}}"""

    response = client.responses.create(
        model="gpt-5.3",
        input=prompt_juge,
        temperature=0.0,
    )

    import json
    try:
        resultat = json.loads(response.output_text)
        resultat["score_normalise"] = resultat["score"] / 5.0
        return resultat
    except json.JSONDecodeError:
        return {"score": 0, "justification": "Erreur de parsing", "score_normalise": 0}

Exécuter une évaluation complète

async def executer_eval(
    dataset: list[dict],
    modele: str,
    prompt_systeme: str,
    grader_fn,
) -> dict:
    """Exécute une évaluation complète sur un dataset."""
    resultats = []

    for cas in dataset:
        response = client.responses.create(
            model=modele,
            instructions=prompt_systeme,
            input=cas["input"],
        )

        score = grader_fn(response.output_text, cas["expected"])
        resultats.append({
            "input": cas["input"],
            "attendu": cas["expected"],
            "obtenu": response.output_text,
            **score,
        })

    # Calculer les métriques agrégées
    scores = [r["score"] for r in resultats if "score" in r]
    return {
        "modele": modele,
        "nb_cas": len(dataset),
        "score_moyen": sum(scores) / len(scores) if scores else 0,
        "score_min": min(scores) if scores else 0,
        "score_max": max(scores) if scores else 0,
        "resultats": resultats,
    }

Points clés à retenir

  • Les evals transforment l’intuition en mesure objective de la qualité
  • Créez vos datasets à partir de cas réels de production
  • Utilisez le grader LLM-as-Judge pour les évaluations nuancées
  • Exécutez les evals avant chaque changement de prompt ou de modèle