dimesum

Startsida / Blogg / Teknik

Teknik

Vi raderade vår utgiftstolk

· 8 min läsning ·

Dimesums regelbaserade utgiftstolk höll en vecka innan fyra vanliga meningar bokförde pengar på fel person och den ersattes av en enda vägransregel.

Vi raderade en tolk vi just hade skrivit klart. Dimesums första försök till tolkning av utgifter i naturligt språk var en regelmotor, och fyra vanliga meningar avvecklade den: dinner 12-08 400 lästes som 12 kr, ravioli 600 lade en medlem vid namn Ravindra i delningen, kirana 500 debiterade en medlem vid namn Kiran, och refund -483.50 blev en debitering på 483,50 kr.

Att förstå en mening är modellens uppgift och ingen annans. Det som ersatte regelmotorn är en enda nivå som kallas amount_only: ett enda tal, som returneras bara när texten innehåller exakt en otvetydig pengakandidat, och aldrig ett påstående om personer. Två kandidater betyder inget belopp. En regel ersatte en växande hög av skydd.

Fyra meningar avslutade regeltolken

Den raderade tolken täckte hela den uppsättning entiteter som vårt AI-designdokument anger: matchning av medlemmar och smeknamn, ett exkluderingslexikon, härledning av betalare, ett kategorilexikon och finjusterade säkerhetsvärden per fält. Var och en av de fyra bristerna nedan hade en självklar lösning. Varje lösning var ett nytt skydd med sin egen blinda fläck.

De fyra brister som avvecklade Dimesums regeltolk, noterade 2026-08-20 i docs/tech/10-ai-design.md.
Vad användaren skrevVad tolken gjordeVarför det hände
dinner 12-08 400Läste beloppet som 12 krEn läsare ser ett datum och en summa. En regex ser tre tal och tar det första.
ravioli 600La en medlem vid namn Ravindra i delningenLuddig smeknamnsmatchning poängsatte en maträtt mot en person.
kirana 500Debiterade en medlem vid namn KiranSamma matchning, den här gången med ett butiksnamn.
refund -483.50Bokförde en debitering på 483,50 krSiffrorna överlevde men inte tecknet, så pengarna pekade åt motsatt håll.

Två av de fyra är samma fel i olika kläder. Luddig matchning kan inte skilja en maträtt från en person eller en butik från en person, för på teckennivå ser ravioli och Ravindra verkligen lika ut. Kräv en längre prefixmatchning så bryter du ravi, som är just det fall matchningen finns till för.

Varje skydd du lägger till väcker två nya formuleringar

En regeltolk misslyckas på ett bestämt sätt: den svarar självsäkert och fel. Ett tomt fält kostar användaren en tryckning. En felaktig delning kostar förtroendet för huvudboken, och i en app för att dela utgifter är huvudboken produkten. De fyra raderna ovan är inte nära ögat, de är pengafel.

Löpbandet är det verkliga argumentet, inte någon enskild defekt. Lägg till ett datumskydd så dyker ordernummerfallet upp. Lägg till ett ordernummerskydd så kommer lägenhetsnummer, sedan antal, sedan bordsnummer. Skyddslistan växer och konvergerar aldrig, för naturligt språk har ingen ändlig uppsättning formuleringar att räkna upp.

Före och efter: en växande hög av skydd, ersatt av en enda vägransregel FÖRE: REGELTOLK (RADERAD) dinner 12-08 400 beloppsregex datumskydd ordernummerskydd smeknamnsmatchning exkluderingslexikon läser 12 kr och bokför det Varje lösning väckte två nya formuleringar. EFTER: amount_only dinner 12-08 400 exakt en otvetydig pengakandidat? nej, tre tal beloppet förblir tomt ja, en siffra returneras i ören Nivån säger ingenting om personer, så den kan inte lägga pengar på fel person.
Den raderade tolken svarade på varje mening. Nivån som ersatte den svarar med ett tal eller ingenting.
Beslutet, daterat

Inmatning i naturligt språk är en AI-funktion, och ingenting i Dimesums kodbas mönstermatchar den. Beslutet landade 2026-08-20, nedskrivet tillsammans med de fyra bristerna, så att ingen bygger om skydden av misstag.

