Embeddings turn text into vectors whose distance is meaning. Generate them, measure similarity with cosine, and build a tiny semantic search — the core primitive behind RAG.
Why: an embedding model maps text to a list of numbers (a vector) so that similar meanings land near each other — this is what lets you search by concept instead of by exact words. When: use embeddings whenever "find things like this" matters more than "find this exact string". Where: every RAG system rests on this one idea.
"How do I get a refund?" -> [0.02, -0.19, 0.44, ...] (e.g. 384 numbers)
"What's your return policy?" -> [0.03, -0.17, 0.41, ...] <- close vector
"The sky is blue today." -> [0.51, 0.22, -0.08, ...] <- far vector
Different words, same meaning -> nearby vectors.
Similarity is just distance in this space.Why: sentence-transformers runs a small open model locally and returns a vector for any text — free, private, and fast enough to embed thousands of chunks. When: use a local model for development and privacy; swap in a hosted embedding API when you need higher quality at scale. Where: the vector length (here 384) is fixed by the model and must match everywhere you compare.
pip install sentence-transformers numpyWhy: encoding a list of texts at once is how you build the searchable index — each row is one text’s vector. When: embed your whole corpus once at ingestion, then embed only the query at search time. Where: the model you use to embed documents MUST be the same one you use to embed queries.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("all-MiniLM-L6-v2") # 384-dim, small and fast
texts = [
"How do I get a refund?",
"What is your return policy?",
"The sky is blue today.",
]
vectors = model.encode(texts)
print(vectors.shape) # (3, 384) — three vectors of length 384Why: cosine similarity scores how aligned two vectors are (1.0 = identical meaning, 0 = unrelated), so ranking by it returns the most relevant text. When: this brute-force loop is perfect for learning and for small corpora; the next lessons scale it with a vector database. Where: this exact scoring is what a vector DB does for you, just faster.
import numpy as np
def cosine(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
query = model.encode("I want my money back")
scores = [cosine(query, v) for v in vectors]
best = int(np.argmax(scores))
print(texts[best], round(scores[best], 3)) # the refund line — matched by meaning