[Desafio] Por que LLMs alucinam na extração de grades e como analisamos um scorecard manuscrito em n8n

:waving_hand: Oi comunidade n8n,

Pipelines de extração de documentos lidam lindamente com faturas e CVs. Mas durante uma noite de jogos, alimentamos nossa IA com algo que parecia simples, mas a quebrou completamente: uma folha de pontuação de Kniffel (Yahtzee) manuscrita.

:mouse_trap: A armadilha da grade

O objetivo: Tirar uma foto, encontrar jogadores com um asterisco manuscrito (*) acima do nome, extrair suas pontuações, calcular o bônus de 35 pontos (se superior $\ge$ 63), somar tudo e enviar para Sheets e Telegram.

Olhe para a foto anexada:

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

É óbvio para os humanos. Mas a IA alucinoua nomes (“Ivan” virou “ban”), mesclou colunas e arruinou a matemática.

Aqui está o porquê dos dados espaciais quebrarem a IA, e a arquitetura de prompt que usamos para corrigi-la:

:eyes: Quebrando o viés “da esquerda para a direita”

As IA leem como um livro (esquerda para direita, linha por linha). Uma folha de pontuação exige o oposto. Para parar o “vazamento de linhas” entre colunas, forçamos uma saída Array de Objetos ([{player_name, sum_top, sum_bottom}]). Isso força a IA a terminar de ler uma coluna vertical antes de passar para a próxima.

:abacus: Matemática determinística, zero julgamento de IA

Já sabemos que o Extrator que estamos usando é incrivelmente confiável na classificação de documentos e na extração de dados estruturados, então decidimos testar seus limites e ver se conseguiria lidar com cálculos também. Grande erro. As IA são mecanismos de previsão de texto, não calculadoras. A solução: Instruímos a extrair apenas números brutos e usamos um nó Code JavaScript puro a jusante para lidar com a matemática e o bônus condicional.

:stop_sign: Estados vazios explícitos impedem alucinações

Os jogadores escreveram traços (-) para caixas vazias. Como a IA não conseguia encaixar um traço de string em um esquema Number, entrou em pânico e saiu com -1, quebrando nosso JavaScript. Adicionar uma restrição rigorosa—“CRÍTICO: Se uma célula é um traço ou vazia, VOCÊ DEVE produzir 0”, corrigiu o pipeline instantaneamente.

:anchor: Ancorar os olhos com rótulos

Para impedir que a IA se desviasse entre colunas, demos a ela coordenadas visuais. Instruímos a olhar para os rótulos impressos no extremo esquerdo (por exemplo, “Dreierpasch”), traçar uma linha horizontal invisível para a coluna com estrela e extrair apenas aquela célula.

:turtle: Uma ressalva honesta

Descarregar matemática no JS garante lógica perfeita, mas o reconhecimento de caligrafia ainda depende da qualidade da foto. Se você precisar de dados impecáveis de escrita confusa, a validação com presença humana ainda é o máximo.

:wrench: Como executar

Usei o easybits Extractor porque ele aplica cumprimento de esquema JSON nativamente sem lutar com prompts de nós HTTP. Tanto cloud quanto self-hosted se encaixam na camada gratuita.

  • n8n Cloud: Procure por easybits no painel de nós.

  • Self-hosted: Instale @easybits/n8n-nodes-extractor a partir de Community Nodes.

:speech_balloon: Feedback bem-vindo

Anexei a imagem bruta. Estou muito curioso: o que acontece quando você executa exatamente essa imagem através de sua configuração atual de OCR ou IA? Você encontrou uma forma mais limpa de extrair colunas verticais de grades horizontais densas?

Abração,
Felix

1 curtida

A abordagem de offloading de matemática é excelente - manter toda a aritmética em um nó Code em vez de confiar no LLM torna o pipeline determinístico independentemente do modelo. A restrição de esquema JSON em ordem de coluna ([{player_name, sum_top, sum_bottom}]) é uma maneira elegante de combater vazamento de linhas.

Uma coisa que vale a pena tentar se a precisão ainda escorregar em fotos de baixa qualidade: recorte cada coluna como uma região de imagem separada antes de passar para o modelo de visão. Até mesmo um recorte baseado em coordenadas aproximadas em um nó Code (usando as dimensões de imagem que você conhece do layout do scorecard) pode reduzir dramaticamente alucinações posicionais, dando ao modelo um contexto mais simples e de uma única coluna.

1 curtida

Opa @nguyenthieutoan, obrigado pelo feedback!

A ideia de recortar cada coluna em uma imagem separada é definitivamente interessante. Minha única preocupação é que isso exigiria que a foto fosse tirada com um enquadramento bem consistente todas as vezes. Caso contrário, há o risco de cortar informações importantes durante o processo de recorte.

Dito isso, é uma ótima sugestão, e vou definitivamente tentar para ver como é prático em uma configuração do mundo real. Obrigado por compartilhar a ideia!

A preocupação com o enquadramento é válida, mas você pode contorná-la adicionando uma sobreposição generosa a cada recorte — digamos um buffer de 10-15% em ambos os lados de cada limite de coluna em vez de cortar na borda exata. O LLM ainda vai se concentrar na coluna correta porque seu schema + prompt o bloqueia em um jogador por vez, e um pouco de sobreposição é muito menos prejudicial do que cortar nos números. Para planilhas impressas como Kniffel, onde os cabeçalhos das colunas têm largura fixa, você também pode derivar os limites de recorte uma vez de uma imagem de referência e reutilizar os mesmos deslocamentos de pixel de forma confiável em todas as fotos tiradas em condições semelhantes (mesma mesa, mesma distância do telefone).

1 curtida

Oi @nguyenthieutoan, faz sentido! Vou definitivamente tentar essa abordagem.

É engraçado mesmo ver como a IA pode ser capaz em geral, mas aí vem uma ideia de caso extremo e de repente você percebe que pode quebrar muitas soluções. Eu realmente gosto de descobrir essas situações, elas tendem a ser as mais esclarecedoras.