Nous avons supprimé notre analyseur de dépenses
Notre analyseur de règles a tenu une semaine avant que quatre phrases ordinaires n'attribuent l'argent à la mauvaise personne et qu'une seule règle de refus ne remplace toute la pile de garde-fous.
Nous avons supprimé un analyseur que nous venions tout juste d'écrire. La première tentative de Dimesum pour analyser les dépenses en langage naturel reposait sur un moteur de règles, et quatre phrases ordinaires l'ont mis à la retraite : dinner 12-08 400 était lu comme 12 €, ravioli 600 ajoutait au partage un membre nommé Ravindra, kirana 500 débitait un membre nommé Kiran, et refund -483.50 devenait un débit de 483,50 €.
Comprendre une phrase, c'est le travail du modèle et de personne d'autre. Ce qui a remplacé le moteur de règles est un unique échelon nommé amount_only : un seul nombre, renvoyé uniquement lorsque le texte contient exactement un candidat monétaire sans ambiguïté, et jamais une affirmation sur des personnes. Deux candidats, aucun montant. Une seule règle a remplacé une pile de garde-fous qui ne cessait de grossir.
Quatre phrases ont mis fin à l'analyseur de règles
L'analyseur supprimé couvrait tout l'ensemble d'entités que spécifie notre document de conception IA : correspondance des membres et des surnoms, un lexique d'exclusion, l'inférence du payeur, un lexique de catégories, et des indices de confiance ajustés par champ. Chacune des quatre défaillances ci-dessous avait un correctif évident. Chaque correctif était un nouveau garde-fou avec son propre angle mort.
| Ce que l'utilisateur a saisi | Ce que l'analyseur a fait | Pourquoi cela s'est produit |
|---|---|---|
dinner 12-08 400 | A lu le montant comme 12 € | Un lecteur voit une date et un total. Une regex voit trois nombres et prend le premier. |
ravioli 600 | A ajouté au partage un membre nommé Ravindra | La correspondance approximative des surnoms a rapproché un plat d'une personne. |
kirana 500 | A débité un membre nommé Kiran | Le même comparateur, cette fois avec un nom de boutique. |
refund -483.50 | A validé un débit de 483,50 € | Les chiffres ont survécu, pas le signe, si bien que l'argent pointait dans le sens inverse. |
Deux des quatre ne sont qu'un seul et même bogue sous des habits différents. La correspondance approximative ne sait pas distinguer un plat d'une personne, ni une boutique d'une personne, car au niveau des caractères ravioli et Ravindra se ressemblent vraiment. Exigez une correspondance de préfixe plus longue et vous cassez ravi, qui est précisément le cas pour lequel le comparateur existe.
Chaque garde-fou ajouté fait surgir deux formulations de plus
Un analyseur de règles échoue d'une manière bien précise : il répond avec assurance et se trompe. Un champ vide coûte une pression à l'utilisateur. Un partage erroné coûte la confiance dans le registre, et dans une application de suivi des dépenses partagées le registre est le produit. Les quatre lignes ci-dessus ne sont pas des quasi-réussites, ce sont des bogues d'argent.
Le vrai argument, c'est le tapis roulant, pas un défaut isolé. Ajoutez un garde-fou pour les dates et le cas du numéro de commande arrive. Ajoutez un garde-fou pour les numéros de commande et les nombres nus arrivent, puis les quantités, puis les numéros de table. La liste des garde-fous grossit et ne converge jamais, car le langage naturel n'a pas d'ensemble fini de formulations à énumérer.
La saisie en langage naturel est une fonctionnalité IA, et rien dans le dépôt Dimesum ne s'en rapproche par motif. La décision a été actée le 2026-08-20, consignée avec les quatre défaillances, pour que personne ne reconstruise les garde-fous par accident.
Ce qui a été livré, c'est un nombre et un refus
amount_only est l'échelon dégradé de notre document de conception IA implémenté à la lettre. Cet échelon dit « le formulaire simple avec préremplissage du montant par regex côté client », de sorte que le palier renvoie un montant et rien d'autre : pas de description, pas de catégorie, pas de payeurs, pas de participants, pas d'exclusions. Il ne coûte rien et n'appelle personne, ce qui explique pourquoi les tests et la CI s'appuient dessus.
L'ambiguïté est un refus, pas un départage
Toute la règle tient dans une seule fonction, extract_amount_minor. Un chiffre n'est renvoyé que lorsque le texte en contient exactement un candidat, si bien que order 90210 dinner 400 et flat 402 rent 15000 renvoient un champ vide au lieu de désigner un gagnant. Un chiffre marqué d'une devise compte comme sans ambiguïté même à côté de nombres nus, et c'est pourquoi split 3 ways €1,200 se lit encore 1 200 €.
Un chiffre nié n'est pas du tout un montant, plutôt que sa valeur absolue. -500, minus 200 et le (500) du comptable reviennent tous vides, car le champ est un débit et garder les chiffres en abandonnant le signe fait pointer l'argent dans le sens contraire du texte. Les parenthèses ne comptent que lorsqu'elles se referment sur le chiffre lui-même, si bien que (500 each) reste une incise entre parenthèses.
La devise décide de l'arithmétique
Les unités mineures sont la seule représentation que prend l'argent dans Dimesum, si bien que le palier convertit avec une table d'exposants ISO 4217 plutôt qu'une multiplication par 100 codée en dur. Le yen japonais n'a aucune sous-unité, et ×100 gonfle un reçu de 1 200 ¥ de cent fois. Un chiffre plus fin que la plus petite unité de la devise est refusé plutôt qu'arrondi, car arrondir un montant que quelqu'un a saisi, c'est en inventer un.
Les mots de nombre indiens font partie de l'écriture d'un chiffre, pas de la compréhension d'une phrase. 1.2k, 2 lakh et 500/- se résolvent tous, comme 1,200. Tout ce qui dépasse le montant maximal du registre revient vide, si bien qu'un chiffre mal saisi vide un champ au lieu de faire déborder un entier en aval.
| Champ | Analyseur de règles (supprimé) | amount_only (actif) | Palier de modèle (câblé, sans clé) |
|---|---|---|---|
| Montant | Deviné à partir de plusieurs nombres | Un chiffre sans ambiguïté, sinon vide | Lu en contexte |
| Description, catégorie | Correspondance de lexique | Toujours nul | Extrait de la phrase |
| Participants, exclusions | Correspondance approximative des noms | Toujours vide | Résolus en identifiants de membres réels |
| Payeur | Inféré à partir de la formulation | Toujours vide | Nommé, avec un montant pouvant être nul |
| Confiance globale | Ajustée par champ | Fixée à 0.3 | Par analyse |
| Prompt rapporté | Aucun n'existait | Nul, aucun prompt n'a été lu | Identifiant et version du prompt |
L'échelon actif ne franchit jamais le seuil d'utilisabilité de 0.6
Notre contrat d'analyse abandonne une analyse en dessous de 0.6 de confiance globale et renvoie l'utilisateur vers le formulaire simple. amount_only rapporte 0.3 à chaque réponse, et la constante est structurelle plutôt qu'ajustée. Un montant seul n'est pas une analyse, donc l'échelon se tient à la moitié du seuil quoi qu'il trouve. Une seule constante pour chaque réponse est ce qui l'y maintient : un score au cas par cas est un score que quelqu'un finit par pousser vers le haut.
Le palier ne rapporte non plus aucun prompt. prompt_id et prompt_version reviennent tous deux nuls, car l'échelon n'a lu aucun prompt. En nommer un attribuerait chaque résultat d'évaluation à une version de prompt que le palier n'a jamais vue, et le banc d'évaluation est le seul instrument autorisé à promouvoir un échelon au rang de suggestion en une pression, à 95 % de précision sur le montant et les participants ensemble.
Deux autres paliers sont déclarés et aucun n'est actif. Le palier cheap-fast dispose d'un adaptateur Groq pour openai/gpt-oss-120b et d'aucune clé ; le palier intermédiaire n'a pas d'adaptateur. Sélectionner l'un ou l'autre échoue au démarrage plutôt qu'à la première requête d'un utilisateur, car un LLM se facture à l'appel et une dépendance facturable doit échouer en position fermée.
Rien ne se valide automatiquement, un vide coûte donc une pression
Un champ vide coûte si peu uniquement parce qu'aucune capture dans Dimesum ne peut écrire d'argent. Une capture crée une suggestion, une personne la confirme, et c'est la confirmation qui crée la dépense. La décision D5 du brief énonce la règle, et .go-arch-lint.yml l'applique : le contexte d'ingestion se voit refuser toute dépendance envers expense ou ledger, si bien qu'une capture ne peut pas valider une écriture au registre, même par erreur. La CI fait échouer l'import, ce que nous avons vérifié en en ajoutant un.
La capture conserve le texte brut quoi que fasse l'analyseur, et nomme les champs non résolus. Le client met en évidence ces vides plutôt que d'afficher un brouillon inventé, c'est là la différence entre un analyseur qui ne dit rien et un qui devine. La confirmation dérive son identifiant de dépense de l'identifiant de suggestion, si bien qu'une double pression rejoue au lieu de débiter deux fois.
La règle qui mérite d'être copiée
Comptez vos garde-fous, pas vos bogues. Une liste de garde-fous qui grossit chaque semaine vous dit que le travail est de la compréhension, et la compréhension appartient à un modèle. Notre prochaine étape est d'exécuter le jeu de référence dans contracts/parse_expense/eval/ contre un vrai palier de modèle, car rien ici ne devient une suggestion en une pression sans ce verdict.
Questions fréquentes
Pourquoi Dimesum a-t-il supprimé son analyseur de dépenses à base de règles ?
Dimesum l'a supprimé parce que quatre phrases ordinaires produisaient des bogues d'argent et chaque garde-fou ajouté faisait apparaître deux formulations de plus. dinner 12-08 400 était lu comme 12 €, ravioli 600 ajoutait un membre nommé Ravindra, kirana 500 débitait un membre nommé Kiran, et refund -483.50 devenait un débit de 483,50 €. Comprendre une phrase, c'est le travail du modèle.
Que renvoie réellement le palier amount_only ?
Le palier amount_only renvoie un nombre et rien d'autre. Il répond par un montant uniquement lorsque le texte contient exactement un candidat monétaire sans ambiguïté, et il ne nomme jamais un participant, un payeur, une description ni une catégorie. Deux candidats, aucun montant du tout. Tout le reste du contrat d'analyse attend un palier de modèle.
Pourquoi amount_only affiche-t-il une confiance de 0.3 au lieu d'un vrai score ?
Le 0.3 est structurel, pas ajusté. Notre contrat d'analyse abandonne une analyse en dessous de 0.6 et renvoie l'utilisateur vers le formulaire simple, et un montant seul n'est pas une analyse, donc l'échelon se tient à la moitié de ce seuil quoi qu'il trouve. Une seule constante fixe pour chaque réponse empêche qu'un score au cas par cas soit poussé vers le haut plus tard.
Une capture en langage naturel peut-elle écrire dans le registre sans intervention humaine ?
Non. Une capture crée une suggestion et une personne la confirme, selon la décision D5 du brief. La règle est appliquée dans .go-arch-lint.yml, qui refuse au contexte d'ingestion toute dépendance envers expense ou ledger, si bien que la CI fait échouer l'import si une capture cherche un jour à atteindre un journal. Confirmer, c'est ce qui crée la dépense.
Que devient l'analyse des dépenses en langage naturel quand aucun palier de modèle n'est disponible ?
Dimesum se dégrade en amount_only et affiche des champs vides. Le palier cheap-fast est câblé sur openai/gpt-oss-120b de Groq et est livré sans clé, et le palier intermédiaire n'a pas d'adaptateur, si bien que sélectionner l'un ou l'autre échoue au démarrage plutôt qu'à la première requête d'un utilisateur. La capture conserve le texte brut dans les deux cas.
Articles populaires
- Le grand livre append-only qui garde les soldes exacts9 min de lecture
- Modifier une dépense partagée doit redéfinir le partage9 min de lecture
- Six bugs de devises dans le partage de dépenses10 min de lecture
- Solder les comptes d'un groupe en moins de virements5 min de lecture
- Partager une addition quand un plat n'a pas été partagé10 min de lecture