Module 3: RAG Infrastructure & Retrieval · 5.75h
01 · UNDERSTAND
Day 13 Theory — Embeddings and Semantic Retrieval
Why keyword search is not enough
People rarely describe a problem using the exact words used in documentation.
A user might write:
“I cannot sign in after changing my phone.”
while the knowledge article is titled:
“MFA device re-enrollment after handset replacement.”
There may be very little exact word overlap, but the meanings are strongly related.
Traditional keyword search can still be extremely useful, especially for exact identifiers and rare technical terms. But we also want a way to retrieve content by meaning.
That is where embeddings become useful.
What an embedding is
An embedding model converts an input such as text into a fixed-length vector: an ordered list of numbers.
For example:
"reset my password"
↓
embedding model
↓
[0.12, -0.44, 0.08, 0.31, ...]
The actual vector may contain hundreds or thousands of dimensions depending on the model.
Do not think of each number as a simple field such as:
[topic, urgency, sentiment, security]
Modern embedding dimensions normally do not have such clean human-readable meanings.
The useful property is relative geometry: inputs that the model considers semantically related tend to have vector representations that are closer according to the similarity function used by that embedding/index system.
A 2D mental model
Real embeddings live in high-dimensional space, which is difficult to visualize.
For intuition, pretend there are only two dimensions:
authentication problems
^
|
MFA reset • | • password reset
|
-------------------------+---------------------->
|
printer issue • | • VPN issue
|
The real model may use thousands of dimensions, but the idea is similar: related concepts can form useful neighborhoods.
This is only a mental model. A visualization does not mean the real embedding space literally has one axis called “authentication.”
Embeddings are learned representations
The embedding model has learned statistical representations from training data.
That means similarity reflects the behavior of that model, not an objective mathematical truth about the world.
Two consequences follow:
- different embedding models can organize the same text differently;
- semantic similarity does not guarantee task relevance or factual correctness.
The embedding is a retrieval signal, not an oracle.
Similarity: how do we compare two vectors?
Once the query and document chunks have vectors, we need a numerical way to compare them.
Common choices include:
- cosine similarity,
- dot product,
- Euclidean or related distance measures.
Use the similarity/distance behavior expected by the embedding model and vector index. Do not casually switch metrics because their numerical scales and ranking behavior can differ.
Cosine similarity in plain English
Cosine similarity focuses on the angle between two vectors.
For vectors A and B:
cosine_similarity(A, B) = (A · B) / (||A|| ||B||)
Where:
A · Bis the dot product,||A||is the magnitude of vector A,||B||is the magnitude of vector B.
You do not need to calculate large embeddings by hand, but you should understand the intuition.
If two vectors point in very similar directions, cosine similarity is high.
A ----->
B ------>
If their directions differ strongly, similarity is lower.
A ----->
B ^
|
This is useful because direction can capture semantic pattern while reducing the influence of raw vector magnitude.
Tiny numerical example
Take two simple vectors:
A = [1, 0]
B = [0.9, 0.1]
C = [0, 1]
A and B point in almost the same direction, so their cosine similarity is high.
A and C are perpendicular, so their cosine similarity is much lower.
Again, real embeddings are far larger, but the comparison principle is the same.
The semantic retrieval pipeline
A minimal semantic search system has two phases.
Phase 1 — indexing
Document
↓
split into chunks
↓
embedding model
↓
vector + text + metadata
↓
vector index
We usually embed chunks ahead of time and store them.
Phase 2 — querying
User query
↓
same/compatible embedding model
↓
query vector
↓
nearest-neighbor search
↓
top candidate chunks
The output is a ranked candidate set.
That candidate set may later be inserted into an LLM prompt as part of Retrieval-Augmented Generation (RAG).
Why query and document embeddings must be compatible
Suppose the documents were embedded using Model A but the query is embedded using an unrelated Model B.
The numbers may have the same length, yet they can represent completely different learned spaces.
Comparing them would be like comparing coordinates from two unrelated maps.
Map A coordinate: [12, 44]
Map B coordinate: [12, 44]
Same numbers do not imply same place.
Unless models are explicitly designed to share a compatible embedding space, query and indexed document vectors should come from the same embedding setup.
Changing embedding model often means planning a re-embedding/index migration.
What a vector database actually stores
A vector database is not “AI memory.”
A useful vector record may contain:
{
"id": "kb-article-42#chunk-3",
"vector": [0.12, -0.44, 0.08],
"text": "To re-enroll MFA after replacing a phone...",
"metadata": {
"source": "identity-handbook",
"section": "MFA",
"version": "2026-08"
}
}
The vector supports similarity search.
The metadata supports things such as:
- filtering,
- source identity,
- access control,
- version management,
- citations,
- deletion/update workflows.
If you store only anonymous vectors without source traceability, maintaining and citing the knowledge base becomes much harder.
Approximate nearest-neighbor search
For a tiny dataset, we could compare the query vector against every stored vector.
At large scale, that becomes expensive.
Vector databases commonly use approximate nearest-neighbor (ANN) indexes to search efficiently.
“Approximate” means the system may trade a small amount of exactness for much faster retrieval.
This introduces another engineering trade-off:
search speed / memory / index cost
versus
retrieval recall / exactness
The exact index algorithm is not today's focus, but remember that vector search itself has tunable behavior.
top_k means candidate count, not confidence
A query such as:
search(query_vector, top_k=5)
means:
Return five of the nearest candidates according to this index and metric.
It does not mean:
These five documents are definitely relevant.
If the knowledge base contains nothing useful, the system may still return the five closest bad matches.
That is why production retrieval often also considers:
- similarity thresholds where appropriate,
- metadata filters,
- reranking,
- no-answer behavior,
- retrieval evaluation.
Nearest does not mean correct
Suppose a user asks:
“Can a contractor reset the CFO's password?”
The nearest chunk may discuss password reset steps but omit the authorization policy.
Semantically related? Yes.
Sufficient to answer safely? No.
Retrieval must be evaluated against the information need, not only vector similarity.
Semantic search failure modes
Ambiguous query
"reset access"
Could mean password, VPN, MFA, account unlock or permissions.
Poor chunking
The correct sentence may be separated from the condition that changes its meaning.
Stale documents
A semantically perfect old policy is still the wrong source.
Missing metadata filters
The system may retrieve a document belonging to another tenant or permission scope.
Embedding-model limitations
Domain-specific terminology may not be represented as well as expected.
Index configuration
Approximate search or poor tuning can miss relevant candidates.
This is why “we use a vector database” is not evidence that RAG works.
Semantic and lexical search complement each other
Keyword/BM25-style retrieval can be strong when the query contains:
- exact error codes,
- product IDs,
- ticket numbers,
- function names,
- rare acronyms.
Semantic retrieval is strong when meaning is expressed with different wording.
Later we combine these strengths through hybrid retrieval.
Metadata is also a security boundary
Imagine two departments have private knowledge bases.
The correct order is conceptually:
identify caller / tenant
↓
apply allowed metadata scope
↓
retrieve candidates inside that scope
Do not retrieve everything and hope the LLM ignores unauthorized chunks.
A vector database is still a data system and needs access-control design.
How we evaluate retrieval
Before asking the LLM to generate an answer, we can evaluate retrieval independently.
Given a test query, ask:
- Was a relevant chunk present in the top
k? - How high did it rank?
- Did filtering exclude the correct document?
- Did irrelevant chunks dominate the result?
This separation is important:
Bad final answer
|
+-- retrieval failure?
|
+-- generation failure despite good retrieval?
If we do not measure retrieval separately, debugging RAG becomes guesswork.
Service Desk connection
Today the Service Desk gains semantic knowledge retrieval.
Before:
User wording -> exact rules / keyword lookup
After:
User question
↓
query embedding
↓
semantic candidate retrieval
↓
source-aware chunks
↓
later: evaluated RAG answer
The principle is:
Embeddings turn inputs into a learned vector representation that lets us rank semantically related candidates. Similarity helps us find evidence; it does not prove that the evidence is correct, sufficient, current or authorized.
02 · APPLY
Lesson Overview
This is the applied companion for Day 13. Read DAY_13_THEORY.md first for the beginner-first teaching of Embeddings and Semantic Retrieval. Then use the real service-desk-day-13/ project to trace, run, debug, and explain the concept.
Service Desk Alignment
Day 13 adds Embeddings and Semantic Retrieval to the running Service Desk. Start with embedding_provider.py, corpus.py, math_utils.py, vector_index.py, failure_cases.py, then follow imports and tests to identify the actual runtime path.
Why This Topic Matters
The theory chapter explains why Embeddings and Semantic Retrieval is needed. Here the goal is evidence: identify where the capability is implemented, what observable behavior changes, and how the repository proves both success and failure behavior.
System Architecture
graph TD
A[User Query Text] --> B[Embedding Model]
B --> C[Query Vector]
C --> D[Cosine Similarity Match]
E[Document KB Corpus] --> F[Vector Index]
F --> D
D --> G[Top-K Semantic Documents]
Worked Code Example: Vector Retrieval
import numpy as np
def cosine_similarity(vec_a: np.ndarray, vec_b: np.ndarray) -> float:
"""Calculates cosine similarity between two dense embedding vectors."""
norm_a = np.linalg.norm(vec_a)
norm_b = np.linalg.norm(vec_b)
if norm_a == 0 or norm_b == 0:
return 0.0
return float(np.dot(vec_a, vec_b) / (norm_a * norm_b))
# Example Vector Comparison
query_vector = np.array([0.15, 0.82, -0.41, 0.09])
doc_vector_1 = np.array([0.14, 0.80, -0.39, 0.10]) # Relevant policy
doc_vector_2 = np.array([-0.50, 0.10, 0.70, -0.20]) # Irrelevant doc
print("Sim Doc 1 (Relevant):", cosine_similarity(query_vector, doc_vector_1))
print("Sim Doc 2 (Irrelevant):", cosine_similarity(query_vector, doc_vector_2))
Code Walkthrough & Mechanics
Read embedding_provider.py, corpus.py, math_utils.py, vector_index.py, failure_cases.py with these theory sections beside you:
- Why keyword search is not enough — locate its implementation and evidence.
- What an embedding is — locate its implementation and evidence.
- A 2D mental model — locate its implementation and evidence.
- Embeddings are learned representations — locate its implementation and evidence.
- Similarity: how do we compare two vectors? — locate its implementation and evidence.
For each concept identify the real function/class/protocol boundary, its input/state, its observable result, and the assertion in tests/test_embeddings.py that proves the behavior. If a concept has no implementation or evidence, record that as a gap rather than inventing one.
Common Mistakes & Debugging Guidance
Use the theory—not generic timeout or .env advice—to decide what can fail today.
Failure lens: revisit Embeddings are learned representations, Similarity: how do we compare two vectors?.
- Reproduce the smallest case that violates one of those expectations.
- Trace the real Day 13 modules until you find the first incorrect state/output/decision.
- Use
tests/test_embeddings.pyas executable evidence. - Add a regression test if the failure is not already represented.
- Fix the smallest responsible boundary and rerun the relevant test before the full suite.
Your debugging explanation must name the topic-specific invariant that failed, not merely say “an exception occurred.”
Practical Lab Instructions
- Summarize these theory ideas before opening the implementation:
- Why keyword search is not enough
- What an embedding is
- A 2D mental model
- Embeddings are learned representations
- Inspect the most relevant real Day 13 modules first:
service_desk/embedding_provider.pyservice_desk/corpus.pyservice_desk/math_utils.pyservice_desk/vector_index.pyservice_desk/failure_cases.py
- Inspect the automated evidence:
tests/test_embeddings.py
- Establish the baseline:
cd service-desk-day-13 PYTHONPATH=. pytest tests/test_embeddings.py -q - Trace one theory concept through the actual nested modules and tests.
- Run one success case and record input → mechanism → observable result.
- Exercise one topic-specific failure/boundary case and name the invariant that protects the system.
- Re-run the relevant tests and explain theory → implementation → evidence.
Done when: another student can reproduce your trace without relying on an invented sample.
Key Takeaways
- Be able to explain Why keyword search is not enough and point to its implementation/evidence in Day 13.
- Be able to explain What an embedding is and point to its implementation/evidence in Day 13.
- Be able to explain A 2D mental model and point to its implementation/evidence in Day 13.
Knowledge Check & Scenario Questions
- Concept: Using Why keyword search is not enough, explain the engineering problem Day 13 is solving without naming a framework as the answer.
- Mechanism: How does What an embedding is appear in the real project? Start from
service_desk/embedding_provider.pyand name the observable state/output/event that changes. - Failure: For A 2D mental model, describe one incorrect implementation or boundary condition and the evidence you would expect in
tests/test_embeddings.py. - Design review: Which assumption in today's design would you verify before reusing this implementation in a different production system?
Official References
- Pinecone Dense Vector Embeddings Guide: https://www.pinecone.io/learn/
- Sentence-Transformers Official Docs: https://www.sbert.net/
03 · EXPLAIN
Interview checkpoint
Explain one design decision from this lesson, the alternative you rejected, and the failure mode or evidence that justified your choice.