Det som levererades är ett tal och en vägran

amount_only är den nedgraderade nivån i vårt AI-designdokument, bokstavligt implementerad. Den nivån lyder "det enkla formuläret med förifyllt beloppsregex på klientsidan", så nivån returnerar ett belopp och inget annat: ingen beskrivning, ingen kategori, inga betalare, inga deltagare, inga undantag. Den kostar ingenting och anropar ingen, vilket är varför test och CI körs på den.

Tvetydighet är en vägran, inte en utslagsröst

Hela regeln finns i en enda funktion, extract_amount_minor. En siffra kommer tillbaka bara när texten innehåller exakt en kandidat till den, så order 90210 dinner 400 och flat 402 rent 15000 returnerar tomt i stället för att välja en vinnare. En valutamärkt siffra räknas som otvetydig även bredvid nakna tal, vilket är varför dela på 3 1 200 kr ändå läses som 1 200 kr.

En negerad siffra är inget belopp alls i stället för sitt absolutvärde. -500, minus 200 och revisorns (500) kommer alla tillbaka tomma, för fältet är en debitering och att behålla siffrorna men släppa tecknet pekar pengarna åt motsatt håll mot texten. Parenteser räknas bara när de sluter kring själva siffran, så (500 each) förblir en parentes.

Hur amount_only väljer mellan en siffra och inget svar samla varje siffra som skulle kunna vara pengar ingen hittad, eller någon av dem med negativt tecken? nej exakt en valutamärkt siffra, som 1 200 kr eller 500 kr? nej inga märken alls, och exakt ett naket tal? ja amount_minor: heltal i minsta enhet, via ISO 4217-exponenten ja nej inget belopp fältet visas tomt ja
Tre frågor, varav två slutar i ett tomt fält. Att välja mellan två rimliga belopp gav 12 kr-middagen.

Valutan avgör aritmetiken

Minsta enheter är den enda representation pengar har i Dimesum, så nivån konverterar med en ISO 4217-exponenttabell i stället för en hårdkodad multiplikation med 100. Den japanska yenen har ingen underenhet alls, och ×100 blåser upp ett kvitto på ¥1,200 hundra gånger. En siffra finare än valutans minsta enhet vägras i stället för att avrundas, för att avrunda ett belopp någon skrivit är att hitta på ett.

Indiska talord är en del av att skriva en siffra, inte en del av att förstå en mening. 1.2k, 2 lakh och 500/- löses alla upp, precis som 1,200. Allt över huvudbokens högsta belopp kommer tillbaka tomt, så en felskriven siffra tömmer ett fält i stället för att spilla över ett heltal längre ner.

Vad varje tolkningsnivå får hävda, per den 29 augusti 2026.
FältRegeltolk (raderad)amount_only (i drift)Modellnivå (kopplad, utan nyckel)
BeloppGissat från flera talEn otvetydig siffra, annars tomtLäst i sammanhang
Beskrivning, kategoriLexikonmatchningAlltid nullUtvunnet ur meningen
Deltagare, undantagLuddig namnmatchningAlltid tomtUpplöst till riktiga medlems-id:n
BetalareHärlett ur formuleringAlltid tomtNamngiven, med ett nullbart belopp
Total säkerhetFinjusterad per fältFast på 0.3Per tolkning
Rapporterad promptIngen fannsNull, ingen prompt lästesPrompt-id och version

Nivån i drift klarar aldrig 0.6-gränsen för användbarhet

Vårt tolkningskontrakt överger en tolkning under 0.6 i total säkerhet och släpper användaren till det enkla formuläret. amount_only rapporterar 0.3 på varje svar, och konstanten är strukturell snarare än finjusterad. Ett ensamt belopp är ingen tolkning, så nivån ligger på halva gränsen oavsett vad den hittade. En konstant för varje svar är det som håller den där: ett värde per fall är ett värde som någon till slut petar uppåt.

