RAGEngineering Hub
Chapter 5 · concept 1 of 4 · show all
  1. 1What RAG is, and why
    1. 1.1Why models need outside knowledge, and what hallucination means
    2. 1.2What RAG is: anatomy of a request
    3. 1.3RAG vs fine tuning vs long context and prompt caching
    4. 1.4Use cases and the quality, latency and cost triangle
    5. Chapter overview
  2. 2Tokens, cost and your first RAG
    1. 2.1Tokens and context windows for RAG
    2. 2.2The RAG lifecycle and your first RAG in miniature
    3. Chapter overview
  3. 3Getting documents in
    1. 3.1Content sources and file formats
    2. 3.2Parsing, layout and OCR
    3. 3.3Tables, images and mixed content
    4. 3.4Cleaning, normalization and quality gates
    5. 3.5Metadata, document identity and versioning
    6. 3.6Updates, deletes, reindexing and ingestion pipelines
    7. Chapter overview
  4. 4Chunking
    1. 4.1Why we chunk, and the main chunking strategies
    2. 4.2Structure aware chunking and tables
    3. 4.3Chunk size, overlap and token based sizing
    4. 4.4Contextual chunking and late chunking
    5. 4.5Parent child and multi representation indexing
    6. 4.6Chunk metadata, provenance and the chunking debugging playbook
    7. Chapter overview
  5. 5Embeddings
    1. 5.1Embeddings and similarity from first principles
    2. 5.2What embeddings cannot do
    3. 5.3Choosing an embedding model: size, cost, language
    4. 5.4Embedding model migration and index rebuilds
    5. Chapter overview
  6. 6Vector stores and scores
    1. 6.1Vector databases, ANN indexes and choosing a store
    2. 6.2Score calibration and thresholds
    3. 6.3Late interaction and multivector retrieval
    4. Chapter overview
  7. 7Finding the right chunks
    1. 7.1Query understanding, rewriting and expansion
    2. 7.2Dense retrieval and keyword search with BM25
    3. 7.3Hybrid retrieval and rank fusion
    4. 7.4topK, no good match, and metadata filtering
    5. 7.5Routing, diversity, deduplication and caching
    6. 7.6Reranking: two stage, cross encoder and LLM rerankers
    7. Chapter overview
  8. 8When retrieval fails
    1. 8.1Retrieval failure analysis playbook
    2. Chapter overview
  9. 9Building the answer
    1. 9.1Context selection, packing and citations
    2. 9.2Grounding prompts and abstention
    3. 9.3Lost in the middle and context management
    4. 9.4Conflicts, staleness and freshness rules
    5. 9.5Hallucination checks and answer verification
    6. 9.6Conversational RAG, answer UX and RAG APIs
    7. Chapter overview
  10. 10Measuring
    1. 10.1Why measure, and what to measure
    2. 10.2Building a golden dataset
    3. 10.3Retrieval metrics: precision, recall, MRR, nDCG
    4. 10.4Faithfulness and answer correctness
    5. 10.5LLM as judge, and judging the judge
    6. 10.6Offline and online evals, regression tests and slices
    7. Chapter overview
  11. 11Safety and operations
    1. 11.1Prompt injection and poisoned documents
    2. 11.2Access control, privacy and governance
    3. 11.3Index lifecycle operations
    4. 11.4Observability and tracing
    5. 11.5Latency, cost, caching and model routing
    6. 11.6Reliability, degraded modes and launch readiness
    7. Chapter overview
  12. 12Scale and advanced RAG
    1. 12.1Scaling, multi tenancy and enterprise architecture
    2. 12.2Choosing an architecture: naive, advanced, agentic, graph
    3. 12.3Agentic RAG and RAG as an MCP tool
    4. 12.4GraphRAG, structured data and multimodal RAG
    5. Chapter overview

Embeddings and similarity from first principles

How a model turns text into a list of numbers, why close numbers mean close meaning, and how cosine similarity ranks chunks, measured on our 34 chunks.

A real question

How can a computer tell that “reboot the router” and “restart my modem” mean the same thing?

An embedding model turns each text into a list of numbers. Texts with similar meaning get similar numbers, so search can compare meaning instead of exact words.

The problem

Why this matters

Search only works when the question and the document use the same words.
The belief

Search only works when the question and the document use the same words.

“How do I restart my modem?” and “Steps to reboot the router” share no words, yet their vectors scored 0.80. An unrelated pair scored 0.39.
What really happens

“How do I restart my modem?” and “Steps to reboot the router” share no words, yet their vectors scored 0.80. An unrelated pair scored 0.39.

Why not the simpler way

Keyword search matches words. People do not always use the words in the document: they say "modem" when the manual says "router", or "money back" when the policy says "reimbursement". Synonym lists help a little and never end. You want search to match on meaning.

The idea

The idea in plain words

An embedding model is a neural network trained so that texts with similar meaning produce similar vectors. A vector is just a list of numbers, here 384 of them. Each chunk becomes one point in a 384-dimensional space, and the question becomes another point. Retrieval finds the chunk points nearest the question point.

A library sorts books by topic, not by title. Books about bicycles sit together even if one says "bike" and another says "cycling".

How it works

