Skip to content
Index of work

AI · Full-stack · Product · 2025

Penny.

Local-first finance tracker that keeps the LLM out of the hot path.

Visit live project

Role

Full-stack Engineer · AI Integration

Team

2 engineers — with Nicholas Kaplun

Stack

FastAPI · Python · Gemini API · React · Vite · Pandas

Year

2025

01 / The problem

Why this needed building.

Most finance trackers treat the LLM as the spine — every transaction round-trips through an API. That's slow, fragile when offline, and expensive at scale. I wanted to see how far rule-based classification could go before the model had to step in.

02 / Approach

How I broke it down.

  1. 01

    Rule-based classifier first — merchant strings, amount heuristics, and a learned table over 15 categories. Handles the long tail of recurring merchants cheaply.

  2. 02

    LLM only as fallback for the unclassified residual. Routes to Gemini with a constrained-output prompt so the response is parseable without retries.

  3. 03

    Eval harness with a 428-transaction test set hand-labeled across all 15 categories. Every change to rules or prompt re-runs the eval.

  4. 04

    Local-first state — the API key is the only thing the user gives up; transactions never leave the device unless they hit the LLM fallback.

03 / Outcomes

What it ended up being good at.

  • Rule layer alone catches the vast majority of transactions; LLM fallback is reserved for genuinely ambiguous cases.

  • 428-transaction eval gates every change — accuracy is observable, not vibed.

  • Lesson encoded: LLMs are great rescue tools and bad spines. Build the cheap deterministic path first, route to the model only when it's the right tool.