Aller au contenu
English

Éviter la contamination

  • Leçon 6
  • 45 min
  • Module 2 : Créer un jeu de données d'évaluation

Ce que vous saurez faire

Séparez les cas servant aux ajustements des cas servant à décider d’une mise en production. Le score doit mesurer l’assistant, pas votre souvenir du test. Mesurez ensuite l’écart honnêtement, même si ce n’est pas celui que vous attendiez.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 45 minutes

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur

  • Le projet eval-harness avec coverage et inputs_version() des deux dernières leçons, et son environnement virtuel actif. python -m pytest -q se termine par 8 passed (8 tests réussis).
  • Si votre projet ne fonctionne plus, copiez l’instantané C11/snapshots/end-of-m01 comme dans Réinitialiser le projet. Refaites ensuite les étapes du tableau de couverture et de la version des entrées.

Aucun compte ni aucune clé. L’expérience de cette leçon a appelé un modèle. Ses résultats sont enregistrés, et vous les lisez seulement.

Une enseignante donne dix problèmes en devoir, puis la classe examine chaque réponse ensemble. Le vendredi, l’examen reprend les dix mêmes problèmes. Tous les élèves obtiennent une excellente note. Ces notes montrent que les élèves se souviennent des réponses. Elles ne disent rien sur leur capacité à résoudre un nouveau problème.

Un jeu de données d’évaluation présente le même piège. Les personnes qui construisent le système ont peut-être vu un cas de test et modifié le système jusqu’à sa réussite. Le cas mesure alors leur mémoire, pas le système.

Le problème : les exemples de la requête elle-même

Section intitulée « Le problème : les exemples de la requête elle-même »

La requête v2, celle de la référence de base, contient deux exemples : un bain d’oiseaux arrivé fissuré et un barbecue qui a pris feu. Chaque exemple contient la réponse que le modèle doit donner. Tous deux font partie des données d’évaluation, dans la partition contaminée. Lisez le premier avec la réponse enregistrée de la référence de base :

Fenêtre de terminal
python -m harness show P-V2-EX1 --run baseline

La sortie :

P-V2-EX1 | split contaminated | slice contaminated | source prompt-v2
Text: The bird bath arrived cracked in two pieces yesterday. Order LK-104233.
Expected: team delivery | needs_human false | next step photo
Reply must: photo | must not: claims_action, asks_secret
Labelled by: course author, 2026-10-09; Grace's policy (teams, H1-H5) applied by reading; the answer written in prompt v2 of the LLM applications course
baseline-v2-chat-small-r1-b6a2b9: {"team": "delivery", "needs_human": false, "reason": "Broken on arrival is a delivery problem."}
Reply: I'm sorry that your bird bath arrived broken. Please send us a photo of the damage within 14 days of delivery. We will then send a replacement at no cost, or a full refund if none is in stock.

La réponse reprend mot pour mot celle de l’exemple dans la requête. C’est aussi le cas pour le billet de soutien concernant le barbecue. Le modèle n’a pas résolu ces billets; il a copié les réponses fournies. Les deux cas réussissent toutes les vérifications, mais ne prouvent rien.

Les 69 autres billets contaminés présentent une version moins évidente du même piège. Dans le cours sur les applications avec des LLM, la requête v2 a été rédigée et comparée sur ces mêmes billets. Leur score peut donc être plus élevé que celui de nouveaux billets.

L’idée : un jeu pour ajuster, un jeu pour décider

Section intitulée « L’idée : un jeu pour ajuster, un jeu pour décider »

La solution est une règle qui précise qui peut consulter quoi. La contamination signifie qu’un cas de test a influencé le système qu’il évalue. Le cas figurait dans la requête, la requête a été ajustée avec ce cas, ou un modèle a été entraîné dessus. Un cas contaminé donne un score trop favorable, sans que le score le révèle.

