Module 1: What Are Embeddings?

Real-World Use Cases of Embeddings

Capsule overview

Embeddings are not just an abstract mathematical concept—they are the underlying technology of the most widely used AI applications in production today.

In this capsule you'll learn the 5 main use cases of embeddings in real systems: semantic search, RAG (Retrieval-Augmented Generation), recommendation systems, document classification, and clustering. You'll see concrete examples of companies that use embeddings in production and understand why they are critical for AI Engineering.

You'll also learn to identify when to use embeddings vs other techniques (keyword search, rules, etc.).


Use Case #1: Semantic Search

What it is:

Search by meaning, not by exact keywords.

Traditional example (keyword search):

User searches: "how to reset my laptop"

Keyword system (BM25):
✅ Finds: "reset laptop", "laptop reset"
❌ Does NOT find: "restart computer", "reboot PC", "restore notebook"

With embeddings (semantic search):

User searches: "how to reset my laptop"

Semantic system:
✅ Finds: "reset laptop"
✅ Finds: "restart computer" (synonym)
✅ Finds: "reboot PC" (conceptual match)
✅ Finds: "restore notebook" (paraphrase)

Why it works: Embeddings capture that "reset", "restart", "reboot", "restore" are conceptually similar.


Typical architecture:

1. Indexing (offline phase):
   Documents → Embed each doc → Store in vector DB

2. Query (online phase):
   User query → Embed query → Find top-K similar → Return docs

Conceptual code:

from openai import OpenAI
import numpy as np

client = OpenAI()

# Step 1: Indexing (once)
documents = [
    "How to reset your Windows laptop",
    "Restart Mac computer",
    "Reboot PC after an update",
    "Restore notebook to factory settings"
]

# Generate embeddings for the documents
doc_embeddings = []
for doc in documents:
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=doc
    )
    doc_embeddings.append(response.data[0].embedding)

# Step 2: Query (each time the user searches)
query = "how to reset my laptop"
query_response = client.embeddings.create(
    model="text-embedding-3-small",
    input=query
)
query_embedding = query_response.data[0].embedding

# Step 3: Calculate similarities
def cosine_similarity(vec_a, vec_b):
    return np.dot(vec_a, vec_b) / (
        np.linalg.norm(vec_a) * np.linalg.norm(vec_b)
    )

similarities = []
for i, doc_emb in enumerate(doc_embeddings):
    sim = cosine_similarity(query_embedding, doc_emb)
    similarities.append((i, sim))

# Sort by similarity
similarities.sort(key=lambda x: x[1], reverse=True)

# Return top-3
print("Most relevant results:")
for rank, (doc_id, sim) in enumerate(similarities[:3], 1):
    print(f"{rank}. {documents[doc_id]} (score: {sim:.4f})")

Expected output:

Most relevant results:
1. How to reset your Windows laptop (score: 0.92)
2. Restore notebook to factory settings (score: 0.88)
3. Restart Mac computer (score: 0.85)

Companies using semantic search:

CompanyApplicationScale
NotionSearch in workspace (docs, notes)Millions of users
SlackSearch messages/filesMillions of messages/day
GitHubCode search (find code by function)Millions of repos
ShopifyProduct search (find products by description)Millions of products
IntercomHelp center searchHundreds of thousands of articles

Competitive advantage: Users find what they're looking for even with imprecise queries.


Use Case #2: RAG (Retrieval-Augmented Generation)

What it is:

Giving an LLM access to external knowledge via semantic search.

Problem without RAG:

User: "What is my company's vacation policy?"

LLM without RAG:
"I don't have information about your company's specific policies.
 Generally, companies offer 15-20 days..."  ❌ Generic

Solution with RAG:

User: "What is my company's vacation policy?"

RAG system:
1. Searches internal docs with embeddings
2. Finds: "Vacation policy 2026: 25 business days..."
3. LLM responds with that context:
   "According to your company's policy (2026), you have 25 business days..." ✅ Specific

Full RAG flow:

1. INDEXING (offline):
   Company documents → Chunking → Embed chunks → Vector DB

2. RETRIEVAL (online):
   User query → Embed query → Top-K similar chunks

3. AUGMENTATION (online):
   Build prompt: "Context: [chunks]\n\nQuestion: [query]"

4. GENERATION (online):
   LLM generates an answer using the context

Conceptual code:

from openai import OpenAI
import numpy as np

