DocuForge : intégrer l'IA dans mon workflow paperless-ngx
Comment je suis passé d'outils existants pour paperless-ngx, à un orchestrateur sur RTX 4090 beaucoup trop bricolé, puis à mon propre pipeline OCR et tagging plus léger.
J’utilise paperless-ngx pour gérer les documents à la maison. Le projet fait déjà très bien l’essentiel : ingérer des documents, faire de l’OCR, les stocker, et les rendre consultables.
Avec le temps, j’ai eu envie d’automatiser un peu plus le workflow :
- de meilleurs titres ;
- des tags automatiques ;
- les correspondants ;
- les types de documents ;
- une meilleure gestion des scans douteux et des photos prises au téléphone ;
- réduire le classement manuel à chaque nouveau document.
Donc forcément, je me suis dit : ça ressemble à un bon cas d’usage pour de l’IA. Et forcément, j’ai fini avec un petit bras robotisé qui appuie sur le bouton power de mon PC.
Commencer avec les projets existants
J’ai d’abord regardé paperless-gpt et paperless-ai. Ils validaient bien l’idée générale : connecter paperless-ngx à un LLM, analyser le texte OCR, puis mettre à jour automatiquement les métadonnées du document.
Mais ça ne correspondait pas tout à fait à ce que je voulais. Certains éléments me semblaient plus lourds que nécessaire, et quand j’ai vu paperless-ai consommer environ 1 Go de RAM au repos, j’ai commencé à envisager une solution plus légère.
Au départ, la solution évidente était d’utiliser ma machine IA principale : un PC avec une RTX 4090. L’inférence aurait été confortable, mais ça voulait aussi dire garder allumée une machine gourmande dans un bureau déjà assez chaud.
Première version : paperless-orchestrator
La première version de mon workflow était une petite API dans un container Docker. Je l’ai appelée paperless-orchestrator.
L’idée était assez simple :
- un document est ajouté à paperless-ngx, manuellement ou via ingestion email ;
- paperless-ngx envoie un webhook à l’orchestrateur ;
- l’orchestrateur met le document en file d’attente pour traitement IA ;
- quand assez de documents sont en attente, il démarre la machine avec la 4090 ;
- le PC exécute l’inférence ;
- les métadonnées du document sont mises à jour.
La file d’attente était importante, parce que je ne voulais pas démarrer le PC pour chaque document. J’avais donc fixé un seuil : ne réveiller la machine qu’à partir de cinq documents en attente.
Jusque-là, c’était raisonnable.
Puis le matériel s’en est mêlé.
Wake-on-LAN, mais non
Mon PC a une carte réseau 10 GbE. Très pratique pour déplacer des données rapidement, beaucoup moins pratique quand elle ne supporte pas le Wake-on-LAN.
L’orchestrateur pouvait donc mettre les documents en attente, mais il n’avait pas de façon propre de démarrer la machine d’inférence.
La solution élégante aurait été de changer la carte réseau ou de revoir l’architecture. À la place, j’ai utilisé un mini bras robotisé SwitchBot pour appuyer physiquement sur le bouton power.
Pour le rendre contrôlable depuis le lab localement, j’ai ajouté un ESP32 en proxy Bluetooth pour Home Assistant. L’orchestrateur pouvait ensuite appeler l’API Home Assistant, qui déclenchait le SwitchBot, qui appuyait physiquement sur le bouton du PC.
C’est le genre de décision qui paraît parfaitement logique à 23 h.
Au démarrage, un script PowerShell lancé via le Planificateur de tâches exposait quelques endpoints locaux :
- démarrer Ollama ;
- arrêter Ollama ;
- planifier un arrêt de la machine.
Trop compliqué ? Oui. Fonctionnel ? Aussi.
Découverte de paper-llama
En parcourant Reddit, je suis tombé sur paper-llama, une alternative Python beaucoup plus légère. Son objectif était proche du mien : connecter une instance privée de paperless-ngx à une instance privée d’Ollama, analyser le texte OCR, puis mettre à jour les métadonnées du document.
J’ai commencé les tests sur mon MacBook Pro M2 Pro, avec des modèles relativement petits comme Qwen 3.5 9B. C’est là que j’ai rencontré un problème : le budget de réflexion du modèle consommait tout le contexte disponible, ce qui le rendait inutilisable.
Ça m’a amené à faire ma première pull request sur un projet open source. Petite modification, mais étape sympa.
Le besoin n’était pas encore couvert
Après avoir testé davantage de documents, je me suis rendu compte que paper-llama ne répondait toujours pas complètement à mon besoin. Le point principal, c’était la spécialisation des modèles.
Le workflow utilisait le même type de modèle moyen pour plusieurs tâches :
- OCR optionnel ;
- tagging ;
- génération de titre ;
- extraction de métadonnées.
Ça peut fonctionner si on a assez de ressources. Mais je voulais quelque chose qui puisse tourner confortablement sur mon MacBook, sans revenir au montage à base de 4090 et de bras robotisé.
Des petits modèles généralistes comme Gemma 4 e4b donnaient de bons résultats pour le tagging et la génération de titres, mais n’étaient pas très convaincants pour l’OCR. Les documents difficiles, surtout les photos de tickets prises au téléphone, restaient problématiques.
Puis j’ai testé GLM-OCR.
C’est minuscule par rapport aux modèles généralistes que j’avais essayés, mais très spécialisé. Pour l’OCR, les résultats étaient excellents, même sur des entrées pénibles comme des tickets mal cadrés.
C’était la pièce qui manquait.
Entrée en scène de DocuForge
DocuForge est ma version du workflow, inspirée de paper-llama mais construite autour de mes contraintes. Le choix d’architecture principal est simple : l’OCR et le tagging ne devraient pas forcément utiliser le même modèle.
Ce sont deux tâches différentes :
- l’OCR a besoin d’un modèle spécialisé en extraction visuelle/texte ;
- le tagging et la génération de titres ont besoin d’un modèle de langage capable de raisonner sur le texte extrait.
Quand paperless-ngx reçoit un nouveau document, il envoie un webhook à DocuForge. À partir de là, le pipeline :
- récupère le document ;
- lance l’OCR avec GLM-OCR ;
- met à jour ou prépare le texte extrait ;
- met le document en file d’attente pour enrichissement metadata ;
- exécute le tagging et la génération de titre pendant une fenêtre horaire configurable.
L’étape OCR est suffisamment légère pour tourner pendant que j’utilise le Mac. Le tagging est différent : Qwen 3.5 9B peut utiliser presque 10 Go de RAM même avec un contexte raisonnable, et mon MacBook a 16 Go. C’est utilisable, mais pas quelque chose que j’ai envie de laisser se battre pour la mémoire pendant que je travaille.
DocuForge dispose donc d’une fenêtre de tagging configurable : une plage horaire pendant laquelle les traitements LLM plus lourds sont autorisés à tourner.
Ça donne un bon compromis :
- les documents peuvent être ingérés immédiatement ;
- l’OCR peut se faire rapidement ;
- le tagging plus lourd attend une période calme ;
- le MacBook reste utilisable dans la journée ;
- la machine avec la 4090 peut rester éteinte.
Si le Mac est indisponible, DocuForge saute simplement le traitement et réessaie le lendemain. Ce sont des documents personnels ; rien n’explose si un ticket est tagué demain plutôt qu’aujourd’hui. Traiter l’enrichissement IA comme du travail de fond a simplifié le design.
Pourquoi ne pas simplement utiliser la 4090 ?
La 4090 est excellente pour les expérimentations LLM locales. Elle est moins adaptée comme appliance always-on de traitement documentaire.
Les raisons principales :
- coût électrique : faire tourner un gros desktop juste pour classifier des documents, ce n’est pas idéal ;
- chaleur : le bureau est déjà assez chaud ;
- complexité opérationnelle : la solution SwitchBot était drôle, mais pas exactement une infrastructure élégante.
J’aime toujours l’absurdité du bras robotisé. Ça marchait, et ça compte. Mais à long terme, je préfère la solution ennuyeuse : des modèles plus petits, une meilleure séparation des tâches, et du traitement en arrière-plan.
Premier projet Python
DocuForge est aussi mon premier vrai projet Python.
Mon expérience professionnelle est surtout côté TypeScript, NestJS et PostgreSQL, donc Python n’est pas mon environnement par défaut. Ce projet est une bonne occasion de travailler avec un autre écosystème sur un problème concret.
Le projet n’est pas encore open source, mais c’est prévu dès qu’il sera assez stable. Je veux que la première version publique soit utile, documentée, et pas juste un tas de scripts qui ne fonctionnent que sur ma machine.
Ce que j’aime dans cette approche
L’architecture finale est beaucoup plus simple que celle du début. La première version impliquait :
- des webhooks paperless-ngx ;
- un orchestrateur custom ;
- Home Assistant ;
- un ESP32 en proxy Bluetooth ;
- un SwitchBot qui appuie sur un bouton physique ;
- une machine Windows avec des endpoints pilotés par PowerShell ;
- Ollama sur un desktop avec RTX 4090.
DocuForge remplace l’essentiel par :
- un webhook ;
- une file d’attente ;
- un modèle OCR léger ;
- un modèle de tagging séparé ;
- une fenêtre de traitement configurable.
C’est toujours un vrai système, mais beaucoup mieux adapté au problème. La première version a été utile parce qu’elle a prouvé le workflow ; une fois le workflow validé, l’architecture devait rétrécir.
La suite
Les prochaines étapes pour DocuForge :
- stabiliser le pipeline ;
- améliorer la gestion des erreurs et des retries ;
- affiner les prompts selon les types de documents ;
- rendre la configuration plus propre ;
- écrire une vraie documentation ;
- publier le projet en open source.
L’objectif n’est pas de construire une énorme plateforme IA. C’est de rendre paperless-ngx un peu plus intelligent, tout en gardant le système privé, léger et compréhensible. Idéalement, sans avoir besoin d’un bras robotisé pour appuyer sur un bouton.
Même si, pour être honnête, je l’ai gardé.