[Défi] Pourquoi les LLM hallucinent sur l'extraction de grille et comment nous avons analysé un bulletin de pointage manuscrit dans n8n

:waving_hand: Salut la communauté n8n,

Les pipelines d’extraction de documents gèrent magnifiquement les factures et les CV. Mais lors d’une soirée jeux, nous avons donné à notre IA quelque chose qui semblait simple mais qui l’a complètement bloquée : une feuille de pointage Kniffel (Yahtzee) manuscrite.

:mouse_trap: Le piège de la grille

L’objectif : Prendre une photo, trouver les joueurs avec un astérisque manuscrit (*) au-dessus de leur nom, extraire leurs scores, calculer le bonus de 35 points (si haut $\ge$ 63), tout totaliser, et envoyer vers Sheets et Telegram.

Regardez la photo jointe :

r/VibeCodersNest - Challenge Why LLMs hallucinate on grid extraction and how we parsed a handwritten scorecard in n8n|562.5xauto

C’est évident pour les humains. Mais l’IA a hallucé des noms (« Ivan » est devenu « ban »), fusionné les colonnes et massacré les calculs.

Voici pourquoi les données spatiales cassent l’IA, et l’architecture d’invite que nous avons utilisée pour la corriger :

:eyes: Briser le biais « De gauche à droite »

Les IA lisent comme un livre (de gauche à droite, ligne par ligne). Une feuille de pointage exige l’inverse. Pour arrêter le « débordement de ligne » entre les colonnes, nous avons forcé une sortie Array of Objects ([{player_name, sum_top, sum_bottom}]). Cela force l’IA à terminer la lecture d’une colonne verticale avant de passer à la suivante.

:abacus: Mathématiques déterministes, zéro jugement de l’IA

Nous savons déjà que l’Extracteur que nous utilisons est incroyablement fiable pour classer les documents et extraire des données structurées, nous avons donc décidé de repousser ses limites et de tester s’il pouvait aussi gérer les calculs. Grosse erreur. Les IA sont des moteurs de prédiction de texte, pas des calculatrices. La solution : Nous lui avons ordonné d’extraire uniquement les nombres bruts, et avons utilisé un nœud Code JavaScript pur en aval pour gérer les mathématiques et le bonus conditionnel.

:stop_sign: Les états vides explicites arrêtent les hallucinations

Les joueurs ont écrit des tirets (-) pour les cases vides. Parce que l’IA ne pouvait pas insérer un tiret dans un schéma Nombre, elle a paniqué et a généré -1, cassant notre JavaScript. Ajouter une contrainte stricte—« CRITIQUE : Si une cellule est un tiret ou vide, vous DEVEZ sortir 0 », a instantanément réparé le pipeline.

:anchor: Ancrer les yeux avec des étiquettes

Pour empêcher l’IA de dériver entre les colonnes, nous lui avons donné des coordonnées visuelles. Nous lui avons ordonné de regarder les étiquettes imprimées à l’extrême gauche (par exemple, « Dreierpasch »), de tracer une ligne horizontale invisible vers la colonne étoilée, et d’extraire uniquement cette cellule.

:turtle: Une honnête limite

Déléguer les mathématiques à JS garantit une logique parfaite, mais la reconnaissance d’écriture manuscrite dépend toujours de la qualité de la photo. Si vous avez besoin de données impeccables à partir d’une écriture désordonnée, la validation en boucle humaine reste le plafond.

:wrench: Comment l’exécuter

J’ai utilisé l’Extracteur easybits parce qu’il applique nativement l’application du schéma JSON sans se battre avec les invites du nœud HTTP. Cloud et auto-hébergé correspondent au niveau gratuit.

  • n8n Cloud : Recherchez easybits dans le panneau des nœuds.

  • Auto-hébergé : Installez @easybits/n8n-nodes-extractor depuis les nœuds communautaires.

:speech_balloon: Les retours sont bienvenus

J’ai joint l’image brute. Je suis très curieux : qu’arrive-t-il lorsque vous exécutez cette image exacte à travers votre configuration OCR ou IA actuelle ? Avez-vous trouvé un moyen plus propre d’extraire les colonnes verticales des grilles horizontales denses ?

Cordialement,
Felix

1 « J'aime »

L’approche de déport des calculs est excellente — conserver toute l’arithmétique dans un nœud Code au lieu de faire confiance au LLM rend le pipeline déterministe quel que soit le modèle. La contrainte de schéma JSON basée sur les colonnes ([{player_name, sum_top, sum_bottom}]) est un moyen efficace de combattre le débordement de lignes.

Une chose qui vaut la peine d’être essayée si la précision baisse encore sur des photos de mauvaise qualité : découper chaque colonne en tant que région d’image distincte avant de la transmettre au modèle de vision. Même un découpage approximatif basé sur les coordonnées dans un nœud Code (en utilisant les dimensions d’image que vous connaissez à partir de la mise en page de la feuille de score) peut réduire dramatiquement les hallucinations positionnelles en donnant au modèle un contexte plus simple et mono-colonne.

1 « J'aime »

@nguyenthieutoan, merci pour tes commentaires !

L’idée de découper chaque colonne en une image séparée est certainement intéressante. Ma seule préoccupation, c’est que ça nécessiterait que la photo soit prise avec un cadrage assez cohérent à chaque fois. Sinon, il y a un risque de couper des informations importantes pendant le processus de découpage.

Cela dit, c’est une excellente suggestion, et je vais définitivement l’essayer pour voir à quel point c’est pratique dans un vrai environnement. Merci de partager cette idée !

La préoccupation concernant le cadrage est valide, mais vous pouvez la contourner en ajoutant un chevauchement généreux à chaque recadrage — par exemple un buffer de 10 à 15 % de chaque côté de chaque limite de colonne plutôt que de couper exactement à la limite. Le LLM se concentrera toujours sur la bonne colonne parce que votre schéma + prompt le verrouille sur un joueur à la fois, et un peu de chevauchement est bien moins dommageable que de couper dans les chiffres. Pour les fiches imprimées comme Kniffel où les en-têtes de colonne ont une largeur fixe, vous pouvez également dériver les limites de recadrage une fois à partir d’une image de référence et réutiliser les mêmes décalages de pixels de manière fiable sur toutes les photos prises dans des conditions similaires (même table, même distance du téléphone).

1 « J'aime »

Salut @nguyenthieutoan, ça semble valide ! Je vais définitivement essayer cette approche.

C’est juste amusant de voir à quel point l’IA peut être capable en général, mais ensuite une seule idée de cas limite arrive et soudainement tu réalises qu’elle peut casser beaucoup de solutions. J’aime vraiment découvrir ces genres de situations, elles tendent à être les plus instructives.