Embeddings, Vector Indexes (HNSW/IVF/PQ) & Cost
Your bar: explain what an embedding actually is, how vector indexes (HNSW / IVF / PQ) trade recall for speed and memory, and how to model the real token and dollar cost of a retrieval pipeline.
Your bar: explain what an embedding actually is and how model, dimensions, and similarity metric trade quality for cost; describe how an approximate-nearest-neighbor index (HNSW / IVF / PQ) buys speed by trading recall and memory; and model the real per-query token and dollar cost of a retrieval pipeline — knowing which levers move it. By the end you can answer:
given millions of chunks and a budget, how do you serve relevant context fast without re-indexing the world or blowing the bill?1This lesson is the engine room under Lesson 2’s semantic search and Lesson 3’s chunks. Each chip is one fact it nails down:
Embeddings Deep-Dive
An embedding turns text into a point in meaning-space. Texts that mean similar things land close together; that closeness is what semantic search measures.3
What an embedding is: meaning becomes geometry
The four knobs you actually choose
| Knob | What it sets | Effect | Trade-off |
|---|---|---|---|
| Model | General vs domain-specific vs multilingual | Decides what “similar” means for your data | Quality vs speed vs price; domain models win on jargon |
| Dimensions | Vector length (e.g. 384 · 768 · 1536 · 3072) | More dims capture more nuance | Bigger = more storage, RAM, and compute per query |
| Metric | Cosine · dot product · Euclidean | How “closeness” is scored | Cosine ignores magnitude; must match how the model was trained |
| What you embed | The chunk (granularity from Lesson 3) | Defines the unit of retrieval | Too big → averaged, diluted vector; too small → lost context |
Dimensions & the similarity metric
Chunk granularity decides embedding quality
Re-embedding is a real migration
| Change you make | Blast radius | Cost driver |
|---|---|---|
| Tune the prompt / retrieval k | None — query-time only | Free; no re-index |
| Truncate Matryoshka dims | Re-index vectors (cheap re-store) | Storage + rebuild, no re-embed |
| Change the embedding model | Re-embed every chunk + rebuild index | Embed tokens for the whole corpus |
| Re-chunk the corpus | Re-embed + re-index everything | Embed tokens + pipeline run |
Memory check
- Why does cosine similarity ignore magnitude? → it measures the angle between vectors, so topic (direction) matters, not document length
- What do Matryoshka embeddings let you trade? → dimensions for cost — truncate to fewer dims and keep most quality without re-embedding
- Why is changing the embedding model a migration? → new and old vectors are in different spaces; you must re-embed the whole corpus and rebuild the index
A teammate wants to bump the embedding model to the newest one “for a quick quality win.” What do you flag?
Hit these points: it’s not a flag flip — old and new vectors are incomparable, so every chunk must be re-embedded and the index rebuilt → that’s a corpus-wide embed-token bill plus pipeline time, and a cutover plan (dual-write / shadow index) so search isn’t down mid-migration → quantify it: tokens-per-chunk × chunk count × price → validate the win with an eval set before committing, since “newest” isn’t always better on your domain → consider Matryoshka truncation or a domain model as cheaper alternatives if the goal is quality-per-dollar.
Vector Index Internals (ANN)
Exact nearest-neighbor search compares the query to every vector — O(n) per query. At millions of vectors that’s too slow, so we approximate: trade a little recall for a huge speed-up.3
Why approximate? Brute force doesn’t scale
The three index families
| Index | How it works | Strength | Cost |
|---|---|---|---|
| HNSW (graph) | Navigable small-world graph; greedily hop toward neighbors | Fast, high recall | Memory-heavy; slower to build |
| IVF (cluster) | Partition space into cells; probe only the nearest few | Tunable speed via #probes | Recall dips if a neighbor is in an un-probed cell |
| PQ (compress) | Product quantization: compress vectors to codes | Big memory savings | Lossy → lower recall; often paired as IVF+PQ |
| Flat (brute force) | Compare against all vectors exactly | Perfect recall, zero build | O(n) per query — only for small corpora |
The trade-off triangle & metadata filtering
pgvector in your existing Postgres is fine and far simpler. Reach for a vector DB when scale, latency, or filtering demand it — not by default.Memory check
- What does ANN trade away, and for what? → a small amount of recall in exchange for sub-linear (much faster) queries
- HNSW vs PQ in one line each? → HNSW = graph, fast + high recall, memory-heavy; PQ = compressed vectors, low memory, lower recall
- Why prefer pre-filter over post-filter? → pre-filter scopes the search (right result count + tenant isolation); post-filter can leave too few results
Your vector search is fast but misses obviously relevant chunks for some queries. Where do you look?
Hit these points: “fast but misses” smells like low recall from the ANN settings — raise ef-search / n-probe and re-measure recall against an exact baseline → check if PQ compression is too aggressive (lossy codes drop near-neighbors) and whether the index needs a rebuild after heavy upserts → verify metadata filtering isn’t post-filtering away good hits, or scoping to the wrong tenant/date → confirm the query is embedded with the same model and normalization as the corpus → if recall is genuinely good and ranking is wrong, that’s a reranker problem (Lesson 2), not the index.
Indexing Pipelines, Freshness & Cost
Indexing is a data pipeline that runs forever, not a one-time script. Documents change, models change, and a stale index quietly serves yesterday’s answer.2
The ingestion pipeline
Freshness: a stale index is the “outdated context” failure
| Freshness need | Mechanism | Why |
|---|---|---|
| Edited / deleted docs | Incremental upsert · change-data-capture | Only re-embed what changed; delete removed chunks |
| New embedding model | Full re-embed + rebuild (Module 1) | Spaces are incompatible — no partial path |
| Volatile facts (prices, stock) | TTLs · prefer live structured fetch | Don’t index data that’s wrong minutes later |
| Stale chunk lingering | Index invalidation on source change | A stale index is Lesson 4’s outdated-context bug |
Where the tokens and dollars go
| Cost line | Roughly equals | Lever to pull |
|---|---|---|
| LLM generation | (input + output tokens) × price | Select fewer-but-better chunks; trim the prompt |
| Query embedding | query tokens × embed price | Cheaper / smaller embedding model |
| ANN search | index compute + latency | Tune recall vs latency; right index family |
| Rerank | top-n candidates × rerank price | Rerank only the top-n, not all candidates |
| Corpus embedding | corpus tokens × embed price (one-off / on re-index) | Batch (not real-time); avoid needless re-embeds |
Memory check
- What are the five stages of the ingestion pipeline? → load → parse → chunk → embed → upsert
- How should you handle fast-changing facts like prices? → TTLs and prefer live structured fetch — don't bake volatile data into the index
- Name three levers to cut per-query cost. → fewer/better chunks, rerank only top-n, cheaper embedding model (and prompt caching, batch embedding)
Your RAG feature’s cost-per-query is 3× the budget. Walk through how you’d bring it down.
Hit these points: first attribute the cost — break it into LLM input, LLM output, query embed, ANN search, rerank; the input tokens usually dominate → cut retrieved chunks to fewer-but-better (helps accuracy too) and trim the prompt; rerank only the top-n → cache the stable prefix with prompt caching (Lesson 9) so you stop re-paying for system prompt and tool defs → drop to a cheaper embedding model or truncated Matryoshka dims if quality holds on an eval set → batch corpus embedding instead of real-time → only then consider a smaller generation model. Measure each change against an eval set so you don’t trade dollars for accuracy.
How do you keep an index fresh for a docs site that updates daily, with some live-changing fields?
Hit these points: split by volatility — stable prose goes through incremental ingestion (CDC / re-embed only changed pages, delete removed ones) → fast-changing fields (price, availability) shouldn’t be embedded at all; fetch them live via a structured tool at query time, or TTL them → wire invalidation so a source edit triggers re-chunk/re-embed of just that doc → on an embedding-model change, run a full re-embed behind a shadow index and cut over → monitor staleness (oldest-chunk age) as an SLO, because a stale index is the silent outdated-context failure from Lesson 4.
Retrieval practice — test the three modules
Q1. An embedding is best described as…
- A compressed copy of the original text the model can later decode back word for word
- A keyword index that records which terms appear in a document for exact-match lookup
- A vector placing the text in meaning-space, where nearby points have similar meaning
- A cache of the model's previous answers keyed by the question that produced each one
Q2. Cosine similarity is popular for embeddings because it…
- Scores the angle between vectors, so topic matters and document length does not
- Counts how many exact keywords two pieces of text happen to share in common
- Always runs faster than every other distance metric on modern vector hardware
- Returns the raw byte size difference between the two text strings being compared
Q3. An approximate-nearest-neighbor (ANN) index exists primarily to…
- Guarantee that the exact true top-k neighbors are returned on every single query
- Compress the original documents so the raw text takes far less disk space overall
- Translate queries between languages before the embedding model ever sees the text
- Trade a little recall for sub-linear search so millions of vectors stay fast to query
Q4. Switching to a different embedding model requires you to…
- Only update a config value, since vectors from any model are directly comparable
- Re-embed the entire corpus and rebuild the index, because the spaces differ
- Re-rank existing results at query time without touching the stored vectors at all
- Lower the similarity threshold so the old vectors keep matching the new queries
Q5. The biggest lever on per-query cost in a RAG pipeline is usually to…
- Buy faster GPUs so the approximate search step finishes a few milliseconds sooner
- Increase the context window size so more retrieved chunks fit into a single call
- Select fewer but better chunks, since input tokens usually dominate the total bill
- Add a second reranker stage that re-scores every candidate the index returns each
Interview — pick your bar
Answer out loud in ~60s, then reveal. Core = recall · Senior = trade-offs & failure modes · Staff = synthesis under ambiguity · System Design = open design round (a different axis, not a harder level).
What is an embedding, and what does "near" mean in embedding space?
Name the three similarity metrics and say what cosine actually measures.
What does ANN stand for, and why approximate instead of exact?
What are the five stages of an ingestion pipeline?
A teammate wants to bump the embedding model "for a quick quality win." What do you flag?
Pick an index family for 50M vectors with tenant filtering and a p99 latency budget. Walk the trade-offs.
Vector search is fast but misses obviously relevant chunks for some queries. Where do you look?
Your RAG cost-per-query is 3× the budget. Name the levers, in order.
Design an embedding + index + cost strategy for a 200M-chunk corpus on a fixed monthly budget.
When is a vector index the wrong tool, and what would you build instead?
A stale index keeps serving a deleted doc. Frame this as a systemic failure and fix it at the root.
- Clarify scope & constraints — corpus size, change rate, query volume, latency SLO, budget ceiling, multi-tenant?
- Data shape & freshness — what's stable prose vs volatile fields; what must be fetched live vs embedded.
- Embedding choice — model, dimensions (Matryoshka headroom), metric; justified on an eval set, not by defaults.
- Index & retrieval — index family vs the recall/latency/memory triangle; pre-filter for tenancy; rerank top-n.
- Ingestion & freshness — incremental CDC, deletes, invalidation, batched embedding; re-embed-the-world handled via shadow index.
- Cost model — per-line cost (LLM input dominates), the levers, and where you'd spend vs save.
- Observability & failure modes — recall, cost-per-query, oldest-chunk age as SLOs; what breaks first.