examples/ — The Curriculum

Fifteen self-contained lessons that teach RAG from first principles

Numbered 00–06. Each lesson is example.js + CONCEPT.md + CODE.md. Self-contained — runnable without reading any other lesson. ~6,954 lines of teaching code.

15 lessons 7 top-level dirs ~6,954 LOC Cognitive locality

15
lessons
3
artefact types
5
retrieval strategies
3
vector-store sub-lessons

📚 What is this & why does it exist?

examples/ is the actual product of this repository. src/ exists as a side-effect of stabilising what the lessons have proven. Every numbered directory is a self-contained, runnable lesson. A learner can read one lesson without having read any other — that is the central design contract.

💡 Real-world analogy

A driving instructor's lesson plan: the first lesson teaches turning the wheel without ever explaining gear ratios. The fifth lesson explains the clutch without re-deriving inertia. Each lesson assumes you remember the previous; none assumes you've read the engineering textbook. The textbook (src/) is on the back shelf for the curious; the lessons (examples/) are the road.

🎯 Why This Module Exists

The lessons exist because RAG appears intimidating in the LangChain era but stops being intimidating once you have written the keyword-scorer, the splitter, the embedding cache, and the rank-fusion equation yourself. The author's wager is that direct hands-on contact with each primitive removes the magic; the curriculum is built around that wager.

The lessons are not a thin demo of the src/ library. They are the source of truth for what should eventually live in src/. Lesson 03 re-implements its own splitter; lesson 06/04 ships a multi-query-retrieval.js helper that has not yet been promoted. Reading the lessons in order, you watch the abstractions emerge.

🏗️ Design Decisions

DecisionChoice MadeAlternativesWhy
Lesson granularityNumbered 00–06 with sub-numbering for complex topicsFlat 1..N; tagged by topicSub-numbering signals "this concept needs multiple passes" without dirtying the top-level.
Code/concept splitThree files per lesson (example.js + CONCEPT.md + CODE.md)Markdown with embedded code; comments onlyLets readers choose their depth: skim concept → read code → run example.
First lesson00 does keyword scoring with no embeddingsStart with embeddingsDemystifies RAG before introducing vector math. Reader sees retrieve+generate working without any "AI".
Self-contained vs importsEach example.js imports only Document/PDFLoader + helpersShared scaffold across lessonsCognitive locality — the lesson must be readable in one tab without chasing imports.
Vector DBembedded-vector-db (HNSW, in-process)Qdrant/Chroma/PG-vector (network)Local-first; no Docker required. Matches "no black boxes".
Embedding modelbge-small-en-v1.5 (384-d)OpenAI text-embedding-3-smallLocal, no API keys, runs on CPU.
Retrieval lessons5 sub-lessons (basic → query-prep → hybrid → multi-query → query-rewriting)One mega-lessonEach sub-lesson teaches one technique in isolation, then the next layers complexity.

Lesson Architecture

