Represent meaning for retrieval
Turn text into vectors, search by similarity, and see how vector indexes trade accuracy for speed.
- Explain embeddings and cosine similarity
- Find the nearest chunks to a query vector
- Explain approximate nearest-neighbor indexes and their recall trade-off
An embedding model turns a piece of text into a vector - a list of a few hundred to a few thousand numbers - so that texts with similar meaning end up close together. “How do I get my money back?” and “refund policy” share no words, but their vectors point in nearly the same direction.
Semantic search embeds every chunk once (at indexing time), embeds the question at query time, and returns the chunks whose vectors are most similar - usually by cosine similarity, the cosine of the angle between two vectors (1 = same direction, 0 = unrelated).
Try it
Up to 15 XPSearch a tiny embedding space
Pick a query and see its nearest chunks. Compare the metrics on “money back”: cosine looks only at direction, so “Long refund essay” (same direction, shorter vector) ranks high; Euclidean distance prefers whatever is physically closest.
- Long refund essay cos 1.00
- Refund policy cos 1.00
- Return window cos 0.98
- Restocking fee cos 0.93
- Shipping times cos 0.30
- Track a parcel cos -0.03
- Office locations cos -0.86
- Careers cos -0.99
The amber triangle is the query. Cosine compares direction from the center; Euclidean compares position. Real embeddings have hundreds of dimensions, and are usually normalized so the two agree.
1import math
2def cosine(first, second):
3 dot = sum(a * b for a, b in zip(first, second))
4 return dot / (math.hypot(*first) * math.hypot(*second))
5print(round(cosine([1, 2], [2, 4]), 3))
6print(round(cosine([1, 0], [0, 1]), 3))1.0 0.0
Vector indexes
Comparing the query with every vector (“brute force”) is exact but slow for millions of chunks. Approximate nearest-neighbor (ANN) indexes make it fast by searching only promising regions:
- IVF clusters vectors around centroids and searches only the nearest few clusters (
nprobe). - HNSW builds a layered graph of neighbors and walks it greedily toward the query.
They trade a little recall for a lot of speed: occasionally the true nearest neighbor lives in a cluster you didn’t probe. Raising nprobe (or HNSW’s ef_search) buys recall back at the cost of speed.
Key takeaways
Embeddings place similar meanings close together; cosine similarity compares directions.
Embed chunks once at indexing time and the query at search time - with the same model.
ANN indexes (IVF, HNSW) trade a little recall for big speedups.
Lesson quiz
6 questions · pass with 5 correct · up to 50 XP
Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.
Practice: write Python
Write Python in the editor and run it against sample inputs. Python runs locally in your browser using a WebAssembly runtime.
Nearest chunks by cosine similarity
The first line is a query vector (space-separated numbers); the second is k. Each following line is id v1 v2 .... Print the k most similar chunks as id similarity (3 decimals), most similar first.
- Money back
Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.
Approximate search with clusters
Input lines: nprobe N, query x y, then centroid NAME x y and vector ID x y lines. Assign each vector to its nearest centroid (Euclidean). Search only the nprobe centroids nearest the query, and find the nearest vector among them.
Print probed: NAMES (nearest first), approximate best: ID, exact best: ID (searching all vectors), and hit if they match, else miss.
- One probe misses
- Two probes hit
Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.
Questions about this lesson
Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.
Loading posts…