client = OpenAI()

# Step 1: Index documents (internal company)
knowledge_base = [
    "Vacation policy 2026: Employees are entitled to 25 business days of vacation per year.",
    "Work schedule: Monday to Friday 9am-6pm. Flexible Fridays (home office).",
    "Medical benefits: Health insurance covered 100% for the employee and 50% for family."
]

kb_embeddings = []
for doc in knowledge_base:
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=doc
    )
    kb_embeddings.append(response.data[0].embedding)

# Step 2: User asks a question
user_question = "How many vacation days do I have?"

# Step 3: Retrieval (find relevant context)
query_response = client.embeddings.create(
    model="text-embedding-3-small",
    input=user_question
)
query_embedding = query_response.data[0].embedding

# Find the top-1 most relevant
similarities = [
    (i, cosine_similarity(query_embedding, kb_emb))
    for i, kb_emb in enumerate(kb_embeddings)
]
similarities.sort(key=lambda x: x[1], reverse=True)
most_relevant_idx = similarities[0][0]
context = knowledge_base[most_relevant_idx]

print(f"Retrieved context: {context}\n")

# Step 4: Augmented prompt
augmented_prompt = f"""Context: {context}

Question: {user_question}

Answer using ONLY the information from the provided context."""

# Step 5: Generation
chat_response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": augmented_prompt}]
)

answer = chat_response.choices[0].message.content
print(f"RAG answer: {answer}")

Output:

Retrieved context: Vacation policy 2026: Employees are entitled to 25 business days of vacation per year.

RAG answer: According to the 2026 vacation policy, you are entitled to 25 business days of vacation per year.

Companies using RAG:

CompanyApplicationData
Notion AIQ&A about the user's workspacePrivate docs
ChatGPT (GPTs)Custom GPTs with specific knowledgePDFs, websites
PerplexitySearch with citationsWeb crawl
Intercom FinCustomer support automationHelp center
GleanEnterprise search with AIConfluence, Google Drive, Slack

Value: LLMs respond with up-to-date, context-specific information.


Use Case #3: Recommendation Systems

What it is:

Suggest similar content based on item embeddings.

Example: Netflix

User is watching: "Stranger Things"

System:
1. Embed the description of "Stranger Things"
2. Search for shows with similar embeddings
3. Recommend: "Dark", "The Umbrella Academy", "Black Mirror"

Why it works: Embeddings capture themes (sci-fi, supernatural, drama).


Typical architecture:

1. Embed all items (movies, products, articles)
2. User consumes item X
3. Search for items with embeddings similar to X
4. Recommend the top-K most similar

Conceptual code:

# Step 1: Index products (e-commerce)
products = [
    {"id": 1, "name": "Laptop Dell XPS 13", "desc": "Compact ultrabook laptop for development"},
    {"id": 2, "name": "Laptop MacBook Pro", "desc": "Premium laptop for creatives and developers"},
    {"id": 3, "name": "Mouse Logitech MX Master", "desc": "Ergonomic mouse for productivity"},
    {"id": 4, "name": "Mechanical keyboard", "desc": "Mechanical keyboard for gaming and programming"},
    {"id": 5, "name": "4K monitor", "desc": "Ultra HD monitor for design and editing"}
]

# Embed products
product_embeddings = {}
for product in products:
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=product["desc"]
    )
    product_embeddings[product["id"]] = response.data[0].embedding

# Step 2: User viewed product X
user_viewed_product_id = 1  # Laptop Dell XPS 13
user_viewed_embedding = product_embeddings[user_viewed_product_id]

# Step 3: Find similar products
similarities = []
for prod_id, prod_emb in product_embeddings.items():
    if prod_id == user_viewed_product_id:
        continue  # Don't recommend the same one
    
    sim = cosine_similarity(user_viewed_embedding, prod_emb)
    similarities.append((prod_id, sim))

similarities.sort(key=lambda x: x[1], reverse=True)

# Step 4: Recommend top-3
print("Recommended products:")
for rank, (prod_id, sim) in enumerate(similarities[:3], 1):
    product = next(p for p in products if p["id"] == prod_id)
    print(f"{rank}. {product['name']} (score: {sim:.4f})")

Output:

Recommended products:
1. Laptop MacBook Pro (score: 0.88)
2. Mechanical keyboard (score: 0.72)
3. 4K monitor (score: 0.68)