Le jeu de données de Larkfield a donc trois parties, avec trois rôles :

  • Les données de développement (dev) sont accessibles. Vous lisez les échecs, modifiez la requête et calculez le score aussi souvent que vous le voulez. Chaque consultation les contamine un peu, et c’est accepté.
  • Les données de réserve sont figées. Personne ne lit leurs cas ni leurs sorties pendant les modifications du système. Vous calculez leur score une fois, pour décider de la mise en production.
  • Les données contaminées contiennent les cas dont on sait qu’ils ont été vus. Elles ne décident jamais d’une mise en production; elles mesurent seulement l’écart entre les cas vus et non vus.

Une fuite est tout ce qui permet aux données de réserve d’influencer le système ou les mesures. Par exemple : un billet copié, la consultation de ses échecs ou une règle corrigée après lecture de ses sorties. Le schéma montre qui a consulté chaque partie pendant la création de ce cours, avec deux fuites réelles.

Une grille de trois colonnes séparées par des lignes épaisses : développement, réserve, contaminées. Cinq lignes. Requête v2 rédigée et comparée : contaminées, utilisées pour la construire. Requête v3 ajustée en v3.1 (cette leçon) : développement, lues. Étiquettes relues après les premières exécutions : développement, lues; réserve, lues : une fuite, en rouge. Règles de formulation des réponses révisées après lecture : développement, lues; réserve, lues : une fuite, en rouge; contaminées, lues. Décision de mise en production : réserve, score calculé une fois, dans une case foncée.
Qui a consulté chaque partie du jeu de données. Les deux marques rouges indiquent des fuites réelles vers les données de réserve pendant la création du cours.
Description textuelle du diagramme

Une grille de trois colonnes séparées par des lignes épaisses : développement, réserve et contaminées. Elle contient cinq lignes d’activités. Premièrement, la requête v2 a été rédigée et comparée sur les billets contaminés. Deuxièmement, dans l’expérience de cette leçon, la requête v3 a été ajustée en v3.1 en lisant seulement les résultats de développement. Troisièmement, les étiquettes ont été relues après les premières exécutions, pour les données de développement et de réserve. Pour la réserve, c’est une fuite, marquée en rouge. Quatrièmement, les règles de formulation qui vérifient les réponses ont été révisées après lecture des réponses des trois parties. Pour la réserve, c’est encore une fuite en rouge. Cinquièmement, la décision de mise en production calcule une seule fois le score des données de réserve, dans une case foncée.

Si la contamination rend les scores trop favorables, la référence de base devrait mieux réussir sur la partition contaminée que sur la réserve. Vous pouvez le mesurer avec les commandes que vous avez déjà.

  1. Calculez le score de la référence de base sur la partition contaminée :

    Fenêtre de terminal
    python -m harness score baseline --split contaminated
    baseline-v2-chat-small-r1-b6a2b9 on contaminated (71 cases)
    answers 71/71 | team 69/70 | needs a person: caught 10/10, false alarms 3
  2. Calculez son score sur les données de développement :

    Fenêtre de terminal
    python -m harness score baseline --split dev
    baseline-v2-chat-small-r1-b6a2b9 on dev (102 cases)
    answers 101/102 | team 97/100 | needs a person: caught 23/23, false alarms 9
  3. Calculez son score sur les données de réserve. Faites-le une fois ici pour mesurer l’écart, comme le permet ce module :

    Fenêtre de terminal
    python -m harness score baseline --split holdout
    Note: the holdout set is frozen. Use it once, for the release decision.
    baseline-v2-chat-small-r1-b6a2b9 on holdout (98 cases)
    answers 98/98 | team 97/97 | needs a person: caught 24/24, false alarms 4

Priya combine l’équipe et needs_human en une seule mesure, les décisions : un cas est correct si les deux le sont. Le module 3 montre comment faire. Le graphique donne la proportion de décisions correctes par partition, avec un intervalle à 95 %. Cet intervalle est la plage où se situe probablement la vraie proportion. Le module 5 montre comment calculer l’intervalle.

Les décisions de la référence de base sur les billets vus et non vus. L'écart attendu n'apparaît pas : développement et réserve diffèrent davantage que contaminées et réserve.
Description textuelle du graphique

