Physics Local Chatbot
A fully offline Retrieval-Augmented Generation chatbot that answers physics questions from your own uploaded textbooks — zero API cost, zero internet dependency.
Overview
What it does
Indexes physics PDFs into a local vector store and answers questions using a locally-running LLM — grounding every answer in retrieved textbook passages.
Problem solved
Cloud LLMs hallucinate on domain-specific content and charge per token. This creates a private, textbook-grounded tutor that never sends your documents anywhere.
Built for
Physics students and teachers who want a Q&A layer on top of their own course materials, with zero ongoing cost.
The pipeline has two phases. In the indexing phase, uploaded PDFs are parsed with PyPDF2, split into overlapping chunks using LangChain's RecursiveCharacterTextSplitter, and embedded with the all-MiniLM-L6-v2 model. The embeddings are stored in a persisted FAISS index.
In the query phase, the user's question is embedded with the same model, the top-4 most similar chunks are retrieved from FAISS, and a local Llama 3 model (served via Ollama) generates an answer grounded exclusively in those chunks — with citations showing which textbook passage each claim comes from.
Technical Architecture
Indexing pipeline
Query pipeline
Chunking strategy uses 500-token chunks with 50-token overlap to prevent formulas and multi-sentence physics concepts from being split mid-way. The overlap ensures context continuity at chunk boundaries — critical for equations that span paragraph lines.
Key Features
Fully offline
All inference runs locally — no data sent to OpenAI, Anthropic, or any cloud service. Zero ongoing API cost.
Source citations
Every answer includes the textbook page and passage it was generated from — prevents hallucination.
Any physics PDF
Works with any textbook or lecture notes — Halliday, Irodov, Feynman lectures, custom PDFs.
Persistent index
FAISS index is saved to disk — no re-indexing required between sessions, PDFs are processed once.
Challenges & Solutions
LLM hallucination on physics formulas
Challenge: Local models occasionally generated plausible-sounding but incorrect equations when context retrieval missed the relevant passage.
Solution: The system prompt explicitly constrains the model to only answer from the retrieved context and say "I don't know" if the context doesn't contain the answer — reducing hallucination rate significantly.
Optimal chunking for equations
Challenge: Physics equations often span multiple lines and their context (what they represent) appears on the surrounding pages.
Solution: Used 500-token chunk size with 50-token overlap, plus a post-processing step that detects incomplete LaTeX-like patterns and merges the chunk with the next one.
First-run latency for large textbooks
Challenge: Indexing a 700-page textbook took ~4 minutes on CPU.
Solution: Persisted the FAISS index to disk so indexing only happens once. Added a progress bar so users see the status during that one-time build.
Results & Impact
$0
Per-query API cost
<30s
PDF indexing time
top-4
Chunks per answer
The system consistently outperforms asking a generic LLM the same questions without context, especially on equations and derivations specific to a given textbook's notation. The citation system also makes it suitable for academic use where source tracing is required.
What I'd do differently
- →Use a hybrid BM25 + dense retrieval approach. Pure vector search misses keyword-specific queries well, but BM25 handles exact formula notation better. A reciprocal rank fusion of both significantly improves physics retrieval where LaTeX notation matters.
- →Add reranking with a cross-encoder. The current retrieval is bi-encoder only. A lightweight cross-encoder reranker (like ms-marco-MiniLM) on the top-20 candidates before presenting top-4 to the LLM would noticeably improve answer relevance on multi-concept questions.
- →Handle LaTeX rendering in the output. The current UI returns LaTeX as raw text strings. Integrating MathJax or KaTeX in the Streamlit interface would make the mathematical output actually readable, which is the whole point for physics.
Code Highlight
A constrained RAG chain that cites sources and refuses to answer outside the retrieved context.
from langchain.chains import RetrievalQAWithSourcesChain
from langchain.prompts import PromptTemplate
PHYSICS_PROMPT = PromptTemplate(
template="""Answer using ONLY the context below.
If the answer is not in the context, say "I couldn't find that in your textbook."
Always cite the source passage at the end.
Context: {context}
Question: {question}
Answer:""",
input_variables=["context", "question"]
)
def build_qa_chain(vectorstore, llm):
retriever = vectorstore.as_retriever(
search_type="mmr", # maximal marginal relevance
search_kwargs={"k": 4, "fetch_k": 20}
)
return RetrievalQAWithSourcesChain.from_chain_type(
llm=llm,
retriever=retriever,
chain_type_kwargs={"prompt": PHYSICS_PROMPT}
)