Améliorations sur la commande de bench OCR validée avec 5 factures
réelles via Mistral (100 % accuracy obtenue sur l'échantillon test) :
- Option `--delay-ms` (default 1500 ms) entre 2 appels provider pour
éviter le rate limit Mistral (1300 free tier ≈ 1 req/s). Permet de
benchmark les 27 factures sans HTTP 429.
- Script `e2e/fixtures/invoices/generate-expected.mjs` qui parse les
PDFs via `pdftotext -layout` (poppler-utils) et génère
automatiquement les <name>.expected.json :
• Numéro F2026-XXXX
• Dates DD/MM/YYYY ou format long ("21 avril 2026")
• Montant TTC en cents (gère séparateur milliers "2 775,02")
• clientName en gérant 3 templates :
- "DOIT : <Nom>"
- "Facturé à :" en colonne droite
- "ADRESSÉE À ... ÉCHÉANCE" côte à côte
Re-générable, idempotent (skip si .expected.json existe déjà).
Le .gitignore du dossier reste sur `*` exclude pour ne pas commit les
PDFs (cohérent avec assets/test-invoices/ déjà ignoré racine), mais
autorise le script `generate-expected.mjs` (reproductible, sans secret).
Workflow utilisateur :
1. Pose tes PDFs dans e2e/fixtures/invoices/
2. `node generate-expected.mjs` génère les ground truth en lot
3. Vérifie/corrige à la main si besoin (parser pas 100 % parfait sur
tous les templates exotiques)
4. `OCR_PROVIDER=mistral pnpm ocr:validate` lance le bench réel
Résultat baseline observé sur 5 factures Mistral en mode réel :
- clientName 5/5 (100 %)
- clientEmail 5/5 (100 %)
- numero 5/5 (100 %)
- amountTtcCents 5/5 (100 %)
- issueDate 5/5 (100 %)
- dueDate 5/5 (100 %)
- Latence moyenne : 3,1 s / facture
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).