dimesum

Forsiden / Blogg / Teknikk

Teknikk

Vi slettet utgiftsparseren vår

· 8 min lesing ·

Dimesum slettet sin regelbaserte utgiftsparser etter at fire vanlige setninger ga pengefeil, og erstattet den med ett trinn som returnerer ett entydig tall eller ingenting.

Vi slettet en parser vi nettopp var ferdige med å skrive. Dimesums første forsøk på utgiftsføring med naturlig språk var en regelmotor, og fire vanlige setninger pensjonerte den: dinner 12-08 400 ble lest som 12 kr, ravioli 600 la et medlem ved navn Ravindra inn i delingen, kirana 500 belastet et medlem ved navn Kiran, og refund -483.50 ble en kostnad på 483,50 kr.

Å forstå en setning er modellens jobb og ingen andres. Det som erstattet regelmotoren er ett trinn kalt amount_only: ett enkelt tall, som bare returneres når teksten inneholder nøyaktig én entydig pengekandidat, og aldri en påstand om personer. To kandidater betyr ingen sum. Én regel erstattet en voksende haug med vakter.

Fire setninger gjorde slutt på regelparseren

Den slettede parseren dekket hele entitetssettet som AI-designdokumentet vårt spesifiserer: matching av medlemmer og kallenavn, et eksklusjonsleksikon, utleding av betaler, et kategorileksikon, og finjusterte konfidenser per felt. Hver av de fire feilene under hadde en åpenbar løsning. Hver løsning var en ny vakt med sitt eget blindpunkt.

De fire feilene som pensjonerte Dimesums regelparser, registrert 2026-08-20 i docs/tech/10-ai-design.md.
Det brukeren skrevDet parseren gjordeHvorfor det skjedde
dinner 12-08 400Leste beløpet som 12 krEn leser ser en dato og en sum. Et regex ser tre tall og tar det første.
ravioli 600La et medlem ved navn Ravindra inn i delingenUskarp kallenavn-matching scoret en rett mot en person.
kirana 500Belastet et medlem ved navn KiranDen samme matcheren, denne gangen med et butikknavn.
refund -483.50Bokførte en kostnad på 483,50 krSifrene overlevde og fortegnet gjorde det ikke, så pengene pekte motsatt vei.

To av de fire er én feil i forskjellige klær. Uskarp matching kan ikke skille en rett fra en person eller en butikk fra en person, for på tegnnivå ligner ravioli og Ravindra faktisk på hverandre. Krev en lengre prefiks-match, og du ødelegger ravi, som er tilfellet matcheren finnes for.

Hver vakt du legger til, får frem to nye formuleringer

En regelparser feiler på én bestemt måte: den svarer selvsikkert og feil. Et tomt felt koster brukeren ett trykk. En feil deling koster tillit til hovedboken, og i en app for deling av utgifter er hovedboken produktet. De fire radene over er ikke nestenbommer, de er pengefeil.

Tredemøllen er det egentlige argumentet, ikke noen enkelt defekt. Legg til en dato-vakt, og ordrenummer-tilfellet dukker opp. Legg til en ordrenummer-vakt, og rene tall dukker opp, så antall, så bordnumre. Vaktlisten vokser og konvergerer aldri, for naturlig språk har ikke noe endelig sett med formuleringer å telle opp.

Før og etter: en voksende stabel med vakter, erstattet av én avvisningsregel FØR: REGELPARSER (SLETTET) dinner 12-08 400 beløps-regex dato-vakt ordrenummer-vakt kallenavn-matcher eksklusjonsleksikon leser 12 kr, og bokfører det Hver løsning fikk frem to nye formuleringer. ETTER: amount_only dinner 12-08 400 nøyaktig én entydig pengekandidat? nei, tre tall beløp forblir tomt ja, ett tall returnert i øre Trinnet sier ingenting om personer, så det kan ikke plassere penger på feil person.
Den slettede parseren svarte på hver setning. Trinnet som erstattet den svarer med et tall eller ingenting.
Beslutningen, datert

Inntasting med naturlig språk er en AI-funksjon, og ingenting i Dimesum-repoet mønstermatcher den. Beslutningen falt 2026-08-20, skrevet ned sammen med de fire feilene, så ingen bygger opp vaktene igjen ved et uhell.