flowchart LR
  subgraph Foundations["Foundations"]
    L0["00
How RAG works
(no embeddings)"] L1A["01/01
node-llama-cpp basics"] L1B["01/02
LLM wrapper"] end subgraph DataPrep["Data prep"] L2["02
Data Loading"] L3["03
Splitting + chunking"] end subgraph Vectors["Vectors"] L4A["04/01
Similarity basics"] L4B["04/02
Generate embeddings"] end subgraph Storage["Storage"] L5A["05/01
In-memory store"] L5B["05/02
NN search (HNSW)"] L5C["05/03
Metadata filtering"] end subgraph Retrieval["Retrieval strategies"] L6A["06/01
Basic retrieval"] L6B["06/02
Query preprocess"] L6C["06/03
Hybrid (dense + BM25)"] L6D["06/04
Multi-query + RRF"] L6E["06/05
Query rewriting"] end L0 --> L1A --> L1B --> L2 --> L3 --> L4A --> L4B L4B --> L5A --> L5B --> L5C L5C --> L6A --> L6B --> L6C --> L6D --> L6E

A Lesson's Anatomy

sequenceDiagram
    participant R as Reader
    participant C as CONCEPT.md
    participant E as example.js
    participant W as CODE.md
    participant Out as Terminal
    R->>C: read concepts (the WHY)
    Note over R,C: 5-min read; sets mental model
    R->>E: open example.js
    R->>W: alongside CODE.md (line walk)
    Note over R,W: dual-pane reading: code + commentary
    R->>E: node example.js
    E->>Out: chalk-formatted output
via OutputHelper Out->>R: visible structure
(headers, spinners, stats) R->>R: tweak config, re-run R->>R: graduate to next lesson

🚶 Step-by-Step Narrated Walkthrough

The reader's path from "what is RAG?" to "I can build my own retrieval strategy":

  1. Lesson 00 (How RAG Works) — 68 LOC of pure JS. Knowledge is an array of strings. naiveKeywordSearch() scores each doc by word overlap. generateAnswer() concatenates retrieved docs (no LLM). The reader sees retrieve+generate work; the failures (e.g., "What is the weather today?") motivate every later lesson.
  2. Lessons 01/01 + 01/02 (LLM basics) — loads a GGUF model with node-llama-cpp; wraps it in a class. The reader learns that "an LLM" is just a function from string to string.
  3. Lesson 02 (Data Loading) — uses PDFLoader from src/ (already promoted). Demonstrates page-split vs single-document modes.
  4. Lesson 03 (Splitting) — re-implements TextSplitter inline (cognitive locality!). Compares character-window, sentence-aware, and recursive strategies on the same PDF.
  5. Lesson 04/01 (Similarity basics) — embeds 5 sample sentences, computes pairwise cosine similarity, prints a heatmap. Reader sees "these two sentences are 0.82 similar".
  6. Lesson 04/02 (Generate embeddings) — scales up to a real document set. Introduces caching as a side-channel.
  7. Lesson 05/01 (In-memory store) — constructs a VectorDB from embedded-vector-db. CRUD operations, similarity search, metadata storage.
  8. Lesson 05/02 (Nearest-neighbour search) — introduces HNSW parameters (ef_construction, ef_search, M). Shows the speed/precision knob.
  9. Lesson 05/03 (Metadata filtering) — where clauses on metadata; pre-filter vs post-filter tradeoffs.
  10. Lesson 06/01 (Basic retrieval) — the canonical retrieve-then-generate loop end to end.
  11. Lesson 06/02 (Query preprocessing) — lowercase, strip stop-words, expand contractions. Heuristic-only; no LLM yet.
  12. Lesson 06/03 (Hybrid search) — combine dense (cosine) + sparse (BM25) scores. Min-max normalisation. Dynamic weighting based on query patterns (regex SKU detection bumps sparse weight).
  13. Lesson 06/04 (Multi-query) — retrieveParallel(queries) → rrfFuse(lists). Reciprocal Rank Fusion at k=60. Promotion candidate for src/retrievers/.
  14. Lesson 06/05 (Query rewriting) — LLM rewrites the user query into 1–3 paraphrases; feed to multi-query.

Lesson Catalogue

#TopicPathLOCWhat's Taught
00How RAG works00_how_rag_works/68Retrieve+generate concept with naive keyword overlap. Sets up motivation for embeddings.
01/01node-llama-cpp basics01_intro_to_llms/01_node_llama_cpp_basics/83Load a GGUF, prompt it, dispose. Just enough llama.cpp.
01/02LLM wrapper01_intro_to_llms/02_building_llm_wrapper/141Wrap llama.cpp in a class with retry, timeout, options. Foreshadows src/llms/LlamaCpp.
02Data loading02_data_loading/98PDF parsing via PDFLoader from src/. splitPages tradeoff.
03Text splitting + chunking03_text_splitting_and_chunking/355Inline splitter implementation; comparison of strategies on the same source PDF.
04/01Text similarity basics04_intro_to_embeddings/01_text_similarity_basics/321Cosine similarity intuition with bge-small embeddings; heatmap of pairwise scores.
04/02Generate embeddings04_intro_to_embeddings/02_generate_embeddings/563Batch embedding, cache, persistence; scales to real documents.
05/01In-memory vector store05_building_vector_store/01_in_memory_store/559VectorDB CRUD, similarity search, metadata storage.
05/02Nearest-neighbour search05_building_vector_store/02_nearest_neighbor_search/677HNSW deep-dive; ef_search sweep; speed vs recall.
05/03Metadata filtering05_building_vector_store/03_metadata_filtering/737Pre-filter vs post-filter; where clauses; large catalogue.
06/01Basic retrieval06_retrieval_strategies/01_basic_retrieval/605Canonical retrieve-then-generate loop. Includes a showcase.js demo.
06/02Query preprocessing06_retrieval_strategies/02_query_preprocessing/736Heuristic normalisation; stop-word removal; contractions.
06/03Hybrid search06_retrieval_strategies/03_hybrid_search/805Dense + sparse with min-max normalisation; dynamic weights for SKU/brand queries.
06/04Multi-query + RRF06_retrieval_strategies/04_multi_query_retrieval/943Parallel retrieval, RRF, dedup. Helper file multi-query-retrieval.js.
06/05Query rewriting06_retrieval_strategies/05_query_rewriting/263LLM-driven paraphrase generation; pipes into 06/04.

⚡ Algorithm Spotlight

AlgorithmLessonWhy This OneComplexity
Naive keyword retrieval — matches = queryWords.filter(w => docWords.includes(w)).length00Pedagogical only. Showing "this works for some queries, fails for others" sets up the motivation for embeddings.O(N·Q)
HNSW kNN search via embedded-vector-db05/02Logarithmic-ish lookup vs O(n) brute force. For 10K vectors gives sub-ms search; for 100K still <10ms.~O(log n)
Reciprocal Rank Fusion — score(d) = Σ 1/(k + rank + 1)06/04Similarity scores from different queries aren't directly comparable. Rank-based fusion sidesteps the calibration problem entirely.O(N K)

Deep-Dive Highlights

Lesson 00 — How RAG Works Foundation

Pure-JS keyword search. The 68-line lesson that demystifies RAG before introducing any vector math.

06 — Retrieval Strategies Advanced

Five sub-lessons: basic, query preprocess, hybrid (dense+BM25), multi-query+RRF, LLM query rewriting. The mature retrieval pipeline.

⚠ What Could Go Wrong

FailureDetectionRecoveryCascading Effects
Missing GGUF in models/node-llama-cpp throws on loadModelReader follows DOWNLOAD.mdLessons 01+ all fail; 00 still works (no model needed).
Network unreachable for PDF downloadpdf-parse fails on URL fetchLesson scripts log error; reader can substitute a local PDFLessons 02, 03 fail.
VectorDB capacity exceededembedded-vector-db raises insert errorReader bumps MAX_ELEMENTSLessons 05, 06 fail to insert beyond 10K docs by default.
OPENAI_API_KEY unsetOpenAIClient constructor throwsOptional — only used in 06/05 fallbackReader can use LlamaPrompter instead.

📊 Performance Profile

The lessons are interactive demos — cold-start dominates everything. Loading bge-small-en-v1.5.Q8_0.gguf (~33 MB) takes ~500 ms; loading a 1B-parameter generation model takes 1–3 s. Once warm, embedding throughput is ~30 docs/s on a modern CPU; HNSW search over 10K vectors is sub-ms; LLM generation runs at 5–30 tok/s depending on the model and quantisation.

The lessons trade speed for clarity. OutputHelper.withSpinner wraps long ops; OutputHelper.runExample reports total elapsed; chalk/ora ensure the reader sees what's happening. None of this matters for production performance — it matters for educational pacing.

Directory Map

examples/ source tree
examples/
├── 00_how_rag_works/                    ← 68 LOC, no embeddings, pure JS
│   ├── example.js  CONCEPT.md  CODE.md
├── 01_intro_to_llms/
│   ├── 01_node_llama_cpp_basics/        ← 83 LOC
│   └── 02_building_llm_wrapper/         ← 141 LOC
├── 02_data_loading/                     ← 98 LOC, uses src/loaders/PDFLoader
├── 03_text_splitting_and_chunking/      ← 355 LOC, inline splitter
├── 04_intro_to_embeddings/
│   ├── 01_text_similarity_basics/       ← 321 LOC
│   └── 02_generate_embeddings/          ← 563 LOC
├── 05_building_vector_store/
│   ├── 01_in_memory_store/              ← 559 LOC
│   ├── 02_nearest_neighbor_search/      ← 677 LOC
│   └── 03_metadata_filtering/           ← 737 LOC
└── 06_retrieval_strategies/
    ├── 01_basic_retrieval/              ← 605 LOC + showcase.js
    ├── 02_query_preprocessing/          ← 736 LOC
    ├── 03_hybrid_search/                ← 805 LOC
    ├── 04_multi_query_retrieval/        ← 943 LOC + multi-query-retrieval.js + config.js + logger.js
    └── 05_query_rewriting/              ← 263 LOC + query-rewriter.js + config.js

Lesson-Level Knobs

LessonKnobDefaultEffect
00topK in naiveKeywordSearch2Number of returned docs.
03chunkSize1000Max chunk length.
03chunkOverlap200Overlap between adjacent chunks.
04+EMBEDDING_MODEL_PATHbge-small.Q8_0Embedding model.
05+DIM384Vector dimensions; must match model.
05+MAX_ELEMENTS10000Vector DB capacity.
05+NS namespaceper lessonVectorDB namespace.
05/02ef_searchvariesHNSW search-time precision/speed knob.
06/01topK5Retrieval cardinality before context injection.
06/03dense vs sparse weight0.5/0.5Hybrid blend; auto-tuned for SKU-like queries.
06/04k in RRF60RRF damping constant; industry default.

Lesson Cards

00 — How RAG Works

The 68-line foundation lesson. Pure JS, no embeddings, no LLM call. Read this first.

01 — LLM basics

node-llama-cpp basics + LLM wrapper. Loads a GGUF, prompts it, disposes.

02 — Data loading

PDF parsing via PDFLoader. splitPages tradeoff.

03 — Text splitting

Compares character-window, sentence-aware, and recursive splitters on the same PDF.

04 — Embeddings

Cosine similarity intuition; batch embedding with cache.

05 — Vector store

HNSW, CRUD, NN search, metadata filtering. Three sub-lessons.

06 — Retrieval strategies

Five sub-lessons culminating in multi-query + RRF + LLM rewriting.