Portfolio Projects ยท Project 8 of 10

Project 8: Invoice & KYC Document Extraction Pipeline

๐Ÿ› ๏ธ Intermediateโฑ 1โ€“2 weekends๐Ÿ“„ Resume lines included

Document intelligence is one of the most deployed GenAI use cases in banking, insurance and operations. You'll turn messy PDFs and scans into validated, structured JSON, measure field-level accuracy, and route low-confidence documents to humans.

What you'll build

Architecture

PDF / image โ†’ (scanned?) OCR (Tesseract / PaddleOCR / cloud OCR) โ†’ text + layout blocks
   โ†’ LLM structured extraction (JSON schema) โ†’ validators (sum check, regex, cross-field rules)
   โ†’ pass โ†’ export (CSV / ERP API)        fail โ†’ human review UI โ†’ corrections โ†’ evaluation set

Tech stack

pdfplumber or PyMuPDF, Tesseract or PaddleOCR, an LLM (or vision LLM) with JSON schema output, Pydantic validators, FastAPI, Streamlit.

Step by step

  1. Collect 30โ€“50 sample documents (public invoice datasets or ones you create) and label the target fields.
  2. Text path: digital PDFs go through pdfplumber with table extraction; scans go through OCR.
  3. Extraction prompt: the schema, field definitions, "return null if missing", never guess.
  4. Pydantic validation: types, formats (dates, GSTIN, amounts), sum(line_items) + tax == total within tolerance.
  5. Confidence: validation-failure flags, plus self-consistency (extract twice and compare) to route to review.
  6. Evaluate: field-level precision and recall, document-level exact match, straight-through-processing rate.
  7. Review UI: side-by-side view of the document and the editable fields; save corrections for the evaluation set.

Starter code

from pydantic import BaseModel, field_validator, model_validator
from datetime import date
import re
class Line(BaseModel): desc: str; qty: float; unit_price: float; amount: float
class Invoice(BaseModel):
    vendor: str; invoice_no: str; invoice_date: date; gstin: str | None = None
    lines: list[Line]; tax: float; total: float
    @field_validator("gstin")
    @classmethod
    def gst_fmt(cls, v):
        if v and not re.fullmatch(r"\d{2}[A-Z]{5}\d{4}[A-Z][1-9A-Z]Z[0-9A-Z]", v): raise ValueError("bad GSTIN")
        return v
    @model_validator(mode="after")
    def totals(self):
        if abs(sum(l.amount for l in self.lines) + self.tax - self.total) > 1: raise ValueError("totals do not add up")
        return self
def extract(text):
    raw = llm_json(f"Extract the invoice into this JSON schema. Use null if missing; never guess.\n{Invoice.model_json_schema()}\n\n{text}")
    try: return Invoice.model_validate(raw), "auto"
    except Exception as e: return raw, f"review: {e}"

Deploy it

A Streamlit app on Hugging Face Spaces with sample documents. Add a metrics table (field F1, STP rate) to the README.

Put it on your resume

Interview questions you'll get

Stretch goals

Created by Ajeetkumar