cd ~ / projects

PDF Chat Assistant — RAG with OCR & Visual Citations

personal · 2026

A production-style RAG chatbot that answers questions grounded in user PDFs — including scanned/image-only PDFs handled per page via Tesseract OCR (triggered only when a page has <50 native chars, to avoid needless OCR cost). Its signature feature is visual, verifiable citations: every retrieved chunk maps back to bounding boxes overlaid on the rendered source page, computed at processing time (pages rendered at 2× zoom) and carried as chunk metadata, so answers point to exactly where they came from. Semantic retrieval uses Sentence-Transformers (all-MiniLM-L6-v2) embeddings in a FAISS store with a scikit-learn TF-IDF fallback, multi-turn memory, and resilient generation (fallback, retries, 2s rate-limit throttling for free-tier Gemma ~30 RPM).

What it is

A RAG chatbot that answers questions grounded in your PDFs — including scanned ones — and proves every answer visually: each retrieved chunk maps back to bounding boxes drawn as red overlays on the rendered source page. Instead of "according to the document," you see the exact rectangle the answer came from.

Pipeline

Ingestion extracts text blocks with their coordinates via PyMuPDF; a per-page check decides whether a page earns OCR — under 50 characters of native text means scanned, so it goes through Tesseract; born-digital pages never pay the OCR cost. Chunks (with bboxes attached) are embedded with Sentence-Transformers into an in-memory FAISS store. The full write-up is in the blog post "Trustworthy RAG."

src/config.py
MODEL_NAME = "gemma-3-27b-it"
EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
CHUNK_SIZE, CHUNK_OVERLAP, TOP_K = 1000, 200, 4
OCR_MIN_NATIVE_CHARS = 50   # below this, a page earns OCR
THROTTLE_SECONDS = 2        # free-tier Gemma ~30 RPM

Trade-offs

In-memory FAISS keeps the app self-contained; any persistent swap (Chroma, pgvector) is isolated behind one module. Multi-turn memory via conversation buffer. Dockerized with Tesseract baked in, deployed on Hugging Face Spaces with a health check.

github