Zucchetti LLM Course — Lecture 4 of 10
From frozen knowledge to live data — and when to use what
Kamyar Zeinalipour · 90 minutes
Section 1 of 5
Why LLMs need external knowledge
LLMs have two critical limitations:
Model knowledge is fixed at training cutoff.
GPT-4 does not know what happened yesterday.
It cannot access your internal documents.
It has never seen your company’s policies.
When the model lacks knowledge, it does not say “I don’t know.”
It generates a plausible-sounding answer.
The answer looks correct.
It is completely fabricated.
Bake knowledge into the weights.
Like memorizing a textbook — you might misremember.
Provide knowledge at query time.
Like an open-book exam — you can check your answers.
The idea: instead of asking the model to remember, let it look things up.
Two phases: Offline (index once) — Online (retrieve & generate per query)
y = answer, x = query, d = retrieved doc — RAG marginalizes over retrieved documents (Lewis et al., 2020)
Lewis et al., “Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks”, NeurIPS 2020
A concrete example of the full pipeline.
Chunk 1 (hr_policy.pdf, p.12): “Employees are entitled to 5 months of mandatory maternity leave...”
Chunk 2 (hr_policy.pdf, p.13): “Optional parental leave can be extended up to 6 additional months...”
Chunk 3 (hr_policy.pdf, p.14): “During maternity leave, employees receive 80% of their salary...”
“Maternity leave is 5 months mandatory at 80% salary, with an option to extend up to 6 additional months. (Source: hr_policy.pdf, pages 12–14)”
Lewis et al., “Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks”, NeurIPS 2020
RAG transforms the generation task from recall to reading comprehension.
“What is Zucchetti’s remote work policy?”
Model output: “Employees may work remotely up to 3 days per week, as per the 2022 Smart Working Agreement.”
β οΈ Completely invented. Confident. Wrong.
“What is Zucchetti’s remote work policy?”
Model output: “According to hr_policy_2024.pdf p.8: employees may request up to 2 remote days per week after 6 months of employment.”
β From document. Verifiable. Cited.
LLMs excel at reading comprehension — RAG converts recall into comprehension.
| π RAG | π§ Fine-Tuning | |
|---|---|---|
| What it changes | Knowledge available | Model behavior / weights |
| Knowledge source | External documents | Baked into parameters |
| Update frequency | Fast (swap or re-index documents β minutes for typical corpora) | Retrain required (hours–days) |
| Cost | Low (embeddings + DB) | High (GPU compute) |
| Hallucination | Significantly reduced (answers grounded in text β but not eliminated: model can still ignore context) | Medium (may still confabulate) |
| Interpretability | High (can show sources) | Low (black box) |
| Best for | Facts, policies, Q&A | Style, format, behavior |
☕
Stretch your legs — we are going deep next
Next up: How Search Evolved — from keywords to meaning — the math behind retrieval
Section 2 of 5
How information retrieval evolved over 50 years
| Era | Method | How It Works | Limitation |
|---|---|---|---|
| 1970s | Boolean | Exact keyword match (AND, OR, NOT) | No ranking, no synonyms |
| 1990s | TF-IDF | Rank by term frequency × rarity | No semantic understanding |
| 1994+ | BM25 | Improved TF-IDF with saturation & length norm | Still keyword-based |
| 2019+ | Dense Retrieval | Embed query & docs as vectors, compare similarity | Needs good embeddings |
Information retrieval before statistics. Simple logic, no ranking.
Documents must contain exact keywords combined with logical operators.
Query: maternity AND leave AND policy NOT termination
No ranking — every matching document is equal. 10,000 results, no idea which is best.
No synonyms — query “car” misses documents saying “automobile”.
Brittle — one typo, zero results. Expert users only.
No context — “python AND programming” matches a document about programming AND snakes separately.
The foundation of keyword search. Still used today in hybrid systems.
How often does the word appear in this document?
More occurrences = more relevant
How rare is this word across all documents?
“the” appears everywhere = low IDF
“maternity” is rare = high IDF
Note: Modern implementations use smoothed IDF = log(N / (df+1)) + 1 to avoid division by zero.
Spärck Jones, “A Statistical Interpretation of Term Specificity and Its Application in Retrieval”, Journal of Documentation, 1972
The default ranking in Elasticsearch, Solr, OpenSearch. Still the strongest sparse retriever.
The 100th “policy” adds almost nothing to the score.
k1 ∈ [1.2, 2.0] controls saturation speed. TF-IDF has no cap.
“policy” 3× in a 2-page memo vs. 50× in a 100-page manual.
b ∈ [0, 1] controls length penalty. b=0.75 is default. The memo wins.
Inverted index finds 6 candidate chunks. BM25 scores each one:
| Chunk | Has “sick”? | Has “leave”? | Has “policy”? | BM25 Score |
|---|---|---|---|---|
| chunk3 | β | β | β | 6.48 π |
| chunk7 | β | β | β | 5.90 |
| chunk41 | β | β | β | 3.36 |
| chunk12 | β | β | β | 1.68 |
| chunk88 | β | β | β | 1.68 |
| chunk1 | β | β | β | 1.44 |
Score = IDF(“sick”)×TF_norm + IDF(“leave”)×TF_norm + IDF(“policy”)×TF_norm — summed across query terms. “sick” is rare (IDF=4.2) so contributes more than “policy” (IDF=1.8).
Robertson & Zaragoza, “The Probabilistic Relevance Framework: BM25 and Beyond”, Foundations and Trends in IR, 2009
In RAG, the unit being indexed and retrieved is always a chunk — not the original full document. Each method works on chunks.
Uses an inverted index — a lookup table: term → [chunk_ids that contain it]
β‘ Fast: only scores chunks that share at least one query term
Uses a vector index (HNSW) — stores chunk embeddings in a navigable graph
β‘ Fast: HNSW navigates from coarse to fine — O(log n)
Instead of matching keywords, we match meaning. The key intuition: similar meanings point in the same direction in vector space.
Each chunk is a bag of words.
Vector has 50,000+ dimensions (one per word in vocabulary).
Most values are zero (sparse).
Match = shared words exist. No synonyms.
Each chunk is a meaning direction in space.
Vector has 384–3072 dims. All non-zero.
Cosine measures angle between two directions.
1 = same direction = same meaning | 0 = unrelated | −1 = opposite
“vacation days” → embed → [0.21, −0.45, 0.78, ...]
“time off” → embed → [0.19, −0.41, 0.82, ...]
cosine similarity = 0.89 — BM25 score = 0 (no shared words)
π‘ Unit-normalized vectors: &lVert;e&rVert; = 1 ⇒ cos(A,B) = A⋅B (how FAISS/ChromaDB compute similarity internally)
Cosine similarity gives the score. But how do you search 1 million vectors fast enough?
Compare query against every vector.
1M × 384 dims = 384M operations → ~300ms
Too slow for real-time use.
Build a smart index that enables shortcuts.
~20 comparisons instead of 1M → ~1ms
300× faster. Trades ~2–5% accuracy for speed.
Two fundamentally different ways to measure relevance. Different trade-offs.
Query and chunk are embedded separately. Relevance = cosine similarity between their vectors.
β All chunk embeddings precomputed offline β at query time: one embedding call + ANN search = milliseconds
β Works with any embedding model (MiniLM, BGE-M3, OpenAI, etc.)
β οΈ Query and chunk never "interact" β relevance is geometric distance only
Query and chunk are processed together by the model. Output: one relevance score.
β Model reads query against chunk β full interaction = higher accuracy
β Cannot precompute anything β must run the model once per candidate at query time
β Too slow to run on 100,000 chunks β only practical on a small candidate set
Cross-encoder runs 100 times β not 100,000. Bi-encoder eliminates 99,900 chunks first.
The bi-encoder works with any embedding model. The field moves fast β here is where it stands in 2025.
| Model | Dims | Tokens | Cost | Status / Use |
|---|---|---|---|---|
| MiniLM-L6-v2 | 384 | 256 | Free | β οΈ Legacy β use for this lab only |
| Qwen3-Embedding-0.6B | 1024 | 32768 | Free (local) | β Modern lightweight β replaces MiniLM |
| BGE-M3 | 1024 | 8192 | Free (local) | β Production workhorse β dense + sparse + hybrid |
| text-embedding-3-large | 3072 | 8191 | Paid API | βοΈ Safe cloud default β supports MRL |
| Gemini Embedding 2 | 3072 | 32768 | Paid API | βοΈ Top-tier β long docs, multimodal |
BGE-M3 β best open-source choice. Unique: one model for dense, sparse, AND hybrid retrieval. 100+ languages. Data stays local.
text-embedding-3-large or Gemini Embedding 2. Easy, high quality. text-3-large supports Matryoshka β truncate dims without quality loss.
Production systems combine keyword and semantic search.
Keyword precision
“Article 47” → exact match
+
Semantic recall
“time off” → “vacation”
=
Precision + recall
Ranked by combined score
BM25 (normalized): 0.72 | Dense: 0.85
Hybrid (α=0.4): 0.4×0.72 + 0.6×0.85 = 0.799
BM25 scores can be 0–20+. Cosine scores are 0–1. You cannot add them raw — BM25 would always dominate.
→ Normalize each to [0,1] first (min-max or softmax), then apply α.
Advanced alternative: Reciprocal Rank Fusion (covered in L5) — avoids score normalization entirely.
Craswell et al., “MS MARCO: Benchmarking Ranking Models in the Large-Data Regime”, SIGIR 2021
Section 3 of 5
The hidden bottleneck of every RAG system
Documents are too long to embed as a single vector. We must split them into chunks.
A 50-page HR manual = ~25,000 tokens
Embedding models have a limit of 256 tokens (MiniLM-L6-v2) or 8192 tokens (BGE-M3)
Even if you could embed it all, one vector cannot capture 50 different topics
Long text → the embedding becomes an average of everything → it matches nothing well.
Analogy: mix all paint colors together → you get grey. The grey matches no specific color. Same with meaning: average of 50 topics = matches none of them.
User asks about “sick leave” → you need just those 3 paragraphs, not the whole manual.
Returning the full 50-page manual as context would fill the entire LLM context window with noise.
Split every N characters. Simple but cuts mid-sentence. Not recommended.
Split on natural boundaries: paragraphs → sentences → characters. Preserves semantic coherence.
Try \n\n (paragraph) first. If the piece is still longer than your target chunk size (e.g. 400 tokens), try . (sentence). Still too long? Try commas. Go coarse to fine until each piece fits.
Use embeddings to detect topic shifts. Most accurate but most complex.
Compare adjacent sentence embeddings. When similarity drops below threshold θ, start a new chunk.
With 50–100 token overlap between consecutive chunks
Overlap = the end of chunk N is repeated at the start of chunk N+1
Same source text. Target size: 400 tokens. | “Employees are entitled to 5 months of mandatory maternity leave. During this period, they receive 80% of their salary. Optional parental leave can be extended up to 6 additional months at reduced pay. Requests must be submitted to HR at least 30 days in advance.”
Split every 50 characters. No understanding of language.
Chunk 1: “Employees are entitled to 5 months of mand”
Chunk 2: “atory maternity leave. During this peri”
Chunk 3: “od, they receive 80% of their salary...”
Word “mandatory” split in half. Neither chunk is meaningful alone.
Try paragraph β sentence β comma. Stop when ≤ 400 tokens.
Chunk 1: “Employees are entitled to 5 months of mandatory maternity leave.”
Chunk 2: “During this period, they receive 80% of their salary.”
Chunk 3: “Optional parental leave... up to 6 months at reduced pay. Requests must be submitted 30 days in advance.”
Each chunk = one complete idea. Clean boundaries, full sentences.
Embed each sentence. Split where cosine similarity drops below θ = 0.7.
s1→s2: cos = 0.91 (same topic, keep)
s2→s3: cos = 0.88 (still related, keep)
s3→s4: cos = 0.41 → topic shift! split here
Chunk 1: sentences 1–3 (leave & salary)
Chunk 2: sentence 4 (HR process)
Most accurate. Needs an embedding model just to chunk β more complex.
Every chunk carries structured data alongside its text and embedding.
A chunk is not just text. It is a tuple: (text, embedding, metadata)
| Metadata Field | Example | Why It Matters |
|---|---|---|
source | hr_policy_2024.pdf | Source attribution — users can verify |
page | 12 | Precise reference |
section | Maternity Leave | Hierarchical filtering |
date | 2024-01-15 | Recency filtering |
department | HR | Access control / routing |
User asks: “What was the remote work policy in 2024?” — How does “in 2024” become date = 2024?
{"date": "2024"}, then the retriever uses that as a filter. Most common.
\b20\d{2}\b) and maps to metadata. Simple but brittle.
Section 4 of 5
How similarity search works at scale
You have 1 million chunks, each with a 384-dim embedding. A user asks a question. How do you find the most similar vectors?
Compare query to every vector.
~300ms per query. Perfect recall, unusable speed.
Build an index that creates intelligent shortcuts.
~1ms per query. 95%+ recall, 300× faster.
| Algorithm | Idea | Used By |
|---|---|---|
| HNSW | Multi-layer navigable graph — coarse to fine | ChromaDB, Qdrant, pgvector |
| IVF (Inverted File) | Partition space into clusters, search only nearby clusters | FAISS, pgvector |
| PQ (Product Quantization) | Compress vectors to reduce memory & speed up comparisons | FAISS (large-scale) |
Hierarchical Navigable Small World — the default algorithm in ChromaDB, Qdrant, and most vector databases.
HNSW builds multiple layers of a navigable graph. To find the nearest chunk:
1. Start at a random node on the top layer
2. Greedily move to whichever neighbor is closest to the query vector
3. When no neighbor is closer → drop to the next layer at the same node
4. Repeat until the bottom layer → return the nearest nodes as results
Each layer dramatically narrows the search space. Total: ~20 hops instead of 1M scans.
collection.query() in ChromaDB runs it automatically. Understanding the intuition helps you tune it (e.g., ef_search, M parameters).Malkov & Yashunin, “Efficient and Robust Approximate Nearest Neighbor Search Using HNSW Graphs”, IEEE TPAMI, 2020
Three categories. The trend: every major database now supports vectors. Choose based on your existing stack.
| Database | Type | ANN | Best For |
|---|---|---|---|
| ChromaDB | Embedded (Python) | HNSW | Prototyping, learning — our lab |
| Qdrant | Client-Server | HNSW | Production RAG, advanced filtering |
| Pinecone | Managed Cloud | Proprietary | Zero-ops enterprise |
| Weaviate | Client-Server | HNSW | Multi-modal, hybrid search built-in |
| Database | Extension | ANN | Best For |
|---|---|---|---|
| PostgreSQL | pgvector | IVFFlat / HNSW | Already on Postgres — no new infra |
| MongoDB | Atlas Vector Search | HNSW | Already on Mongo — document + vector |
| Elasticsearch | kNN search (8.x+) | HNSW | Already on Elastic — keyword + vector |
| Neo4j | Vector Index | HNSW | GraphRAG — relationships + vectors |
| Library | By | ANN | Best For |
|---|---|---|---|
| FAISS | Meta | IVF, PQ, HNSW | Research, billion-scale, GPU |
| Annoy | Spotify | Random projection trees | Read-heavy, memory-mapped |
Five components, each with its own design decisions.
| # | Component | Key Decision | Our Choice (Lab) |
|---|---|---|---|
| 1 | Document Loader | Format handling | pypdf |
| 2 | Text Splitter | Chunk size, overlap, strategy | Recursive, 400 / 80 |
| 3 | Embedding Model | Quality vs. speed vs. cost | MiniLM (384d) |
| 4 | Vector Database | Scale, features, hosting | ChromaDB |
| 5 | Generator (LLM) | Model, prompt template | Gemini 2.0 Flash |
π¬ You will build this exact pipeline in the Lecture 5 combined lab.
Section 4 — Pipeline & Prompt Design
You already master prompting from Lecture 3. Now apply it with grounding constraints specific to RAG.
The prompt template determines answer quality as much as retrieval does.
“Answer based ONLY on the provided context” — prevents hallucination
“If the answer is not in the context, say: I don’t have enough information”
“Always cite which document the information comes from”
Not including grounding → model ignores context and uses its own knowledge → hallucination
Section 4 β Pipeline & Prompt Design
Understanding failures helps you debug and improve your system.
| Failure | Symptom | Root Cause | Solution |
|---|---|---|---|
| Wrong chunks | Answer about wrong topic | Embedding model too weak | Better model; hybrid search → L5 |
| Chunks too small | Incomplete answers | Context fragmented | Increase chunk size |
| Chunks too large | Irrelevant noise in answer | Diluted embeddings | Decrease chunk size |
| No relevant docs | Hallucinated answer | Missing refusal prompt | Add “I don’t know” instruction |
| Ignores context | Uses own knowledge | Weak grounding prompt | Stronger “ONLY context” instruction |
| Stale answers | Outdated information | Documents not re-ingested | Schedule periodic re-indexing |
Section 5 of 5
The most important decision you will make on every project
π¬
Change the instructions
Model stays unchanged
Zero data, zero compute
π
Give the model knowledge
External docs at query time
Low cost, fast updates
π§
Change the model’s behavior
Retrain on your data
High cost, high customization
| Criterion | π¬ Prompting | π RAG | π§ Fine-Tuning |
|---|---|---|---|
| Setup effort | Minutes | Hours | Days–Weeks |
| Data required | None | Documents | 100–10K examples |
| Compute cost | $0 | Low ($) | High ($$$) |
| Update speed | Instant | Minutes | Hours |
| Customization | Low | Medium | High |
| Hallucination | High risk | Reduced (grounded) | Medium |
| Best for | General tasks | Knowledge Q&A | Style / domain |
Does the model already know the answer well enough?
Yes → π¬ Prompting
↓ No
Is the knowledge in documents you can provide?
Yes → π RAG
↓ No
Do you need specific behavior, format, or domain expertise?
Yes → π§ Fine-Tuning (Lecture 9)
↓ Still unsure?
Try RAG + better prompts first. Fine-tune only if that fails.
| βοΈ Cloud API | π Local Inference | |
|---|---|---|
| Setup time | 5β10 min (API key + env) | 15β30 min with Ollama / LM Studio 2β4 h only if full CUDA GPU drivers needed |
| Cost model | Per token ($) | Fixed (hardware cost) |
| Privacy | Data leaves premises | Data stays local |
| Models | GPT-4, Gemini, Claude | LLaMA, Mistral, Phi |
| Quality | Best available | Smaller, quantized |
| Tools | API calls | Ollama, vLLM, LM Studio, π€ HuggingFace |
→ π¬ Prompting — general task, model already knows how. Just good instructions.
→ π RAG — knowledge is in company documents. Ground answers in retrieved chunks.
→ π RAG (not prompting!) — domain-specific product knowledge. Prompting alone would hallucinate specs.
→ π¬ Prompting first, then π§ Fine-Tune if accuracy isn’t enough. Start simple, escalate only if needed.
→ π§ Fine-Tuning — needs specific style, clinical terminology, domain language.
1. RAG = Retrieve β Augment β Generate — give the model an open book
2. Search evolved from keywords (TF-IDF, BM25) to meaning (dense embeddings)
3. Chunking determines quality — 300–500 tokens, recursive, with overlap
4. Vector databases use HNSW for millisecond similarity search at scale
5. Prompt design matters — grounding, refusal, and citation instructions
6. Prompting β RAG β Fine-Tuning — always try the simpler approach first
7. Most RAG failures are retrieval failures — fix chunking and embeddings before touching the LLM
❓
Thank you!
Kamyar Zeinalipour — Zucchetti LLM Course
Next → Lecture 5: Advanced RAG & Intelligent Routing
Theory: Re-ranking · HyDE · Query decomposition · Semantic routing
π¬ Combined Lab: RAG Pipeline (L4) + Query Router (L5) — back-to-back hands-on session