Skip to main content
Jonathan Andrei
Retour aux billets
Juil. 202612 min de lecture

LedgerPilot : même Qwen, même ledger, garde-fou désactivé poste 5 mauvaises écritures, activé en poste 0

La clôture mensuelle est le mauvais endroit pour donner un clavier à une IA qui hallucine. LedgerPilot est un agent de clôture où Qwen propose des écritures et un garde-fou déterministe est la seule voie vers une écriture. Le contrefactuel est toute la revendication : même planificateur qwen-flash, mêmes 39 tâches, même ledger Odoo en direct, garde-fou désactivé poste 5 mauvaises écritures (salaires payés depuis Comptes clients, coût des ventes vers Créances et Revenus, chacune équilibrée, chacune plausible), garde-fou activé poste 0. Le modèle ne s'est pas amélioré ; le ledger, oui. Tourne sur Alibaba Cloud ECS, pilote l'écriture via MCP.

QwenCloudHackathonQwenAlibaba CloudMCPOdooAI AgentsFintechPython
J'ai créé ce billet et le projet LedgerPilot pour la Global AI Hackathon Series avec Qwen Cloud (piste 4 : Autopilot Agent). #QwenCloudHackathon

La clôture mensuelle est l'un des flux de travail les plus sujets aux erreurs en finance, et c'est aussi le pire endroit pour donner un clavier à un LLM qui hallucine. Une seule mauvaise écriture postée dans un système de référence est un ajustement d'audit, pas une commodité. Pourtant le motif commercialisé pour un agent comptable est justement de lui donner l'accès en écriture au ledger et d'espérer que les prompts tiennent. LedgerPilot est la version où le modèle a le droit de raisonner et de proposer, et jamais celui d'écrire. Chaque écriture passe d'abord par un garde-fou déterministe, et le but du projet n'est pas que l'agent poste dans un vrai ERP (beaucoup le font). C'est que le côté écriture soit mesuré.

Architecture LedgerPilot : des entrées non structurées (relevés, factures) alimentent un planificateur Qwen sur Alibaba Cloud Model Studio, dont les propositions passent par un garde-fou déterministe (équilibre, comptes, période, séparation des tâches, limites, rapprochement à la source), qui émet un jeton HMAC lié au contenu avant une écriture idempotente dans Odoo. Les écritures rejetées ne touchent jamais le ledger ; les escaladées attendent un humain. Tout tourne sur une instance ECS d'Alibaba Cloud en ap-southeast-1.
La frontière de confiance, c'est le garde-fou. Le modèle est un proposeur ; le garde-fou est l'autorité. Le jeton HMAC lie l'approbation à l'écriture exacte, donc la falsification par le modèle lui-même est détectable.

Le contrefactuel (toute la revendication en un tableau)

Même planificateur qwen-flash, mêmes 39 tâches de clôture, même ledger Odoo en direct. Chaque proposition est postée deux fois, une avec le garde-fou désactivé, une avec activé. La seule variable, c'est le garde-fou.

  • Garde-fou DÉSACTIVÉ : 39 écritures postées. 5 mauvaises écritures réellement présentes dans le ledger. Exemples de runs commités : salaires payés depuis Comptes clients (Dr 6000 / Cr 1100) au lieu de Caisse ; écriture de coût des ventes imputée à Créances et Revenus (Dr 1100 / Cr 4000) au lieu de COGS et Fournisseurs. Chacune s'équilibre, utilise de vrais comptes postables, et passe un bilan de vérification.
  • Garde-fou ACTIVÉ : 34 écritures postées. 0 mauvaise écriture dans le ledger. Le compte garde-fou-désactivé bouge avec l'échantillonnage du modèle (5 à 7 selon les runs) ; le compte garde-fou-activé a été 0 à chaque run. Cette asymétrie, c'est toute la revendication, et c'est la partie qui ne dérive pas.
Vue du ledger Odoo montrant les 5 mauvaises écritures que le garde-fou a refusées, maintenant visiblement postées avec le garde-fou désactivé. Chaque ligne commence par NG-WRONG dans la colonne référence pour être facilement repérable, et chacune porte une narration expliquant ce que le modèle a imputé et ce que le document source exigeait vraiment.
Les 5 mauvaises écritures ne sont pas hypothétiques. Elles sont ouvertes dans le ledger que vous enverriez à votre auditeur. Même modèle, même ledger, garde-fou désactivé ou activé.
Zoom sur une des mauvaises écritures dans Odoo, montrant les lignes exactes de débit et crédit produites par le modèle (charge salaires débitée contre Comptes clients crédité) avec la narration nommant le document source et l'imputation correcte que le garde-fou aurait exigée
Équilibrée. Vrais comptes. Passerait un bilan de vérification. Et c'est faux. C'est la classe d'erreur pour laquelle le rapprochement est fait.

Le taux de mauvaises écritures, mesuré