Det som ble levert er ett tall og en avvisning

amount_only er det degraderte trinnet fra AI-designdokumentet vårt, implementert bokstavelig. Det trinnet lyder «det enkle skjemaet med forhåndsutfylling av beløp via regex på klientsiden», så nivået returnerer et beløp og ingenting annet: ingen beskrivelse, ingen kategori, ingen betalere, ingen deltakere, ingen eksklusjoner. Det koster ingenting og ringer ingen, og derfor kjører test og CI på det.

Tvetydighet er en avvisning, ikke en omkamp

Hele regelen bor i én funksjon, extract_amount_minor. Et tall kommer tilbake bare når teksten inneholder nøyaktig én kandidat til det, så order 90210 dinner 400 og flat 402 rent 15000 returnerer tomt i stedet for å kåre en vinner. Et tall merket med valuta regnes som entydig selv ved siden av rene tall, og derfor leser split 3 ways ₹1,200 fortsatt 1 200 kr.

Et negert tall er ikke noe beløp i det hele tatt, snarere enn sin absoluttverdi. -500, minus 200 og regnskapsførerens (500) kommer alle tilbake tomme, for feltet er en kostnad, og å beholde sifrene mens fortegnet droppes peker pengene motsatt vei av teksten. Parenteser teller bare når de lukker om selve tallet, så (500 each) forblir en parentes.

Hvordan amount_only velger mellom ett tall og intet svar samle hvert tall som kan være penger ingen funnet, eller noen av dem med negativt fortegn? nei nøyaktig ett tall merket med valuta, som 1 200 kr eller 500/-? nei ingen merker i det hele tatt, og nøyaktig ett rent tall? ja amount_minor: heltalls minorenheter, via ISO 4217-eksponenten ja nei intet beløp feltet vises tomt ja
Tre spørsmål, to av dem ender i et tomt felt. Å velge mellom to plausible beløp ga 12 kr-middagen.

Valutaen bestemmer regnestykket

Minorenheter er den eneste formen penger tar i Dimesum, så nivået konverterer med en ISO 4217-eksponenttabell i stedet for en fastkodet multiplikasjon med 100. Den japanske yenen har ingen underenhet i det hele tatt, og ×100 blåser en kvittering på ¥1 200 opp hundre ganger. Et tall finere enn valutaens minste enhet avvises i stedet for å rundes av, for å runde av et beløp noen har tastet inn er å finne opp ett.

Indiske tallord er en del av å skrive et tall, ikke en del av å forstå en setning. 1.2k, 2 lakh og 500/- løses alle opp, som 1,200. Alt over hovedbokens maksimumsbeløp kommer tilbake tomt, så et feiltastet tall tømmer ett felt i stedet for å flyte over et heltall nedstrøms.

Hva hvert parse-trinn har lov til å hevde, per 29. august 2026.
FeltRegelparser (slettet)amount_only (i drift)Modellnivå (koblet, uten nøkkel)
BeløpGjettet fra flere tallEtt entydig tall, ellers tomtLest i kontekst
Beskrivelse, kategoriLeksikon-matchAlltid nullHentet fra setningen
Deltakere, eksklusjonerUskarp navne-matchingAlltid tomtLøst til ekte medlems-id-er
BetalerUtledet fra formuleringAlltid tomtNavngitt, med et beløp som kan være null
Samlet konfidensFinjustert per feltFast på 0.3Per parse
Prompt rapportertIngen fantesNull, ingen prompt ble lestPrompt-id og versjon

Trinnet i drift passerer aldri den brukbare 0.6-porten

Parse-kontrakten vår forlater en parse under 0.6 samlet konfidens og setter brukeren tilbake til det enkle skjemaet. amount_only rapporterer 0.3 på hvert svar, og konstanten er strukturell snarere enn finjustert. Et ensomt beløp er ikke en parse, så trinnet ligger på halvparten av porten uansett hva det fant. Én konstant for hvert svar er det som holder den der: en score per tilfelle er en score noen til slutt dytter oppover.

