Leçon 1 — Cadrer : le problème et les données

Décider sur des preuves

6 min de lecture

Décider sur des preuves

Lundi, un développeur raccourcit le prompt de tri des tickets : 60 % de jetons en moins, il le teste sur cinq messages, tout paraît correct, la PR est fusionnée. Jeudi, le service client découvre que l'assistant promet des « remboursements immédiats » sur des doubles prélèvements, ce que la politique interdit. Personne n'a eu tort de vouloir réduire le coût. L'erreur est d'avoir décidé sans instrument de mesure.

Le mécanisme : quatre questions, un code de sortie

Un harness d'évaluation répond à quatre questions, toujours dans cet ordre :

  1. Sur quels cas ? Un jeu de données figé, versionné, conçu pour discriminer (leçon 1.2).
  2. Noté comment ? Des scorers, du plus déterministe au plus coûteux (module 2).
  3. Comparé à quoi ? À la version actuellement en production, sur les mêmes cas, avec une marge d'erreur (leçon 3.1).
  4. Pour décider quoi ? Une règle écrite à l'avance, traduite en code de sortie 0 ou 1 (leçon 3.2).
sequenceDiagram
  participant Dev as "Pull request"
  participant H as "Harness"
  participant App as "Application (A puis B)"
  participant J as "Scorers et juge"
  Dev->>H: "modèle ou prompt modifié"
  H->>App: "30 cas, version A (référence)"
  H->>App: "30 cas, version B (candidat)"
  App-->>H: "sorties (cache si déjà vues)"
  H->>J: "noter chaque sortie"
  J-->>H: "scores par cas"
  H->>H: "différences appariées, IC 95 %"
  H-->>Dev: "rapport.html + code 0 ou 1"

On distingue ce harness de deux choses voisines. L'observabilité (traces, tableaux de bord) regarde le trafic réel après coup ; elle détecte, elle n'empêche pas. Un benchmark public mesure un modèle sur une tâche générique ; il ne dit rien de votre prompt ni de vos clients. Le harness travaille avant la mise en production, sur votre tâche, avec vos cas.

La mise en œuvre : le contrat de la cible

Le harness n'appelle jamais une copie du prompt dans un notebook. Il importe le vrai code de l'application, par un contrat minimal :

files/app/support.py (extrait)
def trier(inp: dict[str, Any], *, model: str, client: Any, prompt: str) -> dict[str, Any]:
    r = client.generate_content(
        model=model,
        contents=inp["ticket"],
        config=types.GenerateContentConfig(
            system_instruction=prompt, temperature=0,
            response_mime_type="application/json", response_json_schema=TRIAGE_SCHEMA),
    )
    return json.loads(r.text)

Trois paramètres viennent du harness : le modèle, le client (qui compte chaque jeton, leçon 2.1) et le prompt (pour comparer deux versions sans toucher au code). Tout le reste se déclare dans une suite :

files/suites/triage.yaml (extrait)
nom: triage
jeu: data/tickets.jsonl
cible: app.support:trier
modele: gemini-3.8-flash   # la version en production ; une PR qui le change est comparée à main
prompt: app/prompts/triage_v1.txt
concurrence: 4
budget_usd: 0.50

Installez le dépôt et vérifiez que tout fonctionne sans clé :

cd files
make setup
make test

Résultat obtenu (2026-10-16)

Prêt. Renseignez .env puis : make test
..............................                                           [100%]
30 passed in 0.68s

make setup a pris 14 secondes sur un poste Linux avec Python 3.10.12 (google-genai 2.31.0). Les 30 tests utilisent un faux client Gemini (tests/fakes.py) : ils vérifient la logique du harness, pas la qualité du modèle.

Votre équipe veut évaluer un nouveau prompt. Un data scientist propose de recopier le prompt dans un notebook et d'y appeler Gemini sur 50 tickets, « pour aller vite ». Que répondez-vous ?

Ce qui casse en production

SymptômeCause réelleParade
« Testé sur 5 exemples, ça marchait » puis incidentéchantillon choisi après coup, sans cas difficilesjeu de données écrit avant le changement, avec cas limites (leçon 1.2)
L'évaluation de l'agent a créé de vrais remboursementsles outils de l'agent pointaient vers la productionoutils factices ou bac à sable obligatoires dans la suite ; ici COMMANDES est un dictionnaire en mémoire
Scores excellents, clients mécontentson évalue la catégorie, personne n'évalue le texte envoyé au clientau moins un scorer par sortie visible du client (juge, leçon 2.3)
Deux personnes obtiennent des chiffres différentsmodèle, prompt ou jeu de données non tracéssummary.json enregistre modèle, empreinte du prompt, du code et du jeu de données

La règle de décision

Construisez un harness dès qu'un changement de modèle ou de prompt peut atteindre un client sans relecture humaine, ou dès que le coût d'une erreur dépasse le coût de 0,30 USD d'évaluation. N'en construisez pas pour un prototype jetable ou un usage interne où chaque sortie est relue : une grille de relecture suffit. Surveillez un indicateur dès le premier jour : le nombre de changements fusionnés sans passage par la porte.

Un responsable propose de remplacer le harness par un tableau de bord de satisfaction client, « qui mesure la vraie qualité ». Quel est le meilleur argument pour garder les deux ?

Pratique

Exercice. Écrivez une seconde cible resumer(inp, *, model, client, prompt) qui produit un résumé de ticket en une phrase, et la suite YAML minimale qui l'évalue avec un seul scorer de type regex vérifiant que le résumé fait moins de 200 caractères. Quelles clés de la suite sont obligatoires ?

Voir la solution
def resumer(inp, *, model, client, prompt):
    r = client.generate_content(model=model, contents=inp["ticket"],
                                config=types.GenerateContentConfig(system_instruction=prompt, temperature=0))
    return {"resume": r.text.strip()}
nom: resume
jeu: data/tickets.jsonl
cible: app.support:resumer
prompt: app/prompts/resume_v1.txt
scorers:
  - {nom: longueur, type: regex, champ: resume, motif: "(?s).{1,199}"}

Les clés obligatoires sont nom, jeu, cible et scorers (vérifiées par Suite.load). modele se donne dans la suite ou par --model. Le motif utilise (?s) pour que . accepte les retours à la ligne, et fullmatch impose la longueur totale.

  • Je sais expliquer la différence entre harness, observabilité et benchmark public.
  • Je fais appeler par le harness la fonction de production, jamais une copie.
  • J'ai obtenu 30 passed avec make test, sans clé.
  • Je sais où se déclarent le modèle, le prompt et le budget d'une suite.
Suivant