Panneau du haut : proportion de cas où les deux décisions de la référence de base sont correctes, avec des intervalles par bootstrap à 95 % : contaminées 0.944 [0.887, 0.986] sur 71 cas; développement 0.882 [0.814, 0.941] sur 102; réserve 0.959 [0.918, 0.990] sur 98. Panneau du bas : seulement les billets du cours sur l’apprentissage profond qui ne nécessitent aucune personne : contaminées 0.933 sur 60, développement et réserve ensemble 0.927 sur 110. Tous les intervalles se chevauchent.

Lisez d’abord le panneau du haut. La partition contaminée obtient 0.944, et la réserve obtient 0.959 : les billets non vus réussissent mieux, pas moins bien. Développement et réserve sont deux moitiés aléatoires des mêmes sous-groupes. Leurs scores devraient donc être identiques, mais ils diffèrent de 0.077. C’est plus que la différence entre les données contaminées et la réserve. Les trois intervalles se chevauchent. Les différences sont du bruit d’échantillonnage : des différences dues au hasard entre deux échantillons d’environ 100 cas.

Le panneau du bas compare des éléments semblables, car les partitions contiennent différents types de billets. Il garde seulement les billets du cours sur l’apprentissage profond qui ne nécessitent aucune personne. Les billets vus obtiennent 0.933, les non vus 0.927. Il n’y a pas d’écart ici non plus.

Sur ce jeu de données, la référence de base ne montre donc aucun écart de contamination. Pourquoi? La requête v2 contient seulement 2 exemples, soit 2 des 71 cas contaminés. Les 69 autres billets ont servi à comparer des requêtes, pas à adapter la requête à chaque billet. Une requête avec quelques règles générales ne peut pas les mémoriser. Le risque est réel et peut être important dans un autre système. Ce jeu de données ne le montre pas, et un rapport honnête le précise.

Une expérience : ajuster sur le développement, vérifier sur la réserve

Section intitulée « Une expérience : ajuster sur le développement, vérifier sur la réserve »

Les équipes contaminent souvent leur propre test de façon moins visible : elles ajustent sur les cas dont elles rapportent les résultats. Pour observer l’effet ici, le cours a mené une véritable expérience sur la requête v3 du candidat.

  1. Ajuster seulement sur le développement. L’auteur du cours a lu les échecs de v3 dans ses cinq exécutions de développement enregistrées, sans consulter la réserve. Trois problèmes ressortaient. v3 raisonnait souvent jusqu’à la limite de 1 000 jetons sans donner de réponse. Elle transmettait à une personne les questions auxquelles la politique ne répond pas. Et elle ne disait pas qu’une personne contacterait le client. La requête v3.1a contient une correction pour chaque problème.
  2. Choisir sur le développement. Une deuxième version, v3.1b, ajoutait une ligne sur les billets de sécurité, après deux échecs de développement. Sur les données de développement, v3.1a a réussi 99 décisions sur 102, et v3.1b en a réussi 97. v3.1a a été retenue comme requête v3.1, comme le ferait une équipe.
  3. Figer, puis calculer le score sur la réserve. Des exécutions ultérieures sur les deux partitions ont mesuré la variation entre les exécutions.

Seul le texte de la requête a changé : modèle, schéma, politique et limite de jetons sont restés identiques. L’expérience a coûté 0.38 $ US. Le graphique compare le gain de v3 à v3.1 sur le développement, où la requête a été ajustée, au gain sur la réserve, que personne n’a lue.

Le gain obtenu en ajustant la requête v3 sur le développement, mesuré sur le développement et la réserve, avec des intervalles appariés à 95 %.
Description textuelle du graphique

Panneau du haut : décisions, v3.1 moins v3, exécution 1 de chacune, avec des intervalles par bootstrap appariés à 95 % : développement +0.147 [+0.078, +0.225] sur 102 cas; réserve +0.143 [+0.071, +0.214] sur 98 cas. Panneau du bas : critères de réponse : développement +0.127 [+0.049, +0.206]; réserve +0.061 [−0.010, +0.133].

