AI Engineering Playbook
Embeddings & Search

Embeddings Beyond RAG

Classification, recommendations, anomaly detection, deduplication.

Prerequisites

  • Embeddings — the shared representation these patterns consume.
  • Similarity metrics — cosine vs dot product (popularity in the norm) and why thresholds do not transfer.
  • ANN indexes — scalable nearest-neighbor lookup for recs, dedupe, and anomaly scoring.

The intuition

RAG made embeddings famous, but the underlying move is older and wider: turn text into a point where distance means similarity, then run ordinary algorithms on the points.

Once every ticket, article, or product description is a vector, you can:

  • train a tiny classifier instead of calling an LLM on every row
  • treat "related items" as nearest neighbors
  • flag outliers as points far from everything
  • collapse near-duplicates above a similarity threshold
  • cluster free text into themes without reading 20k rows by hand

Think of embeddings as a universal semantic feature layer. The LLM is still useful at the edges — labeling seed data, naming clusters, adjudicating borderline cases — but the high-volume middle is classical ML and nearest-neighbor search.

Key insight

Knowing when a problem needs an LLM call versus a nearest-neighbor lookup (or a logistic regression on frozen vectors) is the practical judgment that keeps cost, latency, and consistency under control.

Why it exists

Calling a large model for every repetitive judgment does not scale.

Hard constraints:

  1. Volume. Routing 100k tickets/day through an LLM is slow and expensive compared with one embed + a matrix multiply.
  2. Consistency. Prompted classifications drift with prompt and model revisions; a fitted head on fixed vectors is stable until you retrain.
  3. Latency. Online recs and semantic cache lookups need milliseconds, not multi-second generations.
  4. Structure. Many tasks are literally "find similar," "find far," or "group near" — geometry already is the answer.

Alternatives lose on the high-volume path. LLM-per-item wins for open-ended reasoning and constantly shifting label schemas, but loses on cost and determinism. Hand-built keyword rules miss paraphrase. Training a large task-specific neural net from scratch is usually unnecessary once a strong general embedding exists. Embeddings + small models / ANN sit in the productive middle.

The core idea

Embeddings are a general-purpose semantic feature extractor, and RAG is just their most famous consumer. Once text becomes a vector where distance means similarity, a whole family of classic ML problems becomes cheap: classification (train a tiny classifier on embeddings instead of prompting an LLM per item), recommendations ("similar items" = nearest neighbors), anomaly detection (outliers = points far from everything), deduplication (near-duplicates = pairs above a similarity threshold), and clustering (k-means over embeddings to discover themes).

The engineering argument: for high-volume, repetitive judgments, embeddings + a small classical model beat calling an LLM per item by 10–1000× on cost and latency, with more consistent outputs — you pay the embedding once (fractions of a cent per thousand items) and inference is a matrix multiply. The LLM is still useful in these pipelines, but at the edges: labeling training examples, naming discovered clusters, adjudicating borderline cases. Knowing when a problem needs an LLM call versus a nearest-neighbor lookup is exactly the judgment that separates efficient systems from expensive ones.

How it actually works

Each pattern is "embed, then apply a decades-old algorithm," and each has one design decision that matters. Shared skeleton:

Classification. Embed the text, train logistic regression / a small MLP / kNN on labeled examples.

  • Mini-example: support-ticket routing to 12 teams. Embed 1,500 historical tickets (already labeled by which team resolved them), fit logistic regression — millisecond inference, consistent, retrainable in seconds. Zero-shot variant when you have no labels: embed each label description ("billing issues: charges, refunds, invoices…") and assign each ticket to the nearest label vector.
  • Key decision: embeddings + LR needs hundreds of examples and gives you a real, thresholdable probability; LLM prompting needs zero examples but costs per call and drifts with prompt/model changes. Common path: LLM labels a seed set → classifier serves production traffic.

Recommendations ("similar items"). Embed item content (title + description); serve k-NN from a vector index.

  • Mini-example: "related articles" on a docs site — embed every article, and at render time return the 5 nearest neighbors to the current page (excluding itself), filtered to published status. Cold-start-proof for new items, since it's content-based, not behavior-based.
  • Key decision: content embeddings capture "about the same thing," not "users who liked X liked Y" — for behavioral signals you blend with interaction data (e.g. a user vector = mean of embeddings of their recently viewed items, dot product to rank candidates; norms can carry popularity, which is why recommenders favor dot product over cosine).

Anomaly / outlier detection. Score each point by distance to its k-th nearest neighbor or to its cluster centroid; large distance = unlike anything seen.

  • Mini-example: monitoring a support inbox for novel incidents — embed each incoming ticket, compute mean distance to its 10 nearest historical tickets; alert when a burst of tickets appears that is far from all history and close to each other (a new, correlated failure mode, before anyone has written a category for it).
  • Key decision: it flags semantic novelty, not badness — expect benign novelty (new product launch) and route flags to triage, not to automated action.

