Apagamos nosso analisador de despesas em linguagem natural
Nosso analisador por regras durou uma semana, e quatro frases comuns bastaram para aposentá-lo em favor de uma única regra de recusa.
Apagamos um analisador que tínhamos acabado de escrever. A primeira tentativa da Dimesum de interpretar despesas em linguagem natural foi um motor de regras, e quatro frases comuns o aposentaram: dinner 12-08 400 foi lido como R$12, ravioli 600 colocou um membro chamado Ravindra na divisão, kirana 500 cobrou um membro chamado Kiran, e refund -483.50 virou uma cobrança de R$483,50.
Entender uma frase é tarefa do modelo e de mais ninguém. O que substituiu o motor de regras é um único nível chamado amount_only: um único número, retornado apenas quando o texto contém exatamente um candidato a valor sem ambiguidade, e nunca uma afirmação sobre pessoas. Dois candidatos significam nenhum valor. Uma regra substituiu uma pilha crescente de proteções.
Quatro frases encerraram o analisador por regras
O analisador apagado cobria todo o conjunto de entidades que nosso documento de design de IA especifica: correspondência de membros e apelidos, um léxico de exclusão, inferência de pagador, um léxico de categorias, e confianças ajustadas por campo. Cada uma das quatro falhas abaixo tinha uma correção óbvia. Cada correção era uma nova proteção com seu próprio ponto cego.
| O que o usuário digitou | O que o analisador fez | Por que aconteceu |
|---|---|---|
dinner 12-08 400 | Leu o valor como R$12 | Uma pessoa vê uma data e um total. Um regex vê três números e pega o primeiro. |
ravioli 600 | Colocou um membro chamado Ravindra na divisão | A correspondência aproximada de apelidos pontuou um prato contra uma pessoa. |
kirana 500 | Cobrou um membro chamado Kiran | O mesmo comparador, desta vez com o nome de uma loja. |
refund -483.50 | Registrou uma cobrança de R$483,50 | Os dígitos sobreviveram e o sinal não, então o dinheiro apontou para o lado oposto. |
Duas das quatro são o mesmo defeito com roupas diferentes. A correspondência aproximada não distingue um prato de uma pessoa nem uma loja de uma pessoa, porque no nível dos caracteres ravioli e Ravindra realmente se parecem. Exija uma correspondência de prefixo mais longa e você quebra ravi, que é o caso para o qual o comparador existe.
Cada proteção que você adiciona revela duas novas formulações
Um analisador por regras falha de um jeito específico: responde com confiança e de forma errada. Um campo em branco custa ao usuário um toque. Uma divisão errada custa a confiança no livro-razão, e num aplicativo de controle de despesas compartilhadas o livro-razão é o produto. As quatro linhas acima não são quase-acertos, são erros de dinheiro.
A esteira sem fim é o verdadeiro argumento, não um defeito isolado. Adicione uma proteção de data e surge o caso do número de pedido. Adicione uma proteção de número de pedido e surgem números soltos, depois quantidades, depois números de mesa. A lista de proteções cresce e nunca converge, porque a linguagem natural não tem um conjunto finito de formulações a enumerar.
A entrada em linguagem natural é um recurso de IA, e nada no repositório da Dimesum se equipara a ela. A decisão foi tomada em 2026-08-20, registrada junto com as quatro falhas, para que ninguém reconstrua as proteções por acidente.
O que foi lançado é um número e uma recusa
amount_only é o nível degradado do nosso documento de design de IA implementado ao pé da letra. Esse nível diz "o formulário simples com pré-preenchimento de valor por regex no cliente", então o nível retorna um valor e nada mais: sem descrição, sem categoria, sem pagadores, sem participantes, sem exclusões. Não custa nada e não chama ninguém, e por isso os testes e o CI rodam sobre ele.
Ambiguidade é uma recusa, não um critério de desempate
A regra inteira vive em uma função, extract_amount_minor. Um número volta apenas quando o texto contém exatamente um candidato para ele, então order 90210 dinner 400 e flat 402 rent 15000 retornam em branco em vez de escolher um vencedor. Um número com marca de moeda conta como sem ambiguidade mesmo ao lado de números soltos, e por isso split 3 ways R$1.200 ainda lê R$1.200.
Um número negado não é valor nenhum, em vez de seu valor absoluto. -500, minus 200 e o (500) do contador voltam todos vazios, porque o campo é uma cobrança e manter os dígitos descartando o sinal aponta o dinheiro para o lado oposto ao do texto. Os parênteses contam apenas quando se fecham sobre o próprio número, então (500 each) continua sendo um aparte entre parênteses.
A moeda decide a aritmética
As unidades menores são a única representação que o dinheiro assume na Dimesum, então o nível converte com uma tabela de expoentes ISO 4217 em vez de uma multiplicação fixa por 100. O iene japonês não tem sub-unidade nenhuma, e ×100 infla um recibo de ¥1,200 em cem vezes. Um número mais fino do que a menor unidade da moeda é recusado em vez de arredondado, porque arredondar um valor que alguém digitou é inventar um.
As palavras numéricas indianas fazem parte de escrever um número, não de entender uma frase. 1.2k, 2 lakh e 500/- todas se resolvem, como 1,200. Qualquer coisa acima do valor máximo do livro-razão volta em branco, então um número digitado errado deixa um campo em branco em vez de estourar um inteiro adiante.
| Campo | Analisador por regras (apagado) | amount_only (ativo) | Nível de modelo (conectado, sem chave) |
|---|---|---|---|
| Valor | Adivinhado a partir de vários números | Um número sem ambiguidade, senão em branco | Lido no contexto |
| Descrição, categoria | Correspondência por léxico | Sempre nulo | Extraído da frase |
| Participantes, exclusões | Correspondência aproximada de nomes | Sempre vazio | Resolvidos para ids reais de membros |
| Pagador | Inferido pela formulação | Sempre vazio | Nomeado, com um valor que pode ser nulo |
| Confiança geral | Ajustada por campo | Fixa em 0.3 | Por análise |
| Prompt reportado | Nenhum existia | Nulo, nenhum prompt foi lido | Id e versão do prompt |
O nível ativo nunca passa do limite útil de 0.6
Nosso contrato de análise abandona uma análise abaixo de 0.6 de confiança geral e leva o usuário ao formulário simples. amount_only reporta 0.3 em toda resposta, e a constante é estrutural, não ajustada. Um valor sozinho não é uma análise, então o nível fica na metade do limite, seja lá o que tiver encontrado. Uma constante para toda resposta é o que o mantém ali: uma pontuação caso a caso é uma pontuação que alguém acaba empurrando para cima.
O nível também não reporta prompt nenhum. Tanto prompt_id quanto prompt_version voltam nulos, porque o nível não leu nenhum prompt. Nomear um atribuiria todo resultado de avaliação a uma versão de prompt que o nível nunca viu, e o arcabouço de avaliação é o único instrumento autorizado a promover um nível a sugestão de um toque, com 95% de precisão em valor e participantes juntos.
Dois outros níveis estão declarados e nenhum está ativo. O nível cheap-fast tem um adaptador Groq para openai/gpt-oss-120b e nenhuma chave; o nível intermediário não tem adaptador. Selecionar qualquer um deles falha na inicialização em vez de no primeiro pedido do usuário, porque um LLM cobra por chamada e uma dependência cobrável precisa falhar de forma fechada.
Nada é registrado automaticamente, então um branco custa um toque
Um campo em branco custa tão pouco só porque nenhuma captura na Dimesum pode escrever dinheiro. Uma captura cria uma sugestão, uma pessoa a confirma, e a confirmação é o que cria a despesa. A decisão D5 do Brief define a regra, e o .go-arch-lint.yml a aplica: o contexto de ingestão fica proibido de qualquer dependência de despesa ou livro-razão, então uma captura não pode registrar um lançamento nem por engano. O CI reprova a importação, o que verificamos adicionando uma.
A captura mantém o texto bruto faça o que fizer o analisador, e indica quais campos ficaram sem resolver. O cliente destaca esses brancos em vez de mostrar um rascunho inventado, que é a diferença entre um analisador que não diz nada e um que adivinha. A confirmação deriva seu id de despesa do id da sugestão, então um toque duplo se repete em vez de cobrar duas vezes.
A regra que vale copiar
Conte suas proteções, não seus erros. Uma lista de proteções que cresce toda semana está lhe dizendo que o trabalho é compreensão, e compreensão pertence a um modelo. Nosso próximo passo é rodar o conjunto de referência em contracts/parse_expense/eval/ contra um nível de modelo real, porque nada aqui vira uma sugestão de um toque sem esse veredito.
Perguntas frequentes
Por que a Dimesum apagou o analisador de despesas por regras?
A Dimesum o apagou porque quatro frases comuns geraram erros de dinheiro e cada proteção adicionada revelava mais duas formulações. dinner 12-08 400 foi lido como R$12, ravioli 600 adicionou um membro chamado Ravindra, kirana 500 cobrou um membro chamado Kiran, e refund -483.50 virou uma cobrança de R$483,50. Entender uma frase é tarefa do modelo.
O que o nível amount_only realmente retorna?
O nível amount_only retorna um número e nada mais. Ele responde com um valor apenas quando o texto contém exatamente um candidato a dinheiro sem ambiguidade, e nunca nomeia um participante, um pagador, uma descrição ou uma categoria. Dois candidatos significam nenhum valor. Todo o resto do contrato de análise espera por um nível de modelo.
Por que o amount_only informa confiança 0.3 em vez de uma pontuação real?
O 0.3 é estrutural, não ajustado. Nosso contrato de análise abandona uma análise abaixo de 0.6 e leva o usuário ao formulário simples, e um valor sozinho não é uma análise, então o nível fica na metade desse limite, seja lá o que tiver encontrado. Uma constante fixa para toda resposta impede que uma pontuação caso a caso seja empurrada para cima depois.
Uma captura em linguagem natural pode escrever no livro-razão sem um humano?
Não. Uma captura cria uma sugestão e uma pessoa a confirma, conforme a decisão D5 do Brief. A regra é aplicada no .go-arch-lint.yml, que nega ao contexto de ingestão qualquer dependência de despesa ou livro-razão, então o CI reprova a importação se uma captura tentar alcançar um lançamento. Confirmar é o que cria a despesa.
O que acontece com a análise de despesas em linguagem natural quando nenhum nível de modelo está disponível?
A Dimesum degrada para amount_only e mostra campos em branco. O nível cheap-fast está conectado ao openai/gpt-oss-120b da Groq e é lançado sem chave, e o nível intermediário não tem adaptador, então selecionar qualquer um falha na inicialização em vez de no primeiro pedido do usuário. A captura mantém o texto bruto de qualquer forma.
Posts populares
- O livro-razão append-only que mantém saldos exatos9 min de leitura
- Por que editar uma despesa deve refazer a divisão9 min de leitura
- Seis bugs de dinheiro em despesas multimoeda9 min de leitura
- Acerto de contas: menos transferências no grupo5 min de leitura
- Como dividir uma conta detalhada de restaurante10 min de leitura