Companies using recommendations with embeddings:

CompanyApplicationMethod
NetflixMovie/show recommendationsDescription embeddings + user behavior
SpotifySong/playlist recommendationsAudio embeddings + metadata
AmazonProduct recommendationsProduct description embeddings
MediumArticle recommendationsArticle content embeddings
YouTubeVideo recommendationsVideo title/description embeddings

Use Case #4: Document Classification

What it is:

Classify documents into categories using embeddings + a simple classifier.

Example: Email routing

Email: "My invoice has an error in the amount..."

System:
1. Embed the email
2. Compare with category embeddings:
   - Billing
   - Technical Support
   - Sales
3. Classify as "Billing"
4. Route to the correct team

Typical architecture:

1. Train (create category embeddings):
   - Embed examples of each category
   - Calculate an "average embedding" per category

2. Classify (runtime):
   - Embed a new document
   - Calculate similarity to each category
   - Assign to the most similar category

Conceptual code:

# Step 1: Define categories with examples
categories = {
    "Billing": [
        "My invoice has an error",
        "I want to cancel my subscription",
        "I don't recognize this charge"
    ],
    "Technical Support": [
        "The app won't load",
        "Error when logging in",
        "I can't access my account"
    ],
    "Sales": [
        "I want information about the enterprise plan",
        "What is the price of the Pro plan?",
        "I need a demo of the product"
    ]
}

# Step 2: Create an average embedding per category
category_embeddings = {}
for category, examples in categories.items():
    # Embed each example
    embeddings = []
    for example in examples:
        response = client.embeddings.create(
            model="text-embedding-3-small",
            input=example
        )
        embeddings.append(response.data[0].embedding)
    
    # Average
    category_embeddings[category] = np.mean(embeddings, axis=0)

# Step 3: Classify a new document
new_email = "I can't pay my invoice from last month"

email_response = client.embeddings.create(
    model="text-embedding-3-small",
    input=new_email
)
email_embedding = email_response.data[0].embedding

# Step 4: Find the most similar category
similarities = []
for category, cat_emb in category_embeddings.items():
    sim = cosine_similarity(email_embedding, cat_emb)
    similarities.append((category, sim))

similarities.sort(key=lambda x: x[1], reverse=True)

print(f"Email: '{new_email}'")
print(f"\nClassified as: {similarities[0][0]} (score: {similarities[0][1]:.4f})")

Output:

Email: 'I can't pay my invoice from last month'

Classified as: Billing (score: 0.89)

Companies using classification:

CompanyApplicationCategories
ZendeskTicket routing10-50 categories (billing, tech, sales...)
GmailSmart categorizationPrimary, Social, Promotions, Updates
SuperhumanEmail triageImportant, Later, Archive
TwitterContent moderationSpam, Hate speech, Safe

Use Case #5: Document Clustering

What it is:

Automatically group similar documents without predefined categories.

Example: Organizing support tickets

1000 unlabeled tickets

Clustering system:
1. Embed all tickets
2. Apply clustering (K-Means)
3. Automatic result:
   - Cluster 1: Login problems (~250 tickets)
   - Cluster 2: Billing errors (~180 tickets)
   - Cluster 3: App bugs (~320 tickets)
   - Cluster 4: Questions about features (~200 tickets)
   - Cluster 5: Other (~50 tickets)

Typical architecture:

