Construire un jeu annoté
Pourquoi annoter ?
Un jeu annoté (ou jeu de référence, gold set en anglais) est un ensemble de documents dont on connaît la bonne extraction. On compare ce que produit le modèle à cette référence, champ par champ. Sans lui, « ça a l'air de marcher » sur trois exemples relus à l'œil est tout ce que vous pouvez dire.
flowchart TD
R["Documents réels (anonymisés) ou fictifs"] --> C["Choisir : cas simples + cas pièges"]
C --> G["Écrire le guide d'annotation"]
G --> A["Annoter à la main"]
A --> T["Tester la cohérence : schéma, règles métier"]
T --> E["Évaluer les modèles"]
E -->|"désaccord"| D{"Erreur du modèle ou de l'annotation ?"}
D -->|"annotation"| G
Le format
Une ligne JSON par document : un identifiant, le type, le texte, et attendu, la réponse de référence.
{"id": "cmd-02", "type": "commande",
"texte": "Date : mardi 13 octobre 2026\nDe : Paul Girard, Salon de thé Les Glycines\n\nBonjour, il me faudrait 4 THE-200.\nAh, et finalement mettez-en 6, et ajoutez 1 TIS-100. ...",
"attendu": {"client": "Salon de thé Les Glycines", "contact": "Paul Girard",
"lignes": [{"reference": "THE-200", "quantite": 6}, {"reference": "TIS-100", "quantite": 1}],
"date_livraison": "2026-10-14", "urgence": "urgente", "mode": "retrait"}}attendune contient que les champs évalués : pasconfiance, pas le texte libretachedes actions (impossible à comparer exactement).- Une valeur absente s'annote
null, explicitement : c'est une réponse à vérifier comme une autre.
Couvrir les cas pièges
Un jeu fait uniquement de documents propres mesure un 100 % trompeur. Celui du lab mélange cas simples et pièges :
| Document | Piège |
|---|---|
cv-03 | CV en anglais, « PhD » à traduire en doctorat |
cv-04 | e-mail écrit [at], ville absente |
cv-05 | licence « en cours » : le plus haut diplôme obtenu est le bac |
cmd-02 | le client corrige sa quantité ; « demain » à convertir en date ; retrait sur place |
cmd-03 | « une douzaine », et 50 gobelets = 1 lot GOB-50 |
cmd-05 | référence mal tapée CAF-1K ; « dès que possible » = pas de date |
cr-02 | « vendredi » à convertir depuis la date de réunion ; une action sans échéance |
cr-03 | participant arrivé en retard (présent) ; « jeudi prochain » |
cr-05 | notes en style télégraphique, date 14/10/26 |
Le guide d'annotation
Deux personnes qui annotent le même document doivent écrire la même chose. D'où un guide, court, qui tranche les cas ambigus. Celui du lab :
| Question | Règle |
|---|---|
| Nom du projet | sans le mot « Projet » : Entrepôt Nord |
| Participants | présents uniquement, prénoms seuls, y compris arrivés en retard |
| Téléphone | tel qu'écrit dans le document |
| Dates relatives | calculées depuis la date du document |
| Compétences | telles qu'écrites, une par élément |
Pourquoi le champ libre tache des actions n'est-il pas annoté ?
Les textes libres se reformulent. On évalue ce qui se compare exactement (responsable, échéance). Pour un texte libre, il faudrait une autre méthode, comme un juge humain ou un LLM-juge.
Le jeu compte 5 documents par type. Un modèle se trompe sur un seul niveau d'études. Quelle précision pour ce champ ?
4 bonnes réponses sur 5 = 80 %. Sur un si petit jeu, chaque erreur pèse 20 points : utile pour repérer des problèmes, pas pour comparer finement deux modèles. Visez des dizaines de documents par type avant de conclure.
Tester l'annotation elle-même
Une annotation peut être fausse. test_extraction.py vérifie, sans clé, que chaque attendu n'utilise que des champs du schéma et respecte les règles métier :
def test_jeu_annote_coherent_avec_les_schemas():
docs = [json.loads(l) for l in (ICI / "jeu_annote.jsonl").read_text(encoding="utf-8").splitlines()]
assert len(docs) == 15 and len({d["id"] for d in docs}) == 15
for d in docs:
assert set(d["attendu"]) <= set(SCHEMAS[d["type"]].model_fields), d["id"]
if d["type"] != "compte_rendu": # les annotations respectent elles-mêmes les règles métier
SCHEMAS[d["type"]].model_validate({**d["attendu"], "confiance": 1, "champs_incertains": []})Exercice : annoter deux documents de plus
Écrivez deux nouvelles lignes pour le jeu : un CV sans e-mail ni téléphone, et un e-mail de commande qui demande une livraison « lundi prochain ». Ajoutez-les à jeu_annote.jsonl, adaptez le compte dans le test, puis relancez extraction et évaluation.
Voir la solution
{"id": "cv-06", "type": "cv", "texte": "Nora Haddad, Lille. Comptable, 3 ans d'expérience. DCG (bac+3). Compétences : paie, fiscalité.", "attendu": {"nom": "Nora Haddad", "email": null, "telephone": null, "ville": "Lille", "annees_experience": 3, "niveau_etudes": "bac+3", "competences": ["paie", "fiscalité"]}}
{"id": "cmd-06", "type": "commande", "texte": "Date : jeudi 15 octobre 2026\nDe : Léo Martin, Café des Arts\n4 CAF-1KG à livrer lundi prochain, merci.", "attendu": {"client": "Café des Arts", "contact": "Léo Martin", "lignes": [{"reference": "CAF-1KG", "quantite": 4}], "date_livraison": "2026-10-19", "urgence": "normale", "mode": "livraison"}}Le 15 octobre 2026 est un jeudi : « lundi prochain » est le 19. Dans le test, remplacez 15 par 17. Vérifiez chaque date calculée avec Python (date(2026, 10, 15).strftime("%A")) : l'annotateur aussi peut se tromper de jour.
Résultat obtenu
Test de cohérence du jeu, le 2026-10-15 :
test_extraction.py::test_jeu_annote_coherent_avec_les_schemas PASSED [100%]Et le désaccord révélé par l'évaluation de gemini-3.5-flash-lite, qui pose une question au guide d'annotation plutôt qu'au modèle :
cr-04 projet: attendu='Entrepôt Nord' obtenu='Projet Entrepôt Nord'Je sais faire
- Expliquer à quoi sert un jeu annoté
- Choisir des documents qui couvrent les cas pièges
- Écrire un guide d'annotation et l'aligner avec les descriptions du schéma
- Tester automatiquement la cohérence des annotations