Skip to content
back to the map
technique
stage: index
3 min read

Embeddings

Embeddings map text to a space where distance becomes semantics: 'dog' and 'pet' end up close, 'dog' and 'satellite' end up far. It is the engine of RAG's similarity search.

OpenAICoheresentence-transformersVoyage AI

What it is

An embedding is a dense vector of real numbers (typically between 300 and 3072) that represents a piece of text. The useful property is geometric: two texts with similar meaning end up a short distance apart in the space, and two unrelated texts end up far apart. The 'distance' is measured by cosine, inner product or Euclidean.

The vector is generated by a model trained on pairs of text (source and translation, question and answer, or similar titles). After training, the model learned to place words and phrases near similar words and far from others. There is no symbolic rule. The arrangement is purely statistical.

Models and dimensions

Three families dominate. General-purpose models, such as OpenAI's text-embedding-3-small or Cohere's embed-multilingual-v3, return vectors of 384 to 3072 and work reasonably well in any domain. Domain-tuned models (legal, biomedical, code) gain precision when the corpus is technical, and lose it when they leave that domain. Local models via sentence-transformers run without an external call, cost more CPU and are useful when the data cannot leave the infrastructure.

More dimensions cost more storage, more memory and more search time. For a small corpus, 384 dimensions are enough; for a million documents, it is worth measuring whether the gain from going up to 1024 covers the cost.

Similarity in practice

measures the angle between two vectors and ignores magnitude, so two long texts and two short ones can have the same similarity value by content. The formula is the inner product divided by the norm of each vector.

embeddings/cosine.tstypescript
type Vec = number[];

export function dot(a: Vec, b: Vec): number {
  let s = 0;
  for (let i = 0; i < a.length; i++) s += a[i] * b[i];
  return s;
}

export function norm(a: Vec): number {
  return Math.sqrt(dot(a, a));
}

export function cosine(a: Vec, b: Vec): number {
  const denom = norm(a) * norm(b);
  if (denom === 0) return 0;
  return dot(a, b) / denom;
}

// usage
const doc: Vec = await embedder.embed("rag combines search and generation");
const query: Vec = await embedder.embed("how does rag work?");
const score = cosine(doc, query);
console.log(score.toFixed(3));

Choosing the model

Four questions determine the choice. First: is the domain general or technical? A multilingual model helps with PT-BR; a code model helps with codebases. Second: does it need to run offline or can it call an API? Offline forces sentence-transformers or a local BGE. Third: what is the storage budget? Vectors of 3072 dimensions for 10 million documents are 120 GB for embeddings alone. Fourth: do you need semantic consistency over time? Changing the embedding model after indexing forces reindexing everything.

The rule that avoids the most regret: define the model before indexing and freeze that decision. Reindexing is expensive, and switching back and forth between models in the middle of the project degrades retrieval quality without warning.

Strengths

  • approximate semantic understanding without rules: different phrases with the same meaning end up close, with no need for hardcoded synonyms
  • ready-made quality models: open weights (BGE, E5, mE5) and managed APIs cover most cases without training
  • low cost per call: embeddings are typically orders of magnitude cheaper than LLM generation

Limitations

  • lexical blindness: 'lettuce' and 'greens' end up close, but 'lettuce' and 'Lettuce Inc.' (a company) do too, and retrieval cannot tell them apart
  • outdated model: the model saw data up to the training date; new terms have no good position in the space
  • no guarantee of alignment across languages: multilingual models approximate, but literal translations come out ahead of paraphrases
  • cost of switching: changing the embedding model is a full reindex, so the initial decision weighs heavily
Cosine similarityA measure of how aligned two vectors are in embedding space: the cosine of the angle between them, independent of magnitude. For normalized embeddings the inner product is enough and stays fast, which is why it became the default distance in almost every vector database.see in glossary →

connections on the map

updated on 14/10/2026 · sources to go deeper · report an error

Study before

Unlocks

test what you learned

Loading questions…