Nivået rapporterer også ingen prompt overhodet. Både prompt_id og prompt_version kommer tilbake null, for trinnet leste ingen prompt. Å navngi en ville tilskrive hvert eval-resultat en prompt-versjon nivået aldri så, og eval-riggen er det eneste instrumentet som har lov til å forfremme et trinn til ett-trykks forslag, ved 95 % presisjon på beløp og deltakere samlet.

To ytterligere nivåer er deklarert, og ingen av dem er i drift. cheap-fast-nivået har en Groq-adapter for openai/gpt-oss-120b og ingen nøkkel; mid-nivået har ingen adapter. Å velge ett av dem feiler ved oppstart snarere enn ved brukerens første forespørsel, for en LLM fakturerer per kall, og en fakturerbar avhengighet må feile lukket.

Ingenting bokføres automatisk, så et tomt felt koster ett trykk

Et tomt felt koster så lite bare fordi ingen registrering i Dimesum kan skrive penger. En registrering lager et forslag, en person bekrefter det, og bekreftelsen er det som skaper utgiften. Brief-beslutning D5 fastsetter regelen, og .go-arch-lint.yml håndhever den: registreringskonteksten nektes enhver avhengighet av expense eller ledger, så en registrering kan ikke bokføre en journal selv ved en feil. CI feiler importen, noe vi sjekket ved å legge til en.

Registreringen beholder råteksten uansett hva parseren gjør, og navngir hvilke felter som er uløste. Klienten fremhever de tomme feltene i stedet for å vise et oppdiktet utkast, som er forskjellen mellom en parser som ikke sier noe og en som gjetter. Bekreft utleder sin utgifts-id fra forslags-id-en, så et dobbelttrykk spiller av på nytt i stedet for å belaste to ganger.

Regelen verdt å stjele

Tell vaktene dine, ikke feilene. En vaktliste som vokser hver uke forteller deg at jobben er forståelse, og forståelse hører til en modell. Neste steg for oss er å kjøre gullsettet i contracts/parse_expense/eval/ mot et ekte modellnivå, for ingenting her blir et ett-trykks forslag uten den dommen.

Vanlige spørsmål

Hvorfor slettet Dimesum den regelbaserte utgiftsparseren sin?

Dimesum slettet den fordi fire vanlige setninger ga pengefeil, og hver vakt som ble lagt til fikk frem to nye formuleringer. dinner 12-08 400 ble lest som 12 kr, ravioli 600 la til et medlem ved navn Ravindra, kirana 500 belastet et medlem ved navn Kiran, og refund -483.50 ble en kostnad på 483,50 kr. Å forstå en setning er modellens jobb.

Hva returnerer amount_only-nivået egentlig?

amount_only-nivået returnerer ett tall og ingenting annet. Det svarer med et beløp bare når teksten inneholder nøyaktig én entydig pengekandidat, og det navngir aldri en deltaker, en betaler, en beskrivelse eller en kategori. To kandidater betyr ikke noe beløp i det hele tatt. Alt annet i parse-kontrakten venter på et modellnivå.

Hvorfor rapporterer amount_only 0.3 i konfidens i stedet for en ekte score?

0.3 er strukturell, ikke finjustert. Parse-kontrakten vår forlater en parse under 0.6 og setter brukeren tilbake til det enkle skjemaet, og et ensomt beløp er ikke en parse, så trinnet ligger på halvparten av den porten uansett hva det fant. Én fast konstant for hvert svar hindrer at en score per tilfelle senere dyttes oppover.

Kan en registrering med naturlig språk skrive til hovedboken uten et menneske?

Nei. En registrering lager et forslag, og en person bekrefter det, i tråd med Brief-beslutning D5. Regelen håndheves i .go-arch-lint.yml, som nekter registreringskonteksten enhver avhengighet av expense eller ledger, så CI feiler importen hvis en registrering noen gang strekker seg etter en journal. Å bekrefte er det som skaper utgiften.

Hva skjer med utgiftsføring med naturlig språk når ingen modellnivåer er tilgjengelige?

Dimesum degraderer til amount_only og viser tomme felter. cheap-fast-nivået er koblet til Groqs openai/gpt-oss-120b og leveres uten nøkkel, og mid-nivået har ingen adapter, så å velge ett av dem feiler ved oppstart snarere enn ved brukerens første forespørsel. Registreringen beholder råteksten uansett.