Nivån rapporterar också ingen prompt alls. Både prompt_id och prompt_version kommer tillbaka null, för nivån läste ingen prompt. Att namnge en skulle tillskriva varje utvärderingsresultat en promptversion som nivån aldrig såg, och utvärderingsriggen är det enda instrument som får befordra en nivå till förslag med en tryckning, vid 95% precision på belopp och deltagare tillsammans.

Två ytterligare nivåer är deklarerade och ingen är i drift. Nivån cheap-fast har en Groq-adapter för openai/gpt-oss-120b och ingen nyckel; mellannivån har ingen adapter. Att välja endera misslyckas vid start snarare än vid användarens första förfrågan, för en LLM debiterar per anrop och ett debiterbart beroende måste fela stängt.

Ingenting bokförs automatiskt, så ett tomt fält kostar en tryckning

Ett tomt fält kostar så lite bara för att ingen registrering i Dimesum kan bokföra pengar. En registrering skapar ett förslag, en person bekräftar det, och bekräftelsen är det som skapar utgiften. Briefens beslut D5 anger regeln, och .go-arch-lint.yml upprätthåller den: registreringskontexten nekas varje beroende av expense eller ledger, så en registrering kan inte bokföra en journal ens av misstag. CI fäller importen, vilket vi kontrollerade genom att lägga till en.

Registreringen behåller råtexten oavsett vad tolken gör, och namnger vilka fält som är olösta. Klienten markerar de tomma fälten i stället för att visa ett påhittat utkast, vilket är skillnaden mellan en tolk som säger ingenting och en som gissar. Bekräftelsen härleder sitt utgifts-id från förslags-id:t, så en dubbel tryckning spelas upp igen i stället för att debitera två gånger.

Regeln värd att stjäla

Räkna dina skydd, inte dina buggar. En skyddslista som växer varje vecka säger dig att uppgiften är förståelse, och förståelse hör hemma hos en modell. Vårt nästa steg är att köra den gyllene uppsättningen i contracts/parse_expense/eval/ mot en riktig modellnivå, för ingenting här blir ett förslag med en tryckning utan den domen.

Vanliga frågor

Varför tog Dimesum bort sin regelbaserade utgiftstolk?

Dimesum tog bort den för att fyra vanliga meningar gav pengafel och varje tillagt skydd väckte två nya formuleringar. dinner 12-08 400 lästes som 12 kr, ravioli 600 lade till en medlem vid namn Ravindra, kirana 500 debiterade en medlem vid namn Kiran, och refund -483.50 blev en debitering på 483,50 kr. Att förstå en mening är modellens uppgift.

Vad returnerar amount_only-nivån egentligen?

amount_only-nivån returnerar ett tal och inget annat. Den svarar med ett belopp bara när texten innehåller exakt en otvetydig pengakandidat, och den namnger aldrig en deltagare, en betalare, en beskrivning eller en kategori. Två kandidater betyder inget belopp alls. Allt annat i tolkningskontraktet väntar på en modellnivå.

Varför rapporterar amount_only 0.3 i säkerhet i stället för ett riktigt värde?

0.3 är strukturellt, inte finjusterat. Vårt tolkningskontrakt överger en tolkning under 0.6 och släpper användaren till det enkla formuläret, och ett ensamt belopp är ingen tolkning, så nivån ligger på halva den gränsen oavsett vad den hittade. En fast konstant för varje svar hindrar att ett värde per fall petas uppåt senare.

Kan en registrering i naturligt språk bokföra i huvudboken utan en människa?

Nej. En registrering skapar ett förslag och en person bekräftar det, enligt briefens beslut D5. Regeln upprätthålls i .go-arch-lint.yml, som nekar registreringskontexten varje beroende av expense eller ledger, så CI fäller importen om en registrering någonsin sträcker sig efter en journal. Att bekräfta är det som skapar utgiften.

Vad händer med tolkningen av utgifter i naturligt språk när ingen modellnivå finns?

Dimesum nedgraderar till amount_only och visar tomma fält. Nivån cheap-fast är kopplad till Groqs openai/gpt-oss-120b och levereras utan nyckel, och mellannivån har ingen adapter, så att välja endera misslyckas vid start snarare än vid användarens första förfrågan. Registreringen behåller råtexten i vilket fall som helst.