1. Embed all documents
2. Apply a clustering algorithm:
   - K-Means (if you know the # of clusters)
   - DBSCAN (if you do NOT know the # of clusters)
3. Analyze the resulting clusters
4. Label clusters manually (optional)

Conceptual code:

from sklearn.cluster import KMeans
import numpy as np

# Step 1: Documents to cluster
documents = [
    "I can't log in to my account",
    "Error when signing in",
    "My invoice has an incorrect amount",
    "Duplicate charge on my card",
    "The app closes unexpectedly",
    "Crash when opening the app",
    "How does feature X work?",
    "Documentation about feature Y"
]

# Step 2: Generate embeddings
doc_embeddings = []
for doc in documents:
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=doc
    )
    doc_embeddings.append(response.data[0].embedding)

doc_embeddings_array = np.array(doc_embeddings)

# Step 3: Clustering with K-Means (4 clusters)
kmeans = KMeans(n_clusters=4, random_state=42)
clusters = kmeans.fit_predict(doc_embeddings_array)

# Step 4: Show results
from collections import defaultdict
clustered_docs = defaultdict(list)

for doc, cluster_id in zip(documents, clusters):
    clustered_docs[cluster_id].append(doc)

print("Automatic clustering:")
for cluster_id, docs in clustered_docs.items():
    print(f"\nCluster {cluster_id}:")
    for doc in docs:
        print(f"  - {doc}")

Expected output:

Automatic clustering:

Cluster 0:
  - I can't log in to my account
  - Error when signing in

Cluster 1:
  - My invoice has an incorrect amount
  - Duplicate charge on my card

Cluster 2:
  - The app closes unexpectedly
  - Crash when opening the app

Cluster 3:
  - How does feature X work?
  - Documentation about feature Y

Companies using clustering:

CompanyApplicationScale
ZendeskIdentify common topics in ticketsMillions of tickets
TwitterGroup trending tweetsThousands of tweets/second
Google NewsGroup news about the same eventThousands of articles/day
RedditGroup similar postsMillions of posts

Comparison: When to use embeddings

Embeddings vs Keyword Search:

AspectKeyword Search (BM25)Embeddings (Semantic)
Exact search✅ Excellent⚠️ May over-generalize
Synonyms❌ Doesn't find✅ Finds
Paraphrases❌ Doesn't find✅ Finds
IDs, codes✅ Perfect❌ May confuse
Multilingual search❌ Difficult✅ Works (if multilingual model)
Speed✅ Very fast⚠️ Slower (requires a special index)
Cost✅ Free (Elasticsearch)⚠️ API calls (or local GPU)

Best approach: Hybrid (keyword + semantic).


Embeddings vs Rules:

Use CaseRulesEmbeddings
Email routingRules: if "invoice" → Billing✅ Better: Captures variations
Spam detectionRules: Keyword blacklist✅ Better: Generalizes
Product matchingRules: Match exact SKU⚠️ Depends: Exact SKU = rules is better
Content moderationRules: List of banned words✅ Better: Detects context

General rule: If there's linguistic variability → Embeddings. If it's exact (IDs, codes) → Rules.


Advanced use cases

1. Cross-lingual search (multilingual search):

# User searches in Spanish, finds docs in English:
query_es = "cómo instalar Python"
doc_en = "How to install Python on Windows"

# With a multilingual model (OpenAI, multilingual-SBERT):
emb_query_es = embed(query_es)
emb_doc_en = embed(doc_en)

similarity = cosine_similarity(emb_query_es, emb_doc_en)  # → ~0.90 ✅

Application: Multilingual support without translating all the content.


2. Duplicate detection:

# Detect duplicate tickets:
ticket_1 = "I can't log in"
ticket_2 = "Error when signing in"

emb_1 = embed(ticket_1)
emb_2 = embed(ticket_2)

if cosine_similarity(emb_1, emb_2) > 0.90:
    print("Duplicate tickets")  # ✅

Application: Reduce redundant work in customer support.


3. Personalization:

# User profile = average embedding of items they liked:
user_liked_items = [item_1, item_2, item_3]
user_profile_emb = np.mean([embed(item) for item in user_liked_items], axis=0)

# Recommend items similar to the profile:
for item in catalog:
    sim = cosine_similarity(user_profile_emb, embed(item))
    if sim > 0.80:
        recommend(item)

4. Anomaly detection:

# Detect anomalous logs:
normal_logs = ["User login successful", "Payment processed", ...]
normal_embeddings = [embed(log) for log in normal_logs]
avg_normal_emb = np.mean(normal_embeddings, axis=0)

# New log:
new_log = "Database connection failed 50 times"
new_log_emb = embed(new_log)

# If very different from the average → Anomaly
sim = cosine_similarity(new_log_emb, avg_normal_emb)
if sim < 0.60:
    alert("Anomalous log detected")

Exercises

Exercise 1: Simple semantic search

Implement semantic search over 3 documents:

documents = [
    "Python is a programming language",
    "The cat sleeps on the couch",
    "JavaScript is used in web development"
]

query = "A language for programming"

# Find the most relevant document
See solution
from openai import OpenAI
import numpy as np
import os
from dotenv import load_dotenv

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

def get_embedding(text):
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return np.array(response.data[0].embedding)

def cosine_similarity(vec_a, vec_b):
    return np.dot(vec_a, vec_b) / (np.linalg.norm(vec_a) * np.linalg.norm(vec_b))

documents = [
    "Python is a programming language",
    "The cat sleeps on the couch",
    "JavaScript is used in web development"
]

query = "A language for programming"

# Embed documents
doc_embeddings = [get_embedding(doc) for doc in documents]

# Embed query
query_embedding = get_embedding(query)

# Calculate similarities
similarities = [
    (i, cosine_similarity(query_embedding, doc_emb))
    for i, doc_emb in enumerate(doc_embeddings)
]

# Sort
similarities.sort(key=lambda x: x[1], reverse=True)

# Result
print("Results:")
for rank, (doc_id, sim) in enumerate(similarities, 1):
    print(f"{rank}. {documents[doc_id]} (score: {sim:.4f})")

Expected output:

Results:
1. Python is a programming language (score: 0.87)
2. JavaScript is used in web development (score: 0.75)
3. The cat sleeps on the couch (score: 0.58)

Exercise 2: Simple classifier

Classify an email into 2 categories (Technical or Billing):

categories = {
    "Technical": ["The app doesn't work", "Error when logging in"],
    "Billing": ["My invoice is wrong", "I want to cancel my subscription"]
}

new_email = "I can't access my account"

# Classify the email
See solution
# Embed categories (average of examples)
category_embeddings = {}
for category, examples in categories.items():
    embeddings = [get_embedding(ex) for ex in examples]
    category_embeddings[category] = np.mean(embeddings, axis=0)

# Embed the new email
email_embedding = get_embedding(new_email)

# Find the most similar category
similarities = []
for category, cat_emb in category_embeddings.items():
    sim = cosine_similarity(email_embedding, cat_emb)
    similarities.append((category, sim))

similarities.sort(key=lambda x: x[1], reverse=True)

print(f"Email: '{new_email}'")
print(f"Classified as: {similarities[0][0]} (score: {similarities[0][1]:.4f})")

Output:

Email: 'I can't access my account'
Classified as: Technical (score: 0.92)

Exercise 3: Recommendations

Recommend 2 similar products:

products = [
    "Laptop for programming",
    "Wireless mouse",
    "Mechanical keyboard",
    "4K monitor"
]

user_viewed = "Laptop for programming"

# Recommend 2 similar products (excluding the viewed one)
See solution
# Embed products
product_embeddings = {
    prod: get_embedding(prod) for prod in products
}

# Embedding of the viewed product
viewed_embedding = product_embeddings[user_viewed]

# Calculate similarities
similarities = []
for prod, prod_emb in product_embeddings.items():
    if prod == user_viewed:
        continue  # Exclude the same one
    
    sim = cosine_similarity(viewed_embedding, prod_emb)
    similarities.append((prod, sim))

# Sort and take top-2
similarities.sort(key=lambda x: x[1], reverse=True)

print("Recommendations:")
for rank, (prod, sim) in enumerate(similarities[:2], 1):
    print(f"{rank}. {prod} (score: {sim:.4f})")

Expected output:

Recommendations:
1. Mechanical keyboard (score: 0.76)
2. 4K monitor (score: 0.72)

Summary

What you learned:

  • Semantic Search: Search by meaning, not keywords
  • RAG: Give external knowledge to LLMs
  • Recommendations: Suggest similar items
  • Classification: Categorize documents automatically
  • Clustering: Group documents without supervision

Real use cases:

  1. Notion, Slack → Semantic search
  2. ChatGPT, Perplexity → RAG
  3. Netflix, Spotify → Recommendations
  4. Zendesk, Gmail → Classification
  5. Google News, Reddit → Clustering

When to use embeddings:

  • ✅ Linguistic variability (synonyms, paraphrases)
  • ✅ Semantic search
  • ⚠️ Hybrid with keywords for a better result

Additional resources

  1. RAG Explained - Complete guide
  2. Semantic Search Tutorial - SBERT
  3. Recommendations with Embeddings - OpenAI
  4. Document Clustering - Scikit-learn
  5. Hybrid Search - Elasticsearch

In the next capsule

Capsule 06: Embeddings vs Keywords

You'll learn:

  • BM25 (keyword matching) in detail
  • When embeddings > keywords
  • When keywords > embeddings
  • Hybrid search (the best of both worlds)
  • Practical implementation of the comparison

From use cases to architecture decisions.


Module 1 - Embeddings Deep Dive Guide Real applications that transform industries