Build a Retrieval-Augmented Generation system from first principles — one runnable Python file per concept, no frameworks hiding the moving parts. Companion code for the RAG from First Principles series on mefby.com: a 20-part arc, a 12-part core plus a 2026 Frontier Track.
"Build it by hand, understand every line."
Each folder maps 1:1 to an essay. The early parts (2–5) are pure NumPy / standard library and run offline with no API key. Part 6 assembles them into a working "chat with your documents" app; Parts 7–12 layer on hybrid retrieval, reranking, advanced patterns, evaluation, and production hardening. Parts 13–20 are a 2026 Frontier Track continuing past the finale: late-interaction retrieval (ColBERT to ColPali), context-aware chunking, and adaptive routing by query complexity, then RAG vs long-context vs CAG, securing RAG, structured/SQL RAG, building a RAG agent, and conversational RAG.
Every part ships two ways to learn the same concept: a single runnable .py
(the whole idea, top to bottom) and a step-by-step Jupyter notebook (.ipynb)
that rebuilds it cell by cell, with the why spelled out before each small step.
Both run offline — a real embedding model / LLM is used automatically when one is
available, and a transparent, deterministic fallback keeps every cell runnable
otherwise. Part 1 is a concept-only notebook (the code starts in Part 2).
Parts 13–20 are the Frontier Track (2026 advances), a continuation past the Part 12 finale.
Part 11 also ships a second runnable, long_context_vs_rag.py
(notebook): a leakage-free, fictional-corpus
long-context-LLM vs RAG head-to-head.
git clone https://github.com/mftnakrsu/rag-by-hand
cd rag-by-hand
pip install -r requirements.txt
# Parts 2–5 run offline, no API key:
python part-03-measuring-similarity/similarity.py
# Part 6 — the full app. Set one provider (below), then:
python part-06-build-your-first-rag/rag_app.pyPrefer to learn step by step? Open the matching notebook instead — every cell runs offline, no API key required:
pip install jupyter # one-time, to run the notebooks
jupyter notebook part-03-measuring-similarity/similarity.ipynbOr run any notebook in your browser with zero setup — every notebook carries an Open in Colab badge at the top.
similarity.py is pure NumPy and runs with no model, key, or network. You should see:
Three tiny 2-D vectors (real embeddings just have more dimensions):
A = [3, 4]
B = [4, 3]
C = [6, 8] (A doubled: same direction, twice as long)
Pairwise scores -- three metrics, three different verdicts:
pair | euclidean | dot | cosine
--------------------------------------
A,B | 1.41 | 24 | 0.96
A,C | 5.00 | 50 | 1.00
B,C | 5.39 | 48 | 0.96
Top-k retrieval (the RAG ranking function):
query = [1.0, 0.0]
1. score= 1.00 chunk_0 same direction, far away
2. score= 0.97 chunk_2 near-aligned
3. score= 0.71 chunk_1 45 degrees off
Note chunk_0 ties for first despite being 9x longer than the query:
cosine ignores length and rewards pure direction -- the whole point.
The generation step (Part 6 onward) is isolated behind a single generate(prompt)
function so the provider is a one-line swap. Three backends are shown:
OpenAI (the default), Ollama (local, free, no key), and
Anthropic / Claude. Set the matching API key — or run Ollama locally — and
swap the function body. The retrieval, chunking, and similarity code is provider-
agnostic and needs no key at all.
MIT — see LICENSE.
