Skip to content

RaulRC/bill-advisor

Repository files navigation

Bill Advisor

AI-powered analysis of Spanish residential electricity bills (facturas eléctricas). Upload a factura PDF, get a structured breakdown of every charge, audit findings on anomalies, and savings opportunities — all in plain Spanish.

Status: Phase 1 MVP. Extracts, audits, and compares against the PVPC regulated tariff. Datadis hourly integration is Phase 2.

What it does

  1. Extracts every line of a Spanish electricity bill into a typed schema using Claude vision — handles all major comercializadoras (Iberdrola, Endesa, Naturgy, Octopus, Repsol, COR/PVPC, etc.) without per-layout parsers.
  2. Audits the extracted data against a set of deterministic rules — math integrity, IVA validity, contador rental, PVPC component allocation, otros_servicios cancellation candidates, autoconsumo opportunities, and PVPC tariff comparison.
  3. Surfaces findings ranked by severity (critical / warning / info) with quantified annual savings where applicable.

PVPC tariff comparison is built in (see "PVPC Comparator" in AGENTS.md). Hourly-consumption analysis remains Phase 2 — see Next Steps.

Quick start

Requires Python ≥ 3.10, uv, and an Anthropic API key. For PVPC tariff comparison you also need an ESIOS token.

git clone <repo>
cd bill-advisor
uv venv
uv pip install -e .

# Create .env with API keys (unquoted values for Docker compat)
echo "ANTHROPIC_API_KEY=sk-ant-..." > .env
echo "ESIOS_TOKEN=your-esios-token" >> .env

# CLI extraction
.venv/bin/python -m bill_advisor.extraction path/to/factura.pdf

# CLI extraction + audit
.venv/bin/python -m bill_advisor.audit path/to/factura.pdf

# Streamlit UI (debug / quick demos)
.venv/bin/streamlit run app.py

# FastAPI server (production UI backend — consumed by the Next.js frontend)
.venv/bin/uvicorn api.main:app --reload --port 8000

# Docker (production)
docker build -t bill-advisor .
docker run -p 80:8501 -p 8001:8001 \
  -e ANTHROPIC_API_KEY=sk-ant-... \
  -e ESIOS_TOKEN=your-token \
  -v ~/bill-advisor-logs:/app/logs \
  bill-advisor

# Or via CI/CD: push to main → auto-deploys to Lightsail

API endpoints

Once the FastAPI server is running on http://localhost:8000:

Method Path Body Returns
GET /api/health {"status": "ok"}
POST /api/analyze multipart/form-data with pdf field {"factura": {...}, "findings": [...]}

Auto-generated docs (Swagger UI) live at http://localhost:8000/docs.

CI/CD — GitHub Actions + Lightsail

Every push to main triggers a deploy via appleboy/ssh-action:

  1. SSH into Lightsail
  2. git clone fresh copy of the repo
  3. docker build the image
  4. Stop & remove old container
  5. Run new container with port mapping (80→8501, 8001→8001), API keys via -e flags, and a volume mount for persistent logs

API keys (ANTHROPIC_API_KEY, ESIOS_TOKEN) are stored as GitHub secrets — never written to disk on the server. The .env file is for local development only.

Architecture

┌──────────────┐      ┌──────────────────────┐      ┌──────────────┐
│  PDF bytes   │ ───▶ │  extraction.py       │ ───▶ │  Factura     │
│  (Streamlit  │      │  (Claude vision +    │      │  (Pydantic)  │
│   or API)    │      │   tool use)          │      └──────┬───────┘
└──────────────┘      └──────────────────────┘             │
                                                             │
                                                             ▼
                                                     ┌───────────────┐
                                                     │  audit.py     │
                                                     │  (10 checks)  │
                                                     │               │
                                                     │  ┌──────────┐ │
                                                     │  │ PVPC     │─│──▶ pvpc_client.py
                                                     │  │ compare  │ │       │ ESIOS
                                                     │  └──────────┘ │       ▼
                                                     └───────┬───────┘  ┌──────────────┐
                                                             │          │ comparator   │
                                                             ▼          │ .py (recalc) │
                                                     ┌──────────────┐   └──────┬───────┘
                                                     │  Findings    │◀─────────┘
                                                     │  (sorted by  │
                                                     │   severity)  │
                                                     └──────┬───────┘
                                                             │
                                        ┌────────────────────┼────────────────────┐
                                        │                    │                    │
                                        ▼                    ▼                    ▼
                                  ┌──────────┐       ┌───────────┐       ┌──────────────┐
                                  │  app.py  │       │ api/main  │       │  rag/query.py│
                                  │ Streamlit│       │  FastAPI  │       │  (corpus     │
                                  │  UI +    │       │ + rate_   │       │   Q&A chat)  │
                                  │  chat    │       │ limiter   │       │              │
                                  └────┬─────┘       └───────────┘       └──────────────┘
                                       │
                                       ├─────────────────────┐
                                       ▼                     ▼
                               ┌──────────────┐     ┌──────────────┐
                               │  logger.py   │     │  metrics.py  │
                               │  stdout +    │     │  Prometheus  │
                               │  /app/logs/  │     │  :8001       │
                               └──────────────┘     └──────────────┘

