learn.chetana.fr

Étude de cas : fiabiliser le matching par l'éval

17 min readAdvanced

Le point de départ : optimiser à l'aveugle

La leçon précédente se terminait sur un manque : des golden sets qui dormaient dans samples/ sans harness pour les rejouer. Améliorer le matcher ressemblait alors à ça : modifier le scoring, tester trois documents à la main, « ça a l'air mieux », merger. Personne ne pouvait dire si un changement améliorait globalement ou déplaçait le problème. Cette étude de cas raconte comment la boucle a été fermée — en une dizaine de jours et une série de MRs — et ce qu'elle a rapporté, chiffres à l'appui.

Étape 1 — L'outil AVANT les fixes : le harness d'éval

Première décision structurante : ne toucher à aucun algorithme avant d'avoir un banc de mesure. Le harness, isolé sous eval/ (« le supprimer ne casse rien ») :

  • catalogue client réel chargé par le vrai chemin d'import, dans un tenant jetable dédié à l'éval ;
  • runner in-process : chaque cas rejoué dans le vrai graphe d'orchestration (create_orchestrator_graph().ainvoke()) — on mesure la prod, pas une maquette simplifiée ;
  • scorecard : line_f1, precision, recall, match_top1, hallucination_rate ;
  • UI de revue des golds (petit serveur FastAPI + SPA) pour relire et corriger la vérité terrain à la main ;
  • push Langfuse : chaque run devient un dataset run comparable aux précédents dans l'UI (module 7 !) ;
  • ergonomie 3 commandes : just eval-setup / just eval-ui / just eval — un outil que personne n'utilise n'améliore rien.

Étape 2 — Le juge : un corpus de cas durs, un piège par cas

Neuf documents réels ajoutés d'un coup (~184 lignes gold), chacun ciblant UN mode de défaillance étiqueté (case_focus) : le formulaire de 200 références dont 6 seulement commandées (anti-hallucination), les quantités en toutes lettres (« 1 palette », « reliquat »), la couche texte fragmentée (« CO M M A N D E »), les lignes cachées dans un commentaire libre, le bon de commande manuscrit scanné, la photo de téléphone…

Et le geste le plus pro de toute l'histoire : 22 golds de la première version ont été corrigés « par preuve catalogue » — l'équipe a évalué son propre juge avant de s'en servir. Un gold faux est pire que pas de gold : il punit les bons fixes.

Étape 3 — La boucle : 1 fix = 1 branche = son delta dans la MR

La règle de l'epic qui pilote le chantier : chaque MR affiche son score avant/après sur le corpus. La revue de code devient une revue de résultats. Florilège des fixes, chacun né d'un cas d'éval :

  • Doctrine quantités — le principe le plus généralisable : « l'extraction TRANSCRIT, un filtre déterministe DÉCIDE ». Fini le qty=1 par défaut qui transformait un formulaire de 161 références en devis de 161 lignes : le LLM transcrit tout (quantity=null si rien d'écrit), un filtre décide « commandé = quantité écrite » ;
  • Réparer l'image avant le modèle — un scan à l'envers mettait TOUS les moteurs OCR à 0.00. Verdict : « c'est un bug d'image, pas d'IA » → transpose EXIF + retry à 180° (Pillow pour les images, pypdf pour les PDF), en gardant le meilleur des deux essais ;
  • Épingler les modèles — l'alias ocr-latest faisait bouger les scores sans aucun déploiement (le fournisseur change le modèle sous l'alias). Pin de version : la baseline redevient stable — on ne mesure rien sur un sol qui bouge ;
  • Le matcher « réf d'abord » — l'éval a prouvé que des références présentes au catalogue perdaient : le hit exact n'était qu'un candidat parmi d'autres, dilué par le scoring nom-centré. Redesign en étages : forme canonique (majuscules, alphanumérique seul), l'exact sur les colonnes d'identifiants devient décisif, le fuzzy est borné, la corroboration départage — match@1 isolé : 0.86 → 0.89 ;
  • Benchmark à contrat égal — une commande dédiée compare 4 modèles de vision avec le prompt et le schéma exacts de la prod : le spécialiste OCR (0.88) y bat les généralistes (0.79-0.83), et le meilleur généraliste est identifié comme fallback. Sans contrat égal, un benchmark ne compare que des prompts.

Les résultats agrégés du chantier

  • hallucinations de lignes : 0.16 → 0.10
  • justesse des quantités : 0.78 → 0.83
  • document scanné à l'envers : 0 → 119 lignes extraites
  • match@1 bout-en-bout : 0.57 → 0.68 ; match@1 isolé : 0.86 → 0.89

Et une découverte majeure au passage, promue en ticket : le cache d'apprentissage empoisonné (leçon précédente : il écrit dès l'extraction, pas à la validation) — un premier import raté « apprend » ses erreurs et les re-sert. L'éval n'améliore pas que les scores : elle révèle les défauts d'architecture.

Épilogue (une semaine plus tard) : le premier correctif est mergé — un trust gate sur le cache : seuls les matchs confiants sont mémorisés et re-servis (fini d'apprendre les erreurs d'un import raté). La réforme complète (write-on-validation : n'apprendre que ce qu'un humain a validé, + invalidation) reste spécifiée pour la suite. Deux autres retombées directes de l'éval ont suivi le même chemin : le recall des références exactes (les colonnes ean/mpn/source_external_id ignorées par la récupération de candidats) et le « Relancer l'analyse » scopé à la pièce jointe rejouée. La boucle mesure → découverte → fix → re-mesure tourne en régime de croisière.

Les six principes (à voler pour n'importe quel système IA)

  1. L'outil de mesure avant les fixes — pas de baseline, pas d'optimisation ;
  2. Des cas durs réels, un mode de défaillance par cas — le corpus est un cahier des charges exécutable ;
  3. Le juge s'audite aussi — corriger les golds par preuve avant de s'en servir ;
  4. 1 fix = 1 branche = son delta chiffré — la revue de code devient revue de résultats ;
  5. Le déterministe avant le modèle — rotation, quantités, références : répare en code ce qui se répare en code, et épingle les versions de modèles ;
  6. Benchmark à contrat égal, chiffres publiés — même prompt/schéma pour tous les candidats, et chaque avancée annoncée avec son score.
🧩 Quiz1/6

Pourquoi construire le harness AVANT de toucher aux algorithmes ?

🃏 Flashcards1/6