Skip to the solution
EasyAI Engineering15 min read

What are embeddings and what are they used for?

974 views

The problem

Question presented to candidate: "You keep hearing that our search and our RAG feature run on embeddings. Explain what an embedding actually is, how one gets made, and what kinds of product features you can build with them."

What a strong answer should cover:

  • An embedding is a fixed-length list of numbers (a dense vector) produced by an embedding model, arranged so that texts with similar meaning get vectors that point in similar directions
  • It is produced by a dedicated encoder model trained (usually contrastively) on pairs of related and unrelated texts, not by the chat model generating tokens; documents are embedded once at index time, the query at request time
  • Similarity becomes arithmetic (cosine or dot product), which powers semantic search and RAG retrieval, deduplication, clustering, classification by nearest labelled example, recommendations and anomaly detection
  • Embeddings find meaning without shared words, but they miss exact identifiers (error codes, SKUs, names), which is why keyword search is often combined with them
  • Vectors from different models (or versions) live in different spaces and cannot be compared; switching model means re-embedding everything, so the model name and version are stored with the index

Code / implementation expected: Optional โ€” embedding a few sentences and ranking them by cosine similarity, or explaining that call, is enough.

  • embeddings
  • vectors
  • semantic-search

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

15 min read ยท 11 sections

Read the code

Toy embedding model: semantic search, dedup, classification and the model-mismatch trap
// A toy embedding model: every known word has a 6-number vector over the
// concepts [account, payment, food, travel, problem, delivery].
// A sentence vector is the mean of its word vectors, scaled to length 1.
// Real models learn ~1,000 numbers per text from data; the mechanism is the same.
const WORDS = {
  password: [0.9, 0, 0, 0, 0.2, 0], login: [0.9, 0, 0, 0, 0.1, 0],
  reset: [0.6, 0, 0, 0, 0.3, 0], forgot: [0.5, 0, 0, 0, 0.4, 0],
  credentials: [0.9, 0.1, 0, 0, 0, 0], account: [0.8, 0.2, 0, 0, 0, 0],
  refund: [0, 0.9, 0, 0, 0, 0.2], charged: [0, 0.9, 0, 0, 0.2, 0],
  charge: [0, 0.9, 0, 0, 0.1, 0], duplicate: [0, 0.3, 0, 0, 0.4, 0],
  twice: [0, 0.3, 0, 0, 0.3, 0], money: [0, 0.8, 0, 0, 0, 0],
  pizza: [0, 0, 0.9, 0, 0, 0.2], recipe: [0, 0, 0.9, 0, 0, 0],
  flight: [0, 0, 0, 0.9, 0, 0], booking: [0, 0.2, 0, 0.7, 0, 0],
  package: [0, 0, 0, 0, 0, 0.9], late: [0, 0, 0, 0.1, 0.3, 0.6],
  arrived: [0, 0, 0, 0.2, 0, 0.7], track: [0, 0, 0, 0.1, 0, 0.8],
};
const tokens = (t) => t.toLowerCase().split(/[^a-z]+/).filter(Boolean);
function embed(text, perm = [0, 1, 2, 3, 4, 5]) {
  const v = [0, 0, 0, 0, 0, 0];
  for (const w of tokens(text)) if (WORDS[w]) WORDS[w].forEach((x, i) => (v[perm[i]] += x));
  const n = Math.sqrt(v.reduce((s, x) => s + x * x, 0));
  return v.map((x) => x / n);
}
const cos = (a, b) => a.reduce((s, x, i) => s + x * b[i], 0); // unit vectors: dot = cosine
const overlap = (q, d) => { const s = new Set(tokens(d)); return tokens(q).filter((w) => s.has(w)).length; };
const f3 = (x) => x.toFixed(3);

const docs = ["How to reset your password", "Get a refund for a duplicate charge",
  "Track a late package", "Change your flight booking", "Easy pizza recipe"];
const index = docs.map((d) => ({ text: d, vec: embed(d) }));

console.log("1) semantic search: shared words vs embedding cosine");
for (const q of ["I forgot my login credentials", "I was charged twice, give my money back", "my package never arrived"]) {
  const qv = embed(q);
  const ranked = index.map((d) => ({ d, s: cos(qv, d.vec) })).sort((a, b) => b.s - a.s);
  const top = ranked[0].d.text;
  console.log(`  "${q}"`);
  console.log(`    top hit: "${top}"  cosine ${f3(ranked[0].s)}  runner-up ${f3(ranked[1].s)}  shared words with hit: ${overlap(q, top)}`);
}

console.log("2) deduplication: cosine above 0.95 means the same ticket");
const tickets = ["Forgot password, cannot login", "Need to reset my login password", "Pizza recipe please"];
for (let i = 0; i < tickets.length; i++)
  for (let j = i + 1; j < tickets.length; j++) {
    const s = cos(embed(tickets[i]), embed(tickets[j]));
    console.log(`  ${i + 1} vs ${j + 1}: ${f3(s)} ${s > 0.95 ? "duplicate" : "different"}`);
  }

console.log("3) classification: label of the most similar labelled example");
const labelled = [["Reset my account password", "account"], ["Refund the charge", "billing"], ["Track my package", "shipping"]];
for (const t of ["Login credentials not working", "Money charged twice", "Package is late"]) {
  const best = labelled.map(([x, l]) => ({ l, s: cos(embed(t), embed(x)) })).sort((a, b) => b.s - a.s)[0];
  console.log(`  "${t}" -> ${best.l} (${f3(best.s)})`);
}

console.log("4) mixing models: query from model B, documents from model A");
const modelB = [3, 4, 5, 0, 1, 2]; // same knowledge, different coordinate layout
const qb = embed("I forgot my login credentials", modelB);
const wrong = index.map((d) => ({ d, s: cos(qb, d.vec) })).sort((a, b) => b.s - a.s)[0];
console.log(`  top hit: "${wrong.d.text}" cosine ${f3(wrong.s)} -> vectors from different models are not comparable`);

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 2 of 83 in AI engineering

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

Up nextEasy

What belongs in the system prompt versus the user message?

Continue