BillAutopsy:剖析銀行月結單,找出悄悄流失的金錢
BillAutopsy: Cut Open Your Bank Statements and Find What Is Quietly Bleeding Your Wallet
BillAutopsy 以 100% 本機、零雲端方式審核銀行月結單,找出訂閲漲價、免費試用轉付費及重複扣款等隱藏支出。測試中,3/3 份月結單均通過數學核對,發現每年 $748.88 的支出浪費。基準測試中,BillAutopsy 偵測到 5/5 項預設異常,誤報 1 項。
This is a submission for the Hacktoberfest Open-Source AI Challenge: Week 3, "Slow and Steady".
The Problem: Why Looking at Bank PDFs Feels Like a Punishment
Once a quarter, I download my monthly bank and credit card statements to figure out where my money went and why my monthly expenses feel higher than last year. Every single time, I hit the same two walls:
- Bank PDFs Shatter in Spreadsheets: Banks lock historical statements inside multi-column PDFs without clean gridlines. When you copy-paste them into Excel, multi-line descriptions wrap into broken orphan rows and numbers split across lines. Cleaning up 130 rows by hand takes an hour of tedious work.
-
Silent Subscription Bleed: Because every transaction is disguised in cryptic card-processor shorthand (
POS DEBIT 8831 SQ *BLUE BOTTLE,ACH WEB STREAMFLIX INC 9921,ORIG CO: CLOUDVAULT PRO SUB 881), you never notice when a streaming service quietly hikes its price from $15.49 to $18.99, when a gym double-charges you on the same day, or when a "free trial" converts into a $29.00/month drain.
Cloud budgeting apps and online PDF converters claim to solve this, but they ask for the one thing I refuse to hand over: unredacted bank statements. A bank statement contains your account number, home address, salary, and every store you visit. Uploading that to a random third-party server is a massive privacy risk.
So I built BillAutopsy: a 100% local, zero-cloud statement auditor that cuts open your monthly PDFs, mathematically verifies every cent, normalizes messy merchant strings, and surfaces hidden money leaks right on your laptop.
What I Built
BillAutopsy ("Cut open your bank statements. Find what's quietly bleeding your wallet.") uses a strict four-stage division of labor between deterministic Python and open-source AI:
-
Deterministic PDF Extraction & Arithmetic Balance Gate (
extractor.py): Usespdfplumberto extract statement headers and transaction rows, merges wrapped multi-line descriptions, repairs currency numbers split across line breaks, and runs a strict accounting check:Opening Balance + Total Credits - Total Debits == Closing BalanceAn LLM never touches the math. Every penny is verified deterministically first. -
Local Merchant Normalization with Memory (
normalizer.py): Masks account/card numbers via regex, deduplicates raw transaction descriptions (compressing 132 rows into 18 unique merchant patterns), and normalizes them into clean vendor names and categories using local Ollama (gemma3:4b/qwen2.5:0.5b) with a deterministic offline fallback and local SQLite rule persistence (statement_rules.db). -
Tabular Leak & Price-Creep Detection (
auditor.py): Computes recurring billing features (days_between_mean,amount_cv,price_ratio) and uses Prior Labs' TabPFN (TabPFNClassifierwith a statistical variance fallback so the repo runs on any laptop in seconds) to flag Silent Price Creep, Forgotten Trial Conversions, and Duplicate Charges. -
Editorial Web UI & 1-Page Executive Print Report (
app.py): Serves a local FastAPI dashboard featuring an interactive drag-and-drop PDF upload zone, one-click category memory overrides, clean CSV export, and a@media print1-page executive BillAutopsy Report.
Demo
(Direct video link: Watch the BillAutopsy Walkthrough on YouTube)
Test 1: The 3-Month Offline Demo (132 Transactions)
-
Balance Verification:
3/3 Statements Mathematically Reconciled ($0.00 Discrepancy — Zero Math Hallucinations) -
Total Annual Waste Discovered:
$748.88 / yr -
Leaks Caught:
-
CloudVault (Trial Conversion):
$1.00auth on2026-08-11converted into recurring$29.00/mocharges ($336.00 / yr) -
FitPulse App (Trial Conversion):
$0.00trial on2026-08-13converted into recurring$14.99/mocharges ($179.88 / yr) -
Harbor Insurance (Duplicate Charge): Identical
$110.00debits within two days ($110.00 / yr) -
Metro Gym (Duplicate Charge): Identical
$45.00debits on2026-10-15($45.00 / yr) -
Streamflix (Price Creep):
$15.49crept to$18.99(+22.6%,$42.00 / yr) -
Adobe Creative (Price Creep):
$19.99crept to$22.99(+15.0%,$36.00 / yr)
-
CloudVault (Trial Conversion):
Test 2: Live External PDF Upload (test_november_statement.pdf, 7 Rows)
To prove the pipeline works dynamically on newly uploaded files, I generated a separate November 2026 PDF statement outside the codebase (where summary numbers wrapped across lines like $3,248.\n09) and dropped it into the upload zone:
-
Extracted & Reconciled: All
7 rows(✓ reconciled,$3,248.09opening →$6,259.20closing) -
Leaks Caught (
$417.00 / yr):-
Metro Gym (Duplicate Charge):
$45.00charged twice on2026-11-05 -
Streamflix (Price Creep): Crept again from
$18.99to$21.99($36.00 / yr) -
CloudVault (Trial Conversion): Recurring
$29.00charge ($336.00 / yr)
-
Metro Gym (Duplicate Charge):
Code & Repository
GitHub Repository: https://github.com/Droid-Faizan/billautopsy
-
Stack: Python 3.11+, FastAPI, Uvicorn,
pdfplumber, ReportLab,pandas,scikit-learn,pydantic,sqlite3, plus optional local Ollama (gemma3:4b/qwen2.5:0.5b) andtabpfn. -
Zero-Friction Reviewer Setup: Clone the repo, run
pip install -r requirements.txt, and runpython app.py—it works out of the box in under 60 seconds with no API keys or heavy GPU downloads required.
How I Built It & Benchmark Results
I built generate_data.py first so the repository includes 3 months of realistic multi-page PDF bank statements alongside an exact ground_truth.json answer key. That allowed me to benchmark BillAutopsy directly against a naive baseline using python evaluate.py:
| Metric | Naive Baseline | BillAutopsy |
|---|---|---|
| Balance Reconciliation Rate | 100.0% | 100.0% |
| Merchant Grouping Purity | 100.0% | 100.0% |
| Anomaly Detection Recall (Planted Leaks Caught) | 0.0% | 100.0% (5/5 caught) |
| False Alarms on Normal Variable Bills | 0 | 1 |
| Execution Time (seconds) | 0.0014s | 0.0806s |
Notice that single false alarm in the table (1): in the August statement, the random generator placed a normal recurring Harbor Insurance charge one day after an earlier $110.00 charge, which triggered our deterministic 2-day duplicate rule alongside the 5 planted anomalies. Keeping that number in the table shows the exact trade-off between strict duplicate rules and variable billing dates.
Why Open Innovation Matters Here
-
True Financial Privacy: You should never upload raw bank PDFs to a stranger's cloud server. For that reason, BillAutopsy is designed to run locally on
127.0.0.1:8000where your statements never leave your disk. - $0 Forever: Paying a $12/month subscription to an app that finds forgotten subscriptions defeats the purpose. Local open-weight models and tabular statistical checks cost $0 per statement.
-
User-Owned Memory: When you change a merchant's category in the UI, it writes directly to your local
statement_rules.dbSQLite file—no vendor lock-in.
What Broke Along the Way (Agent-Assisted Debugging & Lessons)
I built and iterated on BillAutopsy in modular stages (generate_data.py → extractor.py → normalizer.py → auditor.py → app.py & evaluate.py) using GitHub Copilot as my coding agent, verifying each module in the terminal before moving to the next:
-
Wrapped Decimal Numbers in External PDFs (
$3,248.\n09): When I tested uploading an externally generated November statement PDF,POST /api/uploadthrew adecimal.InvalidOperationerror at line 58 ofextractor.pybecause the PDF table cell wrapped the cents onto a new line ($3,248.\n09), and non-transaction summary tables were being scanned by the row parser. Directing a surgical fix in_amount()to rejoin split decimals via regex and filtering_transaction_rows()to require valid date + digit balance cells resolved the crash without breaking existing tests. -
Slow Connections & Heavy ML Wheels: Pulling multi-gigabyte model weights and 124 MB PyTorch wheels over a ~200 KB/s night-time connection timed out during setup. Designing
normalizer.pyandauditor.pywith clean local fallbacks meant development and testing could run in 80 milliseconds on any CPU while still supporting local Ollama and TabPFN when installed. - Static Demo State vs. Live Upload State: My first UI iteration rendered the 3-month demo on startup but didn't dynamically replace the DOM state when a single external PDF was uploaded. Wiring the drag-and-drop zone to replace the state cleanly made single-file auditing just as smooth as the 3-month demo.
Prize Categories Targeted
- Slow and Steady (Overall Challenge): Automates the dreaded quarterly chore of manually fixing broken PDF bank statement tables and hunting for creeping subscriptions, while keeping all financial data 100% local.
-
Best Use of TabPFN (Featured Category):
auditor.pystructures transaction histories into tabular time-series features (occurrence_count,days_between_mean,amount_cv,price_ratio) built for Prior Labs' TabPFN (TabPFNClassifier) to separate recurring subscription price hikes and trial conversions from normal variable spending. -
Best Use of Gemma (Featured Category):
normalizer.pyintegrates with local Ollama (gemma3:4b) to normalize deduplicated raw payment-processor strings (POS DEBIT 8831 SQ *BLUE BOTTLE) into clean canonical merchant names and categories offline without leaking PII. -
Best Use of GitHub Copilot (Partner Category): Built end-to-end with GitHub Copilot across modular steps (
generate_data.py,extractor.py,normalizer.py,auditor.py,app.py, andevaluate.py), including live stack-trace debugging on wrapped PDF table cells (verified in the commit history of Droid-Faizan/billautopsy). - Best Use of Entire (Partner Category): Documented the complete modular agent-driven build process, prompt decomposition, and failure recovery that produced every file in the repository.
來源:DEV Community · dev.to
