Local question answering over a single text-based PDF. The app reads one PDF at a time, chunks it, embeds it, retrieves relevant evidence, reranks that evidence, answers only from the document, and returns a citation to the source page/chunk.
- load one text-based PDF at a time
- extract and clean page text locally
- create chunks with page-level metadata
- build embeddings and a FAISS index locally
- retrieve and rerank supporting chunks
- generate an answer grounded in retrieved text
- show the source page and chunk
- refuse unsupported questions with an out-of-scope message
- keep processing fully local
- process multiple PDFs together
- handle scanned/image-only PDFs without OCR
- answer from outside the uploaded PDF
- use cloud APIs or external search
- reliably reason over complex visual layouts, tables, or charts
PDF -> page extraction -> cleaning -> chunking -> embeddings -> FAISS retrieval
-> reranking -> QA model -> answer + citation / out-of-scope
pdf-chatbot-with-rag-for-document-qa/
├── README.md
├── PROJECT_PLAN.md
├── requirements.txt
├── Project/
│ ├── Data/
│ │ ├── source/
│ │ ├── extracted/
│ │ ├── normalized/
│ │ ├── embeddings/
│ │ └── indexes/
│ ├── tests/
│ │ └── test_smoke.py
│ ├── main.py
│ ├── pdf_reader.py
│ ├── chunker.py
│ ├── embedder.py
│ ├── retriever.py
│ ├── reranker.py
│ ├── qa_engine.py
│ ├── scope_checker.py
│ └── app.py
└── assets/
└── screenshots/
PyMuPDFfor PDF text extractionsentence-transformersfor chunk embeddingsfaiss-cpufor vector searchsentence-transformerscross-encoder for rerankingtransformersfor extractive QAtorchfor model executionnumpyfor vector operationsgradiofor the local interfacepytestfor smoke tests
pdf_reader.py: extract raw page text, normalize it page by page, skip empty pages after cleaningchunker.py: build page-bounded chunks with overlap and metadataembedder.py: load the embedding model, encode chunks/questions, and save embedding artifactsretriever.py: build, persist, load, and query the FAISS indexreranker.py: reorder retrieved chunks by query relevanceqa_engine.py: produce an answer from top evidencescope_checker.py: decide answer vs abstainapp.py: local UI for upload, question input, answer, and citationmain.py: CLI or smoke-entry script
For supported questions:
Answer: <grounded answer>
Source: page <n>, chunk <id>
For unsupported questions:
This is outside the scope of the PDF.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python Project/main.py --pdf <your-file.pdf>PyMuPDF
sentence-transformers
faiss-cpu
transformers
torch
numpy
gradio
pytest
tqdm
- Define the MVP clearly.
- Create the runnable project skeleton.
- Ingest the PDF page by page.
- Chunk and annotate the text.
- Generate embeddings.
- Build retrieval.
- Add reranking.
- Generate grounded answers.
- Add abstention logic.
- Evaluate with in-scope and out-of-scope questions.
- Wrap it in a local UI.
- Finalize documentation and screenshots.
The first version is successful when:
- the app handles one text PDF end to end
- answers are grounded in retrieved evidence
- the source page/chunk is returned
- unsupported questions are rejected instead of hallucinated
- everything runs locally on the machine
This project is licensed under the GNU General Public License v3.0. See LICENSE for details.