Notation par code
Sur cette page
Un notateur par modèle juge le fond, mais il ne vérifie pas de façon fiable qu’une sortie est du code valide, du JSON bien formé ou une expression régulière correcte. Pour cela, rien ne vaut un validateur programmatique. Cette leçon écrit les trois validateurs du cours officiel, les branche sur le jeu de test, et combine leur note avec celle du notateur par modèle.
Pourquoi noter avec du code
Un notateur par code est une fonction Python qui prend la réponse de Claude et renvoie une note. Il est déterministe (même entrée, même note), instantané et gratuit, puisqu’il n’appelle pas l’API. Pour notre assistant AWS, il couvre deux des trois critères : le format (rien d’autre que du Python, du JSON ou une expression régulière, sans explication) et la syntaxe valide. Le troisième, le respect de la tâche, reste au notateur par modèle.
Les trois validateurs
import ast
import json
import re
def validate_json(text: str) -> int:
try:
json.loads(text.strip())
return 10
except json.JSONDecodeError:
return 0
def validate_python(text: str) -> int:
try:
ast.parse(text.strip())
return 10
except SyntaxError:
return 0
def validate_regex(text: str) -> int:
try:
re.compile(text.strip())
return 10
except re.error:
return 0
Le principe est le même pour les trois : on essaie de lire la sortie avec l’outil du langage visé. Si cela fonctionne, 10 ; sinon 0. Le strip() du cours officiel retire les espaces et retours à la ligne qui feraient échouer une sortie juste.
Indiquer le format attendu
Pour savoir quel validateur appliquer, chaque cas de test doit dire le format attendu. Notre jeu de la leçon 18 le porte déjà dans son champ type ; le cours officiel ajoute pour cela un champ format à l’exemple de sortie de sa consigne de génération.
{
"input": "Crée une fonction Python qui valide un nom d'utilisateur IAM AWS.",
"type": "python",
"context": "AWS IAM"
}
def grade_syntax(output: str, test_case: dict) -> int:
validators = {"json": validate_json, "python": validate_python, "regex": validate_regex}
validator = validators.get(test_case["type"])
if validator is None:
raise ValueError(f"Format inconnu : {test_case['type']}")
return validator(output)
Écrire le prompt en conséquence
Si les notes de syntaxe sont mauvaises, c’est le plus souvent parce que Claude entoure le code d’explications ou d’un bloc Markdown. Le correctif est dans la consigne, comme le montre le cours officiel :
Réponds uniquement avec du Python, du JSON ou une expression régulière brute.
N'ajoute ni commentaire, ni explication, ni bloc de code Markdown.
Le cours officiel ajoute un message assistant prérempli qui ouvre un bloc de code, pour pousser Claude à écrire le code brut. Sur les modèles actuels, ce préremplissage renvoie une erreur 400 : la consigne explicite suffit, et, si une sortie doit être garantie, les sorties structurées peuvent porter le code dans un champ texte (leçon 13).
Combiner les deux notations
La note finale combine la forme (code) et le fond (modèle). Le cours officiel prend la moyenne simple ; ajustez les poids selon ce qui compte le plus pour vous.
from anthropic import Anthropic
client = Anthropic()
model = "claude-sonnet-5-5"
SCHEMA_NOTE = {
"type": "object",
"properties": {
"raisonnement": {"type": "string"},
"score": {"type": "integer", "description": "Note de 1 à 10"},
},
"required": ["raisonnement", "score"],
"additionalProperties": False,
}
def run_prompt(test_case: dict) -> str:
prompt = f"""Résous la tâche suivante :
{test_case["input"]}
Réponds uniquement avec du Python, du JSON ou une expression régulière brute.
N'ajoute ni commentaire, ni explication, ni bloc de code Markdown."""
message = client.messages.create(model=model, max_tokens=4096,
messages=[{"role": "user", "content": prompt}])
return "".join(block.text for block in message.content if block.type == "text")
def grade_by_model(output: str, test_case: dict):
prompt = f"""Tu es un relecteur de code exigeant. Évalue cette solution produite par une IA.
<tache>{test_case["input"]}</tache>
<solution>{output}</solution>
Juge si la solution répond exactement à la tâche et si elle est correcte.
Donne un court raisonnement, puis une note de 1 à 10."""
message = client.messages.create(
model=model,
max_tokens=4096, # la réflexion compte dans ce plafond
messages=[{"role": "user", "content": prompt}],
output_config={"format": {"type": "json_schema", "schema": SCHEMA_NOTE}},
)
text = next((b.text for b in message.content if b.type == "text"), "")
try:
return json.loads(text)["score"]
except (json.JSONDecodeError, KeyError):
return None # notation impossible : comptée à part, jamais inventée
def run_test_case(test_case: dict) -> dict:
output = run_prompt(test_case)
syntax_score = grade_syntax(output, test_case)
model_score = grade_by_model(output, test_case)
score = None if model_score is None else (model_score + syntax_score) / 2
return {"test_case": test_case, "output": output, "syntax_score": syntax_score,
"model_score": model_score, "score": score}
Ancienne version de cette leçon, et piège à connaître : demander « réponds uniquement par un nombre » avec max_tokens=10, puis convertir le texte en nombre. Sur un modèle qui réfléchit avant de répondre, ces dix tokens sont consommés par la réflexion, la réponse arrive vide, et la conversion échoue. Un schéma avec un champ entier et un plafond raisonnable règlent les deux problèmes.
La note de base qui sort de cette évaluation n’est ni bonne ni mauvaise en soi : ce qui compte, c’est de pouvoir la faire monter en améliorant le prompt, ce qui remplace l’appréciation subjective par une mesure.
À vous de jouer
Valider vos sorties. Vérifiez que chaque cas de votre jeu porte son type, branchez grade_syntax et relancez l’évaluation avec le prompt naïf puis avec la consigne « uniquement du code brut ». Comparez la note de syntaxe moyenne.
Piéger les validateurs. Trouvez une sortie qui passe validate_python mais ne répond pas du tout à la tâche, et une expression régulière valide mais fausse. C’est la raison d’être du notateur par modèle.
Sources officielles : Code based grading (Claude Academy) · Structured outputs et Errors : prefill not supported (documentation Claude)
Points clés à retenir
- Un notateur par code est déterministe, instantané et gratuit :
json.loads,ast.parse,re.compilevérifient la syntaxe. - Le champ
typede chaque cas (leformatdu cours officiel) dit quel validateur appliquer. - La consigne « uniquement du code brut » fait monter les notes de syntaxe ; le préremplissage du cours officiel ne fonctionne plus.
- On combine forme (code) et fond (modèle) ; un notateur par modèle qui renvoie un nombre passe par un schéma, jamais par
max_tokens=10.