Modules:

Module Responsibility
bill_advisor/schemas.py Pydantic models for Factura. Single source of truth with 11 models.
bill_advisor/extraction.py Claude API call: PDF → tool-use forcing the record_factura_extraida schema. System prompt + tool schema prompt-cached (~90% cost saving on repeats).
bill_advisor/audit.py 10 deterministic anomaly checks against a validated Factura. No LLM. The PVPC comparison check (_check_pvpc_comparison) internally delegates to comparator.py.
bill_advisor/comparator.py PVPC tariff recompute engine: distributes kWh uniformly across hours per period, recomputes cost at hourly PVPC rates, returns ComparisonResult.
bill_advisor/pvpc_client.py ESIOS API client — fetches indicator #1001 (PVPC 2.0TD Península hourly prices). Returns dict[date, dict[int, float]] in €/kWh.
bill_advisor/logger.py Module-level logger with two handlers: stdout (Docker logs) + file (/app/logs/bill-advisor.log). Messages prefixed [Bill Advisor].
bill_advisor/metrics.py Prometheus counters and histogram with embedded HTTP server on port 8001. Tracks sessions, extractions, errors, PVPC results, chat questions, extraction duration.
bill_advisor/rag/query.py Claude Sonnet 4 Q&A backed by 7 corpus .md files in rag/corpus/. Accepts messages list for conversation memory. Prompt-cached system prompt.
api/main.py FastAPI server with GET /api/health and POST /api/analyze. CORS for localhost:3000. Wraps extraction + audit.
api/rate_limiter.py In-memory sliding-window rate limiter: 2 req/min per IP on POST /api/analyze. Returns 429 with Retry-After.
app.py Streamlit UI. Single-page: upload → extraction → audit → findings + RAG chat. Conversation stored in session state (not backend).

Design decisions

Key decisions are documented as ADRs in docs/adr/. Highlights:

  • ADR-0001 — Vision LLM (not OCR + regex per-layout)
  • ADR-0002 — Tool-use API path, not messages.parse (schema complexity blew the grammar compiler)
  • ADR-0004 — PVPC sub-block in the schema (the first extraction missed mercado mayorista entirely)
  • ADR-0005 — PDF-first input strategy
  • ADR-0008 — Caching strategy (~90% input-cost saving on repeated extractions)
  • ADR-0009 — Direct ESIOS call (not MCP) for PVPC prices
  • ADR-0010 — Uniform kWh distribution in PVPC comparison
  • ADR-0011 — Deterministic audit (zero LLM)
  • ADR-0012 — Defer tests and CI to post-MVP
  • ADR-0013 — In-memory rate limiting (2 req/min per IP)
  • ADR-0014 — Structured logging with stdlib logging

What's next

See docs/next-steps.md. Headline items:

  1. Build Grafana dashboard for usage metrics
  2. Write unit tests for audit.py and comparator.py
  3. Phase 2 — Datadis integration for hourly consumption profiles

Disclaimers

  • Outputs are educativos, never financial or regulatory advice.
  • "Ahorro estimado €X/año" figures are based on the single bill extracted and assume similar consumption patterns going forward — they're estimates, not guarantees.
  • Always verify extracted values against the original factura. The extractor's notas_extraccion flags anything that needed inference or human review.

License

MIT.

About

AI-powered auditor for Spanish residential electricity bills via Claude vision

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors