2

Por que a IA do meu app de finanças não faz nenhuma conta

Estou construindo a Melvi, um app de controle financeiro que puxa contas e cartões pelo Open Finance e mostra o que ainda vai cair nos próximos meses.

Logo no começo eu fechei uma regra: todo número que aparece na tela sai do código, e a IA só explica. Queria contar o porquê e como ficou na prática, porque acho que muita gente que coloca LLM em produto passa pela mesma dúvida.

IA erra conta e não avisa

Pede pra um modelo somar vinte lançamentos e ele acerta quase sempre. O problema é esse "quase". Em app de finanças, um total errado em R$ 37 já basta pra pessoa desconfiar de tudo na primeira vez que bate com o extrato. E o modelo erra com a mesma confiança de quando acerta.

Então separei as coisas. A conta fica em funções puras, sem banco, sem rede e sem modelo: entram os lançamentos, sai o resultado, e os mesmos dados dão sempre o mesmo número. O modelo só entra depois, recebendo o resultado pronto e o mínimo de contexto que a pergunta pede. A resposta dele vem num formato estruturado e passa por um schema. Se vier quebrada, o app mostra uma resposta fixa e segue a vida, sem ficar tentando de novo.

Dinheiro em centavos, como inteiro

Todo valor fica em centavos, como inteiro. No TypeScript eu uso bigint:

0.1 + 0.2;   // 0.30000000000000004
10n + 20n;   // 30n

Com bigint, se alguém tentar somar centavos com um number comum o compilador reclama, e divisão nunca vira fração. Onde mais pega é na divisão: R$ 100,00 em três parcelas não dá três de R$ 33,33, porque some um centavo. A função de dividir joga o centavo que sobra nas primeiras parcelas:

export function split(amount: Money, parts: number): Money[] {
  const count = BigInt(parts);
  const base = amount.cents / count;
  const leftover = amount.cents % count;
  return Array.from({ length: parts }, (_, index) =>
    money(BigInt(index) < leftover ? base + 1n : base, amount.currency),
  );
}
// R$ 100,00 em 3 → 33,34 + 33,33 + 33,33

A soma das parcelas sempre fecha com o total, e tem teste garantindo isso.

O mês é o mês em que o dinheiro sai

Quando alguém pergunta "quanto sobra esse mês", o que importa é quando o dinheiro sai da conta, e não quando a compra foi feita. Compra no cartão cai no mês da fatura. Compra parcelada entra parcela por parcela, cada uma no seu mês, sem somar o total de novo. Pagar a fatura não conta de novo o que já estava nela. E transferência entre as suas próprias contas não é nem renda nem gasto.

É aqui que muita planilha se perde, e é o tipo de regra que um modelo aplica de um jeito numa resposta e de outro na seguinte. No código ela mora num lugar só e tem teste.

E a IA faz o quê, então?

Explica o número, responde coisas tipo "por que sobrou menos que no mês passado?" e sugere o que fazer em seguida. Quando a resposta tem número, ela mostra o período, de onde vieram os dados e como o valor foi montado, pra pessoa conseguir conferir. E as ferramentas que o modelo usa só leem ou sugerem: pra mudar qualquer coisa, a pessoa tem que confirmar.

Só leitura

Por último, nenhum código do app mexe em dinheiro. O Open Finance é usado só pra ler, e quem autoriza e encerra a conexão é a própria pessoa, no app do banco dela.

Deixei a regra completa do cálculo publicada no site, com os termos e os casos chatos. Se você trabalha com finanças pessoais ou usa LLM em produto, conta aí nos comentários como resolveu isso.

Carregando publicação patrocinada...
1

O split fecha bem pra parcela igual. Onde complica é no rateio proporcional, quando o valor não se divide em partes iguais e sim por percentual, tipo uma conta de R$ 100 dividida 50/30/20 entre três pessoas, ou um gasto rateado entre categorias. Aí jogar o centavo que sobra nas primeiras posições deixa de ser neutro: quem leva o centavo passa a depender da ordem da lista, e reordenar as partes muda o resultado.

O que resolve é o método do maior resto. Calcula a parte exata de cada um, trunca pra centavo e distribui os centavos que sobraram pra quem teve a maior fração cortada. Continua fechando com o total, e o resultado não depende da ordem. Se a Melvi um dia dividir conta entre pessoas ou por categoria com percentual, vale deixar essa função do lado da split, com o mesmo teste de soma.

1

No caminho da resposta fixa, depois que o schema falha e não há retry, o que continua na tela? Os valores que saíram das funções puras ficam, com o período e a fonte, ou a resposta fixa substitui essa trilha também?