Évaluation par modèle
Sur cette page
Leçon mise à jour le 5 novembre 2026. Le cours d’origine extrayait la note par pré-remplissage et séquence d’arrêt, une technique que les modèles actuels refusent (erreur 400). Le notateur utilise désormais les sorties structurées (
output_config) de la leçon « Données structurées » ; letry/exceptreste utile pour les refus ou les réponses coupées par la limite de jetons.
Mise à jour du 5 novembre 2026 — température. Sur Claude Opus 4.7 et tous les modèles suivants, une valeur de
temperature,top_poutop_kdifférente de la valeur par défaut renvoie une erreur 400, et la version d’API2026-10-21refuse ces trois paramètres dans tous les cas. Ils ont donc été retirés du code de cette leçon. Pour rendre une évaluation fiable sans eux, répétez les exécutions et regardez la dispersion des scores plutôt qu’un score isolé.
Les trois types de notation
Le choix du notateur détermine ce que votre évaluation est capable de mesurer, et il vaut mieux le faire dans l’ordre du tableau ci-dessous. Chaque fois qu’un critère peut se vérifier par du code — le JSON est-il valide, la réponse contient-elle le champ attendu, fait-elle moins de 200 mots — écrivez le code : c’est gratuit, instantané et sans ambiguïté. Le notateur par modèle ne se justifie que pour ce qu’aucun test ne sait trancher.
| Type | Description | Quand l’utiliser |
|---|---|---|
| Code | Vérification automatique par programme | Réponses factuelles, formats stricts |
| Modèle | Un autre appel AI évalue la réponse | Réponses ouvertes, qualité subjective |
| Humain | Un évaluateur humain note | Dernière validation, cas critiques |
La notation par code est la plus fiable quand elle s’applique (ex: “la réponse est-elle du JSON valide ?”). Mais pour des critères comme “le code est-il bien commenté ?” ou “l’explication est-elle claire ?”, on a besoin d’un notateur par modèle.
Le principe
L’idée surprend au premier abord — demander à un modèle de juger un modèle — mais la tâche n’est pas la même des deux côtés. Produire une bonne réponse est difficile ; reconnaître si une réponse remplit trois critères explicites l’est beaucoup moins. C’est cette asymétrie qui rend la notation automatique fiable, à condition que les critères soient réellement explicites.
- La requête originale
- La réponse générée
- Les critères d’évaluation
- L’instruction de produire un score et une justification
Définir les critères d’évaluation
Pour notre assistant AWS, on définit trois critères :
CRITERES_EVALUATION = """Évalue la réponse selon ces critères :
1. FORMAT (0-3 points) : La sortie est-elle dans le format demandé (Python/JSON/Regex) ?
Est-elle bien formatée et lisible ?
2. VALIDITÉ (0-4 points) : Le code/config généré est-il syntaxiquement correct ?
Fonctionnerait-il tel quel sans modification ?
3. ADÉQUATION (0-3 points) : La réponse correspond-elle précisément à la demande ?
Tous les éléments demandés sont-ils présents ?
Score total sur 10."""
Implémenter grade_by_model()
from anthropic import AnthropicVertex
import json
SCHEMA_EVALUATION = {
"type": "object",
"properties": {
"forces": {"type": "string"},
"faiblesses": {"type": "string"},
"raisonnement": {"type": "string", "description": "Courte justification de la note, en une ou deux phrases"},
"score": {"type": "integer", "description": "Note de 1 à 10"},
},
"required": ["forces", "faiblesses", "raisonnement", "score"],
"additionalProperties": False,
}
client = AnthropicVertex(project_id="votre-projet", region="global")
def grade_by_model(test_input, test_type, response):
"""
Utilise Claude pour noter une réponse.
Retourne un dict avec score, forces, faiblesses et raisonnement.
"""
grading_prompt = f"""Tu es un évaluateur strict de code AWS.
REQUÊTE ORIGINALE :
{test_input}
TYPE ATTENDU : {test_type}
RÉPONSE À ÉVALUER :
{response}
{CRITERES_EVALUATION}
Analyse la réponse et fournis :
1. Les points forts (forces)
2. Les points faibles (faiblesses)
3. Une courte justification de ta note (raisonnement)
4. Un score final sur 10
Réponds en JSON."""
grading_response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=4096,
messages=[{"role": "user", "content": grading_prompt}],
output_config={"format": {"type": "json_schema", "schema": SCHEMA_EVALUATION}},
)
text = next((b.text for b in grading_response.content if b.type == "text"), "")
try:
return json.loads(text)
except json.JSONDecodeError:
# Notation impossible (réponse tronquée, refus) : on ne l'invente pas
return {
"forces": "",
"faiblesses": "Notation impossible",
"raisonnement": f"stop_reason : {grading_response.stop_reason}",
"score": None,
}
Le cours officiel insiste sur un point : demander au notateur ses forces, ses faiblesses et un raisonnement en plus de la note. Sans ce contexte, les modèles ont tendance à donner des notes moyennes, autour de 6. Son notateur extrait le JSON par préremplissage d’un bloc de code ; ici, le schéma des sorties structurées le garantit, sur les modèles actuels qui refusent le préremplissage. Et quand la notation échoue (réponse tronquée, refus), on ne lui attribue pas une note par défaut : un 5 inventé fausserait la moyenne ; on marque le cas None et on le compte à part.
Intégrer dans le pipeline
On remplace le score placeholder de run_test_case() :
def run_test_case(test_case, prompt_template):
"""
Exécute un cas de test avec notation par modèle.
"""
# Obtenir la réponse de Claude
response = run_prompt(test_case, prompt_template)
# Notation par modèle (remplace le score provisoire de 10)
evaluation = grade_by_model(
test_input=test_case["input"],
test_type=test_case["type"],
response=response
)
return {
"input": test_case["input"],
"type": test_case["type"],
"context": test_case["context"],
"difficulty": test_case.get("difficulty", "non spécifié"),
"response": response,
"score": evaluation.get("score", 0),
"forces": evaluation.get("forces", ""),
"faiblesses": evaluation.get("faiblesses", ""),
"raisonnement": evaluation.get("raisonnement", "")
}
Pipeline complet
Voici le code complet qui intègre tout :
from anthropic import AnthropicVertex
import json
from collections import defaultdict
client = AnthropicVertex(project_id="votre-projet", region="global")
SCHEMA_EVALUATION = {
"type": "object",
"properties": {
"forces": {"type": "string"},
"faiblesses": {"type": "string"},
"raisonnement": {"type": "string", "description": "Courte justification de la note, en une ou deux phrases"},
"score": {"type": "integer", "description": "Note de 1 à 10"},
},
"required": ["forces", "faiblesses", "raisonnement", "score"],
"additionalProperties": False,
}
CRITERES_EVALUATION = """Évalue la réponse selon ces critères :
1. FORMAT (0-3 points) : La sortie est-elle dans le format demandé ?
2. VALIDITÉ (0-4 points) : Le code est-il syntaxiquement correct ?
3. ADÉQUATION (0-3 points) : La réponse correspond-elle à la demande ?
Score total sur 10."""
PROMPT_TEMPLATE = """Tu es un expert AWS. Génère du code propre et fonctionnel.
Contexte : {context}
Type de sortie attendu : {type}
Requête : {input}"""
def run_prompt(test_case, prompt_template):
prompt = prompt_template.format(
context=test_case["context"],
type=test_case["type"],
input=test_case["input"]
)
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=4096,
messages=[{"role": "user", "content": prompt}]
)
return "".join(block.text for block in response.content if block.type == "text")
def grade_by_model(test_input, test_type, response):
grading_prompt = f"""Tu es un évaluateur strict de code AWS.
REQUÊTE : {test_input}
TYPE ATTENDU : {test_type}
RÉPONSE À ÉVALUER :
{response}
{CRITERES_EVALUATION}
Réponds avec : forces, faiblesses, une courte justification (raisonnement), score (entier 1-10)."""
grading_response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=4096,
messages=[{"role": "user", "content": grading_prompt}],
output_config={"format": {"type": "json_schema", "schema": SCHEMA_EVALUATION}},
)
text = next((b.text for b in grading_response.content if b.type == "text"), "")
try:
return json.loads(text)
except json.JSONDecodeError:
return {"forces": "", "faiblesses": "Notation impossible", "raisonnement": "", "score": None}
def run_test_case(test_case, prompt_template):
response = run_prompt(test_case, prompt_template)
evaluation = grade_by_model(test_case["input"], test_case["type"], response)
return {
"input": test_case["input"],
"type": test_case["type"],
"response": response,
"score": evaluation.get("score", 0),
"forces": evaluation.get("forces", ""),
"faiblesses": evaluation.get("faiblesses", "")
}
def run_eval(dataset, prompt_template):
results = []
for i, test_case in enumerate(dataset):
print(f"[{i+1}/{len(dataset)}] {test_case['input'][:50]}...")
result = run_test_case(test_case, prompt_template)
results.append(result)
print(f" -> Score : {result['score']}/10")
notes = [r["score"] for r in results if r["score"] is not None]
avg_score = sum(notes) / len(notes)
print(f"\n{'='*50}")
print(f"Score moyen : {avg_score:.2f}/10 ({len(results) - len(notes)} notation(s) impossible(s))")
return results, avg_score
# Lancer l'évaluation
with open("dataset.json", "r", encoding="utf-8") as f:
dataset = json.load(f)
results, avg_score = run_eval(dataset, PROMPT_TEMPLATE)
# Sauvegarder
with open("eval_results.json", "w", encoding="utf-8") as f:
json.dump(results, f, indent=2, ensure_ascii=False)
Analyser les résultats
# Score moyen par type
scores_par_type = defaultdict(list)
for r in results:
if r["score"] is not None:
scores_par_type[r["type"]].append(r["score"])
print("Scores par type :")
for t, scores in sorted(scores_par_type.items()):
avg = sum(scores) / len(scores)
print(f" {t:10s} : {avg:.1f}/10 (n={len(scores)})")
# Cas les plus faibles
print("\nCas les plus faibles :")
worst = sorted((r for r in results if r["score"] is not None), key=lambda r: r["score"])[:3]
for r in worst:
print(f" Score {r['score']}/10 : {r['input'][:60]}...")
print(f" Faiblesses : {r['faiblesses']}")
Points d’attention
Coût : chaque cas de test nécessite maintenant deux appels API (un pour la réponse, un pour la notation). Sur 100 cas, ça fait 200 appels. Utilisez Haiku pour la notation si le budget est serré :
# Variante économique : Haiku pour la notation
def grade_by_model_haiku(test_input, test_type, response):
# Même code, avec model="claude-haiku-5-5"
...
Biais du notateur : un modèle qui évalue ses propres réponses est structurellement indulgent — il a produit ce texte parce qu’il le jugeait bon, et on lui demande ensuite de le juger. Deux parades fonctionnent : des critères assez précis pour ne laisser aucune marge d’appréciation, et l’exigence d’une justification avant le score, qui oblige à motiver plutôt qu’à valider par réflexe. Pour atténuer ce biais, soyez très précis dans les critères et demandez explicitement de chercher les faiblesses.
Reproductibilité : les modèles actuels n’acceptent plus de température réglable. Pour des résultats stables, répétez chaque notation (trois exécutions au minimum) et retenez la moyenne, en surveillant les cas où les notes divergent fortement.
Résumé
Le pipeline d’évaluation est maintenant complet :
Dataset → run_prompt() → Réponse Claude → grade_by_model() → Score + Analyse
↓ ↓
Prompt à évaluer Critères d'évaluation
Vous pouvez désormais :
- Mesurer objectivement la qualité d’un prompt
- Comparer deux versions sur les mêmes données
- Identifier les faiblesses spécifiques (par type, par difficulté)
- Itérer avec confiance vers un prompt optimal
À vous de jouer
Votre notateur. Écrivez la grille de trois critères de votre assistant (leçon 10), branchez grade_by_model sur votre jeu de test et lancez l’évaluation. Lisez les trois cas les plus faibles et leurs faiblesses : les trouvez-vous justes ?
Contrôler le juge. Notez vous-même dix réponses, sans regarder les notes du modèle, puis comparez. Là où vous divergez de plus de deux points, précisez la grille et relancez.
Sources officielles : Model based grading (Claude Academy) · Define success criteria and build evaluations et Structured outputs (documentation Claude)
Points clés à retenir
- Trois notateurs : code, modèle, humain ; le code partout où il suffit, le modèle pour ce qui demande du jugement.
- Un notateur par modèle reçoit la requête, la réponse et des critères explicites, et rend forces, faiblesses, raisonnement et note.
- Les sorties structurées garantissent un verdict exploitable ; une notation impossible se marque, elle ne s’invente pas.
- Deux appels par cas : Haiku peut noter à moindre coût ; on répète les notations et on vérifie le juge contre un humain.