Inspiration

I run a small tech business in Abidjan. Every purchase leaves either a thermal receipt that fades in three weeks or a Mobile Money SMS buried under a hundred others. So I never really knew two things: where my money went, and when I'd hit zero — usually around the 20th of the month. And when I needed credit, I had no way to prove I was solvent: in the WAEMU zone only ~25 % of adults are strictly banked, and financial literacy is among the lowest in the world.

Wariteban ("wari te ban" — money well managed never runs out) is the tool I built for my own Monday mornings. It was submitted in the Taskmaster track because the chore is real, multi-step, and mine.

What it does

  • Reads the proof of purchase. A photo of a crumpled receipt or a Wave / Orange Money / MTN screenshot → merchant, amount, date, payment channel, plain-language category. The receipt image is archived in Cloud Storage.
  • Keeps the books without asking you to be an accountant. You pick "Rent" or "Transport"; the SYSCOHADA account code is assigned in the background and only surfaces in the ledger export for your accountant.
  • Projects your cash. The Anti-Red Radar: D+7 / D+30 forecast, burn rate, days until red, and the single most effective action, with the facts it is based on.
  • Works at night, alone. Every day at 05:00 (Africa/Abidjan) Cloud Scheduler → Pub/Sub → Cloud Run runs a sweep of every account. The agent decides: a repayment due in 3 days, an unpaid invoice 88 days old, a tontine falling due, a red balance in 10 days → it writes tasks and pushes a notification. In the demo account this produces six decisions with a single command.
  • Answers and then acts. "Can I afford 200,000 XOF?" → a Google ADK agent calls nine deterministic tools on your real data, answers with figures, and proposes an action ("Set 75,500 XOF aside") that becomes a tracked task.
  • Proves you're solvent. An explainable Wari Score built only from documented entries — each point tied to a verifiable fact — exportable as an activity statement for a microfinance institution.
  • Offline-first PWA under 1 MB precache, built for a low-end Android on 2G/3G, with an outbox that syncs by itself when the network comes back, and per-account mirroring so your books follow you from phone to phone.

How we built it

One rule shaped every decision: the LLM never computes an amount. The model perceives (reads an image) and phrases (talks to the user); every number on screen comes out of a deterministic, tested Python function.

  • Gemini 3.5 Flash-Lite via the GenAI SDK with a Pydantic response_schema reads the image — one call, no free text parsing.
  • Google ADK (LlmAgent + InMemoryRunner) on Gemini 3.7 Flash runs the advisor: it plans which of nine Python tools to call and puts the result into words. The action it proposes is derived in Python from the tools it actually called.
  • A deterministic classifier assigns the accounting account; the model's suggestion is only a last-resort hint, never an override of a matching rule.
  • Cloud Run (europe-west1) hosts the FastAPI backend; Firestore holds per-user state; Cloud Storage the receipt vault; Secret Manager the Gemini key and the internal job token; Pub/Sub + Cloud Scheduler drive the nightly sweep (push subscription with OIDC — identity and audience verified); Firebase Auth protects every endpoint (Cloud Run is public, so the ID-token check on FastAPI is the real perimeter); FCM delivers the night's decisions; Firebase Hosting serves the PWA; Cloud Trace receives the ADK spans with prompt content masked.
  • The nightly job calls no model at all: it is the part of the system that must be cheap, idempotent (Pub/Sub delivers at least once — we dedupe on messageId) and exactly reproducible.
  • Per-user memory: a merchant you correct once is classified correctly forever, and the 25 merchants you visit most are injected into the vision prompt — the cheapest accuracy lever we found.
  • 139 offline tests cover the deterministic half; a separate live script exercises the real Gemini calls.

Challenges we ran into

  • Costs are driven by output tokens, not images. On Gemini 3.5 Flash, reasoning tokens (billed at output price) weighed 833 tokens per receipt — more than the visible answer. Measuring instead of guessing moved the reader to Flash-Lite (0.61 XOF and 1.9 s per receipt, same amount read on every test document) and the advisor to 3.7 Flash (1.99 XOF a question instead of 4.83, same tools, same figures). Removing the line-item list from the schema cut 44 % of the cost and made extraction more stable.
  • Firestore "phantom" parent documents. users/{uid} never exists as a document when you only write sub-collections, so stream() returned nobody and the nightly sweep silently scanned zero users in production. list_documents() fixed it; a test now pins the semantics.
  • The temptation to fake it. An early version returned a plausible receipt when the model was unreachable. We removed it: today a model failure is an explicit 503, the credit is refunded, and the receipt stays archived. In a financial tool, a fabricated number is worse than no number.
  • Multi-device state. The first sync pushed the phone's profile over the server's and wiped the account on the next device. Now the server is the source of truth: the phone pushes an outbox and pulls the account state at login.

Accomplishments that we're proud of

  • A background workflow that is demonstrably autonomous: one gcloud scheduler jobs run, and six concrete, correct decisions appear in Firestore and on the phone.
  • A clean LLM / deterministic boundary that judges can verify by reading the code, not by trusting the README.
  • "Fail loud, never invent": no fallback data anywhere on the financial path.
  • A consent-first corpus: anonymised corrections are only kept when the user opts in (off by default).
  • The founder uses it daily.

What we learned

  • Measure model cost per task with your own documents before choosing a model; the cheapest model won on accuracy and latency for reading.
  • In an agentic system the interesting engineering is in what you don't let the model do.
  • Idempotency and at-least-once delivery matter the day your agent starts sending notifications.
  • An offline-first PWA and a server-authoritative account are not in tension if the phone is treated as a mirror.

What's next

Account deletion and data export endpoints, signed receipt URLs via IAM signBlob, a per-user Pub/Sub fan-out for the sweep, and the accountant hand-off (monthly ledger + receipt archive) as a scheduled job.

Built With

  • cloud-pub-sub
  • cloud-scheduler
  • cloud-trace
  • fastapi
  • firebase-auth
  • firebase-cloud-messaging
  • firebase-hosting
  • firestore
  • gemini
  • gemini-3.5-flash-lite
  • gemini-3.7-flash
  • google-adk
  • google-cloud
  • google-cloud-run
  • google-genai
  • pwa
  • pydantic
  • python
  • react
  • secret-manager
  • tailwindcss
  • typescript
  • vite
  • workbox
Share this project:

Updates

Submission history