Skip to content

KevRojo/rag-pdf-assistant

 
 

Repository files navigation

📚 PDF Study Assistant (RAG)

A Retrieval-Augmented Generation (RAG) app built from scratch that lets you ask questions about a PDF and get answers grounded only in that document's content — not the LLM's general training knowledge.

Built as a hands-on portfolio project to demonstrate understanding of the full RAG pipeline: chunking, embeddings, vector search, and grounded generation — using Google's free-tier Gemini API, ChromaDB, and LangChain.

Use case in this repo: a 50-question AI/ML interview prep PDF, turned into an interactive study assistant.


🖼️ Demo

App screenshot

Example:

Q: What is the difference between bagging and boosting?

A: Bagging trains multiple models in parallel on bootstrapped samples and averages their outputs to reduce variance. Boosting trains models sequentially, where each new model focuses on correcting the errors of the previous ones, primarily reducing bias.

Ask something not in the PDF (e.g. "What is a Support Vector Machine?") and the app correctly responds: "I don't have enough information in the document to answer that." — proof the answers are grounded, not hallucinated from the model's general knowledge.


🧠 How It Works

PDF → chunked text → embeddings (vectors) → stored in ChromaDB
                                                    ↓
Your question → embedding → similarity search → top-k relevant chunks
                                                    ↓
                     relevant chunks + question → Gemini → grounded answer

This is the standard two-stage RAG pattern:

  • Retrieval — find the most semantically relevant chunks of the source document
  • Generation — feed those chunks + the question into an LLM, instructed to answer only from the provided context

🏗️ Architecture / File Structure

rag-pdf-assistant/
├── app.py          ← Streamlit UI
├── loader.py       ← PDF loading + chunking (pypdf + RecursiveCharacterTextSplitter)
├── embedder.py      ← Embeddings (Gemini) + ChromaDB storage
├── retriever.py     ← Similarity search over the vector store
├── generator.py     ← Prompt construction + Gemini answer generation
├── requirements.txt
└── README.md

Each stage of the pipeline is isolated into its own file so components can be swapped independently — e.g. replacing ChromaDB with Pinecone/FAISS, or the LLM with a different provider, without touching the retrieval or UI logic.


⚙️ Tech Stack

  • LangChain — orchestration (document loading, text splitting, vector store interface)
  • Google Gemini API (free tier) — embeddings (gemini-embedding-001) + generation (gemini-2.5-flash-lite)
  • ChromaDB — local, on-disk vector database (no server, no external API key needed)
  • Streamlit — web UI
  • pypdf — PDF text extraction

🚀 Setup & Run Locally

1. Clone the repo

git clone https://github.com/khushikhurana15/rag-pdf-assistant.git
cd rag-pdf-assistant

2. Install dependencies

pip install -r requirements.txt

3. Add your Gemini API key

Create a .env file in the project root:

GOOGLE_API_KEY=your_gemini_api_key_here

Get a free key at Google AI Studio.

4. Add your PDF

Place a PDF in the project root and update the filename in loader.py / embedder.py (default expects ai_ml_interview_qa.pdf).

5. Build the vector store (first run only)

python embedder.py

This embeds the PDF's chunks and saves them to a local chroma_db/ folder. You only need to re-run this if the source PDF changes.

6. Launch the app

streamlit run app.py

🧪 Design Decisions Worth Highlighting

  • Chunking with overlap (chunk_size=800, chunk_overlap=150) — prevents cutting a question off from its answer at a chunk boundary while keeping chunks focused enough for precise retrieval.
  • Low generation temperature (0.2) — keeps answers grounded and consistent rather than creative, which matters for factual Q&A.
  • Explicit "say you don't know" instruction in the prompt — a direct, practical hallucination-reduction technique (rather than just hoping the model behaves).
  • Adjustable k (chunks retrieved) exposed in the UI — lets you see live how retrieval breadth trades off against answer focus.
  • Separation of retrieval and generation into distinct files — retrieval logic (how chunks are found) is fully decoupled from generation logic (how answers are produced), so either can be swapped or upgraded independently.

🔧 Known Limitations / Future Improvements

  • No retrieval confidence threshold. similarity_search always returns the top-k chunks even if none are truly relevant. Currently the LLM prompt is relied on to say "I don't know" when context is weak — this works well in practice, but a more robust approach would gate retrieval itself: if the best match's distance score exceeds a threshold, skip calling the LLM entirely and return "not found in document" directly.
  • langchain_community.vectorstores.Chroma is deprecated in favor of the standalone langchain-chroma package. Still functional as of this writing, but worth migrating.
  • No re-ranking step. Retrieval is pure vector similarity; a production system might add a cross-encoder re-ranker on top of the initial top-k for higher precision.
  • Single PDF, static corpus. No support yet for uploading new PDFs through the UI or incrementally updating the vector store.
  • No conversation memory. Each question is answered independently; there's no multi-turn context carried between questions.

📄 License

MIT — feel free to fork and adapt.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Python 100.0%