BXUP Track progress & test yourself — free
Data Science & ML/AI · Playbook · Core · ~4 min read
DATA SCIENCE & ML/AI · PLAYBOOK · CORE

Document-to-Table: Structured and Vision Extraction from Messy Documents

Contracts, invoices, scanned reports, and slide decks hold analytical data that never reaches a table.

Tier: Core · Workflow area: Document Intelligence & Multimodal Extraction

This playbook converts heterogeneous documents into a clean, typed dataset: parse layout-aware where possible, fall back to vision models where parsing fails (scans, charts, complex tables), extract against a Pydantic schema, and attach an eval sample so you can state the error rate of the resulting table.

When to use

  • Turning a folder of PDFs (50–50,000) into rows: contract terms, invoice line items, lab-report values, filing metrics.
  • Chart/figure understanding: extracting the numbers behind plots in reports where the source data is gone.
  • Replacing a brittle template-based (per-vendor regex) extraction system with one schema-driven pipeline.

Skip vision models when documents are born-digital with clean text layers — a parser plus text extraction (pb-14) is cheaper and more accurate.

Prerequisites

  • docling or unstructured for parsing; pypdf/pdfplumber for triage; the pb-14 extraction stack.
  • A multimodal LLM API (Anthropic/OpenAI/Gemini — all current flagships accept PDFs and images).
  • 20 hand-extracted "gold" documents covering your ugliest cases — build these first; they are the eval.

The workflow

  1. Triage the corpus. Classify each document programmatically: has text layer? page count? contains tables/charts? scanned? Route accordingly — text-parse route, vision route, or hybrid. Log the mix; it drives cost.

  2. Parse route (born-digital docs). Docling converts PDF → structured Markdown preserving tables and headings:

    from docling.document_converter import DocumentConverter
    md = DocumentConverter().convert("contract.pdf").document.export_to_markdown()
    

    Feed the Markdown (or the relevant sections — split on headings for long docs) into the pb-14 extraction function with your target schema.

  3. Vision route (scans, charts, hostile layouts). Send the document or page images directly; current APIs take PDFs natively:

    import base64
    pdf_b64 = base64.standard_b64encode(open("scan.pdf", "rb").read()).decode()
    resp = client.messages.parse(
        model="claude-opus-4-8", max_tokens=2048,
        messages=[{"role": "user", "content": [
            {"type": "document", "source": {"type": "base64",
              "media_type": "application/pdf", "data": pdf_b64}},
            {"type": "text", "text": "Extract the fields defined by the schema."}]}],
        output_format=InvoiceSchema)
    

    For chart extraction, add schema fields for axis labels, units, and per-series data points, plus read_confidence: Literal["exact","approximate"] so downstream analysis knows which numbers were eyeballed off a bar chart.

  4. Schema design for documents. Beyond pb-14 basics: include source_page: int and a short evidence_quote: str per critical field — verbatim quotes make human verification 10x faster and hallucinations obvious (a quote that isn't in the document is an automatic reject).

  5. Two-pass verification for high-stakes fields. Extract, then run a second cheap-tier call: "Given this document and this extracted value with its quote, confirm or correct." Disagreements go to human review. This catches most silent misreads for a few percent extra cost.

  6. Evaluate against gold, then run the corpus. Field-level accuracy on the 20 gold documents, broken down by route (parse vs vision) and field. Batch the full corpus via pb-16's pipeline; sample 2% for ongoing QA.

Warning

Numeric fields from vision extraction (totals, dates, IDs) are the highest-risk outputs in this entire track — a transposed digit in a contract value is worse than a missing value. Always cross-check derived arithmetic (line items sum to total?) as schema-level validators.

Tool variants

  • Anthropic: native PDF input (base64 or Files API file_id for reuse across many questions on one doc); ≤32 MB per request. Pair with messages.parse for typed output.
  • OpenAI:client.responses.parse with input_file/image content parts and text_format=Schema.
  • Docling vs unstructured: Docling excels at table structure; unstructured has broader format coverage (email, HTML, DOCX). Try both on your gold set — the parse route's quality ceiling is the parser.
  • Local/OSS route: Docling + a local VLM (Qwen2.5-VL-7B-class) for air-gapped corpora; expect a measurable accuracy drop on dense tables — quantify it on the gold set before committing.

Verification

  • Field-level accuracy on gold documents ≥ target (set per field: IDs/amounts need ~99%, free-text summaries can tolerate less), reported per route.
  • Every evidence quote verified to exist verbatim in the source for a 30-field sample (Ctrl+F test).
  • Arithmetic validators (sums, date ordering, unit sanity) pass on ≥98% of rows; failures routed to review, not dropped.
  • Route mix and per-document cost logged; vision route reserved for documents that actually need it.

Pitfalls

  • One mega-prompt for a 100-page document. Accuracy degrades and cost explodes; split by section/page and extract locally, then aggregate. Long-context stuffing is a measured decision (see pb-21), not a default.
  • Trusting the text layer of a "digital" PDF. Many PDFs have garbage OCR text layers under clean-looking pages; spot-check parser output before blaming the LLM.
  • Schema fields the document may legitimately lack. Distinguish null = "not present in document" from extraction failure; add an explicit field_present: bool where absence is meaningful (e.g. missing termination clause).
  • Charts without units. Extracted series are useless if the model guessed the axis scale; require units and reject rows where units are missing.
  • Gold set built from easy documents. Sample gold docs from the triage's hardest buckets, or your measured accuracy will be a fantasy.

Restricted-environment variant

  • Contracts and client financials usually cannot hit public endpoints: run Docling locally + local VLM extraction, keeping the identical schema/gold-eval harness to quantify the local-vs-hosted accuracy gap for the client conversation.
  • If hosted is permitted under DPA with redaction: mask party names and amounts with reversible placeholders before upload — but note redaction breaks arithmetic validators; validate pre-redaction locally.
  • Never upload signed contracts to consumer-tier tools; enterprise tenant or local only. This is a compliance line, not a preference.

Success criteria

  • A tidy dataset (one row per document or line item) with typed columns, provenance (source_page, quote), and a stated field-level error rate.
  • Gold-set evaluation and route-level accuracy documented; high-stakes numeric fields protected by validators and two-pass checks.
  • Cost per document known, with the vision route used surgically rather than by default.
  • A new document type onboards by writing a schema + 5 gold examples — no per-vendor code.
This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in