Aller au contenu principal
Leçon 15 sur 20 · Production, Évals et Optimisation

Évaluer ses prompts : méthode, graders et passage à Promptfoo

Leçon 15 sur 20 Mis à jour le 3 novembre 2026 Avancé
Sur cette page
  1. Pourquoi évaluer systématiquement
  2. Construire un dataset qui vous ressemble
  3. Choisir le bon grader
  4. Assembler l’évaluation complète
  5. Industrialiser avec Promptfoo
  6. Points clés à retenir

Leçon corrigée le 3 novembre 2026 d’après la documentation d’OpenAI : temperature et top_p ne sont acceptés qu’avec un effort de raisonnement none, alors que les modèles GPT-5.6 raisonnent par défaut (medium). Les appels qui fixent ces paramètres précisent donc reasoning={"effort": "none"} (API Responses) ou reasoning_effort="none" (Chat Completions). Sources : Using GPT-6 · GPT-5.6 Terra.

Mise à jour du 2 novembre 2026 — plateforme Evals. OpenAI a annoncé la fin de sa plateforme Evals : les évaluations existantes sont en lecture seule depuis le 31 octobre 2026, et le tableau de bord comme l’API ferment le 30 novembre 2026, graders compris. OpenAI publie un guide de migration vers Promptfoo. Les principes d’évaluation de cette leçon restent valables ; c’est l’outil qui change.

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 ? La méthode spontanée consiste à essayer trois ou quatre exemples, à trouver les réponses meilleures, et à déployer. Elle échoue régulièrement, parce que les exemples testés sont précisément ceux qui posaient problème la semaine dernière : vous mesurez l’amélioration là où vous l’avez cherchée, et vous ne voyez pas les régressions introduites sur les cas qui fonctionnaient déjà.

Les évaluations (evals) transforment cette intuition en mesure objective. Jusqu’en 2026, OpenAI proposait pour cela une plateforme intégrée, Evals ; elle est en lecture seule depuis le 31 octobre 2026 et ferme le 30 novembre, et OpenAI recommande désormais Promptfoo, un outil open source d’évaluation et de red teaming. La bonne nouvelle, c’est que les deux partagent les mêmes briques — des données de test, des prompts, des modèles et des critères de notation : ce que vous apprenez ici en code Python se transpose tel quel, et la dernière partie de la leçon montre le passage à Promptfoo.

Trois notions structurent l’ensemble. Le dataset est un ensemble de cas de test avec entrées et sorties attendues : c’est votre référence, l’équivalent d’une suite de tests unitaires. Le grader est un critère d’évaluation — correspondance exacte, LLM-as-judge, score — qui décide si une réponse donnée est bonne. Le run est une exécution d’évaluation sur un dataset avec un modèle et un prompt donnés ; c’est l’unité que vous comparerez d’une version à l’autre.

Construire un dataset qui vous ressemble

Un cas de test se compose d’une entrée, d’une sortie attendue et de métadonnées. Ces dernières ne sont pas décoratives : ce sont elles qui vous permettront, après coup, de constater qu’un score global de 87 % cache un effondrement sur la catégorie « code » compensé par d’excellents résultats en géographie. Sans catégorie ni difficulté, vous n’avez qu’un chiffre agrégé, et un chiffre agrégé ne dit jamais où intervenir.

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": "mathématiques", "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")

Un dataset inventé au bureau évalue votre imagination, pas votre application. Vos utilisateurs formulent leurs demandes autrement que vous : fautes de frappe, questions à moitié posées, contexte implicite. La meilleure source de cas de test reste donc votre propre production, à condition de faire valider les réponses de référence par un humain — sans quoi vous figeriez les erreurs actuelles comme norme.

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

Choisir le bon grader

Le grader à correspondance exacte est le plus sévère et le plus rapide. Il ne convient qu’aux tâches dont la sortie est contrainte : une classification en trois catégories, l’extraction d’un numéro de facture, une réponse par oui ou non. Employé sur du texte libre, il donnerait zéro à une réponse parfaite simplement formulée autrement que la référence.

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"}

