Vision OCR
1. Quoi ? — Définition et contexte
Section intitulée « 1. Quoi ? — Définition et contexte »Vision OCR est un sous-workflow N8N appelé depuis le Telegram Orchestrator chaque fois qu’un utilisateur envoie une photo. Il classe l’image dans une des cinq catégories de documents reconnus, puis applique un schéma d’extraction spécialisé pour retourner des données structurées prêtes à être consommées par les workflows métier (Odoo, notes, factures).
Métadonnées
Section intitulée « Métadonnées »| Champ | Valeur |
|---|---|
| Workflow ID | 2ZDgU3TWbF4OeOKY |
| Type | Execute Workflow Trigger (passthrough) |
| Nodes | 14 |
| Appelé par | Binary Content Handler (QtkJDN8XAGlpcSPV) |
| Modèle | gemini-flash-yolo via cli-ollama |
| Issue | #176 |
Architecture visuelle
Section intitulée « Architecture visuelle »Les 6 types de documents
Section intitulée « Les 6 types de documents »| Type | Champs extraits | Usage typique |
|---|---|---|
business_card | name, function, company_name, email, phone, mobile, street, city, zip, country, website, comment | Création de contact Odoo |
invoice | vendor, invoice_number, date, items[], subtotal, tax, total, currency | Pièce comptable Odoo |
screenshot | visible_text, ui_elements[], error_messages[], context | Discussion conversationnelle, tickets |
handwritten_note | transcribed_text, confidence, language | Note dans Obsidian vault |
general_document | full_text, document_title, key_sections[] | Texte recherche-able |
not_document | (aucun) | Photo de scène, fallback IA Router |
2. Pourquoi ? — Enjeux et motivations
Section intitulée « 2. Pourquoi ? — Enjeux et motivations »Le problème sans Vision OCR
Section intitulée « Le problème sans Vision OCR »Avant ce sous-workflow, chaque photo envoyée au bot Telegram était traitée comme un message vide ou nécessitait une saisie manuelle. Photographier une carte de visite et taper ensuite tous les champs dans Odoo prenait plusieurs minutes par contact.
| Problème sans extraction | Conséquence |
|---|---|
| Saisie manuelle | Quelques minutes par carte, taux d’erreur élevé |
| Pas de structure | Impossible de filtrer/rechercher après coup |
| Pas de classification | Toutes les photos traitées identiquement |
| Pas de fallback | Une photo de paysage générait une erreur |
Pourquoi un seul prompt ne suffit pas
Section intitulée « Pourquoi un seul prompt ne suffit pas »Demander à un modèle vision “extrait les informations utiles” sur n’importe quelle image produit des résultats inégaux. Une carte de visite a des champs prévisibles, une facture a un format différent, un screenshot d’erreur n’a pas de “champs”. Le pipeline en deux phases résout cette tension :
| Approche | Avantage | Inconvénient |
|---|---|---|
| Prompt générique unique | Simple, 1 appel LLM | Champs incohérents, format JSON instable |
| Pipeline classify → extract | Schéma adapté à chaque type | 2 appels LLM, latence x2 |
L’overhead de latence (≈ 4-6 s) est acceptable parce que l’utilisateur attend déjà la transcription. La qualité de l’extraction et la stabilité du contrat de sortie justifient le coût.
Pourquoi Gemini Flash plutôt que GPT-4 Vision
Section intitulée « Pourquoi Gemini Flash plutôt que GPT-4 Vision »| Critère | Gemini Flash | GPT-4 Vision | Claude 3 Vision |
|---|---|---|---|
| Coût | Gratuit (cli-ollama) | $0.01 / image | $0.005 / image |
| Latence | 1-3 s | 3-8 s | 2-5 s |
| JSON structuré | Variable, parsing défensif requis | Stable | Stable |
| OCR latin | Excellent | Excellent | Excellent |
| OCR manuscrit | Bon | Excellent | Très bon |
| Hébergement | Self-hosted (cli-ollama) | API OpenAI | API Anthropic |
Le choix Gemini Flash s’aligne avec la stratégie multi-provider de cli-ollama : pas de coût à l’image, latence acceptable, JSON parseable avec un fallback regex défensif.
3. Comment ? — Mise en œuvre technique
Section intitulée « 3. Comment ? — Mise en œuvre technique »Le contrat de sortie
Section intitulée « Le contrat de sortie »Trois statuts possibles, tous renvoyés via le même format :
Succès :
{ "status": "success", "docType": "invoice", "extracted": { "vendor": "Amazon", "invoice_number": "INV-2026-001", "total": 23.98, "currency": "EUR" }, "text": "<b>Facture</b>\n\nVendeur: <b>Amazon</b>\n..."}Fallback (pas un document) :
{ "status": "fallback", "docType": "not_document"}Erreur (cli-ollama injoignable) :
{ "status": "error", "error": "cli-ollama request failed (HTTP 500)"}Le champ text contient une représentation HTML formatée prête à envoyer dans Telegram. Le champ extracted contient les données structurées qu’un workflow appelant peut consommer pour créer un enregistrement Odoo, par exemple.
Stratégie de fallback
Section intitulée « Stratégie de fallback »Le parsing JSON Gemini est défensif sur plusieurs niveaux :
| Cas | Comportement |
|---|---|
| JSON propre | Parse direct |
| JSON enveloppé dans backticks markdown | Strip ``` puis parse |
| JSON avec préambule textuel | Regex \{[\s\S]*\} puis parse |
| JSON malformé | Fallback sur general_document avec text: <raw response> |
| Type inconnu retourné | Force not_document |
| HTTP error cli-ollama | status: error propagé sans crash |
Cette défensive est nécessaire parce que Gemini Flash retourne parfois du texte additionnel (“Voici le résultat:”) avant le JSON, ou enveloppe le JSON dans des backticks. Sans le fallback, le workflow appelant recevait des erreurs de parsing au lieu d’un fallback gracieux.
Prompts par type
Section intitulée « Prompts par type »Le détecteur initial classifie l’image avec un prompt court :
Analyze this image and classify it as ONE of:- business_card- invoice- screenshot- handwritten_note- general_document- not_documentReturn ONLY JSON: {"type": "...", "confidence": 0.95}Puis le routage Switch envoie l’image vers un prompt d’extraction spécialisé. Exemple pour une carte de visite — les champs sont alignés sur le modèle res.partner d’Odoo pour permettre une création directe via XML-RPC :
Extract contact information from this business card.Return ONLY JSON (null if not found):{ "name": "Full name", "function": "Job title", "company_name": "Company", "email": "...", "phone": "...", "mobile": "...", "street": "...", "city": "...", "zip": "...", "country": "...", "website": "...", "comment": "LinkedIn or notes"}Intégration avec le Telegram Orchestrator
Section intitulée « Intégration avec le Telegram Orchestrator »Le Binary Content Handler appelle Vision OCR via Execute Workflow puis route le résultat selon le status :
| Status | Routing |
|---|---|
success + docType=business_card | Proposition d’enregistrer comme contact Odoo (boutons Telegram) |
success + docType=invoice | Proposition de stocker comme pièce comptable |
success + docType=screenshot/handwritten/general | Affichage direct + bouton “Discuter” (lance une conversation) |
fallback (not_document) | Bascule vers IA Router pour traitement conversationnel |
error | Notification d’erreur via Notification Hub |
Performance
Section intitulée « Performance »| Étape | Latence typique |
|---|---|
| Encodage base64 + transfert | 100-500 ms (selon taille photo) |
| Détection classification | 1-2 s |
| Extraction par schéma | 2-4 s |
| Format HTML + return | < 100 ms |
| Total bout-en-bout | 3-7 s |
La compression Telegram réduit déjà les photos à ~1 MB max, ce qui maintient la latence dans une fourchette acceptable même pour des cartes de visite haute résolution.
4. Et si ? — Perspectives et limites
Section intitulée « 4. Et si ? — Perspectives et limites »Limites actuelles
Section intitulée « Limites actuelles »| Limite | Impact | Mitigation |
|---|---|---|
| Latence x2 | 2 appels LLM successifs | Acceptable, l’utilisateur attend déjà |
| Pas de classification multi-doc | Une photo avec carte + ticket = 1 seul type | Demander 2 photos séparées |
| Pas de validation Odoo | Email malformé créerait un partner invalide | Validation côté workflow appelant |
| Pas de mémoire entre photos | Chaque photo est traitée isolément | Le système conversationnel garde le contexte une fois la photo extraite |
Scénarios d’évolution
Section intitulée « Scénarios d’évolution »Si la qualité d’extraction se dégrade :
- Passer à
gemini-pro-yolopour les types complexes (factures multi-lignes) - Ajouter une étape de validation/correction par un second prompt
- Comparer avec un modèle alternatif (Claude Vision via Anthropic API si quota disponible)
Si de nouveaux types de documents émergent :
- Ajouter un nouveau case dans le Switch + un prompt d’extraction dédié
- Garder le contrat
{status, docType, extracted, text}inchangé pour ne pas casser les workflows appelants - Exemples candidats :
id_card,passport,recipe,prescription
Si les volumes augmentent significativement :
- Mutualiser le cache de classification (mêmes images à 1-2 jours d’écart)
- Passer à un modèle Vision local quantisé (LLaVA, MiniCPM) hébergé sur le VPS pour zéro latence réseau
- Batching de plusieurs images en un seul appel API
Pages liées
Section intitulée « Pages liées »Infrastructure
Section intitulée « Infrastructure »- AI Stack — cli-ollama et le routing Gemini/Claude
- N8N en mode Queue — Backend qui exécute le sous-workflow
Workflows
Section intitulée « Workflows »- Telegram Orchestrator — Binary Content Handler appelant
- Système conversationnel — Suite logique après extraction d’un screenshot
- Voice Transcription — Pipeline analogue pour l’audio
Référence
Section intitulée « Référence »- Glossaire — OCR, Vision, Sub-workflow, cli-ollama