Outil standalone qui mesure la qualité d'extraction du provider OCR
courant (Mock, Mistral, ou autre futur) sur un set de factures avec
ground truth, séparé de la suite Playwright (qui reste sur OCR mock
pour la rapidité CI).
Pourquoi : permet de valider qu'un changement de provider (Mistral
upgrade, ajout Document AI, custom prompt) maintient la précision sur
les factures réelles avant de l'activer en prod.
Architecture :
- Lit `e2e/fixtures/invoices/<name>.pdf` (ou .png/.jpg)
- À côté, `<name>.expected.json` avec la ground truth
- Pour chaque facture : upload temporaire vers le storage courant
(MinIO en dev), appelle provider.extract(), compare field-by-field
- Cleanup du fichier temp après extraction
- Sommaire : accuracy globale, par champ, latence moyenne, exit 1
si une fixture a échoué (utile CI)
Tolérances par champ :
- amountTtcCents : exact (la précision financière compte)
- issueDate / dueDate : jour exact
- numero : exact (trim, case-insensitive)
- clientName : Jaccard similarity ≥ 85 % sur les tokens
(tolère "SARL" final manquant, espaces, etc.)
- clientEmail : exact (lowercased) ou null
Usage :
pnpm ocr:validate # provider courant (.env)
OCR_PROVIDER=mistral MISTRAL_API_KEY=... pnpm ocr:validate
node ace ocr:validate --fixtures-dir=./other --out=report.json
Sécurité :
- `.gitignore` exclut tous les fichiers de e2e/fixtures/invoices/
sauf README + .gitignore eux-mêmes — les vraies factures ne fuitent
pas dans le repo public
À faire par Arthur :
1. Dépose 10-20 vraies factures (anonymisées si possible) dans
e2e/fixtures/invoices/
2. Pour chaque, écrit le .expected.json (5 min par facture)
3. Lance `OCR_PROVIDER=mistral pnpm ocr:validate` → ajuste prompt ou
post-process si l'accuracy descend sous le seuil
Format ground truth + seuils cibles documentés dans le README du dossier.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fixtures OCR — factures réelles pour bench provider
Dossier de référence pour la commande node ace ocr:validate qui mesure
la qualité d'extraction OCR (Mock, Mistral, ou autre futur provider) sur
un set de factures réelles avec ground truth.
Comment ajouter une facture
- Dépose ton PDF dans ce dossier :
ma-facture.pdf(ou.png,.jpg). - Crée à côté
ma-facture.expected.jsonavec les valeurs lisibles à l'œil sur la facture :
{
"expected": {
"clientName": "Boulangerie Martin SARL",
"clientEmail": "compta@boulangerie-martin.fr",
"numero": "F-2024-0042",
"amountTtcCents": 124000,
"issueDate": "2024-04-15",
"dueDate": "2024-05-15"
},
"notes": "facture B2B classique, en-tête simple"
}
Champs :
| Champ | Format | Note |
|---|---|---|
clientName |
String | Le nom de la société/personne facturée. Variantes acceptées via similarité Jaccard ≥ 85 % (tokens) — utile si l'OCR rate un "SARL" final. |
clientEmail |
String OR null |
null si pas d'email sur la facture. |
numero |
String | Tel qu'imprimé. Comparaison case-insensitive trim. |
amountTtcCents |
Integer | Montant TTC en centimes. Comparaison exacte (la précision financière compte). |
issueDate |
YYYY-MM-DD |
Comparaison au jour près. |
dueDate |
YYYY-MM-DD |
Idem. |
Le champ notes est libre, ignoré par la commande — pratique pour
documenter "facture avec logo qui couvre 30 % du haut", "facture
manuscrite scannée", etc.
Comment lancer le bench
# Avec le provider courant (.env) — par défaut "mock"
pnpm --filter @rubis/api exec node ace ocr:validate
# Forcer Mistral (vrai OCR — coûte des tokens API)
OCR_PROVIDER=mistral MISTRAL_API_KEY=sk-... \
pnpm --filter @rubis/api exec node ace ocr:validate
# Avec un dossier custom + rapport JSON
node ace ocr:validate --fixtures-dir=./other-pdfs --out=report.json
Lecture du résultat
La commande affiche :
- Pour chaque facture : check par champ (✓/✗) avec expected vs got + confidence du provider
- Sommaire : nombre de factures 100 % match, accuracy globale champs, latence moyenne
- Précision par champ : pour chaque type (amount, dueDate, clientName…), le ratio de matches
Exit code 1 si une facture a échoué → utile en CI pour bloquer un déploiement OCR si la qualité a chuté.
Diversité du set
Pour un bench représentatif, viser au moins :
- 5 factures B2B (en-tête société, SIRET, TVA, montant rond)
- 5 factures de prestation (taux horaire, jours, multilignes)
- 2-3 factures avec spécificités (sous-totaux multiples, devises mixtes, scan basse qualité)
- 1 facture mauvaise : floue, manuscrite, photo prise à la volée — pour mesurer le worst case et calibrer la confidence du provider
Les vraies factures peuvent contenir des infos sensibles — ne pas
commit dans le repo public. Le .gitignore exclut ce dossier par
défaut (cf. e2e/fixtures/invoices/.gitignore).
Cibler un seuil de qualité
Calibrage suggéré V1 (à ajuster après premier run réel) :
amountTtcCents: ≥ 98 % d'accuracy (zéro tolérance sur l'argent)dueDate/issueDate: ≥ 95 %numero: ≥ 95 %clientName: ≥ 90 % (avec fuzzy)clientEmail: ≥ 80 % (souvent absent → null fréquent)
En-dessous, soit améliorer le provider (prompt Mistral, post-processing), soit baisser la confiance affichée à l'user dans le SPA (badge "à vérifier" sur les drafts).