Première évaluation avec promptfoo
Sur cette page
Cette leçon porte l’évaluation des pattes d’animaux dans promptfoo, un outil libre qui exécute des prompts sur des fournisseurs et des tests, puis compare les résultats dans un tableau de bord. L’objectif n’est pas le prompt, déjà connu, mais l’outillage : un fichier de configuration, des prompts en Python, des tests en CSV, une transformation de sortie, deux modèles comparés d’une ligne. Toute la syntaxe a été relue sur la documentation de promptfoo le 5 novembre 2026 ; elle diffère par endroits du carnet de 2024, et la leçon le signale.
Pourquoi un outil
Le carnet cite plusieurs outils d’évaluation (promptfoo, Vellum, Scale, PromptLayer, ChainForge) et en choisit un, libre et simple : promptfoo. Il fournit, prêts à l’emploi, l’exécution en série, la comparaison entre prompts, modèles et fournisseurs, la mise en cache des réponses, et une interface web pour inspecter chaque résultat. Ce qu’on écrivait à la main à la leçon 4 (collecter, noter, afficher) devient de la configuration.
Initialiser
Dans un dossier vide :
npx promptfoo@latest init
La commande crée un fichier promptfooconfig.yaml, où tout se configure : les fournisseurs (les modèles à interroger), les prompts à évaluer, les tests à exécuter. On peut en vider le contenu d’exemple. Promptfoo lit la clé d’API dans la variable d’environnement ANTHROPIC_API_KEY :
export ANTHROPIC_API_KEY=votre_cle
Le fournisseur
Les modèles Anthropic se désignent par un identifiant anthropic:messages:<modèle>. La liste de 2024 (Claude 3 Haiku, Claude 3.5 Sonnet, Claude 2…) ne vaut plus : ces modèles sont retirés de l’API, et promptfoo documente pour chacun un remplaçant. Au 5 novembre 2026, sa page sur le fournisseur Anthropic liste notamment anthropic:messages:claude-opus-5-5, anthropic:messages:claude-sonnet-5-5, anthropic:messages:claude-opus-5, anthropic:messages:claude-sonnet-5 et anthropic:messages:claude-haiku-4-5, et précise pour les modèles de la famille 5 ce qu’il retire des requêtes de lui-même (temperature, top_p, top_k, que ces modèles refusent) et comment il convertit un ancien réglage de réflexion en réflexion adaptative. Claude Haiku 5.5, utilisé aux leçons 3 et 4, n’y figure pas encore (promptfoo 0.125.1, publiée le 25 octobre 2026) : l’outil l’accepte en signalant un modèle inconnu, mais ne sait pas calculer son coût, sauf si vous renseignez inputCost et outputCost dans la configuration du fournisseur. Pour garder la colonne des coûts, on commence avec le plus économique de la liste :
description: "Animal Legs Eval"
providers:
- anthropic:messages:claude-haiku-4-5
Les prompts
Promptfoo accepte des prompts en texte dans le YAML, dans des fichiers texte, JSON ou YAML, ou dans des fonctions Python. Le carnet préfère les fonctions Python, une par prompt, dans un seul fichier. Les fichiers du dossier archivé prennent directement la variable (def simple_prompt(animal_statement)) et se déclarent sans préfixe (prompts.py:simple_prompt) ; la documentation actuelle de promptfoo décrit des fonctions qui reçoivent un dictionnaire context, dont la clé vars contient les variables du test, et des déclarations préfixées par file://. Le code ci-dessous suit la documentation actuelle.
# prompts.py
def simple_prompt(context):
animal_statement = context["vars"]["animal_statement"]
return 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"""
def better_prompt(context):
animal_statement = context["vars"]["animal_statement"]
return 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 only respond with a single digit like 2 or 9"""
On les déclare dans la configuration avec le préfixe file://, le nom du fichier et celui de la fonction :
prompts:
- file://prompts.py:simple_prompt
- file://prompts.py:better_prompt
Les tests
Promptfoo offre plusieurs façons de définir les tests ; la plus courante est un fichier CSV dont les colonnes deviennent des variables. Une colonne spéciale, __expected, porte la logique de notation. Sans préfixe, sa valeur est comparée par égalité exacte à la sortie, ce qui est tout ce dont cette évaluation a besoin.
animal_statement,__expected
"The animal is a human.","2"
"The animal is a snake.","0"
"The fox lost a leg, but then magically grew back the leg he lost and a mysterious extra leg on top of that.","5"
"The animal is a dog.","4"
"The animal is a cat with two extra legs.","6"
"The animal is an elephant.","4"
"The animal is a bird.","2"
"The animal is a fish.","0"
"The animal is a spider with two extra legs","10"
"The animal is an octopus.","8"
"The animal is an octopus that lost two legs and then regrew three legs.","9"
"The animal is a two-headed, eight-legged mythical creature.","8"
tests: file://dataset.csv
Exécuter et regarder
npx promptfoo@latest eval
Pour chaque prompt, promptfoo prend chaque phrase du CSV, construit le prompt complet, envoie la requête, et compare la sortie à la valeur attendue. Les résultats s’affichent dans le terminal, une colonne par prompt ; dans le carnet, simple_prompt échouait sur chaque ligne et better_prompt réussissait tout sauf les cas qui demandent un raisonnement. Le tableau de bord en dit plus :
npx promptfoo@latest view
Une loupe dans chaque cellule ouvre le détail : on y voit que simple_prompt trouvait la bonne réponse, noyée dans du texte explicatif, ce qui fait échouer l’égalité exacte. La leçon 3 avait montré la même chose en lisant des listes Python ; ici, c’est un clic.
Un troisième prompt et une transformation
Le carnet ajoute le prompt à raisonnement de la leçon 3, qui demandait de raisonner dans des balises <thinking> puis de répondre dans des balises <answer>. Les modèles actuels raisonnent nativement et peuvent décliner la première consigne ; on garde la seconde, qui isole la réponse finale :
def chain_of_thought_prompt(context):
animal_statement = context["vars"]["animal_statement"]
return 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?
Account for any legs lost, regrown or added in the statement.
Output your final answer inside of <answer> tags.
Inside the <answer> tags return just the number of legs as an integer and nothing else."""
Pour noter cette sortie, il faut extraire le contenu de la balise avant la comparaison : promptfoo appelle cela une transformation. On l’écrit en Python dans un fichier dont la fonction get_transform reçoit la sortie et le contexte. Le carnet déclenchait l’extraction sur la présence de <thinking> ; on la déclenche ici sur <answer>, la seule balise demandée :
# transform.py
def get_transform(output, context):
if "<answer>" in output:
try:
return output.split("<answer>")[1].split("</answer>")[0].strip()
except Exception as e:
print(f"Error in get_transform: {e}")
return output
return output
defaultTest:
options:
transform: file://transform.py
defaultTest applique le réglage à tous les tests, et le prompt s’ajoute à la liste par file://prompts.py:chain_of_thought_prompt. Dans le carnet, le prompt à raisonnement obtenait 100 % sur Claude 3 Haiku. Vous pouvez aussi déclarer une sortie structurée dans la configuration du fournisseur (config.output_format, avec un JSON Schema) ; la page du fournisseur Anthropic chez promptfoo en donne la syntaxe.
Comparer deux modèles
C’est l’un des atouts de promptfoo : une ligne de plus dans providers, et toute l’évaluation tourne sur un second modèle.
providers:
- anthropic:messages:claude-haiku-4-5
- anthropic:messages:claude-sonnet-5-5
Le carnet comparait Claude 3 Haiku et Claude 3.5 Sonnet, et observait que le modèle le plus capable passait l’évaluation à 100 % même avec simple_prompt, qui faisait 0 % sur Haiku. Il notait aussi une surprise instructive : avec le prompt à raisonnement, Sonnet se trompait sur la pieuvre, parce qu’il raisonnait que ses appendices sont des bras et non des pattes. Un modèle plus « intelligent » peut faire baisser un score si le prompt laisse une ambiguïté ; la correction est dans le prompt (préciser ce qui compte comme patte), et c’est l’évaluation qui l’a révélé. Avec les modèles de 2026, relancez la comparaison vous-même plutôt que de supposer ces résultats.
À vous de jouer
Reproduire le dossier. Téléchargez le dossier de la leçon dans le dépôt archivé, remplacez les identifiants de modèles par ceux de la liste actuelle, adaptez les fonctions de prompt à la signature context, et lancez eval puis view. Comparez vos colonnes à celles décrites dans le carnet.
Fermer l’ambiguïté. Ajoutez au prompt à raisonnement une phrase qui définit ce qui compte comme une patte, et vérifiez sur les deux modèles que le cas de la pieuvre passe sans faire échouer un autre cas. C’est votre première régression surveillée.
Sources officielles : 05_prompt_foo_code_graded_animals/lesson.ipynb (anthropics/courses, archivé) · Getting started, Prompts, Test cases et Anthropic provider (documentation promptfoo, relue le 5 novembre 2026)
Points clés à retenir
promptfooconfig.yamlréunit fournisseurs, prompts et tests ;evalexécute,viewouvre le tableau de bord.- Les modèles Anthropic se désignent par
anthropic:messages:<modèle>; la liste de 2024 est retirée, la page du fournisseur donne les identifiants actuels. - Une fonction de prompt Python reçoit
contextet lit ses variables danscontext["vars"]; on la déclare parfile://prompts.py:fonction. - Dans un CSV, la colonne
__expectedporte la notation ; sans préfixe, c’est une égalité exacte. - Une transformation (
get_transform) prépare la sortie avant la notation ; une ligne de plus dansproviderscompare un second modèle.