Le grader par inclusion assouplit la contrainte : il vérifie la présence de mots-clés attendus et rend un score partiel. C’est l’outil adapté aux réponses qui doivent couvrir des points obligatoires sans que la formulation importe — une réponse de support qui doit mentionner le délai de rétractation et l’adresse de retour, par exemple. Sa faiblesse est connue : un texte contenant les bons mots dans un ordre absurde obtiendrait un score parfait.

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",
    }

Reste le plus puissant : un autre LLM évalue la qualité de la réponse. Le grader LLM-as-Judge apprécie ce qu’aucune règle mécanique ne sait apprécier — la clarté, la complétude, la justesse du ton. Deux détails d’implémentation le rendent utilisable en pratique. temperature=0.0 d’abord : sans cela, le même juge attribuerait des notes différentes à la même réponse d’une exécution à l’autre, et votre benchmark perdrait toute valeur comparative. Le repli sur json.JSONDecodeError ensuite : un juge qui commente au lieu de rendre du JSON ne doit pas faire tomber la campagne entière.

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.6-terra",
        reasoning={"effort": "none"},  # temperature exige l'effort none
        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}

Assembler l’évaluation complète

L’orchestration ne présente aucune difficulté conceptuelle : on parcourt le dataset, on appelle le modèle avec le prompt système à tester, on applique le grader, on agrège. Le point intéressant est que la fonction de notation arrive en paramètre, ce qui vous laisse rejouer exactement le même dataset avec des critères différents selon la campagne — inclusion pour un contrôle rapide en cours de développement, juge LLM pour la validation avant mise en production.

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,
        "résultats": resultats,
    }

Le rapport conserve score_min et le détail de chaque cas, et c’est délibéré. La moyenne vous dit si vous avancez ; les cas les plus mal notés vous disent pourquoi. Prenez l’habitude de lire les cinq pires réponses de chaque exécution avant de commenter la moyenne : c’est là, et rarement ailleurs, que se trouve la prochaine correction à apporter.

Industrialiser avec Promptfoo

Écrire ses evals à la main en Python est formateur ; les maintenir dans la durée est plus confortable avec un outil dédié. Le guide de migration d’OpenAI décrit la logique de Promptfoo : l’évaluation tient dans un fichier de configuration, promptfooconfig.yaml, qui réunit les prompts, le ou les modèles, les cas de test et les critères — appelés ici des assertions. Elle s’exécute en ligne de commande, en local ou dans votre intégration continue, et vit à côté du code de l’application.

description: Classification des tickets de support
prompts:
  - "Classe ce ticket dans une seule catégorie (facturation, technique, compte) : {{ticket}}"
providers:
  - openai:gpt-6.1-sol
tests:
  - vars:
      ticket: "Ma carte a été débitée deux fois ce mois-ci."
    assert:
      - type: icontains
        value: facturation
  - vars:
      ticket: "L'application plante quand j'ouvre un PDF."
    assert:
      - type: icontains
        value: technique
promptfoo eval -c promptfooconfig.yaml --no-cache
promptfoo view
promptfoo validate config -c promptfooconfig.yaml   # en cas d'erreur de configuration

Les graders de cette leçon se retrouvent sous forme d’assertions : la correspondance exacte, l’inclusion de mots-clés, et le jugement par un modèle. OpenAI prévient sur ce dernier point : un grader recréé, surtout un juge LLM, doit être revalidé avant de servir à décider d’une régression, car les scores ne seront pas forcément identiques d’un outil à l’autre.

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, validés par un humain
  • Choisissez le grader selon la tâche : exact pour le contraint, inclusion pour les points obligatoires, LLM-as-Judge pour le nuancé
  • Fixez temperature=0.0 sur le juge pour rendre les scores reproductibles
  • Exécutez les evals avant chaque changement de prompt ou de modèle
  • La plateforme Evals d’OpenAI ferme le 30 novembre 2026 : Promptfoo, recommandé par OpenAI, reprend dataset, prompts, modèles et critères dans un fichier de configuration