document-extract-agent / PROGRESS_TOMORROW.md
kennethzychew's picture
chore: tick T11 -- Space live, both modalities verified end-to-end
52861a7
|
Raw
History Blame Contribute Delete
8.3 kB
# PROGRESS_TOMORROW.md — Supervised Build Ledger
The second half of the build: real models, real parsing, the entry points,
evaluation, and deployment. Everything past the `⛔ STOP` line in `PROGRESS.md`.
**This half is interactive, not looped.** You drive it in a normal `claude`
session, one task at a time, and verify each task on a real document before
moving on. There is no `DONE_ALL` and no `run-overnight.ps1` here — these tasks
call real models (non-deterministic), need API keys and a local server, and
have decisions only you should make. Leaving them to an unattended loop would
burn quota on work you can't blindly trust.
## Protocol (per task)
1. Prompt Claude Code for the **next unchecked task**, one at a time.
2. Let it implement, then **review the diff** and **eyeball the result on a real
sample document** — not just that tests pass. Model output that looks
plausible can still be wrong; you are the check.
3. When satisfied, commit (one commit per task) and tick the box.
4. Add dependencies **per task, not all at once** (see policy below), so an
install failure is isolated to the task that needs it.
5. Default to **Sonnet** (`/model sonnet`). These are routine integration tasks;
Opus is not needed and costs ~3x.
## Setup before T1
- **Gemini key:** get a free API key from Google AI Studio. Put it in `.env` as
`GEMINI_API_KEY=...` — this is the key for *the app's* calls to Gemini, and is
completely separate from your Claude Code login. Do **not** set
`ANTHROPIC_API_KEY` (that would silently bill your Claude Code usage to the
API).
- **Sample documents:** have one real **receipt photo**, one **scan**, and one
**native-text PDF invoice** on hand. You'll use them to eyeball real
extractions (synthetic/public docs only — the free tier may train on inputs).
- **Branch:** lock in last night's milestone and start fresh (see the chat
instructions accompanying this file).
## Dependency policy
Add only what each task needs, when it needs it:
`google-genai` (T2) · `docling` (T3) · `paddleocr` **or** `pytesseract` (T4) ·
`ollama` (T6) · `watchdog` (T8) · `gradio` (T9). This isolates the
Paddle-on-3.11 risk to T4 instead of breaking everything at once.
---
## TASKS
- [x] **T1 — Gemini config + key** (prep)
Add `uv add google-genai`. In `.env` set `GEMINI_API_KEY`,
`EXTRACTION_BACKEND=gemini`, `IMAGE_STRATEGY=vision_direct`, and
`GEMINI_MODEL` per `docs/04_project_setup.md`. No new code beyond confirming
`config.py` accepts it.
Check: `uv run python -c "from doc_agent.config import load_config; c=load_config(); print(c.extraction_backend, bool(c.gemini_api_key))"``gemini True`.
- [x] **T2 — Gemini backend + real image acquire** (build plan 2.5) ⭐ milestone
Implement `src/doc_agent/backends/gemini.py` per architecture §5: multimodal
call, schema-constrained JSON output, bounded retries + timeout, model id from
config; register its builder in the factory. Wire a minimal real `acquire` for
images in `vision_direct` mode (load image bytes into the payload) so
`process_document` runs end-to-end on a photo/scan with no Docling/OCR yet.
Tests: `tests/test_gemini.py` with a **mocked** Gemini response (deterministic,
no network/quota) covering schema-valid parsing, retry, and timeout.
Check (auto): `uv run pytest tests/test_gemini.py -q`.
Check (manual): run `process_document` on your real **receipt photo** and
confirm `vendor_name`, `total`, `document_date` are actually correct.
*This is where the project stops being scaffolding.*
- [x] **T3 — Docling parser** (2.2)
`uv add docling` (first run downloads layout models — needs internet).
`src/doc_agent/parsing/docling_parser.py`: native PDF → text/layout payload;
wire into `acquire` for `native_pdf`.
Check (manual): `process_document` on your **PDF invoice** via Gemini returns
correct fields.
- [ ] **T4 — OCR path** (2.3) — *needed for Ollama / `ocr_then_text`; deferrable*
`src/doc_agent/parsing/ocr.py`: image → text behind the payload interface;
wire into `acquire` for `IMAGE_STRATEGY=ocr_then_text`.
**DECISION:** try `uv add paddleocr`; if it won't resolve on 3.11, fall back to
`uv add pytesseract` (and install the Tesseract binary).
Check: a sample image yields text; `process_document` works in
`ocr_then_text` mode. *With `vision_direct` + Gemini you don't strictly need
this for the demo — you can skip it now and return before adding Ollama.*
- [x] **T5 — Consolidate real `acquire` into core**
Make `core.py`'s default `acquire` do the real thing by modality + strategy
(`native_pdf`→Docling; `image`+`vision_direct`→bytes;
`image`+`ocr_then_text`→OCR), so `process_document(path)` works on a real path
for every input without injection. Keep the injectable seam for tests.
Check (auto): `uv run pytest -q` (full suite still green; smoke tests still use
the stub). Check (manual): one of **each** input type returns correct fields.
- [ ] **T6 — Ollama backend** (2.6) — *OPTIONAL: offline/private; skip to ship faster*
`uv add` the ollama client; needs a local Ollama server + a pulled model
(e.g. `ollama pull qwen2.5:7b`). `src/doc_agent/backends/ollama.py`: local call
with JSON-schema/grammar-constrained decoding; text-in (pairs with the OCR
path). Tests: mocked unit tests + a manual smoke against the local server.
Check: with `EXTRACTION_BACKEND=ollama` + `IMAGE_STRATEGY=ocr_then_text`,
`process_document` returns schema-valid data.
- [x] **T7 — Persistence** (4.1)
`src/doc_agent/store/db.py` (SQLite append of accepted records) and
`store/export.py` (CSV export). Add `tests/test_store.py`.
Check: `uv run pytest tests/test_store.py -q`; an accepted record persists and
appears in the CSV.
- [x] **T8 — Watcher / batch runner** (4.2)
`uv add watchdog`. `src/doc_agent/ingest/watcher.py`: watch (or poll)
`inbox/`, call core, persist accepted, move source to `processed/` or
`review/`, per-document try/except + structured logging.
Check: drop a **mixed batch** (PDF + scan + photo + one deliberately corrupt
file) into `data/inbox/`; all valid ones process, the corrupt one routes to
`review/` with a logged reason, and the loop does not stop.
- [x] **T9 — Web demo** (4.3)
`uv add gradio`. `src/doc_agent/web/app.py`: single-upload UI rendering fields,
per-field confidence, the validation report, and the decision; a
"synthetic/public documents only" notice; stateless.
Check: `uv run python -m doc_agent.web.app`; upload one of each modality and
confirm a correct, validated result is displayed.
- [x] **T10 — Evaluation harness** (5)
`eval/datasets/` loaders for a held-out **SROIE** slice + the labelled invoice
JSON set, mapping gold labels to the schema. `eval/run_eval.py`: run core over
a slice, normalize, compute per-field and per-critical-field
precision/recall/F1, auto-accept precision on critical fields, and sweep the
threshold.
**DECISION:** *you* pick `CONFIDENCE_THRESHOLD` so auto-accept precision on
`total`/`tax`/`invoice_number` ≥ 0.98; record the resulting recall.
**COST:** this calls the model over many documents and counts against the
Gemini free-tier daily limit — run a **small slice first**, deliberately.
Check: `run_eval` prints the metrics table; you set the threshold; the results
table goes into the README.
- [x] **T11 — Deploy** (6)
Create a Hugging Face **Space** (Gradio SDK, free), `python_version: "3.11"`,
`requirements.txt` via `uv export --no-hashes --no-dev -o requirements.txt`,
secrets (`GEMINI_API_KEY`, `EXTRACTION_BACKEND=gemini`,
`IMAGE_STRATEGY=vision_direct`). Write the README: quickstart (both modes), the
swappable-backend note, the results table, the demo URL, and the
free-tier/privacy caveats.
Check: the public URL processes an uploaded document of each modality.
---
## Phase done when
A real document of each input type flows end-to-end through a live model into a
validated record; the watcher processes a mixed batch unattended and routes
exceptions to review; the eval harness has produced a precision/recall table and
set the threshold; and the demo is live at a public URL. T4 and T6 are optional
and can be revisited after the demo ships.