Actualités

Bon Dossier : trois versions, un système intelligent de reconnaissance des reçus sur l’appareil

Comment un scanner de reçus est devenu une plateforme respectueuse de la vie privée pour iPhone, iPad et macOS, avec OCR, Core ML, synchronisation CloudKit, exports, outils d’entraînement et contrôles de production.

Bon Dossier : trois versions, un système intelligent de reconnaissance des reçus sur l’appareil

"Bon Dossier" n'est pas devenu difficile parce que l'application avait une interface utilisateur compliquée. Il est devenu difficile parce que le monde réel est hostile au nettoyage des données. Les reçus sont étroits, courbés, s'estompés, pliés, photographiés sous de mauvaises lumières, imprimés dans de nombreuses langues, remplis de résumés fiscaux, texte de fidélité, métadonnées terminales, réductions, remboursements, numéros de code à barres et totals qui sont parfois divisés sur plusieurs lignes. Une simple démonstration OCR peut reconnaître le texte. Une application de production doit décider quel texte est vrai.

Le projet a donc évolué à travers trois versions pratiques. Ce ne sont pas seulement des versions marketing. Ce sont des époques d'ingénierie: d'abord la fondation, puis le moteur de reconnaissance, puis la plateforme de niveau de production. Chaque version a résolu une classe de problème différente, et chacune a exposé la couche suivante de complexité.

Version 1

La fondation

Un paquet Swift, un modèle de domaine, un analyseur, une couche d'exportation et une coquille SwiftUI qui rendaient les reçus testables avant l'existence des couches de caméra et de synchronisation.

Version 2

Le moteur du renseignement

Les classificateurs Vision OCR, Core ML, les zones de mise en page de style YOLO, l'assemblage des lignes d'éléments, les portes de validation et l'apprentissage local ont remplacé le partage naïf.

Version 3

La plateforme de production

Core Data, CloudKit, sauvegardes, extensions de partage, exportations, métadonnées d'archives, macOS, audits et applications de formation ont transformé le reconnaisseur en produit.

La contrainte initiale: privée, locale et vérifiable

La décision architecturale la plus importante est venue avant le scanner: ReceiptVault devait être utile sans télécharger de documents financiers personnels sur un serveur. Cela exclut le chemin facile d'envoi d'images vers un OCR en ligne ou un service de modèle linguistique. L'application de production serait basée sur Apple Vision, VisionKit, Core ML, Swift parsers et validation déterministe, tous fonctionnant localement. iCloud pourrait synchroniser l'archive privée de l'utilisateur, mais il n'y aurait pas de backend "Bon Dossier".

Cette restriction de confidentialité a façonné chaque processus ultérieur. Les données de formation ont été traitées comme du matériel de projet local. Les corrections de révision ont été stockées comme des exemples locaux. Les modèles candidats ont été promus seulement après des rapports et des vérifications de régression. Même la couche Apple Foundation Models a été conçue comme un passe d'examen local facultatif avec une validation stricte du schéma JSON et un retour au modèle de reçus regroupés.

Version 1: Construire les os devant la caméra

La première version était délibérément peu charmante. Au lieu de commencer avec un écran de caméra et espérant que le reste suivrait, le projet a commencé avec un package testable Swift appelé ReceiptVaultCore. Ce package contenait le modèle de domaine, le parseur de reçus, le catégorisateur, le détecteur de duplicates, le code d'exportation et plus tard la plupart de la logique de reconnaissance et de persistance. L'objectif initial était simple: rendre le modèle de données de réception exécutable et testable sans dépendre du simulateur, des autorisations de la caméra ou du OCR en direct.

Le modèle principal s'est développé autour de ParsedReceipt: marchand, adresse, date d'achat, monnaie, total, sous-total, taxe, méthode de paiement, numéro de reçu, texte OCR, notes, articles de ligne, actifs de numérisation et statut d'examen. À partir de là, le projet a ajouté TSV export, Excel export, regroupement duplié, inférence de catégorie, rappels de date limite et validation de qualité. L'interface utilisateur de l'application pourrait maintenant travailler avec des reçus comme des enregistrements réels, et non comme des chaînes OCR lâches.

Le shell SwiftUI suivait le même principe. iPhone utilisait une structure d'onglets pour l'archivage, l'analyse, les informations et les paramètres. iPad utilisait une barre latérale et un modèle de navigation partagée. Les vues étaient construites avec des conteneurs système tels que TabView, NavigationStack, NavigationSplitView, Liste et Section plutôt qu'un cadre d'interface utilisateur personnalisé. Cela gardait l'application natif, accessible et mesurable.

La première décision importante du processus: Les tests de domaine sont arrivés avant l'intégration de la caméra. Le comportement du parseur a été verrouillé avec des fixtures. Le projet Xcode a été généré et protégé par des scripts. Les commandes de construction et de test ont été capturées afin que le projet puisse être reproduit au lieu d'être soigné manuellement à l'intérieur de Xcode.

Les outils dans la version 1

  • Swift 6 et SwiftPM pour le module de base partagé.
  • Génération de projets Xcode à travers des scripts Ruby, y compris des garanties de synchronisation de source.
  • XCTest pour les contrôles de régression au niveau de l'analyseur, de l'exportateur, de l'archivage et de la source.
  • Simulateur construit des scripts pour la vérification iPhone et iPad.
  • Réception d'images de fixation et reconstruction de texte pour rendre mesurables les modifications du parseur.
  • Un document d'état de mise en œuvre vivant qui suivait les travaux terminés, en cours et bloqués.

Un des premiers obstacles à l'infrastructure était la dérive de la chaîne d'outils: la ligne de commande Swift installée n'était pas toujours alignée sur le SDK Xcode. La solution de travail était explicite et répétable: utiliser le test xcrun Swift et la chaîne d'outils sélectionnée par Xcode pour les commandes de paquets. Cela semble banal, mais cela a empêché de faux échecs de se masquer comme des régressions d'applications.

Version 2: De OCR texte à réception Compréhension

La deuxième version a commencé quand il est devenu clair que OCR seul n'était pas le produit. Apple Vision peut lire de nombreuses lignes d'un reçu, mais il ne sait pas quel numéro est le total final, quelle ligne est une base de TVA, quel montant est un rabais, quelle date est un timestamp terminal, ou où le bloc d'articles commence. "Bon Dossier" avait besoin d'un moteur de compréhension du reçu.

Le parcours d'analyse a utilisé VisionKit pour la capture de documents sur iPhone et iPad, préservant les scans originaux en tant qu'actifs tandis que OCR et le traitement pouvaient utiliser des versions coupées ou tournées. OCR sortie est devenue entrée, pas vérité. Des lignes reconnues, des boîtes de lignes, des valeurs de confiance et des informations ultérieures sur la structure des documents ont été transmises à un pipeline hybride: des modèles locaux Core ML ont proposé des rôles et des régions, Swift ont assemblé des candidats et des validateurs déterministes ont décidé quel candidat était acceptable.

Cela a changé le projet d'une application avec des règles de partage en un système d'apprentissage automatique hors ligne. La base de code a développé des scripts pour les étiquettes de démarrage, les classifiateurs de lignes de formation, les modèles de détecteur et de localisateur de formation, la préparation de ensembles de données YOLO, l'évaluation de layouts invisibles, la génération de négatifs non reçus, l'audit de la structure des étiquettes, la dérivation de ensembles de données spécialisés et l'exécution de vérifications post-formation. L'application de production est restée locale et déterministe, mais le flux de travail de développement est devenu une boucle de mesure, d'isolement, d'étiquetage, de formation, de comparaison et de promotion.

Le pipeline de la reconnaissance

CoucheLe butOutils représentatifs
CaptureAcquérir des images de réception tout en conservant les originaux pour examen ultérieur, partage et sauvegarde.VisionKit, AVFoundation, SwiftUI révision de l'analyse, les flux de récolte et de rotation.
Extraction de texteReconnaître les lignes OCR, la géométrie, la confiance et les structures de documents.Apple Vision OCR, liste fixe de reconnaissance de langue de réception, structure de document iOS 26, le cas échéant.
La mise en pageDécouvrez le reçu, le marchand, le poste, le total, la TVA et les zones de paiement.YOLO/trainage en ultralytique, Core ML exportations de localisateurs, audits de structures, évaluation de la qualité des zones.
La sémantiqueClassifier les lignes et les montants comme marchand, élément, total, TVA, paiement, métadonnées ou ignorer.Créez des classifiateurs ML/Core ML, modèle de retrait JSON, classements de champs, spécialistes des zones.
AssembléeConstruire des rangées de postes, des totaux, des ventilations fiscales et des faits de paiement à partir de candidats à la ligne bruyante.Swift parseurs, assembleur de rangs d'éléments, parseur de première zone, arbitre d'éléments de ligne.
ValidationAcceptez uniquement les sorties qui respectent les mathématiques des reçus et évitez les pièges du terminal, des codes à barres et des impôts.Validateur de qualité, détection de devises, mathématiques à la TVA incluse, portes totales finales, fixations de régression.

Le changement technique le plus important: la validation est devenue le patron

Un modèle peut suggérer. Un analyseur peut en déduire. Mais le reçu final doit être équilibré. "Bon Dossier" traite finalement la validation arithmétique comme le gardien du portail: les lignes des éléments doivent se fermer contre le total final, la TVA ne doit pas être double comptée, le sous-total et les bases fiscales ne doivent pas gagner sur les totaux finaux, et les métadonnées de paiement ou de carte ne doivent pas devenir des montants d'achat.

Cette règle a exposé des erreurs subtiles. Pour les reçus européens inclus dans la TVA, le contrôle correct est généralement total == somme ((articles), pas total == articles + impôt. Ce dernier double compte la TVA et peut bénir exactement le total erroné. La fixation de cette règle a changé le système de " semble plausible " à " mathématiquement honnête " pour une grande classe de reçus.

D'autres corrections ont également été concrètes: les résumés de réduction ont cessé de prévaloir sur les totaux finaux, les en-têtes de colonne tels que "Price" et "Total" ont cessé de confondre les prix des articles avec les totaux de reçus, les lignes de base fiscale ont cessé de gagner sur les totaux de paiement, et les étiquettes divisées telles que "Total" suivies du montant sur la ligne suivante ont reçu une manipulation spéciale. C'étaient des corrections au modèle de réalité de l'application.

Données d'entraînement sans se tromper

Le projet a utilisé plusieurs types d'étiquettes parce que toutes les preuves de formation ne méritent pas la même confiance. Les étiquettes acceptées à la main sont devenues de l'or. Les exemples dérivés de la vision et de la sauvegarde des applications ont été traités comme des données de démarrage ou argentées. Les corrections manuelles ont été stockées comme des exemples traçables. La ligne de production a explicitement évité la surécriture des étiquettes acceptées YOLO, et les modèles candidats étaient censés être évalués par rapport à des validations fixes et à des échecs d'essai.

Cela importait parce que les systèmes de réception sont très faciles à surcharger accidentellement. Un bug documenté rendait inutile la formation des spécialistes de la zone: l'entrée spécialisée contenait la fonction d'étiquette similaire à la réponse, ce qui donnait au modèle un écho artificiel. Un autre nettoyage a supprimé les raccourcis commerçants hardcodés et les pseudonymes de frappe OCR. Le résultat était plus honnête et temporairement plus douloureux, mais il a rendu les problèmes restants visibles au lieu de les cacher derrière les tricheurs.

Numéros représentatifs de la qualité des modèles enregistrés au cours de l'effort

  • Un indice de référence actuel sur l'appareil couvrait 728 reçus et 35 361 lignes OCR.
  • Les attentes d'analyse révisées ont atteint 545 correspondances sur 546 reçus révisés, avec un déséquilibre de date révisé encore visible.
  • L'exactitude de la classification des lignes a été enregistrée à 96,3%.
  • Le temps moyen d'analyse a été enregistré autour de 47,5 ms, avec p95 autour de 81,4 ms sur le pipeline local sans réseau.
  • L'ambition de sortie est restée plus élevée que cela: près de 99% de l'automatisation nécessite des reçus réels plus vérifiés et des étiquettes de ligne d'article.

Version 3: Transformer un reconnaisseur en produit

La troisième version parlait de tout ce qui rend un moteur intelligent utilisable: persistance, synchronisation, recherche, importation de fichiers, sauvegardes, exportations, paramètres, manifestes de confidentialité, notes App Store, accessibilité, performance, localisation, macOS et outils opérationnels. C'est là que de nombreux prototypes s'effondrent, car l'ingénierie des produits est principalement le travail que les utilisateurs ne voient pas jusqu'à ce qu'elle échoue.

La persistance a commencé avec une archive locale JSON parce qu'elle était simple, inspectable et rapide à évoluer. C'était correct pour la phase initiale, mais ce n'était pas suffisant pour une archive privée iPhone/iPad. La direction de production est passée à une abstraction de référentiel avec Core Data comme magasin principal et NSPersistentCloudKitContainer pour la synchronisation privée iCloud. La conception a explicitement maintenu les actifs d'analyse d'origine, les checksums, les miniatures et les métadonnées de reçu coordonnées plutôt que de prétendre qu'un reçu n'est qu'une ligne dans une table.

L'archivage est également devenu plus qu'une liste. Il a adopté une organisation similaire à Mail: Tous les reçus, dossiers, dossiers intelligents, drapeaux, recherche à portée de main, sélection, actions de déplacement, exportation et remise de courrier. L'importation s'est étendue à travers les fichiers, courrier, images, PDF et charges de texte. Intents App et liens profonds pouvaient diriger les utilisateurs vers Archive, Scan, Insights et Settings. Le travail de mise au point et d'extension de partage ont fait ressembler les reçus aux citoyens du système plutôt que les données des applications piégées.

Core Data et CloudKit: Les pièces dures étaient opérationnelles

La décision Core Data a été pragmatique. SwiftData est agréable pour les prototypes, mais ReceiptVault avait besoin d'un contrôle explicite de la migration, de la manipulation d'actifs binaires, de crochets de débogage CloudKit, de la manipulation des conflits et d'années d'évolution des schémas futurs. Core Data a donné ces leviers. L'application pouvait toujours exposer une interface de référentiel propre à l'interface utilisateur, tandis que la couche de stockage gérait la migration à partir de JSON et la restauration des fichiers d'analyse manquants.

Les risques opérationnels étaient réels. L'initialisation du schéma CloudKit devait inclure des entités programmatiques telles que des enregistrements de reçus, des enregistrements d'actifs de numérisation, des dossiers, des marqueurs et des dossiers intelligents. Les schémas vides ont produit des défaillances partielles. Les constructions de production avaient besoin du schéma CloudKit promu dans l'environnement de production. Les droits de sortie devaient utiliser des APN de production pour les notifications de synchronisation silencieuses. Ce ne sont pas des problèmes glamour, mais ils décident si la synchronisation fonctionne pour les vrais utilisateurs.

Les sauvegardes, les exportations et le coût des analyses originales

"Bon Dossier" L'application a ajouté l'exportation TSV, l'exportation réelle XLSX, l'exportation d'archives filtrée, l'exportation de reçus sélectionnés, la sauvegarde manuelle et la restauration et les feuilles de partage. Elle a également gardé les pages d'analyse originales plutôt que de jeter les preuves après l'analyse. C'est la bonne décision de produit, mais elle crée des performances et du travail de stockage.

Une vérification de production a clairement identifié le compromis: le stockage d'images d'analyse à la fois sur le disque et en tant qu'enregistrements binaires externes Core Data double le stockage local, tandis que les sauvegardes intégrées JSON peuvent augmenter la mémoire car tous les octets d'analyse sont matérialisés à la fois. La voie de la solution était tout aussi claire: déplacer le travail coûteux de référentiel et de sauvegarde hors du fil principal, le flux ou la masse de grandes sauvegardes, les miniatures de cache, et être conscient de savoir si Core Data ou le système de fichiers est la seule source pour les octets d'analyse.

La version macOS n'était pas un spectateur

Le plan macOS était intentionnellement ambitieux: pas un compagnon de lecture uniquement, mais un ordinateur de bureau natif ReceiptVault qui partage le même archive privé iCloud et la même logique de base. L'interface utilise une structure de bureau à trois panneaux: barre latérale, liste de reçus et panneau de détails. La cible Mac réutilise le noyau partagé, OCR/ML pipeline, l'archivage, l'exportation, la sauvegarde et la logique de synchronisation tout en adaptant la présentation, les commandes, l'importation de fichiers et la capture par caméra pour les conventions macOS.

Cela a ajouté une autre couche d'outils: macOS droits, App Sandbox, accès à des fichiers sélectionnés par l'utilisateur, chaînes de confidentialité de la caméra, commandes de menu, planification de la caméra de continuité, panneaux d'importation natifs et App Store considérations de sortie multiplateforme. Cela a également prouvé la valeur de garder ReceiptVaultCore séparé des coquilles d'application. L'interface utilisateur de la plate-forme peut différer; l'intelligence des reçus ne devrait pas.

Les procédés d'ingénierie à l'origine de l'œuvre

Le projet a survécu parce qu'il n'a pas été exécuté comme une énorme explosion de fonctionnalités. Il utilisait la livraison en couches, les audits de source, les scripts locaux, les fixtures, les rapports de modèle et les portes documentées.

Construire et vérifier

Les tests SwiftPM, les constructions de simulateurs, les tests de protection des sources, les vérifications du générateur de projet, les liens de localisation, les scripts CI et la vérification manuelle des captures d'écran ont empêché le produit de dériver.

Les données et les opérations ML

Le pipeline a séparé les étiquettes d'or, d'argent et de démarrage, conservé les voitures de bord, vérifié YOLO structures, formé des modèles locaux et promu des objets seulement après comparaison.

Le durcissement du produit

Les audits de performances, l'inventaire de la confidentialité, les notes de l'app store, les vérifications d'accessibilité, les diagnostics de synchronisation et les examens des bloqueurs de sortie ont rendu visibles les modes d'échec cachés.

Outils et techniques représentatifs

  • Les produits suivants sont classés dans les catégories suivantes: app shells pour iPhone, iPad et macOS, avec des listes natives, des vues partagées, des paramètres et des flux de partage.
  • SwiftPM: un module de base partagé et des outils ML exécutables pour maintenir la logique commerciale testable en dehors de la cible de l'application.
  • XCTest: analyse, archivage, sauvegarde, exportation, localisation, protection des sources, app-intention et tests contractuels ML.
  • Apple Vision et VisionKit: capture par scan, OCR, géométrie des lignes et extraction de la structure des documents.
  • Core ML et créer ML: détecteur de réception, localisateur, classificateur, classeur de champ et modèles de rôle de ligne locaux.
  • YOLO et Ultralytics: expérimentation des régions de mise en page, des étiquettes de zones et des modèles d'exportation pour les régions de réception et de contenu.
  • Les scripts Python, Swift et Ruby: préparation des ensembles de données, génération de projets, formation des modèles, audits, rapports et synchronisation des ressources.
  • Les produits suivants sont les produits suivants: stockage d'archives privées synchronisées, migration à partir de JSON et analyse des actifs.
  • CryptoKit: SHA-256 est une somme de vérification de l'intégrité des actifs de scan et d'un parcours futur pour les sauvegardes cryptées.
  • Intégration de l'écosystème des applications: Extension de partage, importation de documents, Intents d'applications, liens profonds, enregistrements Spotlight, exportation de style Mail et sauvegardes manuelles.

Les obstacles et leurs solutions

HurdlePourquoi cela comptait- il ?Direction de la solution
OCR a produit du texte, pas de la vérité.Les recettes totales, les bases de TVA, les lignes de paiement et les numéros de codes à barres ressemblaient à des chaînes brutes.Combinez la géométrie OCR, les rôles Core ML, le contexte de la zone et la validation déterministique.
Les lignes d'articles étaient structurellement désordonnées.Les descriptions, les quantités et les prix apparaissaient souvent dans des colonnes séparées ou des blocs visuels séparés.Construisez un assembleur de lignes d'éléments, utilisez des zones d'éléments et ajoutez des arbitres d'éléments de lignes avec des vérifications de somme.
Les modèles peuvent s'adapter silencieusement.Des étiquettes fuyantes et des raccourcis commerçants hardcodés ont rendu les scores mieux que le comportement réel.Supprimez les fuites de réponse, séparez les niveaux de confiance des étiquettes et remplacez les tricheurs par la mémoire de correction et des filtres génériques.
CloudKit synchronisation nécessite une configuration opérationnelle exacte.Un schéma de production manquant ou un droit APN erroné peut faire échouer la synchronisation malgré le code d'application correct.Ajouter une initialisation explicite du schéma, des audits de sortie et des contrôles TestFlight sur plusieurs appareils.
Les scanners originaux créent une pression de mémoire et de stockage.Les gros reçus, les sauvegardes et les images en double stockage pourraient geler l'interface utilisateur ou augmenter la mémoire.Mettre en cache des miniatures, déplacer le travail du référentiel hors du fil principal, faire des sauvegardes en continu et rationaliser le stockage de l'analyse.
Il était facile de sous-estimer la conformité à la libération.Les manifestes de confidentialité, les droits, la protection des données et les notes App Store peuvent bloquer le téléchargement ou la révision.Effectuer des audits de préparation à la production avant le sprint final, pas après la construction du binary.

Ce qui en a fait un effort d'ingénierie herculeuse

La partie difficile n'était pas un seul algorithme. C'était le nombre de limites que tous devaient maintenir en même temps. Un scan devait devenir un résultat OCR. Le résultat OCR devait devenir un reçu. Le reçu devait devenir recherchable, exportable, synchronisable, restaurable et explicable. Le système de reconnaissance devait s'améliorer sans fuite de données privées ou en fonction d'un serveur. L'interface utilisateur devait rester en forme et calme pendant que le pipeline en dessous faisait un travail plus proche de la documentation forensique que de l'analyse des formulaires.

Chaque version a rendu la version précédente plus petite. La version 1 a prouvé que l'application pouvait exister comme un produit Swift propre. La version 2 a prouvé que la compréhension de la réception avait besoin d'un véritable pipeline local de ML et de validation. La version 3 a prouvé qu'un reconnaisseur n'est pas un produit tant que les opérations de persistance, de synchronisation, d'importation, d'exportation, de confidentialité, de performance et de sortie ne sont pas fiables.

La base de codes contient maintenant la forme de ce parcours: un module de base partagé, une application shell, des services de scanner, des vues d'archives, des référentiels Core Data, la configuration CloudKit, des contrats ML, des caches de modèle, des scripts de formation, des coureurs de ligne de production, de la documentation de l'app store, des audits de performance et des dizaines de tests ciblés. Il s'agit d'une plateforme de renseignement sur le recevoir qui a dû apprendre la différence entre le texte et les preuves.

Des leçons pour des projets similaires

  1. Commencez par un noyau testable. Une caméra et OCR sont bruyants.
  2. Traitez OCR comme preuve et non comme sortie. La géométrie, la confiance, la mise en page et les mathématiques des reçus font partie de la vérité.
  3. Suggestion de modèle séparée de l'acceptation du produit. La validation déterministique devrait décider si une analyse est suffisamment bonne.
  4. Ne laissez pas les résultats d'entraînement devenir un théâtre. Protégez-vous des fuites, des raccourcis codés et de la contamination.
  5. Planifiez les actifs plus tôt. Les images originales modifient l'architecture de stockage, de sauvegarde, de synchronisation et de performance.
  6. Auditer la configuration de la production sous forme de code. Les droits, les manifestes de confidentialité et les schémas CloudKit font partie de l'application.
  7. Gardez l'interface utilisateur spécifique à la plateforme mince. Un noyau partagé fait possible iPhone, iPad et macOS sans dupliquer le cerveau de réception.

L'histoire de développement de Bon Dossier rappelle que l'intelligence locale sérieuse n'est pas une caractéristique unique. C'est une chaîne de confiance: capture, OCR, mise en page, classification, analyse, validation, persistance, synchronisation, exportation et libération de discipline. Brisez un lien et l'utilisateur voit l'ensemble du système comme faux. Renforcez chaque lien et un reçu papier désordonné devient des données privées fiables.

Retour aux actualités

À découvrir

Articles associés

2 mai 2026

Dix heures de débogage dans AccountView

La maintenance logicielle est parfois prévisible. Parfois, elle exige dix heures dans Visual FoxPro, d’anciens contrôles ActiveX, des fichiers DBF temporaires, du code compilé obscurci, des visionneuses cachées, des composants PDF instables et un système de gestion du transport particulièrement tenace.

Lire la suite

Vous souhaitez travailler avec nous ?

Contactez-nous pour échanger sur votre projet.