Paydar:用手提電腦讀取發票,預測哪些客戶會遲付款
Paydar: my laptop reads my invoices and tells me which clients will pay late
Paydar 是一款面向自由工作者的現金流預測工具,以本機運行的 Gemma 4 E2B(2B、約 4.3 GB)讀取發票,並透過 TabPFN-3.5 API 根據付款紀錄預測遲付風險及可能付款日期範圍。它逐週預測銀行結餘,並按風險草擬提醒電郵;發票 PDF、客戶名稱及電郵不會離開用戶電腦。
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle
What I Built
Freelancers rarely go under for lack of work. They go under in the gap between doing the work and getting paid for it.
Meet Maya, the made-up illustrator in Paydar's demo. She has $25,440 in open invoices and $1,400 in the bank. On paper she's having a great month. In practice, Acme Retail always pays about two weeks late, her café client goes quiet at the end of every month when rent is due, and Orbit Media pays small invoices on time but sits on anything over $2,500 until a second manager signs it. None of that is written down anywhere except in her payment history, and none of it is visible from her invoicing tool, which only knows "due" and "overdue".
By the end of every month, maya gets to feel emptiness because of the lack of funds. These funds often lead her to taking debt for the time period till she gets her operational expenditure. To help her it was decided to build a project.
Paydar is a cash-flow radar for people like Maya. You drop invoices into a folder (PDFs, forwarded emails, phone photos) and it:
- Reads them on your laptop with Gemma 4 (the 2B "E2B" model, about 4.3 GB), pulling out the client, dates, terms, total, and two details that turn out to matter: whether there's a PO number and whether the invoice mentions a late fee.
- Predicts how each invoice will actually be paid with TabPFN, trained on your own payment history: the chance it lands more than a week late, and a likely pay-date range.
- Projects your bank balance week by week, under an expected case and a "everyone drags their feet" case, so you see a tight week coming a month out instead of the morning rent bounces.
- Drafts reminder emails timed to the risk. The trick I like most: clients who are likely to pay late get a friendly heads-up three days before the due date, not an awkward chase after it.
You can also just ask it things. "Can I afford a $1,500 laptop on November 3rd?" gets checked against the pessimistic forecast, not a gut feeling.
Who pays for this? Solo freelancers and two-to-five person studios who invoice a handful of repeat clients and live close to the line between invoices. Because every model runs on the user's own machine (or on a pseudonymized table, more on that below), my cost per customer is close to zero, so it can be priced like a small utility: "$6/month or a one-time $39" rather than another $30/month SaaS seat.
Demo
![Paydar dashboard]
The dashboard Paydar writes after every run. It's a single HTML file that works offline.
Code
Paydar
A private cash-flow radar for freelancers. Drop your invoices in a folder. A small Gemma model on your laptop reads them, TabPFN learns how each of your clients actually pays, and you get a week-by-week view of your bank balance, which invoices will land late, and reminder emails timed to go out before the slow payers slip.
Your invoice PDFs, client names and emails never leave your computer.
$ npm run radar
9 open invoices worth $25,440. About $25,440 should land in the next 30 days.
Likely late: Bluebird Café $640 (100%, expect ~2026-10-21); Acme Retail Group $7,400 (87%, expect ~2026-11-11).
Tight week ahead: if slow payers drag their feet, you'd be down to $14 in the week of 2026-10-26.
3 reminder emails drafted; first one goes out 2026-10-14.
(Output from the demo workspace with the offline baseline. Numbers change with Gemma + TabPFN.)
How it works
flowchart LR
…
TypeScript, MIT licensed. npm run demo creates a synthetic freelancer with 184 paid invoices and nine open ones, so you can try it without your own data. The test suite (26 tests) runs with a fake Gemma and a fake TabPFN server, so CI needs no models or keys.
How I Built It
flowchart LR
subgraph laptop["Your laptop"]
inbox["inbox/ PDFs, emails, photos"] --> gemma["Gemma 4 E2B (Ollama)"]
history["history.csv (paid invoices)"] --> feats["features + pseudonymizer"]
gemma --> feats
forecast["weekly cash forecast"] --> nudges["reminder drafts (Gemma)"]
nudges --> report["offline dashboard"]
end
feats -->|"C01, C02… + numbers"| tabpfn["TabPFN-3.5 API"]
tabpfn -->|"P(late), pay-date range"| forecastGemma 4 E2B, run locally with Ollama, does all the reading and writing. The interesting part was making a 2-billion-parameter model reliable at a form-filling job. Three things did it:
-
Constrained decoding. Ollama's
formatfield takes a JSON Schema and constrains generation to it, so the model cannot return a malformed date or a missing field. I generate the schema from the same Zod object I use to validate the result, then re-validate anyway and retry once with the error message if something's off. -
Thinking off. Gemma 4 reasons before answering by default. That's great for hard questions and pure waste for "what's the invoice date" on a laptop CPU, so Paydar sends
think: false, after checking that the model reports thinking as a capability. - Code does the arithmetic, the model doesn't. Gemma extracts what's printed. Due dates from "Net 30", client-name matching ("Helix Labs Inc." is "Helix Labs" in your history), and sanity checks happen in plain TypeScript, and anything shaky becomes a visible warning rather than a silent guess.
TabPFN does the predicting. It's a tabular foundation model: you hand it your training table and it predicts in a single forward pass, no training loop, no hyperparameter tuning. That matters here because a freelancer's whole payment history is a couple of hundred rows, which is too little for most models to learn from and exactly where TabPFN shines. Paydar builds 17 features per invoice: the client's track record, their most recent payment, how big this invoice is compared with what they usually get, month-end timing, terms, and the PO and late-fee flags Gemma extracted. Then it makes two calls to the /tabpfn/* API with model_path: "v3.5_default": a classifier for "more than 7 days late?" and a regressor asked for the 10th, 50th and 90th percentile of days late. Those quantiles become the shaded range on the chart.
One rule I was strict about: every feature only uses what was knowable on the invoice's issue date. A payment that arrived after an invoice was sent can't be used to describe that invoice. Without that rule the backtest would quietly grade itself on the answers.
Mastra ties it together. The pipeline is a Mastra workflow with five typed steps (scan inbox, predict, forecast, draft reminders, write report), which made each step testable on its own and gave me Mastra Studio for free to watch runs step by step. On top sits a Mastra agent, also on local Gemma, with six tools: radar status, refresh, "can I afford", client history, draft a reminder, mark as paid. Its instructions say to quote numbers only from tools, so the small model's job is choosing the right tool and explaining the answer, not doing maths.
Does TabPFN beat common sense? The bar isn't zero, it's what a careful freelancer does in a spreadsheet: "this client usually pays N days late." npm run eval replays the history, trains on invoices paid before a cutoff, predicts the ones issued after, and scores both:
| Predictor | Late? AUC ↑ | Brier ↓ | Pay-date error ↓ | 80% range hit rate |
|---|---|---|---|---|
| Per-client average (baseline) | 0.58 | 0.264 | 6.3 days | 0.63 |
| TabPFN-3.5 | TODO | TODO | TODO | TODO |
Demo data: 132 training invoices, 46 test invoices. The baseline row is real output from npm run eval on the demo workspace.
The demo clients have habits a per-client average can't see by design: Orbit's $2,500 sign-off threshold, the café's month-end crunch, Helix Labs slowly getting worse over nine months, Kite & Co freezing payments over the holidays. TODO: one or two sentences on which of these TabPFN picked up in your run (look at the Orbit $3,900 invoice and Helix Labs: the baseline rates them "On track" and "Watch"). It's synthetic data, so the honest test is your own history, which is why eval exists.
Why Does Open Innovation Matter?
Because invoices are the most sensitive documents a freelancer has. An invoice carries a client's legal name, address, sometimes bank details, my rates, and what I built for them, often under an NDA. "Upload your invoices to our AI" is a non-starter for a lot of people, and for many it would breach a contract. With Gemma running in Ollama, the documents are read on the same laptop they were written on. Paydar's dashboard ends with a "What left this computer" box that lists exactly which columns went anywhere, and the answer for documents, names and emails is "nothing".
Because I could choose exactly what to share, and how little. TabPFN is used through Prior Labs' hosted API, but what it receives is a table of numbers and pseudonyms: clients become C01, C02, no invoice numbers, no descriptions. The model gets the pattern and none of the identities. With no API key, Paydar falls back to an offline baseline, so you can run the whole thing with Wi-Fi off. TabPFN itself is open too: the package is on GitHub with weights on Hugging Face for non-commercial use.
Because the business only works if each customer costs me almost nothing. A tool aimed at people who are short on cash can't carry a per-document API bill. Here the heavy lifting runs on hardware the customer already owns, so the price can be small and flat.
Because I could fit the model to the machine, not the other way around. The default is Gemma 4 E2B, quantization-aware trained, about 4.3 GB, chosen because my target user has an ordinary 8 GB laptop, not a GPU. Someone with 16 GB can switch to E4B with one environment variable, and any other Ollama model works the same way. Constrained JSON output, turning thinking off, a retry with the validation error: those are knobs I control because the stack is open.
Where open was harder: speed. Reading a document on a laptop CPU takes TODO-seconds (measure it on your machine) versus near-instant from a hosted API, so Paydar caches every extraction by file hash and never reads the same document twice. Scanned PDFs without a text layer need to come in as photos so Gemma's vision can read them. For a nightly "how's my cash" check, I'll take that trade.
Prize Categories
- Best Use of Gemma: Gemma 4 E2B runs locally through Ollama to read invoices (text and photos) into a constrained JSON schema, draft reminder emails, and power the chat agent.
-
Best Use of TabPFN: TabPFN-3.5 (classifier plus quantile regressor via the
/tabpfn/*API) inside a wider prediction agent, with a walk-forward backtest against a per-client baseline. - Best Use of Mastra: a five-step Mastra workflow orchestrating open models, plus a Mastra agent with six tools on a local Gemma model.
來源:DEV Community · dev.to
