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