Step by step

  1. The model reads a chunk and outputs 384 numbers, a vector. It does this once per chunk, when you build the index.
    1

    Text becomes numbers

    The model reads a chunk and outputs 384 numbers, a vector. It does this once per chunk, when you build the index.

  2. The question is turned into a vector too. Cosine similarity measures how close the two point: 1 is the same direction, 0 is unrelated.
    2

    Compare directions

    The question is turned into a vector too. Cosine similarity measures how close the two point: 1 is the same direction, 0 is unrelated.

  3. Score every chunk, sort from high to low, and pass the best few to the model.
    3

    Sort and keep the top

    Score every chunk, sort from high to low, and pass the best few to the model.

In detail

Cosine similarity is the dot product of two vectors divided by their lengths: cos(a, b) = (a · b) / (|a| |b|). It ranges from -1 to 1. Our model normalizes every vector to length 1, so cosine and dot product give the same number and the cheaper dot product is enough.

Asymmetric prefixes. Some models want a hint on the query. bge-small expects "Represent this sentence for searching relevant passages: " in front of questions and nothing in front of passages. Leaving it out silently lowers quality.

Scores are relative. On our corpus even the least related chunk scored 0.395 for the E4012 question. A score means "closer than the others", not "relevant". Thresholds are their own topic (RAG-23).

The code

Try it in Python

from sentence_transformers import SentenceTransformer
import numpy as np

model = SentenceTransformer("BAAI/bge-small-en-v1.5")
PREFIX = "Represent this sentence for searching relevant passages: "

def search(question: str, chunks: list[str], k: int = 3):
    docs = model.encode(chunks, normalize_embeddings=True)          # once, at index time
    q = model.encode(PREFIX + question, normalize_embeddings=True)  # per question
    scores = docs @ q                                               # dot product = cosine here
    best = np.argsort(-scores)[:k]
    return [(chunks[i], float(scores[i])) for i in best]

See it

What we measured

Measured · vector search on 47 golden questions

Polder corpus, recursive 512 token chunks (34), Xenova/bge-small-en-v1.5 q8, CLS pooling, normalized, query prefix.

Vector length (dimensions)384
Right chunk ranked first30 of 47 (0.638)
Right chunk in top 344 of 47 (0.936)
Right chunk in top 546 of 47 (0.979)
Paraphrase pair, no shared wordsunrelated pair 0.3910.796

Measured 2026-11-04 with corpus/scripts/measure-parts-4-7.mts. The paraphrase and unrelated pairs are sentences we wrote for the test.

Try it on a real example

“What does error E4012 mean on the FiberBox?” scored against all 34 chunks

Show
1.FiberBox manual #0 · 0.719The manual's introduction: “…the error codes shown in the Polder app…”–
2.FiberBox manual #2 · 0.691The error code table, with the E4012 row.✓
3.Network bulletin 2 · 0.644A bulletin about a fibre outage.–

The table is found, but second: the introduction talks about error codes in general and scores 0.025 higher. Similar topic is not the same as the answer.

Choosing

Use it when, avoid it when

Use it when
  • Questions phrased differently from the documents
  • Many languages or synonyms in the corpus
Avoid it when
  • Exact codes, IDs and numbers on their own: add keyword search (RAG-27)
  • Reading a single score as relevant or not

What it costs · One embedding per chunk at index time, one per question at query time. A small local model runs on a CPU.

In production

Embedding is an index-time cost per chunk and a query-time cost per question. With a small local model, our 34 chunks took about 56 ms each on a laptop CPU. The bigger risk is quiet mismatch: a different model, a missing prefix or a changed normalization makes every score wrong without any error message. Store the model name and settings next to the vectors.

Watch out

Common mistakes

  • A high score means relevant. Unrelated chunks scored around 0.4 here, never 0.
  • Any two embedding models are interchangeable. Each model has its own space; vectors from two models cannot be compared (RAG-25).
  • Cosine and dot product differ. For normalized vectors they are the same number.

Check yourself

Remember this

Remember these

  • An embedding is a fixed-length list of numbers for a piece of text: 384 for our model.
  • Cosine similarity compares directions; with length-1 vectors it equals the dot product.
  • Use the same model for chunks and questions, or the numbers mean nothing.
  • On our corpus, the right chunk came first for 30 of 47 questions and in the top 3 for 44.

Say it in an interview

  1. 01An embedding model maps text to a vector so similar meaning lands close together.
  2. 02I rank chunks by cosine similarity; with normalized vectors that is a dot product.
  3. 03The same model and prefix must embed both chunks and queries.
  4. 04Scores are relative: on my corpus unrelated chunks still scored about 0.4.
  5. 05With bge-small on 34 chunks, the right chunk was in the top 3 for 44 of 47 questions.
Try it yourself: Embedding LabCompare two sentences by similarity, and see one question scored against every chunk.

Quick quiz

Three questions

Question 1

Q1

Our bge-small vectors all have length 1. What does that let you do?

Question 2

Q2

"How do I restart my modem?" and "Steps to reboot the router" share no words. What cosine did they get?

Question 3

Q3 · from RAG-03

Why do we need an embedding model before the model that writes the answer?

Saved on this device. No account needed.