Lisez d’abord les décisions. Ajustée sur le développement, la requête v3.1 a gagné +0.147 sur le développement et +0.143 sur la réserve. C’est le même gain, à une fraction de point près. Sur toutes les exécutions, v3 a obtenu en moyenne 0.788 sur le développement (5 exécutions), et v3.1, 0.958 (3 exécutions). Sur la réserve, les moyennes étaient 0.840 et 0.990. Les trois corrections visaient des causes communes à tous les billets, comme la limite de jetons et une règle mal comprise. Corriger une cause générale fonctionne aussi sur des billets que personne n’a lus.

Les critères de réponse racontent une autre histoire. Le gain sur le développement est de +0.127; celui sur la réserve est seulement de +0.061, et son intervalle inclut 0. Sur toutes les exécutions, on passe de 0.804 → 0.944 sur le développement, mais de 0.878 → 0.915 sur la réserve. Deux raisons sont visibles dans les exécutions enregistrées :

  • Une correction propre à un échec du développement. Dans cinq exécutions de développement, v3 a oublié 16 fois de dire qu’une personne contacterait le client. Dans trois exécutions sur la réserve, elle ne l’a jamais oublié. La nouvelle ligne corrigeait un échec absent de la réserve.
  • Un choix fondé sur le bruit. v3.1 oublie encore « cessez de l’utiliser » dans certains billets de sécurité : 14 fois dans trois exécutions sur la réserve. v3.1b contenait la ligne qui corrige ce problème. Elle a perdu sur le développement par 2 décisions dans une exécution. Les propres exécutions de développement de v3.1a variaient de 3 d’une exécution à l’autre.

Sur ce jeu de données, les ajustements sur le développement n’ont donc pas gonflé le gain des décisions. Mais ils ont fait paraître le gain des réponses deux à quatre fois plus grand que sur la réserve. Le résultat de développement de l’exécution ayant servi à choisir la requête est le plus flatteur que vous avez. C’est le résultat de la réserve qu’il faut rapporter.

Les deux marques rouges du schéma sont réelles. Les étiquettes ont été relues après les premières exécutions, réserve comprise, comme le montrait Étiqueter et versionner. Et les règles de formulation qui vérifient les réponses (créées au module 3) ont été révisées trois fois après lecture des réponses de toutes les partitions :

  1. « Vous n’avez pas besoin d’envoyer votre numéro de carte complet » a d’abord été signalé comme une demande de numéro. Les règles ont appris à lire une négation.
  2. « Ne l’utilisez plus » et « un agent de soutien doit s’en occuper » n’étaient pas reconnus. Les règles ont appris ces formulations.
  3. Les promesses niées, comme « nous ne pouvons pas promettre un remboursement », étaient signalées comme des promesses. Les règles ont aussi appris à les reconnaître.

Chaque correction était justifiée, mais certaines provenaient des réponses de la réserve. Un vérificateur corrigé jusqu’à bien lire la réserve réussit mieux sur celle-ci que sur de nouvelles réponses. Les scores des critères de réponse sur la réserve sont donc un peu optimistes. Le système n’a pas vu le test, mais le vérificateur l’a vu.

Ce que le cours a fait : l’erreur est consignée dans le journal d’exécution et ici, pour éviter de présenter ces scores comme exempts de contamination. Les décisions sur la réserve restent exemptes de cette contamination. Elles n’utilisent pas les règles de formulation, et la révision n’a changé aucune étiquette d’équipe ou needs_human. Les règles de formulation sont maintenant figées; toute modification ultérieure est d’abord testée sur le développement. La règle pour la prochaine fois : figez les étiquettes et le vérificateur avant que quelqu’un lise une sortie de réserve.

