Aller au contenu principal
Leçon 7 sur 9 · Évaluation de prompts

Notateurs personnalisés en Python

Leçon 7 sur 9 Mis à jour le 5 novembre 2026 Intermédiaire
Sur cette page
  1. Le prompt et ses variables
  2. Les tests en YAML
  3. La fonction de notation
  4. Exécuter
  5. Un notateur, c’est du code qu’on teste
  6. À vous de jouer
  7. Points clés à retenir

Les assertions intégrées couvrent beaucoup de besoins, mais pas tous. Cette leçon montre comment écrire sa propre logique de notation en Python, sur un exemple assumé comme un peu absurde : demander un paragraphe qui mentionne un sujet exactement N fois, et compter. Au passage, deux autres façons de configurer promptfoo : des prompts écrits directement dans le YAML avec des variables, et des tests déclarés en YAML plutôt qu’en CSV.

Le prompt et ses variables

Le gabarit tient en une phrase : « Write a short paragraph about {{topic}}. Make sure you mention {{topic}} exactly {{count}} times, no more or fewer. » Avec topic = tweezers et count = 7, le notateur doit vérifier que « tweezers » apparaît sept fois. Les doubles accolades sont la syntaxe de gabarit de promptfoo (Nunjucks) : chaque test fournit ses valeurs, promptfoo remplace.

description: Count mentions

prompts:
  - >-
    Write a short paragraph about {{topic}}. Make sure you mention {{topic}} exactly {{count}} times, no more or fewer. Only use lower case letters in your output.

providers:
  - anthropic:messages:claude-haiku-4-5
  - anthropic:messages:claude-sonnet-5-5

Deux fournisseurs d’emblée : le carnet comparait Claude 3 Haiku et Claude 3.5 Sonnet sur cette tâche ; les identifiants ci-dessus sont ceux de la liste actuelle.

Les tests en YAML

Au lieu d’un CSV, les tests se déclarent sous tests, chacun avec ses variables. Sans logique de notation pour l’instant, on peut déjà exécuter pour vérifier que les variables sont bien remplacées : chaque sortie est un paragraphe sur le sujet demandé.

tests:
  - vars:
      topic: sheep
      count: 3
  - vars:
      topic: fowl
      count: 2
  - vars:
      topic: gallows
      count: 4
  - vars:
      topic: tweezers
      count: 7
  - vars:
      topic: jeans
      count: 6

La fonction de notation

Promptfoo cherche dans un fichier Python une fonction get_assert, à laquelle il passe la sortie du modèle et un dictionnaire context (les variables, le prompt, le fournisseur, la réponse complète). La fonction renvoie un booléen (réussi ou non), un nombre (un score), ou un dictionnaire GradingResult avec trois clés : pass, score, reason.

# count.py
import re

def get_assert(output, context):
    topic = context["vars"]["topic"]
    goal_count = int(context["vars"]["count"])
    pattern = rf"(^|\s)\b{re.escape(topic)}\b"

    actual_count = len(re.findall(pattern, output.lower()))
    pass_result = goal_count == actual_count

    return {
        "pass": pass_result,
        "score": 1 if pass_result else 0,
        "reason": f"Expected {topic} to appear {goal_count} times. Actual: {actual_count}",
    }

La fonction lit le sujet et le nombre attendu dans le contexte, compte les occurrences du mot entier par expression régulière, puis renvoie le verdict et une explication lisible dans le tableau de bord. La documentation actuelle permet aussi des scores nommés (named_scores) et des sous-résultats (componentResults), utiles quand un notateur vérifie plusieurs choses à la fois.

Le circuit d'un notateur Python Quatre étapes : promptfoo appelle get_assert avec la sortie et le contexte ; la fonction lit les variables ; elle calcule ; elle renvoie un booléen, un score ou un GradingResult (pass, score, reason) affiché dans le tableau de bord. ASSERTION PYTHON Le circuit d'un notateur Python 1 Appel get_assert(output, context) pour chaque test et chaque prompt 2 Lire context['vars']['topic'], ['count'] ; aussi prompt, provider, réponse complète 3 Calculer re.findall du mot entier, comparaison au nombre attendu 4 Renvoyer {'pass': bool, 'score': float, 'reason': str} ; ou un booléen, ou un score defaultTest.assert avec type: python et value: file://count.py branche la fonction sur tous les tests ; :nom après le fichier pour une autre fonction ; named_scores pour plusieurs métriques.
D'après le carnet « Promptfoo: custom code graders » (anthropics/courses, carnet archivé) et la page Python assertions (documentation promptfoo).

On branche le notateur sur tous les tests par defaultTest :

defaultTest:
  assert:
    - type: python
      value: file://count.py

Pour une fonction nommée autrement, on ajoute son nom après deux-points : file://count.py:ma_fonction. Une assertion python accepte aussi une expression ou un bloc multi-lignes directement dans le YAML, pratique pour une règle d’une ligne.

Exécuter

npx promptfoo@latest eval
npx promptfoo@latest view

Dans le carnet, Claude 3.5 Sonnet réussissait 100 % des cas et Claude 3 Haiku 20 % : compter exactement en écrivant est plus dur qu’il n’y paraît pour un petit modèle de 2024. La loupe du tableau de bord montre le paragraphe complet et la raison du verdict (« Expected tweezers to appear 7 times. Actual: 6 »). Ce qui compte n’est pas le résultat, probablement différent aujourd’hui, mais le fait qu’entre les assertions intégrées et les fonctions personnalisées, on peut écrire à peu près n’importe quelle évaluation notée par code.

Un notateur, c’est du code qu’on teste

Le notateur de cette leçon a ses propres angles morts. L’expression exige un espace ou le début du texte avant le mot : un sujet placé entre guillemets ou après une parenthèse ouvrante ne compte pas. À l’inverse, « tweezers-like » compte, puisque le trait d’union marque une limite de mot. Et si le prompt exige des minuscules, c’est lower() qui rattrape un modèle qui l’oublie. Un notateur qui se trompe fausse toute l’évaluation en silence. La leçon 2 l’a dit pour les juges LLM, elle vaut pour le code : noter quelques sorties à la main et vérifier que le notateur est d’accord avec vous, avant de lui confier cinq cents lignes.

À vous de jouer

Compléter le notateur. Faites-lui renvoyer un score partiel (1 moins l’écart relatif) au lieu de 0 ou 1, et ajoutez un named_scores par sujet. Comparez le classement des deux modèles avec et sans score partiel.

Recomposer l’égalité d’ensembles. Reprenez le jeu de la leçon 6 et écrivez un get_assert qui découpe la sortie sur les virgules et compare l’ensemble obtenu à context["vars"]["expected"] (une colonne du CSV). Vous retrouvez la sévérité de la leçon 4 dans promptfoo.

Sources officielles : 07_prompt_foo_custom_graders/lesson.ipynb (anthropics/courses, archivé) · Python assertions (documentation promptfoo, relue le 5 novembre 2026)

Points clés à retenir

  • Les prompts peuvent vivre dans le YAML avec des variables {{nom}} ; les tests aussi, sous tests, avec leurs vars.
  • Une assertion python appelle get_assert(output, context) et attend un booléen, un score ou un GradingResult (pass, score, reason).
  • defaultTest.assert applique le notateur à tous les tests ; un nom de fonction s’ajoute après deux-points.
  • Assertions intégrées plus fonctions personnalisées : presque toute évaluation par code est possible.
  • Un notateur est du code : il a ses cas limites et se vérifie à la main sur un échantillon.