Aller au contenu principal

Créer des évaluations personnalisées

Des évaluations adaptées à votre métier

Les graders génériques ne suffisent pas pour évaluer la qualité dans votre domaine spécifique. Un chatbot médical, un assistant juridique ou un outil de rédaction marketing ont chacun des critères de qualité différents. Cette leçon vous montre comment créer des évaluations sur mesure.

Concevoir des critères métier

Définir une rubrique d’évaluation

from dataclasses import dataclass

@dataclass
class Critere:
    nom: str
    description: str
    poids: float  # 0 à 1, somme des poids = 1

@dataclass
class Rubrique:
    nom: str
    criteres: list[Critere]

    def valider(self) -> bool:
        total = sum(c.poids for c in self.criteres)
        return abs(total - 1.0) < 0.01

# Exemple : rubrique pour un assistant de rédaction marketing
rubrique_marketing = Rubrique(
    nom="Qualité rédaction marketing",
    criteres=[
        Critere("ton", "Le ton est professionnel et engageant", 0.20),
        Critere("cta", "Contient un appel à l'action clair", 0.15),
        Critere("benefices", "Met en avant les bénéfices client", 0.25),
        Critere("longueur", "Respecte la longueur demandée", 0.10),
        Critere("grammaire", "Aucune faute de grammaire ou orthographe", 0.15),
        Critere("marque", "Respecte les guidelines de la marque", 0.15),
    ],
)

Implémenter un grader multi-critères

import openai
import json

client = openai.OpenAI()

def evaluer_multi_criteres(
    question: str,
    reponse: str,
    rubrique: Rubrique,
) -> dict:
    """Évalue une réponse selon une rubrique multi-critères."""

    criteres_texte = "\n".join(
        f"- {c.nom} (poids: {c.poids}): {c.description}"
        for c in rubrique.criteres
    )

    prompt = (
        "Évaluez cette réponse selon les critères suivants.\n"
        "Pour chaque critère, donnez un score de 0 à 5.\n\n"
        f"Question posée : {question}\n"
        f"Réponse à évaluer : {reponse}\n\n"
        f"Critères :\n{criteres_texte}\n\n"
        "Répondez en JSON avec un objet scores contenant pour chaque critère "
        "un score (0-5) et un commentaire court."
    )

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

    try:
        resultat = json.loads(response.output_text)
        scores = resultat.get("scores", {})

        # Calculer le score pondéré
        score_pondere = 0
        for critere in rubrique.criteres:
            if critere.nom in scores:
                score_brut = scores[critere.nom]["score"] / 5.0
                score_pondere += score_brut * critere.poids

        return {
            "score_global": score_pondere,
            "scores_details": scores,
            "rubrique": rubrique.nom,
        }
    except (json.JSONDecodeError, KeyError):
        return {"score_global": 0, "erreur": "Parsing échoué"}

Évaluations par type de tâche

Évaluation de code

import subprocess
import tempfile

def evaluer_code_python(
    code_genere: str,
    tests: list[str],
) -> dict:
    """Évalue du code Python en l'exécutant contre des tests."""
    resultats_tests = []

    for i, test in enumerate(tests):
        code_complet = f"{code_genere}\n\n{test}"

        with tempfile.NamedTemporaryFile(
            mode="w", suffix=".py", delete=False
        ) as f:
            f.write(code_complet)
            f.flush()

            try:
                resultat = subprocess.run(
                    ["python3", f.name],
                    capture_output=True,
                    text=True,
                    timeout=10,
                )
                succes = resultat.returncode == 0
                resultats_tests.append({
                    "test": i,
                    "succes": succes,
                    "sortie": resultat.stdout[:200],
                    "erreur": resultat.stderr[:200] if not succes else "",
                })
            except subprocess.TimeoutExpired:
                resultats_tests.append({
                    "test": i,
                    "succes": False,
                    "erreur": "Timeout (10s)",
                })

    nb_succes = sum(1 for r in resultats_tests if r["succes"])
    return {
        "score": nb_succes / len(tests) if tests else 0,
        "tests_passes": nb_succes,
        "tests_total": len(tests),
        "details": resultats_tests,
    }

Évaluation de résumé

def evaluer_resume(
    texte_original: str,
    resume: str,
    longueur_cible: int = 100,
) -> dict:
    """Évalue la qualité d'un résumé."""
    mots_resume = len(resume.split())
    ratio_longueur = min(
        mots_resume / longueur_cible,
        longueur_cible / max(mots_resume, 1),
    )

    # Fidélité via LLM
    response = client.responses.create(
        model="gpt-5.3",
        input=(
            f"Le résumé suivant est-il fidèle au texte original ? "
            f"Contient-il des informations inventées ?\n\n"
            f"Texte original (extrait) : {texte_original[:2000]}\n\n"
            f"Résumé : {resume}\n\n"
            f"Répondez en JSON avec les clés : fidelite (0-5), "
            f"hallucinations (oui/non), commentaire (court)."
        ),
        temperature=0.0,
    )

    try:
        fidelite = json.loads(response.output_text)
    except json.JSONDecodeError:
        fidelite = {"fidelite": 0, "hallucinations": "inconnu"}

    return {
        "score_longueur": ratio_longueur,
        "score_fidelite": fidelite.get("fidelite", 0) / 5.0,
        "hallucinations": fidelite.get("hallucinations", "inconnu"),
        "mots_resume": mots_resume,
        "longueur_cible": longueur_cible,
    }

Construire un pipeline d’évaluation

class PipelineEval:
    """Pipeline d'évaluation réutilisable."""

    def __init__(self, nom: str, rubrique: Rubrique):
        self.nom = nom
        self.rubrique = rubrique
        self.historique: list[dict] = []

    def evaluer_batch(
        self,
        dataset: list[dict],
        modele: str,
        prompt_systeme: str,
    ) -> dict:
        """Évalue un batch complet et retourne les métriques."""
        resultats = []

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

            eval_result = evaluer_multi_criteres(
                cas["input"],
                response.output_text,
                self.rubrique,
            )
            resultats.append(eval_result)

        scores = [r["score_global"] for r in resultats]
        rapport = {
            "pipeline": self.nom,
            "modele": modele,
            "nb_cas": len(dataset),
            "score_moyen": sum(scores) / len(scores),
            "score_min": min(scores),
            "score_max": max(scores),
        }

        self.historique.append(rapport)
        return rapport

Points clés à retenir

  • Créez des rubriques d’évaluation spécifiques à votre domaine métier
  • Combinez évaluation automatique (code, longueur) et LLM-as-Judge (qualité)
  • Testez le code généré avec des tests unitaires réels
  • Construisez un pipeline réutilisable pour évaluer chaque changement