Bon Service est une application web qui permet aux chefs de rédiger, standardiser et partager leurs recettes avec les membres de leur équipe de cuisine. Un des principaux défis que nous avons rencontrés était de permettre un accès à des données réelles. Notre solution initiale était de permettre de saisir manuellement les ingrédients, les prix et d'autres informations pertinentes telles que la provenance, le nom du fournisseur et la date de la dernière mise à jour du prix.
Bien que cette approche soit utilisée pour la version gratuite, la version payante devrait offrir quelque chose de plus robuste, qui permettrait réellement de faire gagner du temps lors de la gestion et d'éliminer le fardeau de la saisie des données.
Pour y parvenir, nous devions extraire les ingrédients, les prix et d'autres informations pertinentes à partir des reçus des fournisseurs de nos clients. Cela m'a conduit au développement d'une simple API d'extraction de reçus utilisant Python, GPT-4o d'OpenAI et Paddle OCR.
L'étincelle d'inspiration
Mon collègue Rémi et moi étions en train de travailler sur un projet scolaire complètement différent au moment où OpenAI venait d'ajouter le modèle GPT-Vision à leur API. À la fin de la journée, nous avons décidé d'expérimenter avec le modèle et de voir s'il serait possible de le solliciter correctement pour extraire des ingrédients depuis des PDF et des images d'anciens reçus que j'avais sous la main.
Après environ 25 minutes, nous étions en mesure de voir qu'il serait théoriquement possible d'utiliser GPT pour formater les informations à condition de pouvoir lui fournir des données de bonne qualité. Cependant, GPT Vision n'arrivait pas à interpréter les reçus rapidement, surtout ceux qui n'étaient pas en anglais.
Cela m'a cependant donné l'idée d'explorer d'autres modèles OCR qui pourraient être mieux adaptés à la tâche et de fournir le texte extrait à GPT-4o pour formater les données.
Trouver le meilleur modèle OCR pour notre scénario d'utilisation
Tesseract OCR
Je devais d'abord trouver un modèle OCR capable d'extraire le texte des reçus. J'ai commencé par faire des recherches sur les modèles OCR open-source et j'ai trouvé Tesseract. Celui-ci semblait être un bon choix en fonction de ce qui était requis pour notre application. Cette librairie utilise TypeScript, qui est le langage que nous utilisons pour le projet. Il était donc possible de l'exécuter sur le même serveur que notre application.
Sur papier, Tesseract était un modèle intéressant, mais il présentait trois problèmes flagrants :
son exactitude en français était sous-optimale;
l'extraction de texte à partir de reçus manuscrits était presque impossible;
il ne pouvait pas traiter les PDF directement.
Cela signifiait que si je voulais utiliser Tesseract, je devais convertir les reçus PDF en images, puis extraire le texte de celles-ci. Cette tâche est rapidement devenue beaucoup plus compliquée que je ne le pensais, surtout en TypeScript.
En cherchant un moyen de convertir mes PDF, tous les signes m'ont dirigé vers Python. Python avait des moyens plus simples de faire les conversions, mais il offrait également une variété d'autres options pour l'OCR. Spontanément, j'ai décidé que j'allais donc réécrire l'API en Python.
PaddleOCR
Après quelques heures de recherches supplémentaires, je suis tombé sur PaddleOCR, un outil de reconnaissance optique de caractères (OCR) open-source développé par PaddlePaddle, une plateforme de deep learning créée par Baidu. PaddleOCR est conçu pour fournir une solution complète de détection et de reconnaissance de texte dans les images. Il prend en charge une multitude de langues et est reconnu pour son exactitude et son efficacité.
Les modèles de base sont très puissants et il pouvait traiter à la fois les images et les PDF directement. Cela m'a suffi pour l'essayer.
En quelques heures à peine, j'ai réécrit le code en Python et j'avais un prototype fonctionnel en main. Il ne me restait plus qu'à construire l'API en utilisant Flask et à fournir la sortie textuelle à GPT-4o pour formater les données et les retourner à mon application Next.js.
Construire l'API
Pour utiliser l'extracteur de reçus, j'ai dû écrire une simple API en utilisant Flask. Lors de l'appel de l'API, l'application Next.js doit passer le fichier en entrée ainsi que le nom du fournisseur. L'API devrait ensuite retourner les données extraites sous le format JSON.
J'ai choisi Flask car il était simple à mettre en place, de plus je l'avais utilisé pour un bot
qui automatise les inscriptions à des classes de CrossFit. (Peut-être je vais écrire un article sur ça dans le futur).
Écrire l'application Flask
À partir du prototype initial, j'ai construit une simple application Flask pour gérer les demandes et l'extraction.
Ajouter une clé API et CORS pour plus de sécurité
Afin de protéger cette API contre les abus, j'ai ajouté une clé d'API et activé les CORS. De cette manière, seules les applications autorisées pourraient y faire des demandes.
Les clés API sont stockées dans une base de données et ne sont accessibles que par l'API elle-même. Si aucune clé valide n'est fournie, la demande sera rejetée.
Nous modifions l'en-tête car l'API va maintenant recevoir une string
qui sera utilisée pour modifier l'invite de base. Il est donc maintenant possible d'ajouter des notes sur mesure, permettant ainsi de travailler avec n'importe quel fournisseur.
Connecter l'API à OpenAI
Pour utiliser l'API, nous devions la connecter au modèle GPT-4o
d'OpenAI. Il s’agit d’un LLM puissant qui peut générer du texte en fonction d'une invite. Nous avons utilisé la bibliothèque Python openai
pour nous connecter a leur API et envoyer la requête.
Envoyer le reçu à GPT-4o
Une fois l'API connectée à OpenAI, nous pouvons envoyer le reçu au modèle et obtenir les ingrédients et les prix extraits. Nous avons utilisé la fonction extract_receipts
pour envoyer le contenu texte des reçus à GPT-4o et ensuite retourner les données extraites dans un format JSON.
Cette petite application fonctionnait étonnamment bien, elle renvoyait les données dans dans le même format sans jamais vraiment faire d'erreur. Il ne restait plus qu'à la déployer sur mon serveur personnel et à faire les appels depuis Bon Service.
Déploiement
Faire fonctionner PaddleOCR sur autre chose qu'une machine Linux était si compliqué que j'ai décidé d'utiliser un conteneur Docker. J'ai créé un Dockerfile qui construit un conteneur Python et installe les dépendances requises.
L'image python:3.10-slim
est un bon point de départ pour le conteneur, mais j'ai dû installer quelques dépendances supplémentaires pour le faire fonctionner.
Jusqu'à présent, chaque fois qu'une requête était envoyée à l'API, PaddleOCR téléchargeait les modèles. Pour éviter cela, je les ajoute maintenant lors de la construction du conteneur. De cette manière, les modèles n'auraient pas besoin d'être téléchargés à chaque fois qu'une requête était faite.
Dans notre application Flask, nous devons simplement ajouter les lignes de code qui pointent vers les modèles de détection et de reconnaissance.
Puisque notre API est déployée avec Docker il serait donc facile d'utiliser un serveur web comme NGINX
afin de créer un load balancer qui distribuerait les requêtes entre plusieurs conteneurs différents. Cela permettrait potentiellement de traiter les requêtes plus rapidement.
Cette application sera maintenant déployée sur un droplet DigitalOcean, mais vous pourriez aussi choisir votre propre VPS.
Conclusion
En conclusion, cette API est un outil puissant qui peut être utilisé pour extraire les ingrédients et les prix des reçus. Sa capacité à gérer différentes langues et différents formats de reçus en fait un atout précieux pour notre application.
J'espère que vous trouvez cet article utile et informatif. Si vous avez des questions ou des commentaires, n'hésitez pas à me contacter à hello@juliencm.dev. Je suis toujours heureux d'avoir de vos nouvelles !
Merci d'avoir lu mon blog !
Peace nerds,
Julien