AI Engineering Learning Hub

Embeddings

What text embeddings are, how sentence-transformers turn sentences into vectors, and how to pick a model for RAG.

What it is

An embedding is a list of numbers that represents the meaning of a piece of text. The sentence 'Abdur built a RAG chatbot for a real-estate company' becomes a vector of 384 floats. A different sentence with similar meaning, such as 'he developed a retrieval-based assistant for property listings', becomes a vector that sits close to the first one in that 384-dimensional space.

The model that produces these vectors is trained so that distance means semantic similarity, not keyword overlap. That is the whole trick: once text is a vector, 'find the paragraph that answers this question' becomes 'find the vector nearest to this one', which is plain arithmetic.

The model I use for this site is all-MiniLM-L6-v2 from the sentence-transformers library. It runs on CPU, loads in a couple of seconds, produces 384-dimensional vectors, and handles up to 256 word pieces per input. OpenAI's text-embedding-3-small produces 1536 dimensions and is more accurate on long or technical text, but it costs money per call and needs a network round trip.

Why it's needed

Keyword search fails the moment the user's words differ from the document's words. A recruiter asking 'has he done any work with vector databases?' would never match a paragraph that says 'LangChain + ChromaDB chatbot' with plain string matching. Embeddings close that gap because 'vector databases' and 'ChromaDB' land near each other in the vector space.

Every RAG system, semantic search feature, recommendation engine and clustering job starts with embeddings. If the embeddings are poor, nothing downstream can fix it, so understanding what they are and what they cannot do is the first real AI-engineering skill.

How it works

  • Load an embedding model once at startup. Loading per request is the most common performance bug I see in beginner code.
  • Call model.encode() on a list of strings. Batching is much faster than encoding one string at a time.
  • Each string becomes a fixed-length vector regardless of its length; a 5-word sentence and a 200-word paragraph both become 384 numbers.
  • Normalize the vectors (unit length) so cosine similarity reduces to a dot product. sentence-transformers can do this with normalize_embeddings=True.
  • Store the vectors next to the original text. You retrieve by vector but you show, and feed the LLM, the original text.
  • Always embed queries with the same model that embedded the documents. Vectors from two different models are not comparable.

Code example

from sentence_transformers import SentenceTransformer
import numpy as np

# Load once, reuse for every request (this takes ~2s, encoding takes ~ms)
model = SentenceTransformer("all-MiniLM-L6-v2")

sentences = [
    "Abdur built a RAG chatbot with LangChain and ChromaDB.",
    "He developed a retrieval-based assistant for property listings.",
    "The recipe needs two eggs and a cup of flour.",
]

# normalize_embeddings=True gives unit vectors, so dot product == cosine
vectors = model.encode(sentences, normalize_embeddings=True)
print(vectors.shape)  # (3, 384)

# Compare the first sentence with the other two
for i in (1, 2):
    score = float(np.dot(vectors[0], vectors[i]))
    print(f"similarity(0, {i}) = {score:.3f}")

# Typical output: ~0.6 for the paraphrase, ~0.05 for the recipe

Where I used this

This portfolio's chatbot embeds knowledge_base.txt once at startup with sentence-transformers (all-MiniLM-L6-v2) and keeps the vectors in memory as a NumPy matrix, so every question is answered against my real career data in a few milliseconds of retrieval time.

Use cases

  • Semantic search over documentation, tickets or a knowledge base
  • The retrieval step of every RAG pipeline
  • Deduplication and clustering of similar records
  • Recommendations: 'users who read this also read' without any user history
  • Classification by nearest labelled example when you have little training data

Common mistakes

  • Mixing models: embedding documents with one model and queries with another gives meaningless similarity scores.
  • Ignoring the token limit: MiniLM silently truncates after 256 word pieces, so the end of a long paragraph is never seen. Chunk first.
  • Embedding on every request: the model load belongs at startup, not inside the endpoint.
  • Expecting exact matches: embeddings capture meaning, so 'Python developer' and 'Django engineer' score high, but a product code like 'SKU-4471' will not retrieve reliably. Use keyword search for that.
  • Treating the dimension count as quality: 1536 dimensions is not automatically better than 384. Benchmark on your own data.

Interview questions

What does an embedding actually represent?
A point in a high-dimensional space where distance reflects meaning. Two texts that say similar things, even with different words, end up close together. The individual numbers have no human-readable meaning; only the geometry matters.
Why use a local model like all-MiniLM-L6-v2 instead of the OpenAI embedding API?
It is free, runs on CPU in milliseconds, keeps data on your own server and has no network dependency. For a small knowledge base like a portfolio it retrieves just as well. I would switch to a hosted model when the corpus is large, multilingual or highly technical and recall measurably improves.
Can you compare embeddings from two different models?
No. Each model defines its own space. A vector from MiniLM and a vector from text-embedding-3-small are different lengths and have unrelated axes. If you change the embedding model you must re-embed the entire corpus.
What is the difference between a word embedding and a sentence embedding?
Word embeddings like word2vec give one vector per word with no context, so 'bank' is the same vector in 'river bank' and 'bank account'. Sentence embeddings from transformer models encode the whole input, so context changes the vector. RAG uses sentence or passage embeddings.
How do you decide the embedding model for a project?
Build a small test set of real questions with the chunk that should be retrieved, then measure recall at k for two or three candidate models. Also weigh latency, cost, token limit and whether the data can leave your server. The MTEB leaderboard is a starting point, not an answer.
Why normalize embeddings?
With unit-length vectors, cosine similarity equals the dot product, which is faster and lets you use plain matrix multiplication or any vector index in inner-product mode. It also removes the effect of text length on vector magnitude.
Ask the AI assistant about this topic

Get new topics by email

One short, practical AI-engineering lesson whenever a new topic is published. No spam, unsubscribe any time.

Need this built for your product?

I build production RAG pipelines, LLM automations and voice agents. Open to freelance projects and full-time roles.