Les personnes suivent une règle de consultation; un fichier a besoin d’une vérification. Une fuite courante est un billet copié d’une partition à une autre. Vous ajoutez maintenant une vérification qui le refuse, avec les autres règles du jeu de données. Elle s’exécute à chaque chargement du jeu.

  1. Ouvrez harness/dataset.py. Trouvez la classe Case. En dessous, laissez deux lignes vides et ajoutez l’erreur que la vérification déclenche :

    harness/dataset.py
    class DatasetProblem(Exception):
    """The evaluation set breaks one of its own rules."""
  2. Trouvez la fonction by_id. En dessous, avant coverage, ajoutez ces deux fonctions, chacune suivie de deux lignes vides :

    harness/dataset.py
    def text_key(text: str) -> str:
    """Two tickets with the same words (case and spacing ignored) get the same key."""
    return hashlib.sha256(" ".join(text.lower().split()).encode("utf-8")).hexdigest()[:16]
    def check(cases: list[Case]) -> None:
    """The rules of the set. Raises DatasetProblem with every broken rule."""
    problems = []
    ids = Counter(c.case_id for c in cases)
    problems += [f"{i}: the case ID is used {n} times" for i, n in ids.items() if n > 1]
    seen: dict[str, Case] = {}
    for c in cases:
    if c.expected.needs_human != bool(c.expected.rule):
    problems.append(f"{c.case_id}: needs_human and rule disagree")
    if c.text.strip():
    other = seen.get(text_key(c.text))
    if other is not None and other.split != c.split:
    problems.append(f"{c.case_id}: the same text is in {other.split} ({other.case_id}) and {c.split}")
    seen[text_key(c.text)] = c
    if not c.label.by:
    problems.append(f"{c.case_id}: no labeller recorded")
    if problems:
    raise DatasetProblem("; ".join(problems))

    La vérification a quatre règles. Chaque identifiant de cas est utilisé une seule fois. needs_human est vrai exactement quand une règle est nommée. Aucun texte ne figure dans deux partitions. Chaque étiquette nomme son auteur. La vérification recueille d’abord tous les problèmes pour qu’une seule exécution les rapporte tous.

  3. Dans load_cases, trouvez la ligne qui lit les cas (elle se termine par if line]). En dessous, ajoutez une ligne :

    harness/dataset.py
    check(cases)
  4. Ouvrez harness/__main__.py. À la fin de main(), remplacez except KeyError as e: par cette ligne pour afficher une erreur claire si le jeu est invalide :

    harness/__main__.py
    except (KeyError, dataset.DatasetProblem) as e:
  5. Copiez le fichier tests/test_dataset.py de l’instantané des laboratoires C11/snapshots/end-of-m02 dans votre dossier tests. Il contient cinq tests : un caractère modifie la version; une modification d’étiquette conserve la version des entrées; un billet contaminé copié dans le développement est refusé; needs_human doit nommer sa règle; chaque étiquette nomme son auteur.

  6. Exécutez les tests :

    Fenêtre de terminal
    python -m pytest -q
    ............. [100%]
    13 passed in 0.16s

    Le temps diffère sur votre ordinateur. Votre projet est maintenant identique à l’instantané end-of-m02.

  1. Faites une copie de sauvegarde du jeu de données :

    Fenêtre de terminal
    cp data/cases.jsonl data/cases.backup.jsonl
  2. Ouvrez data/cases.jsonl. Trouvez la ligne de P-V2-EX1, copiez-la au complet et collez-la comme nouvelle dernière ligne. Dans cette nouvelle ligne, remplacez "case_id": "P-V2-EX1" par "case_id": "T-99999" et "split": "contaminated" par "split": "dev". Enregistrez le fichier.

  3. Exécutez python -m harness cases. Le jeu refuse de se charger :

    Error: T-99999: the same text is in contaminated (P-V2-EX1) and dev
  4. Dans la nouvelle ligne, remplacez le mot yesterday (hier) par today (aujourd’hui). Enregistrez, puis exécutez encore python -m harness cases :

    272 cases, dataset version 4150ad262503
    dev 103 needs a person: 23 French: 10
    holdout 98 needs a person: 24 French: 10
    contaminated 71 needs a person: 10 French: 1

    La copie presque identique est acceptée.

  5. Rétablissez le jeu de données original :

    Fenêtre de terminal
    mv data/cases.backup.jsonl data/cases.jsonl
  6. Exécutez python -m harness cases. La première ligne doit être 271 cases, dataset version {fig.dataset.version} (271 cas, version du jeu de données indiquée).

