跳到正文
DEV Community· Md Faizan·· 3 小時前AI 評分45

BillAutopsy:剖析銀行月結單,找出悄悄流失的金錢

BillAutopsy: Cut Open Your Bank Statements and Find What Is Quietly Bleeding Your Wallet

AI 導讀

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:

  1. 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.
  2. 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:

  1. Deterministic PDF Extraction & Arithmetic Balance Gate (extractor.py): Uses pdfplumber to 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 Balance An LLM never touches the math. Every penny is verified deterministically first.
  2. 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).
  3. Tabular Leak & Price-Creep Detection (auditor.py): Computes recurring billing features (days_between_mean, amount_cv, price_ratio) and uses Prior Labs' TabPFN (TabPFNClassifier with a statistical variance fallback so the repo runs on any laptop in seconds) to flag Silent Price Creep, Forgotten Trial Conversions, and Duplicate Charges.
  4. 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 print 1-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.00 auth on 2026-08-11 converted into recurring $29.00/mo charges ($336.00 / yr)
    • FitPulse App (Trial Conversion): $0.00 trial on 2026-08-13 converted into recurring $14.99/mo charges ($179.88 / yr)
    • Harbor Insurance (Duplicate Charge): Identical $110.00 debits within two days ($110.00 / yr)
    • Metro Gym (Duplicate Charge): Identical $45.00 debits on 2026-10-15 ($45.00 / yr)
    • Streamflix (Price Creep): $15.49 crept to $18.99 (+22.6%, $42.00 / yr)
    • Adobe Creative (Price Creep): $19.99 crept to $22.99 (+15.0%, $36.00 / yr)

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.09 opening → $6,259.20 closing)
  • Leaks Caught ($417.00 / yr):
    • Metro Gym (Duplicate Charge): $45.00 charged twice on 2026-11-05
    • Streamflix (Price Creep): Crept again from $18.99 to $21.99 ($36.00 / yr)
    • CloudVault (Trial Conversion): Recurring $29.00 charge ($336.00 / yr)

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) and tabpfn.
  • Zero-Friction Reviewer Setup: Clone the repo, run pip install -r requirements.txt, and run python 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:8000 where 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.db SQLite 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:

  1. Wrapped Decimal Numbers in External PDFs ($3,248.\n09): When I tested uploading an externally generated November statement PDF, POST /api/upload threw a decimal.InvalidOperation error at line 58 of extractor.py because 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.
  2. 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.py and auditor.py with clean local fallbacks meant development and testing could run in 80 milliseconds on any CPU while still supporting local Ollama and TabPFN when installed.
  3. 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.py structures 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.py integrates 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, and evaluate.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