Blog

Grounded RAG: how Scholar_Mate AI avoids hallucinating on your own notes

Building a RAG assistant for real student notes meant handling scans and handwriting, not just clean PDFs — and making it comfortable saying 'I don't know' instead of guessing.

  • RAG
  • OCR
  • LangChain
  • ChromaDB

Real notes are never clean

Every RAG tutorial assumes a tidy, typed PDF. Real student notes are a mess of typed slides, phone-scanned textbook pages, and actual handwriting — and a tool that only handles the first category is useless for the coursework it's supposed to help with. Scholar_Mate AI's OCR fallback exists because that mismatch was the whole point of building it in the first place.

The pipeline tries text extraction first with pypdf; if a page has no embedded text, PyMuPDF renders it as an image and PaddleOCR (or OpenAI Vision, or an Auto mode that tries PaddleOCR first) reads it. That fallback chain, not the RAG loop itself, is where most of the actual engineering time went.

Saying 'I don't know' is a feature, not a limitation

The grounding prompt is intentionally strict: if the retrieved chunks don't contain the answer, the assistant says so — "I could not find this information in the uploaded document" — instead of falling back on the model's general knowledge. For exam prep specifically, a plausible-sounding wrong answer is worse than an honest non-answer, because it doesn't get double-checked the way an obvious refusal does.

Every answer also shows the source page number and the retrieved chunk text, so a student can verify the claim against their own notes in one click instead of trusting the model's word for it.

Making a slow pipeline feel fast

OCR is slow, and a 40-page scanned PDF indexing in the background for two minutes while a student stares at a spinner is a bad experience. The fix was smart background indexing — index the first handful of pages immediately so questions can start flowing while the rest indexes behind the scenes — plus OCR result caching so a page never gets OCR'd twice. PaddleOCR also runs in its own worker process, isolated from the main FastAPI server, because native OCR crashes are exactly the kind of thing you don't want taking down the whole app.