Une première évaluation notée par code
Sur cette page
Cette leçon déroule la boucle complète sur un exemple volontairement simple : compter les pattes d’un animal décrit dans une phrase. Douze cas, un notateur par égalité exacte, trois versions de prompt dont le score passe de 67 % à 100 %. Le code est celui du carnet, relu pour les modèles actuels : claude-haiku-5-5 pour les appels, et une sortie structurée là où le carnet demandait au modèle de raisonner dans des balises.
Le jeu de cas
Le jeu est une liste de dictionnaires, chacun avec une phrase sur un animal et la réponse attendue. Certaines phrases sont piégeuses à dessein : un renard qui perd une patte, la retrouve et en gagne une de plus, une araignée avec deux pattes en trop, une pieuvre qui en perd deux et en regagne trois.
eval_data = [
{"animal_statement": "The animal is a human.", "golden_answer": "2"},
{"animal_statement": "The animal is a snake.", "golden_answer": "0"},
{"animal_statement": "The fox lost a leg, but then magically grew back the leg he lost and a mysterious extra leg on top of that.", "golden_answer": "5"},
{"animal_statement": "The animal is a dog.", "golden_answer": "4"},
{"animal_statement": "The animal is a cat with two extra legs.", "golden_answer": "6"},
{"animal_statement": "The animal is an elephant.", "golden_answer": "4"},
{"animal_statement": "The animal is a bird.", "golden_answer": "2"},
{"animal_statement": "The animal is a fish.", "golden_answer": "0"},
{"animal_statement": "The animal is a spider with two extra legs", "golden_answer": "10"},
{"animal_statement": "The animal is an octopus.", "golden_answer": "8"},
{"animal_statement": "The animal is an octopus that lost two legs and then regrew three legs.", "golden_answer": "9"},
{"animal_statement": "The animal is a two-headed, eight-legged mythical creature.", "golden_answer": "8"},
]
Premier prompt et score de référence
Une fonction construit la liste de messages à partir d’une phrase ; une autre envoie la requête et renvoie le texte. Le modèle du carnet était Claude 3 Haiku, retiré depuis ; claude-haiku-5-5 tient le même rôle de modèle économique pour une évaluation qu’on relancera souvent.
from anthropic import Anthropic
client = Anthropic()
MODEL = "claude-haiku-5-5"
def build_input_prompt(animal_statement):
user_content = f"""You will be provided a statement about an animal and your job is to determine how many legs that animal has.
Here is the animal statement.
<animal_statement>{animal_statement}</animal_statement>
How many legs does the animal have? Please respond with a number"""
return [{"role": "user", "content": user_content}]
def get_completion(messages):
response = client.messages.create(model=MODEL, max_tokens=1024, messages=messages)
# la réponse est une liste de blocs : un bloc de réflexion peut précéder le texte
return next(block.text for block in response.content if block.type == "text")
Le carnet limitait max_tokens à 200. Sur Haiku 5.5, la réflexion adaptative est active par défaut (effort moyen) et ses jetons comptent dans max_tokens : 1 024 laisse la marge nécessaire sans changer le coût d’une réponse courte. Pour la même raison, la fonction ne lit pas response.content[0].text comme le carnet : quand le modèle réfléchit, le premier bloc de la réponse est un bloc de réflexion, et le texte vient après ; on prend donc le premier bloc de type text. On collecte ensuite les douze sorties d’un coup, puis on note par égalité exacte :
outputs = [get_completion(build_input_prompt(q["animal_statement"])) for q in eval_data]
def grade_completion(output, golden_answer):
return output == golden_answer
grades = [grade_completion(o, q["golden_answer"]) for o, q in zip(outputs, eval_data)]
print(f"Score: {sum(grades) / len(grades) * 100}%")
Le carnet obtenait 66,7 %. En lisant les sorties à côté des réponses attendues, deux problèmes sautent aux yeux. Un problème de format : pour l’oiseau, le modèle répond « Based on the provided animal statement… the animal has 2 legs », ce qui est juste mais ne vaut pas "2" pour une égalité exacte. Un problème de raisonnement : 8 pour l’araignée augmentée (attendu 10), 5 pour la pieuvre (attendu 9). L’évaluation ne se contente pas de donner un score : elle montre quoi corriger, et dans quel ordre.
Deuxième prompt : le format
On s’attaque au problème le plus simple. La seule différence est la dernière ligne : « Respond only with a numeric digit, like 2 or 6, and nothing else. »
def build_input_prompt2(animal_statement):
user_content = f"""You will be provided a statement about an animal and your job is to determine how many legs that animal has.
Here is the animal statement.
<animal_statement>{animal_statement}</animal_statement>
How many legs does the animal have? Respond only with a numeric digit, like 2 or 6, and nothing else."""
return [{"role": "user", "content": user_content}]
Toutes les sorties sont désormais des chiffres ; le score du carnet monte à 75 %. Les erreurs de raisonnement subsistent (6 pour le renard, 8 pour l’araignée, 5 pour la pieuvre). Le carnet rappelle au passage que douze cas sont bien trop peu pour conclure quoi que ce soit de solide : la leçon porte sur la méthode, pas sur les chiffres.
Troisième prompt : le raisonnement
Le carnet demandait au modèle de raisonner pas à pas dans des balises <thinking>, puis de donner sa réponse dans des balises <answer>, extraites par une expression régulière avant la notation. Le score atteignait 100 %. Avec les modèles actuels, le raisonnement n’a plus besoin d’être demandé : la réflexion adaptative est active par défaut sur la famille 5.5, réglable par effort, et un modèle peut décliner la consigne d’écrire son raisonnement dans des balises. Ce qu’il faut garder, c’est l’idée d’une réponse finale isolée et extractible ; la façon la plus sûre de l’obtenir aujourd’hui est une sortie structurée.
from pydantic import BaseModel
class LegCount(BaseModel):
legs: int
def build_input_prompt3(animal_statement):
user_content = f"""You will be provided a statement about an animal and your job is to determine how many legs that animal has.
Account for any legs lost, regrown or added in the statement.
Here is the animal statement.
<animal_statement>{animal_statement}</animal_statement>
How many legs does the animal have?"""
return [{"role": "user", "content": user_content}]
def get_leg_count(messages):
response = client.messages.parse(model=MODEL, max_tokens=1024, messages=messages, output_format=LegCount)
return str(response.parsed_output.legs)
outputs3 = [get_leg_count(build_input_prompt3(q["animal_statement"])) for q in eval_data]
grades3 = [grade_completion(o, q["golden_answer"]) for o, q in zip(outputs3, eval_data)]
print(f"Score: {sum(grades3) / len(grades3) * 100}%")
Le schéma garantit un entier, donc la question du format disparaît pour de bon ; la consigne de tenir compte des pattes perdues ou ajoutées cible les cas piégeux. Si vous voulez reproduire le carnet au plus près, gardez ses balises <answer> et sa fonction d’extraction : c’est le modèle de la transformation que promptfoo appliquera à la leçon 5.
import re
def extract_answer(text):
match = re.search(r"<answer>(.*?)</answer>", text, re.S)
return match.group(1).strip() if match else None
Ce que l’évaluation apporte ici, c’est la certitude que la modification a aidé : sans elle, « raisonner avant de répondre » resterait une intuition. Et ce que la leçon 9 ajoutera : avant de conclure, relancer le même jeu deux ou trois fois, parce qu’un modèle peut répondre différemment d’une exécution à l’autre.
À vous de jouer
Rejouer les trois versions. Exécutez les trois prompts sur les douze cas avec claude-haiku-5-5, puis avec claude-sonnet-5-5, et relevez les scores. Lancez chaque configuration deux fois : les scores bougent-ils ? Sur douze cas, un seul changement vaut huit points.
Casser le notateur. Ajoutez un cas dont la réponse attendue est « 4 » et faites répondre le modèle « four » : l’égalité exacte échoue sur une réponse juste. Normalisez (casse, espaces, chiffres en lettres) ou imposez le schéma, et notez laquelle des deux solutions résiste le mieux à un modèle changé.
Sources officielles : 03_code_graded.ipynb (anthropics/courses, archivé) · Structured outputs (documentation Claude) · Extended thinking (documentation Claude)
Points clés à retenir
- La boucle : jeu de cas, premier prompt, score de référence, lecture des échecs, modification, nouveau score.
- Lire les sorties à côté des réponses attendues sépare les problèmes de format des problèmes de raisonnement ; on corrige le plus simple d’abord.
- Une égalité exacte exige une sortie exactement formée : une consigne de format, ou mieux, une sortie structurée.
- Les balises de raisonnement du carnet appartiennent à 2024 ; la réflexion adaptative est native, et la réponse finale s’isole par un schéma.
- Douze cas montrent la méthode, pas une vérité : relancer, et viser des dizaines de cas réalistes.