C’est la limite d’une vérification par code : elle détecte seulement les mots identiques. Un billet modifié d’un seul mot reste le même test, et la vérification ne peut pas le voir. Pour trouver les copies presque identiques, il faut mesurer la similarité entre deux textes et faire lire les paires proches à une personne.

Des modèles de langage et l’auteur du cours ont rédigé tous les billets de ce jeu de données. Cela a trois effets sur les chiffres. Les billets sont plus propres que les vrais, donc les scores réels seraient plus bas. Ils ont le style des modèles de langage, qu’un modèle peut lire plus facilement que celui d’un client. Et l’auteur a choisi les sous-groupes : le jeu teste donc seulement les problèmes auxquels il a pensé.

Un score sur ce jeu de données signifie donc « sur des billets comme ceux-ci ». Avant que Larkfield se fie à une décision de mise en production, le jeu doit contenir de vrais billets. Leurs données personnelles doivent être retirées, et l’équipe de Grace doit les étiqueter. Le module 6 montre comment ajouter de nouveaux cas au jeu tout en gardant la réserve figée.

Omar demande un seul chiffre : « De combien la requête v3.1 est-elle meilleure que v3? » Vous avez le résultat sur les données de développement de l’exécution qui vous a fait choisir v3.1. Vous avez aussi le résultat sur les données réservées et les exécutions suivantes. Écrivez deux phrases pour Omar. Quel chiffre donnez-vous, et que dites-vous des autres?

Afficher un exemple de réponse

« La requête v3.1 prend environ 15 bonnes décisions de plus que v3 sur 100 billets de soutien. Ce résultat vient des billets réservés, que personne n’a lus pendant l’ajustement; les données de développement montrent le même gain. Ses réponses se sont aussi améliorées, mais seulement d’environ 4 sur 100 dans les données réservées, et non de 14 comme le suggèrent les données de développement. Une partie du gain en développement venait de corrections d’échecs présents seulement dans ces données. »

Ne donnez pas le chiffre de développement de l’exécution qui vous a fait choisir v3.1. C’est le meilleur essai, sur les cas consultés pendant l’ajustement. Les données réservées ont maintenant servi; une autre modification de la requête exige de nouveaux cas jamais vus.

Erreur courante

« Testons-la sur les exemples utilisés pour ajuster la requête »

Symptôme : Tomás propose le test de mise en production : « Testons-la sur les exemples utilisés pour ajuster la requête. Ce sont nos meilleurs cas. » La requête réussit chacun d’eux, et le rapport indique 100 %.

Cause : les exemples ont influencé la requête. Chaque échec sur ces exemples a été corrigé pendant sa rédaction. Le modèle peut même copier les réponses des exemples, comme l’a fait la référence de base. Le score mesure à quel point la requête se souvient de ses exemples, pas sa performance pour le prochain client.

Diagnostic : évaluez la même requête sur des cas que personne n’a utilisés pendant l’ajustement, puis comparez. Sur le jeu de Larkfield : python -m harness score baseline --split contaminated, puis --split holdout. Un grand écart indique une contamination. L’absence d’écart, comme ici, signifie que ce jeu ne montre aucune contamination. Cela ne fait pas des exemples d’ajustement un test équitable.

Correction : gardez les exemples d’ajustement dans une partition distincte. Décidez de la mise en production uniquement à partir des données réservées figées. Ajoutez check() pour empêcher qu’un billet d’ajustement soit copié par accident dans les données de développement ou les données réservées.

Vérifiez votre compréhension

La référence de base obtient 0,944 sur la partition contaminée et 0,959 sur les données réservées. Tomás dit : « La contamination n'a donc pas d'importance, et nous pouvons tester sur les exemples d'ajustement. » Que répondez-vous?

