
Turn Any Text Into a Number: Foundations of Embeddings With Sentence Transformers
Chris Harper
3 min read
Jul 6, 2026 · 04:05 UTC
TL;DR: In 5 lines of Python you can turn any text into a dense vector with sentence-transformers — this post walks from zero to a working semantic search engine and builds the intuition every AI engineer needs.
What you'll be able to do after this:
- Generate dense vector embeddings for any text string using a pre-trained model in under 5 lines of Python
- Measure semantic similarity between sentences (not just keyword overlap) using cosine similarity
- Build a minimal semantic search engine over a list of documents — the exact retrieval core of every RAG pipeline
Resource: Creating and Visualizing Embeddings with Sentence Transformers — TensorTeach (YouTube)
What an embedding is (the intuition)
A text embedding is a list of numbers — a vector — that represents the meaning of a sentence. Two sentences with similar meaning end up close together in this vector space, regardless of exact wording. "The dog chased the cat" and "The hound pursued the feline" produce similar vectors; "stock market close" produces a very different one.
The model does this by running text through a transformer encoder and pooling the output into a fixed-length vector — typically 384 dimensions for the common starter models.
Install and load a model
pip install sentence-transformers
from sentence_transformers import SentenceTransformer
# all-MiniLM-L6-v2: 384-dim, 22M params, fast, accurate enough for most tasks
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
The first call downloads the model weights from Hugging Face (~80MB). After that it runs locally with no API calls.
Encode text and measure similarity
sentences = [
"Claude Code automates pull request reviews.",
"Automated PR reviews with Claude.",
"Stock market futures are trending upward.",
]
embeddings = model.encode(sentences)
# shape: (3, 384) — one 384-d vector per sentence
from sentence_transformers import util
sim_01 = util.cos_sim(embeddings[0], embeddings[1]) # ~0.89 — same meaning
sim_02 = util.cos_sim(embeddings[0], embeddings[2]) # ~0.14 — different topic
print(sim_01, sim_02)
Cosine similarity ranges from -1 to 1. Semantically unrelated sentences typically score < 0.3; near-identical meanings score > 0.8.
Minimal semantic search
docs = [
"How to configure a Claude Code hook",
"LoRA fine-tuning on a free Colab GPU",
"Deploying vLLM on a Linux server",
"What is cosine similarity in embeddings?",
]
doc_embeddings = model.encode(docs)
query = "set up a post-tool hook in Claude Code"
query_embedding = model.encode(query)
scores = util.cos_sim(query_embedding, doc_embeddings)[0]
top = scores.topk(2)
for score, idx in zip(top.values, top.indices):
print(f"{score:.2f} {docs[idx]}")
# 0.74 How to configure a Claude Code hook
# 0.31 What is cosine similarity in embeddings?
This is the retrieval core of every RAG pipeline: encode the query, encode the docs, rank by cosine similarity, feed the top-k to the LLM.
What to try next
Once you have vectors, the natural next step is storing them in a vector database — pgvector, FAISS, or Chroma — so you can search millions of documents efficiently instead of a flat list. That's where the retrieval stack goes from a demo to production.
Sources: Creating and Visualizing Embeddings with Sentence Transformers — TensorTeach (YouTube) · Quickstart — sbert.net · Computing Embeddings — sbert.net · sentence-transformers/all-MiniLM-L6-v2 — Hugging Face