Deux chiffres appuient le contrefactuel : la logique de décision du garde-fou est solide, et elle tient sur la vraie sortie du modèle. Sur un corpus synthétique de 204 cas (12 scénarios × 14 classes d'erreurs + contrôles propres) le garde-fou attrape 100 % des erreurs (156 bloquées + 12 escaladées) avec 0 % de faux rejets sur les contrôles propres. Sur la vraie sortie Qwen pour 39 tâches en langage naturel : qwen3.7-max est précis à 97,4 % et chaque erreur qu'il fait est attrapée ; qwen-flash est à 82,1 % et chaque erreur qu'il fait est attrapée. Le garde-fou a écrit 0 mauvaise écriture dans les deux cas (IC Wilson 95 % : ≤ 9,18 % pour max, ≤ 10,72 % pour flash).

Le plus intéressant : un modèle plus faible et moins cher (qwen-flash) fait 7 × plus d'erreurs que le flagship (qwen3.7-max), et le ledger reste propre. C'est tout l'argument du garde-fou : la justesse vient du garde-fou, pas du modèle qui a raison.

Interface web montrant une écriture propre qui a passé les 8 vérifications du garde-fou et été approuvée, avec chaque vérification listée en vert (équilibre, validité des comptes, période, séparation des tâches, seuil d'approbation, rapprochement au document source, jeton HMAC, idempotence)
Le chemin heureux. Huit vérifications, chacune avec une réponse auditable, et le jeton qui n'est émis que si les huit passent.
Interface web montrant une proposition refusée parce que le rapprochement au document source a détecté qu'elle débitait le mauvais compte, avec la vérification en échec en rouge et le compte correct listé en dessous
Équilibrée, vrais comptes, passe un bilan de vérification, et refusée. Le rapprochement attrape l'erreur sémantique que la vérification d'équilibre ne voit pas.
Interface web montrant une proposition refusée parce que débits et crédits ne concordent pas ; la ligne de vérification d'équilibre est en rouge et l'écart est affiché
L'ennuyeuse. La première vérification que tout agent comptable devrait faire, et la première que tout agent purement prompté saute.
Interface web montrant une proposition qui a passé toutes les vérifications sauf le seuil d'approbation parce qu'elle dépasse la limite de délégation d'autorité pour le préparateur, donc elle est escaladée à un approbateur humain au lieu d'être postée
Au-dessus du seuil, le garde-fou n'approuve pas en silence. L'humain dans la boucle est un résultat du garde-fou, pas une pensée après-coup.

Qwen pilote l'écriture via MCP, et ne peut toujours rien écrire de faux

Le garde-fou est exposé comme serveur MCP tournant sur la boîte ECS, attaché à Qwen sur Model Studio comme outil MCP SSE via la Responses API. Le modèle est l'appelant de l'outil d'écriture, pas l'autorité. validate_write est en lecture seule. execute_approved_write rejoue tout le garde-fou et vérifie un jeton HMAC lié au hash du contenu de l'écriture avant de toucher Odoo. Trois cas de bout en bout : une écriture correcte reçoit un vrai account.move posté dans l'Odoo en direct ; le modèle sommé de gonfler le montant avant d'écrire voit le hash changer, le jeton ne se valide plus et le serveur refuse ; une écriture avec mauvais compte est refusée par le rapprochement. La falsification par le modèle appelant est détectable, pas seulement découragée. Mettre le garde-fou derrière l'outil, c'est ce qui permet de donner au modèle l'accès en écriture sans lui donner la capacité d'écrire quelque chose de faux.

Interface Odoo 19 montrant le vrai account.move posté MISC/2026/06/0002 (services de juin, 1 280,00) écrit par l'agent depuis l'instance ECS d'Alibaba Cloud, avec les lignes débit et crédit et la référence qui la relie au document source
Vrai account.move posté MISC/2026/06/0002 écrit par l'agent depuis l'instance ECS. Pas simulé, pas une capture d'intention, une vraie ligne dans le grand livre.

Le backend tourne sur Alibaba Cloud ECS

L'agent est déployé sur une instance ECS d'Alibaba Cloud (i-t4n1i5p7bz4ypj122e6q, ecs.t6-c1m2.large, Ubuntu 24.04, ap-southeast-1), provisionnée de bout en bout par un script Python via les OpenAPI ECS et VPC. Tout dans ce billet (la suite de 79 tests, le stress-test hors ligne du garde-fou, la mesure live de Qwen, le contrefactuel contre le vrai Odoo, l'aller-retour MCP et l'ingestion de factures par vision) a été exécuté sur cette instance. Elle sert l'interface web du garde-fou sur le port 80 à ledgerpilot.jonathanandrei.com. Le transcript de preuve inclut les valeurs renvoyées par le service de métadonnées d'instance ECS, qui ne répond que sur une vraie instance ECS, donc le déploiement n'est pas seulement affirmé, il est falsifiable.

Console Alibaba Cloud ECS montrant l'instance LedgerPilot en état Running avec son id, son type (ecs.t6-c1m2.large), sa région (ap-southeast-1) et son IP publique
La console ECS. Provisionnée par un script qui appelle les OpenAPI ECS et VPC, détruite par le même script avec un flag --destroy.

Limites honnêtes

  • Le rapprochement est par document, pas par ligne. Chaque type de document porte une politique d'imputation : l'ensemble des comptes autorisés pour ce type de document. Le garde-fou enforce l'ensemble plus le montant. Un choix permis-mais-mauvais à l'intérieur de l'ensemble apparaîtrait comme un taux non nul. C'est pourquoi la métrique est falsifiable plutôt que nulle par construction ; dans les runs commités, chaque erreur du modèle est tombée hors de l'ensemble permis. En production, on escaladerait les choix vraiment ambigus à un humain plutôt que d'accepter n'importe quel compte permis.
  • La mesure live varie de quelques points entre runs parce que l'échantillonnage n'est pas parfaitement reproductible à température 0. Les transcripts bruts pour les deux modèles sont commités pour que les chiffres soient vérifiables plutôt qu'affirmés. Ce qui n'a pas bougé entre runs est l'essentiel : le garde-fou a attrapé chaque erreur et écrit zéro mauvaise écriture.
  • L'idempotence est sûre au réessai séquentiel, pas à la concurrence. Le client cherche le hash du contenu de l'écriture avant de créer le move, donc un re-run ne double-poste pas. Deux agents qui écrivent la même écriture au même instant pourraient encore se marcher dessus ; la production utiliserait une contrainte unique sur le hash dans l'ERP.
  • Les chiffres de mauvaises écritures des 204 cas et des 39 tâches sont mesurés à partir de tâches en langage naturel et de documents source structurés, pas d'images scannées OCR. Le chemin vision est réel et exercé (Qwen3-VL-Plus lit une facture PNG et pilote le garde-fou), mais une revendication production mesurerait le taux à partir de documents scannés de bout en bout.
  • Le rollback et un journal d'audit / file de revue persistants ne sont pas implémentés. Un move posté peut être annulé manuellement dans Odoo aujourd'hui ; les écritures rejetées sont refusées avec la vérification en échec comme raison, mais cet enregistrement n'est pas encore persisté dans une file.
Le côté écriture est la partie non résolue. Un agent comptable purement prompté qui poste dans un système de référence est pire que pas d'automatisation, parce qu'une mauvaise écriture d'apparence plausible est un ajustement d'audit. LedgerPilot répond par un garde-fou déterministe dont le taux de mauvaises écritures est mesuré avec un intervalle de confiance, dont la sûreté est montrée comme contrefactuel sur un vrai ledger, et dont l'autorité d'écriture se trouve derrière un jeton HMAC que même le modèle qui pilote ne peut pas forger. Même modèle, même ledger, garde-fou désactivé ou activé : 5 contre 0. C'est tout le propos.
Projet associé

LedgerPilot: Same Qwen, Same 39 Close Tasks, Same Live ERP. Gate Off: 5 Wrong Journal Entries Posted. Gate On: 0.

Voir le projet
Viva : 91 soumissions C notées au maximum sur 626 cachent un défaut que l'autograder n'a jamais testé
Article précédent

Viva : 91 soumissions C notées au maximum sur 626 cachent un défaut que l'autograder n'a jamais testé

Une suite de tests qui passe prouve qu'un programme a fonctionné sur les entrées que l'enseignant a pris la peine de vérifier. Elle ne prouve pas que l'étudiant peut expliquer ce que le programme fait. Viva comble cet écart : GPT-5.6 lit l'énoncé et propose une entrée que la suite n'a jamais essayée, Viva exécute le programme de l'étudiant et le corrigé dessus, et ne pose une question à l'étudiant que si les deux ne s'accordent pas. Sur 626 vraies soumissions C notées au maximum, 91 (14,5 %) cachent un défaut que l'autograder n'a jamais testé, et 83 d'entre elles (13,3 %) échouent sur une entrée aussi simple que `5 5 3`. Pas une prédiction, un rejeu : chaque constat porte une commande rejouable.

Juil. 202610 min de lecture
Clatterfall : une bille, un subreddit, une exécution partagée par jour, une machine que personne ne peut bâtir seul
Article suivant

Clatterfall : une bille, un subreddit, une exécution partagée par jour, une machine que personne ne peut bâtir seul

r/Clatterfall est une descente continue, bâtie par une foule, une pièce par personne par jour. Chaque matin, toute la machine engagée est ré-exécutée comme une simulation canonique unique que tout le monde regarde ensemble, et les pièces que la bille abandonne se dissolvent. Trois règles portent le poids : on ne peut bâtir que sur le chemin réel de la bille (la frontière), l'exécution quotidienne est simulée côté serveur une fois et rejouée identique au pixel partout, et les pièces que la bille cesse de toucher se dissolvent. Non-IA et fier de l'être : chaque pixel dessiné à partir de primitives Phaser Graphics à l'exécution.

Juil. 202611 min de lecture