Afficher la réponse

Les intervalles se chevauchent. L’écart entre les données de développement et les données réservées dépasse celui entre la partition contaminée et les données réservées. Ces différences viennent du bruit d’échantillonnage. Ce résultat concerne cette requête sur ce jeu. Sur les deux exemples que le modèle copie mot pour mot, le score ne prouve rien. Une requête ajustée plus étroitement aux exemples pourrait montrer un grand écart. Seuls des cas jamais utilisés pour l’ajustement peuvent révéler un écart lorsqu’il existe. La décision de mise en production repose donc sur eux.

Vérifiez votre compréhension

Les règles de formulation qui vérifient les réponses ont été corrigées après la lecture des réponses sur les données réservées. Quels scores sur ces données deviennent optimistes, et lesquels restent inchangés? Pourquoi?

Afficher la réponse

Les scores des critères de réponse sur les données réservées deviennent optimistes. Le vérificateur a été corrigé jusqu’à ce qu’il lise correctement ces réponses. Il les traite donc mieux que de nouvelles réponses. Les décisions restent inchangées : elles n’utilisent pas les règles de formulation. La révision n’a modifié aucune étiquette team ou needs_human. La prochaine fois, figez les étiquettes et le vérificateur avant que quiconque lise une sortie sur les données réservées.

Vérifiez votre compréhension

Vérification pratique : après les étapes de cette leçon, exécutez python -m pytest -q. Combien de tests réussissent? Nommez ensuite le test qui échouerait si quelqu'un copiait un billet contaminé dans les données de développement.

Afficher la réponse

13 tests réussissent. Le test est test_a_contaminated_ticket_copied_into_dev_is_refused dans tests/test_dataset.py. Il copie un cas contaminé avec un nouvel identifiant dans les données de développement. Il attend que check() déclenche DatasetProblem avec « same text is in contaminated » (le même texte est dans la partition contaminée).

Exercice du module : un petit jeu étiqueté et sa fiche

Section intitulée « Exercice du module : un petit jeu étiqueté et sa fiche »

Créez votre propre petit jeu d’évaluation dans votre projet, avec des étiquettes et une fiche du jeu de données.

  1. Créez un dossier practice dans le projet. Créez-y un fichier cases.jsonl.

  2. Écrivez 12 cas, un objet JSON par ligne, au format de data/cases.jsonl. Utilisez cette ligne comme modèle : c’est un nouveau billet, étiqueté à partir de la politique.

    practice/cases.jsonl
    {"attachments": "", "case_id": "P-001", "criteria": {"must": ["photo"], "must_not": ["claims_action", "asks_secret"]}, "critical": false, "expected": {"needs_human": false, "next_step": "photo", "rule": "", "team": "delivery"}, "label": {"by": "your name", "changed": "", "date": "2026-10-12", "method": "Grace's policy (teams, H1-H5) applied by reading", "reviewed": "", "team_source": "my reading of the policy"}, "language": "en", "slice": "normal", "source": "practice", "split": "dev", "style": "plain", "tags": [], "text": "The garden hose reel arrived with a cracked handle. Order LK-400112."}
  3. Couvrez au moins quatre sous-groupes, dont french (français) et needs_person (nécessite une personne). Incluez une question à laquelle la politique ne répond pas. Placez 8 cas dans dev (développement) et 4 dans holdout (données réservées).

  4. Écrivez les étiquettes avant de regarder toute sortie du système. Remplissez label.by avec votre nom et label.method avec votre méthode de décision.

  5. Chargez et vérifiez votre jeu, avec sa version et sa couverture :

    Fenêtre de terminal
    python -c "from pathlib import Path; from harness.dataset import load_cases, version, coverage; p = Path('practice/cases.jsonl'); c = load_cases(p, split='all'); print(len(c), 'cases, version', version(p)); print(coverage(c))"

    Avec seulement la ligne modèle, la sortie est :

    1 cases, version c112d6920cac
    {'normal': {'dev': 1}}

    Si une règle n’est pas respectée, l’erreur indique le cas et la règle. Corrigez le problème et exécutez la commande de nouveau.

  6. Rédigez practice/dataset.md, la fiche du jeu de données. Utilisez ces titres : Source, Qui a étiqueté et comment, Partitions et rôle de chacune, Couverture (le tableau de l’étape 5), Ce qui manque, Version. Lisez d’abord la fiche de Larkfield dans data/dataset.md comme exemple.

