Handbooks  /  Embeddings
Handbook~15 min readFoundationsworked math + runnable code
The Embeddings Handbook

Meaning,
as geometry.

Computers can't compare the meaning of two sentences — but they're superb at comparing vectors. Embeddings bridge that gap: a model turns text into a point in space so that "how similar are these?" becomes "how close are these points?" Suddenly semantic search, recommendations, clustering, and RAG are all just geometry. This handbook builds the whole idea from one operation — the angle between two vectors — and shows you the arithmetic that every vector database runs a billion times a second.

01

Meaning as vectors

An embedding is a vector — an ordered list of numbers — that stands in for a piece of content: a word, a sentence, a whole document, an image. The magic isn't the vector; it's the training. Embedding models are trained so that content with similar meaning gets nearby vectors. "dog" sits near "puppy"; "bank" (money) drifts away from "bank" (river). Meaning becomes position.

Once meaning is position, hard questions turn easy. "Which of these 10,000 documents is most relevant to my question?" becomes "which vector is closest to the query vector?" — a geometry problem with fast, exact answers. That single reframing is why embeddings underpin semantic search, clustering, deduplication, recommendation, and retrieval-augmented generation. The rest of this handbook is about the one measurement that makes "closest" precise.

The one-sentence version

An embedding turns content into a vector so that similar meaning lands nearby — reducing "how related are these?" to measuring the angle between two vectors.

02

The angle is the answer

The default way to compare two embeddings is cosine similarity — the cosine of the angle between them. It's the dot product of the two vectors divided by the product of their lengths:

cos(a, b)  =  a · b  /  (‖a‖ · ‖b‖)     where   a · b = Σ aᵢbᵢ,   ‖a‖ = √(a · a)

Same direction → cosine 1 (most similar). Perpendicular → cosine 0 (unrelated). Opposite → cosine −1. It runs from −1 to 1.

The crucial property is that cosine measures direction, not magnitude. A long document and a short one about the same topic should count as similar even though one vector is much longer — dividing by the lengths strips magnitude away and leaves only the angle. That's usually exactly what you want for meaning: "aboutness" shouldn't depend on how many words you used. (When magnitude does carry signal, raw dot product or Euclidean distance may be preferred — but for semantic text, cosine is the safe default.)

03

Normalize, then dot

Here's the trick that vector databases live on. Dividing by lengths on every comparison is wasteful when you're doing millions of them. So instead, L2-normalize every vector once — scale it to unit length — up front. After that, the denominator is always 1, and the dot product equals the cosine similarity:

â = a / ‖a‖  (so ‖â‖ = 1)   ⟹   â · b̂  =  cos(a, b)

Normalize once at index time; then ranking by cosine is just ranking by dot product — which is what fast maximum-inner-product search (and indexes like HNSW) are built to do.

Why normalization is the standard move
StoreL2-normalize every document vector once at index time.
QueryNormalize the query vector, then take dot products.
RankDot product now equals cosine — highest dot = most similar.
ScaleMaximum-inner-product search is exactly what ANN indexes accelerate.

This is why so much embedding code normalizes vectors and then never mentions cosine again — the cosine is baked in. It also makes distances behave nicely: on unit vectors, Euclidean distance and cosine similarity become monotonically related, so nearest-by-distance and most-similar-by-cosine agree.

04

More dimensions

Real embeddings aren't 2-D — they're hundreds to thousands of dimensions (384, 768, 1536, 3072 are common). More dimensions give the model more room to separate fine distinctions in meaning, which is why bigger embedding models often retrieve better. But dimensions aren't free: storage and search cost scale with them, and very high dimensions bring the curse of dimensionality — distances between random points concentrate, so "nearest" gets less discriminative unless the embedding genuinely uses those dimensions well.

The practical upshot is a trade-off you tune. Smaller vectors are cheaper to store and faster to search; larger ones can be more accurate. Techniques like quantization compress vectors to reclaim memory, and some modern models support Matryoshka embeddings you can truncate to a shorter prefix for a cheaper approximation. The geometry — cosine on normalized vectors — stays identical no matter the dimension; only the cost and the fidelity change.

RUN IT YOURSELF

Cosine, normalization, nearest

The whole toolkit is a few lines. Cosine similarity is the dot product over the product of lengths, so parallel vectors score 1 and perpendicular ones score 0 — direction, not magnitude. L2-normalization scales a vector to unit length, and once both vectors are normalized their dot product exactly equals the cosine, which is why vector databases store normalized embeddings and just take dot products. Nearest-neighbor search is then: whichever stored vector has the highest cosine with the query is the semantic match. Change the vectors and watch the ranking shift.

CPython · WebAssembly
05

Embeddings in the wild

Once you can measure similarity, a lot of products fall out of the same primitive:

Use caseHow embeddings power it
Semantic search / RAGEmbed documents once; embed the query; retrieve the nearest vectors as context for the model.
RecommendationsEmbed items and users; recommend items whose vectors are nearest to what someone liked.
Clustering & dedupGroup nearby vectors; near-duplicate content has near-identical embeddings.
ClassificationEmbed text and compare to labelled centroids, or train a light classifier on the vectors.
Anomaly detectionPoints far from every cluster are outliers worth flagging.

In practice the pipeline is: pick an embedding model, embed and normalize your corpus, store the vectors in an index (with an ANN structure for scale), and at query time embed, normalize, and retrieve by dot product. The vector database handles the index; the embedding model determines quality. Both matter: a great index over bad embeddings retrieves the wrong things quickly.

06

Pitfalls

Embeddings are simple to use and easy to misuse. A few traps worth naming: model mismatch — you must embed queries and documents with the same model (and often the same instruction/prefix); mixing models compares vectors from different spaces, which is meaningless. forgetting to normalize — if you rank by dot product but didn't normalize, longer documents get an unfair boost. domain drift — a general model may not separate your niche jargon well; sometimes a domain-tuned model or a late-interaction retriever helps.

And the deepest one: embeddings capture similarity, not truth or relevance-to-your-task. Two sentences can be semantically close yet one is right and one is wrong; a passage can be on-topic but unhelpful. That's why strong retrieval systems pair embeddings with a reranker and, for questions, a generator that reads the retrieved text. Embeddings get you the right neighborhood fast; the last mile of precision usually needs another step. Used with that awareness, though, this one idea — meaning as geometry — is the most reusable primitive in applied AI.

Worth knowing

Cosine similarity has no absolute scale you can trust across models — a "0.8" from one model isn't comparable to "0.8" from another, and even within a model the useful threshold varies by domain. Calibrate thresholds on your own data rather than copying a magic number.

Frequently asked

Quick answers

What is an embedding?

A vector that represents content so that similar meaning maps to nearby vectors — turning questions about meaning into geometry.

What is cosine similarity?

The cosine of the angle between two vectors (dot product over the product of lengths) — it measures direction, not magnitude, from −1 to 1.

Why normalize?

After L2-normalizing to unit length, the dot product equals the cosine, so you can rank by fast dot products (maximum-inner-product search).

How do embeddings power RAG?

Embed documents once, embed the query, retrieve the nearest vectors, and feed those documents to the model as context.

Finished this one? 0 / 99 Handbooks done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.