Common misconception

"Anomaly score high ⇒ incident" is false. High score means unlike the baseline, which includes good surprises. Always human (or workflow) triage; roll the baseline forward or yesterday's novelty is forever anomalous.

Deduplication / near-duplicate detection. Pairs with similarity above a threshold (typically ~0.9–0.97, calibrated on your data) are duplicate candidates.

  • Mini-example: dedupe a scraped job-postings corpus where the same role appears on 4 boards with different boilerplate — exact hashing misses them; embedding similarity at ~0.95 catches them. Directly relevant to RAG hygiene too: duplicated chunks waste context and crowd out diverse evidence in top-k.
  • Key decision: naive all-pairs is O(N²) — at scale, query each item against the ANN index for neighbors above the threshold (this is sentence-transformers' "paraphrase mining" pattern), and always human-spot-check the threshold: 0.93 can be a paraphrase in one corpus and merely-related in another.

Clustering / topic discovery. k-means or HDBSCAN over embeddings (often after UMAP dimensionality reduction); then label each cluster.

  • Mini-example: 20k pieces of free-text NPS feedback → embed → cluster → sample 10 items per cluster and have an LLM name each ("checkout errors on mobile", "pricing confusion") → sized, named themes with example quotes, instead of an intern reading for a week.
  • Key decision: k-means forces every point into one of exactly k spherical clusters; HDBSCAN finds a variable number and leaves noise unassigned — better for messy feedback, at the cost of tuning.

Key insight

The LLM does the small-N language work (names, labels, edge cases). Embeddings + classical methods do the large-N geometry work. Invert that split and both cost and consistency suffer.

Two more worth naming: semantic caching (embed incoming LLM queries; if a cached query is within threshold, serve its stored answer — cuts cost/latency on repetitive traffic, with false-hit risk to manage) and semantic routing (nearest intent-exemplar decides which model/pipeline handles the request).

The flows

FlowSequenceWhen it appliesWhat breaks it
Offline classify train → online servelabel seed (human/LLM) → embed corpus → fit LR/MLP → embed new item → predict + thresholdStable taxonomies, high volumeModel swap without retrain; label drift; threshold from another domain
Content recs queryembed item (or user as mean of items) → ANN top-k → filter business rules → showRelated docs/products, cold startUsing cosine when popularity should live in norms; no filter for status/ACL
Dedupe batchembed all → for each, ANN neighbors above τ → union-find groups → human sampleCorpus hygiene, scrape merge, RAG prepτ uncalibrated; O(N²) without ANN; treating groups as exact dups without review
Anomaly streamembed event → distance to k-th historical NN → score vs rolling baseline → triage queueNovel incident detectionStatic baseline; auto-remediation on novelty alone
Cluster + nameembed → optional UMAP → HDBSCAN/k-means → sample → LLM names → reportTheme discovery on feedbackForced k on messy data; no noise bucket; over-trusting cluster names
Semantic cache lookupembed query → NN in cache index → if sim ≥ τ and context keys match → return stored answerRepetitive LLM trafficLoose τ; missing user/tenant in cache key; stale answers without TTL

On the governed enterprise platform, the same embedding service that powers RAG also feeds ticket routing classifiers, near-duplicate detection on uploaded session docs, and semantic cache for frequent policy questions — each with its own calibrated thresholds and entitlement rules.

A worked example

Task: categorize ~100k support tickets/month into 15 queues on the platform.

StepDetail
Seed labels2,000 historical tickets with queue already set by resolvers; optional LLM assist to expand thin classes to ~100+ examples each
Embedsame production model as RAG (e.g. 1024-d), store model version on each row
Trainlogistic regression on vectors → 15-way probabilities; few seconds on CPU
Onlineembed new ticket (~20–40 ms) + predict (~1 ms); route if max prob ≥ 0.55, else "needs human triage"
Cost sketchembed 100k short tickets ≪ LLM 100k classifications; ratio often orders of magnitude on API bills
Weeklysample errors; refresh training data; watch prediction distribution for drift

Zero-shot bootstrap (week 0): embed label descriptions for each queue; assign by nearest label vector; use that to pre-sort for human correction; then fit LR.

Contrast — LLM every ticket: flexible when queues change daily and need reasoning; slower, costlier, and prompt-sensitive. Use LLM when categories are unstable or each item needs multi-step judgment; use embeddings+LR when the taxonomy is stable and volume is high.

What each omission looks like in production

  • LLM labels production forever → cost and latency explode; outputs wobble when prompts change.
  • No probability threshold → low-confidence tickets auto-route to the wrong team at scale.
  • Change embedding model, keep old LR weights → features are in a new space; accuracy collapses.
  • Dedupe threshold copied from a blog (0.9) → either merges unrelated policies or misses real duplicates; always calibrate.
  • Semantic cache without tenant/user in the key → user A receives user B's cached answer; data leak, not just a quality bug. See security.
  • Anomaly alerts auto-page on-call → product launches and reorgs create pages; novelty ≠ severity.

Common drill-downs

These are the questions a careful engineer asks one level down when embeddings leave the RAG path.

You need to categorize 100k support tickets into 15 categories. LLM or embeddings? Embeddings + logistic regression: label a seed set (possibly LLM-assisted), train, then classify at ~zero marginal cost with consistent, thresholdable outputs. Per-ticket LLM prompting costs orders of magnitude more, is slower, and drifts with prompts. LLM wins only when categories change constantly or need reasoning per item.

How do you do zero-shot classification with embeddings alone? Embed each label's description, embed the item, assign to the most similar label. Works surprisingly well when labels are semantically distinct; upgrade path is replacing label vectors with centroids of a few labeled examples per class.

How would you find near-duplicates in 10M documents without O(N²) comparisons? Embed everything into an ANN index; for each doc, query its neighbors and keep pairs above a calibrated threshold (~0.95); union-find the pairs into duplicate groups. ANN turns all-pairs into N cheap top-k queries.

Recommendations from embeddings — what's the limitation vs collaborative filtering? Content embeddings capture topical similarity and handle cold-start, but not taste ("users who bought X buy Y"). Production blends both: content vectors for new/long-tail items, behavioral signals (interaction-derived vectors, dot product with popularity in the norm) for the head.

How do embeddings power anomaly detection, and what's the failure mode? Score by distance to k-th nearest historical neighbor or cluster centroid; far = semantically novel. Failure mode: novelty ≠ problem — benign new topics flag too, so it's a triage signal; and the baseline must roll forward or it goes stale.

How would you turn 20k pieces of free-text feedback into themes? Embed → (optionally UMAP) → cluster (HDBSCAN for messy data, k-means for a fixed k) → LLM names each cluster from sampled members → report cluster sizes, names, exemplar quotes. Embeddings do the heavy lifting; the LLM only summarizes ~dozens of samples.

What is semantic caching and its main risk? Serve a stored response when a new query embeds within threshold of a cached one. Risk: false hits — similar-looking queries with different intent ("cancel my order" vs "cancel my subscription") — so thresholds must be strict, entries TTL'd, and cache keys should include user/context where answers differ.

What breaks across all these systems when you change embedding model? Everything downstream of the vectors: stored vectors are invalid (full re-embed), all thresholds (dedupe, cache, anomaly) need recalibration, and classifiers need retraining — vectors from different models share no geometry. Treat the model ID as part of every vector's schema.

Production concerns

  • Thresholds don't transfer. Every model has its own similarity distribution — recalibrate dedupe/cache/anomaly thresholds per model, per corpus, and re-derive them after any model swap (which also invalidates every stored vector — same re-embed tax as RAG). See similarity metrics.
  • Drift: classifiers on embeddings drift with vocabulary and traffic mix; monitor prediction distributions and refresh training data. Anomaly baselines need rolling windows or yesterday's novelty is forever anomalous. See observability.
  • Cost asymmetry is the point: embedding ~1M short items costs on the order of single-digit dollars with small API models (and less self-hosted); classifying 1M items with an LLM costs orders of magnitude more and runs far slower. State the ratio explicitly in system designs. See cost.
  • Batch vs online: clustering and dedupe are naturally offline batch jobs; classification/recs/caching serve online — different infra, same vectors. Reuse one embedding store where possible.
  • Privacy: embeddings are derived data — they leak content (inversion attacks recover substantial text) and must inherit the source data's access controls and retention rules. On the platform, personal vector stores and org-wide indexes both inherit IAM entitlements. See security.
  • Latency: online classify/recs should stay in the embed + ANN budget (tens of ms), not the generation budget. See latency.

Test yourself

Product wants 'AI categorization' of every inbound email via GPT-class models. Volume is 80k/day and labels are a stable 12-way taxonomy. How do you respond?

Semantic cache hit rate is high but CSAT drops. Queries like 'cancel order' and 'cancel subscription' collide. What do you change?

You cluster 20k NPS comments with k-means k=10. Leadership loves the neat slide. What did you likely hide?

Near-duplicate detection at 0.95 cosine merges two different legal policies that share boilerplate. Fix?

After swapping the embedding model, ticket routing accuracy tanks but 'the classifier code did not change.' Explain and remediate.

Go deeper

Where this connects

  • Embeddings — the shared model, versioning, and re-embed economics every pattern inherits.
  • ANN indexes — the lookup engine behind recs, dedupe, anomaly k-NN, and semantic cache.
  • The RAG pipeline — still the primary consumer; these patterns reuse the same embed+index investment.
  • Cost — where the 10–1000× gap between embed-once and LLM-per-item shows up in real budgets.
Hybrid Search

On this page