Afficher le contenu d’une bonne fiche

Une bonne fiche est courte et honnête. Source : « 12 billets écrits par moi le 12 octobre; aucun ne vient d’un vrai client. » Qui a étiqueté et comment : « J’ai étiqueté chaque cas en lisant data/policy.md, avant de regarder toute sortie. Personne d’autre n’a vérifié les étiquettes. » Partitions : « 8 cas de développement pour essayer des requêtes; 4 cas réservés, figés; je les évaluerai une seule fois. » Couverture : le tableau, avec les nombres de cas. Ce qui manque : par exemple, « seulement 2 billets en français; aucune pièce jointe; aucune conversation; mon propre style d’écriture ». Version : les 12 caractères de l’étape 5, et la date.

Expliquez à Grace, dans un court paragraphe, les lacunes de couverture du jeu de Larkfield. Expliquez aussi comment vous gardez les données réservées à l’écart des modifications de requêtes. Nommez deux lacunes et les deux fuites de ce cours.

Afficher un exemple de réponse

« Le jeu couvre six sous-groupes, mais certains sont petits : 20 billets en français, seulement 4 sans demande lisible et 5 menaces juridiques. Il ne contient aucun vrai billet ni aucune conversation. Ses scores signifient donc “sur des billets comme ceux-ci”. Nous ajustons les requêtes uniquement sur les données de développement. Nous évaluons les données réservées une seule fois pour la mise en production. Le code refuse un billet copié entre les partitions. Deux fuites se sont produites : les étiquettes et les règles de formulation ont été révisées après la lecture des sorties réservées. Les scores des critères sur les données réservées sont donc un peu optimistes. Désormais, nous figeons les étiquettes et le vérificateur avant que quiconque lise une sortie sur les données réservées. »

Si votre projet ne fonctionne plus à la fin de ce module, suivez Réinitialiser le projet avec l’instantané C11/snapshots/end-of-m02. Ensuite, python -m pytest -q doit indiquer 13 passed (13 tests réussis).

Récapitulatif

  • La contamination signifie qu’un cas de test a influencé le système qu’il teste. Son score est trop bon, et rien dans le score ne l’indique.
  • Trois parties, trois rôles : dev sert à l’ajustement. Les données réservées sont figées et évaluées une seule fois pour la mise en production. La partition contaminée sert uniquement à mesurer l’écart.
  • La référence de base copie mot pour mot les deux exemples de la requête v2. Sur les 69 autres billets déjà vus, elle ne montre aucun écart de contamination : les différences viennent du bruit d’échantillonnage. Rapportez ce résultat honnêtement.
  • L’expérience : ajustée uniquement sur les données de développement, la requête v3.1 a gagné +0.147 décisions en développement et +0.143 sur les données réservées. Mais elle a gagné +0.127 critères de réponse en développement et seulement +0.061 sur les données réservées. Rapportez le chiffre des données réservées.
  • Le vérificateur peut aussi être contaminé : les règles de formulation ont été corrigées après la lecture des réponses réservées. Les scores des critères sur les données réservées sont donc un peu optimistes. Figez les étiquettes et le vérificateur avant que quiconque lise une sortie sur les données réservées.
  • check() refuse un billet copié entre les partitions, mais pas une copie presque identique. Votre projet correspond à end-of-m02 : 13 passed.

Ensuite, le module 3 transforme les étiquettes en mesures. Il commence par les deux décisions : la précision et le rappel pour needs_human (nécessite une intervention humaine). Il explique pourquoi manquer un billet lié à la sécurité coûte plus cher que mal acheminer une question.

Leçon suivante : Métriques de prédiction