# Comparação de modelos — 2026-08-25

Corpus de desenvolvimento conhecido. Holdout bloqueado. Produção usada apenas para `SELECT` do
gabarito; nenhum valor pessoal foi escrito em ficheiro ou mostrado nos relatórios.

## Decisão definitiva

O motor da `3.0.0-dev` fica fixado em `AuditAid/PaddleOCR-VL-1.6-0.9B:latest`, blob Ollama
`71306567488b`. Não executar `ollama pull` deste nome durante a afinação: uma mudança do blob
invalida a comparação. É o único modelo OCR que completou os 30 scans e demonstrou simultaneamente
menos erros e melhor tempo que o Tesseract.

O `glm-ocr:latest` permanece apenas como experiência rejeitada. `qwen3-vl:4b` e `qwen3.5:4b` são
VLM generalistas e ficam excluídos por decisão de âmbito: o projecto passa a avaliar apenas modelos
especializados em OCR. O código experimental específico desses modelos não deve orientar a
afinação do Paddle.

| Modelo | Classe | Ensaio máximo | Resultado | Decisão |
|---|---|---|---|---|
| PaddleOCR-VL 1.6 0.9B | OCR/VLM | 30 scans | 8 erros, 97 vazios, média 18,7 s | Escolhido |
| GLM-OCR | OCR/VLM | piloto 5; integral interrompido | lacunas críticas e processo >3 min | Rejeitado |
| Qwen3-VL 4B | VLM generalista | controlo não pessoal | 0 caracteres, 1400 tokens | Fora do âmbito |
| Qwen3.5 4B | VLM generalista | controlo e tentativa piloto | piloto 5/5 em timeout | Fora do âmbito |

## PaddleOCR-VL 1.6 0.9B

Baseline integral de 30 scans: 8 erros, 97 vazios, média 18733 ms e p95 29532 ms. Passa velocidade
e erros totais, mas reprova cobertura e os gates por campo. Continua a melhor baseline completa até
nova medição.

Problemas observados:

- cobertura inferior ao Tesseract: 97 vazios contra 58;
- vazios concentrados em NISS (25), NCC (18), validade do CC (17) e filiação (17);
- dois erros de `naturalidade`, quatro de `val_rc` e dois de `especialidade`;
- a transcrição geral do RC não trouxe a data de emissão nos casos medidos;
- o recorte focalizado de naturalidade devolveu texto sem o rótulo, por vezes excessivamente longo,
  pelo que não podia ser ancorado com segurança;
- a configuração de naturalidade focada mais validação RC pelos 90 dias reduziu erros à custa de
  150 vazios e foi rejeitada;
- ainda não foi demonstrado o gate de zero fabricações, nem aberto o holdout.

Correcções aceites até agora: remoção do fallback inseguro que escolhia a maior data do RC e duas
leituras focadas da validade com consenso. Não se corrigem algarismos por aproximação.

## GLM-OCR

Modelo: `glm-ocr:latest`.

O primeiro ensaio falhou os cinco scans porque o prompt longo do Paddle fazia o pacote Ollama gerar
até ao limite. Num logótipo não pessoal: 1400 tokens/1828 caracteres/10,7 s; com parâmetros oficiais
mas sem terminação: 2400 tokens/3180 caracteres/18,0 s. O início estava correcto, seguido por blocos
Markdown vazios repetidos.

Adaptação por modelo no backend:

- prompt oficial `Text Recognition:`;
- `temperature=0`, `top_p=0.00001`, `top_k=1`, `repeat_penalty=1.1`;
- sequência de paragem para o bloco Markdown duplicado e truncagem defensiva.

O mesmo logótipo passou para 36 tokens, 23 caracteres e 0,58 s, preservando o texto correcto.

Piloto real de cinco scans (2, 12, 18, 31, 34), sem fallback: média 21517 ms, p95 23789 ms, zero
falhas. Pontos fortes: NCC 4 certos/1 vazio, validade CC 4/1, filiação 5/0. Pontos fracos: NISS
0 certos/5 vazios, `val_rc` 0/5, naturalidade 2 erros/2 vazios e especialidade 1 erro.

O corpus integral foi iniciado, mas interrompido de forma controlada: depois do scan 2, o segundo
processo ficou quase três minutos sem concluir. Reprova o orçamento de tempo e não justificava
continuar uma corrida potencialmente muito longa. GLM-OCR fica rejeitado como motor único nesta
configuração; não foi aberto o holdout.

Problemas consolidados: sensibilidade extrema ao prompt, repetição de blocos Markdown, NISS e
`val_rc` sempre vazios no piloto, erros de naturalidade/especialidade e latência sem limite prático
em pelo menos um documento real.

## Qwen3-VL 4B

Modelo: `qwen3-vl:4b`.

O primeiro controlo não pessoal, através de `/api/generate`, esgotou os 1400 tokens em 23,9 s e
devolveu zero caracteres. A integração foi alinhada com o pacote oficial Ollama (`/api/chat` e
`think=false`), mas repetiu o resultado: 1400 tokens, zero caracteres e 23,1 s.

Foi ainda aplicado o modo não pensante recomendado pela documentação Qwen: `/no_think` no pedido,
`temperature=0.7`, `top_p=0.8` e `top_k=20`. O terceiro controlo voltou a terminar no limite de
1400 tokens, com zero caracteres, em 23,4 s. O modelo fica rejeitado sem tocar no piloto pessoal:
esta combinação modelo/Ollama não entrega uma transcrição utilizável nem no caso de controlo.

## Qwen3.5 4B

Modelo: `qwen3.5:4b`. VLM generalista, não modelo OCR; excluído do âmbito definitivo.

O controlo não pessoal funcionou por `/api/chat` com modo não pensante: 23 caracteres, 13 tokens e
1,2 s. No piloto real, porém, os cinco scans excederam o timeout operacional de 35 s e não houve
qualquer resultado pontuável.

Um diagnóstico isolado do scan 2 com limite de 90 s concluiu oito páginas em 42490 ms. O RC sozinho
consumiu 21025 ms, 1032 tokens e devolveu 2596 caracteres; as restantes páginas demoraram entre
1025 e 4585 ms. Isto mostrou transcrição excessiva do RC e incumprimento do orçamento multipágina.
Não foi medida exactidão, porque nenhum dos cinco pedidos normais concluiu. Chegou a ser preparado
localmente um prompt reduzido para o RC, mas não foi instalado no Windows e foi retirado do
código-fonte quando o âmbito ficou restringido a modelos OCR.

## Próximo trabalho

Restaurar o runtime Windows para PaddleOCR-VL e trabalhar exclusivamente nos vazios e erros da sua
baseline, começando por NISS/NCC/validade CC/filiação e mantendo os gates por campo. Não voltar a
comparar Qwen neste ciclo. GLM só deve ser reaberto mediante decisão explícita e uma correcção
demonstrável para a latência extrema.
