What Embeddings Are and When to Use Them

Ma Mahalakshmi V Updated 16 Sep 2026
7 min read ·Lesson 115 of 224

Embeddings: What They Are & When to Use Them

Unit 10 introduced embeddings and walked through a basic similarity-search pipeline: turn text into vectors, compare vectors, return the closest matches. This unit goes further into practical semantic-search engineering — cleaning and chunking text before embedding, storing vectors in a real database, filtering by metadata, evaluating retrieval quality with the same methodology used in Unit 13, and building two complete applications. Before going further, it is worth sharpening exactly when embeddings are the right tool for a job and when they are not, because that decision shapes every design choice in the lessons that follow.

The Geometric Intuition, Restated Briefly

An embedding is a list of floating-point numbers — a vector — produced by a model that has been trained so that texts with similar meaning end up close together in that vector space, and texts with different meaning end up far apart. Unit 10 covered the "without the maths" version of this. The one addition worth making here: no individual number in the vector corresponds to a human-readable concept like "is about finance" or "has a positive tone." The meaning is encoded in the relationships between the numbers across all dimensions at once. This matters practically because it means you cannot debug a bad embedding by inspecting individual coordinates — you can only evaluate it by measuring how well it separates similar text from dissimilar text, which is exactly the subject of Lesson 9 in this unit.

Three Questions Before Reaching for Embeddings

Before writing any embedding code, answer these three questions honestly. They save far more time than they cost.

1. Does the task actually require matching by meaning, or would exact/fuzzy text matching work?

If a user searches for "invoice #4471" and you have that exact string in your data, a database LIKE query or full-text search index will find it faster, more reliably, and more cheaply than an embedding lookup. Embeddings shine when the query and the matching document use different words for the same idea — "cancel my plan" should match a document titled "How to end your subscription" even though they share almost no words.

2. Is there enough distinguishing text to embed?

Embeddings compare meaning derived from text. A product catalog where every row is {"sku": "A1", "price": 9.99} gives an embedding model almost nothing to work with. A support ticket with three sentences of free-text description gives it plenty. If your content is mostly structured fields and very little prose, structured filtering (Lesson 7 in this unit) will outperform semantic search on its own.

3. Can the application tolerate approximate, ranked results instead of exact answers?

Semantic search returns a ranked list of "probably relevant" items, not a guaranteed correct answer. It is well suited to search, recommendation, and retrieval-for-generation (RAG). It is poorly suited to anything that needs a deterministic, auditable answer, such as "does this customer currently have an active subscription?" — that belongs in a database query, not a vector comparison.

Semantic Search vs. Alternatives

ApproachMatches byGood forWeak for
Keyword / full-text searchExact or stemmed word overlapKnown terms, IDs, exact phrasesSynonyms, paraphrasing, different languages
Semantic search (embeddings)Meaning / conceptual similarityParaphrase-tolerant search, clustering, recommendationsExact lookups, numeric filters, precise facts
Structured query (SQL/filters)Exact field valuesDates, prices, statuses, IDsFree-text meaning
Classification / fine-tuningLearned category boundariesFixed, known categories (spam/not-spam)Open-ended, growing sets of categories

In production systems these are usually combined, not chosen exclusively. A search feature might use semantic search to find candidates and a structured filter to restrict them to "published in the last 30 days" — this combination is the subject of Lesson 7.

When Embeddings Are the Right Tool

  • Semantic search over unstructured text — documentation, support tickets, articles, transcripts — where users phrase queries differently than the source text.
  • Duplicate or near-duplicate detection — finding support tickets that describe the same underlying issue, or flagging near-identical job listings.
  • Recommendation by similarity — "articles like this one," based on content rather than explicit tags.
  • Clustering and topic discovery — grouping thousands of free-text responses into themes without predefined categories.
  • Retrieval for RAG — fetching the most relevant chunks of a knowledge base to include in a prompt before generation.

When Embeddings Are NOT the Right Tool

  • Exact-match lookups (order IDs, email addresses, exact filenames) — use a database index instead; it is faster and always correct.
  • Numeric or range queries ("orders over $500 from last week") — this is what SQL WHERE clauses exist for.
  • Small, fixed, well-labeled category sets — a simple classifier or even a lookup table will be more accurate and cheaper than nearest-neighbor search over embeddings.
  • Tasks requiring multi-step reasoning over the result — embeddings retrieve relevant text; they do not reason about it. If the task is "calculate the total refund owed across these five tickets," retrieval is only the first step, not the answer.
  • Situations demanding full explainability of "why did I get this exact match" — cosine similarity scores are useful signals but are not a human-auditable justification the way "this row's status column equals active" is.

A Practical Decision Helper

The three questions above can be encoded as a lightweight heuristic to apply consistently across a codebase or a team, rather than re-litigating the decision informally every time a new feature is proposed.

def recommend_search_strategy(task: dict) -> dict:
    """Suggest a search strategy based on a task description.

    `task` is expected to have boolean/str fields describing the
    matching need. This is a decision aid, not a hard rule — it
    exists to make the reasoning from this lesson explicit and
    reusable in code review.
    """
    needs_meaning_match = task.get("paraphrase_tolerant", False)
    has_enough_text = task.get("avg_text_length_chars", 0) >= 50
    needs_exact_answer = task.get("requires_deterministic_result", False)
    fixed_small_categories = task.get("fixed_category_count", None)

    if needs_exact_answer:
        return {"strategy": "structured_query", "reason": "Deterministic answer required."}

    if fixed_small_categories is not None and fixed_small_categories <= 20:
        return {"strategy": "classification", "reason": "Small, fixed category set."}

    if needs_meaning_match and has_enough_text:
        return {"strategy": "semantic_search", "reason": "Paraphrase-tolerant matching over sufficient text."}

    if needs_meaning_match and not has_enough_text:
        return {"strategy": "structured_query_with_light_text_search",
                "reason": "Meaning matters, but text is too short for reliable embeddings."}

    return {"strategy": "keyword_search", "reason": "No strong signal that meaning-based matching is needed."}

def test_recommend_search_strategy():
    exact_lookup = {"requires_deterministic_result": True}
    assert recommend_search_strategy(exact_lookup)["strategy"] == "structured_query"

    support_search = {"paraphrase_tolerant": True, "avg_text_length_chars": 300}
    assert recommend_search_strategy(support_search)["strategy"] == "semantic_search"

    tag_field = {"paraphrase_tolerant": False, "fixed_category_count": 5}
    assert recommend_search_strategy(tag_field)["strategy"] == "classification"

    print("PASS: recommend_search_strategy covers key branches")

test_recommend_search_strategy()

This function does not call any API — it is a plain decision table expressed in code, which is the point. It walks through the checks in priority order: a deterministic requirement always overrides everything else, a small fixed category set favors classification, and only after ruling those out does it recommend semantic search, and only when there is enough text to make embeddings meaningful. The test uses three representative task descriptions and fake dictionaries rather than real data, confirming each branch produces the expected recommendation. Wiring this kind of check into a design-review checklist prevents a common failure mode: reaching for embeddings by default because they feel modern, on a task a simple WHERE clause would have solved more reliably and far more cheaply.

Common Mistakes

  • Defaulting to semantic search for everything. Embeddings add latency, cost, and infrastructure (a vector index, an embedding pipeline) that a keyword or structured query does not need. Reach for embeddings only when Question 1 above is genuinely "yes."
  • Embedding data that carries no real semantic content. IDs, dates, and enum values compress poorly into meaningful vectors. Embed the free-text fields; filter on the structured fields directly.
  • Treating similarity score as a probability of correctness. A cosine similarity of 0.82 is a relative ranking signal, not a calibrated confidence percentage — Lesson 4 covers this distinction in depth.

Best Practices

  • Write down the decision, not just the code. A short comment or design note stating why semantic search was chosen over keyword search for a given feature saves a future maintainer from re-deriving the reasoning.
  • Combine techniques instead of picking one. Most production search features layer structured filters, keyword matching, and semantic search together; each compensates for the others' blind spots.
  • Prototype cheaply before committing. Because the strategy decision is reversible early on, test semantic search against a small, representative sample of real queries before investing in the full pipeline covered in the rest of this unit.

0 Comments

Reviewed before they appear

No comments yet.

OpenAI SDK
Introduction to the OpenAI SDK Setting Up Python Creating an API Key Your First Call — client.responses.create() and response.output_text Understanding Billing, Credits, and What a Request Costs Why Responses Replaced Chat Completions Anatomy of a Request: model, input, and instructions Anatomy of a Response: The Typed output Array, Not Just Text Roles: User, Assistant, and Developer/System Choosing a Model, and Reading the Models Page Instead of Memorizing Names Instructions vs. Input Writing Prompts That Get Consistent Results Few-Shot Examples Reasoning Models and the reasoning Parameter Debugging a Prompt That Misbehaves Why Streaming Matters for User Experience stream=True and Iterating Over Events Handling the Event Types You Actually Care About Background Mode for Long-Running Jobs Project — Add Live Streaming to Your Chatbot The Problem With Parsing Free Text JSON Schema and Strict Mode Pydantic Models With the SDK's Parse Helpers Handling Refusals and Validation Failures Project — A Resume-to-JSON Extractor Working With input_image input_file, PDFs, and the Files API Image Generation Speech-to-Text and Text-to-Speech Project: A PDF Question-Answering Script What Function Calling Is Defining a Tool Schema The Full Loop Multiple Tools Errors, Timeouts, and Untrusted Arguments Project: A Weather Assistant Web Search File Search and Vector Stores Code Interpreter Remote MCP Servers and Connectors Project: A Research Assistant What an Embedding Is, Without the Maths Generating and Storing Embeddings Similarity Search From Scratch Hosted Vector Stores vs. Rolling Your Own A Small RAG App Over a Folder of Notes Agents vs. a Single API Call — When You Need One pip install openai Giving Agents Tools Handoffs and Multi-Agent Triage Guardrails and Approvals Tracing and Observing What Your Agent Did A Multi-Agent Support Desk Error Codes and What Each One Means Retries, Timeouts, and Backoff Rate Limits and Spend Limits Prompt Caching and Cost Optimisation The Batch API for Bulk Work Async Clients and Concurrency Moderation and Safety Best Practices Designing the App Backend With FastAPI Streaming to a Simple Frontend Deploying and a Cost/Safety Checklist Why Web Search Is Useful for Current Information Using the Web Search Tool with the Responses API Configuring Search Behavior for Application Use Cases Understanding Citations and Source Attribution Where to Go Next Building a Research Assistant with Web Search Combining Web Search with Structured Outputs Handling Conflicting or Low-Quality Web Sources Reducing Unsupported Claims with Grounded Generation Testing Freshness-Sensitive AI Answers Production Considerations for Web-Grounded Applications Understanding File Search and Retrieval-Augmented Generation Creating and Organizing Vector Stores Uploading Documents for Retrieval Connecting Vector Stores to Responses API Requests Designing Document Metadata and Filtering Strategies Building a PDF Question-Answering Application Improving Retrieval Quality With Better Document Preparation Handling Missing Evidence and Retrieval Failures Combining File Search With Web Search Building a Production Knowledge-Base Assistant What the Code Interpreter Tool Is Designed For Running Python-Based Analysis Through the OpenAI SDK Uploading Datasets for Analysis Analyzing CSV and Spreadsheet Data Generating Charts and Data Summaries Handling Generated Files and Downloadable Artifacts Building a Data-Analysis Assistant Combining Code Execution with Structured Outputs Validating Generated Calculations and Results Security and Sandbox Considerations for Code Execution Understanding Multimodal Input with the OpenAI SDK Sending Images to a Model Image Analysis from URLs and Uploaded Files Extracting Text and Information from Screenshots Building an Image-Question-Answering Application Combining Image Input with Structured Output Analyzing Multiple Images in One Request Handling Image Quality and Input Limitations Designing Multimodal Prompts for Reliable Results Building a Practical Vision-Powered Python Application Understanding Speech-to-Text and Text-to-Speech Workflows Transcribing Audio with the OpenAI SDK Working with Uploaded Audio Files Handling Timestamps and Transcription Metadata Building a Meeting Transcription Workflow Generating Spoken Responses from Text Handling Long Audio and Processing Failures Combining Audio with Text and Tool Calling Building an End-to-End Python Voice Application What Embeddings Are and When to Use Them Generating Embeddings With the OpenAI API Preparing Text for Embedding Comparing Vectors With Cosine Similarity Building a Simple Semantic Search Engine in Python Storing Embeddings in a Database Metadata Filtering for Semantic Search Chunking Strategies for Better Retrieval Evaluating Semantic Search Quality Building a Document Similarity Application When Batch Processing Makes Sense Designing Large-Volume AI Processing Pipelines Using Asynchronous Python with the OpenAI SDK Running Concurrent Requests Safely Controlling Concurrency and Avoiding Rate Limits Tracking Batch Job Progress Handling Partial Failures in Bulk Workloads Retrying Failed Items Without Duplicating Successful Work Designing Resumable AI Processing Jobs Building a Production Batch-Processing Pipeline Batch Processing Makes Sense Large-Scale AI Processing Pipelines Async Python with OpenAI SDK Safe Concurrent Requests Concurrency & Rate Limits Batch Progress Tracking Partial Failure Handling Safe Retry Handling Resumable AI Jobs Production Batch Pipeline System–User Data Separation Reusable App Instructions Prompt Templates & Variables Extraction & Classification Prompts Summarization & Transformation Prompts Explicit Output Requirements Prompt Version Management Prompt Testing & Evaluation Reusable Python Prompt Library API Key Security Secure API Key Storage Secure Secret Management Prompt Injection Prevention Trusted vs. Untrusted Content Tool Argument Validation Sensitive Data Handling Secure Logging AI Action Authorization Production AI Security Checklist Why AI Applications Need Evaluation Beyond Unit Tests Unit Testing OpenAI SDK Integration Code Mocking API Responses in Python Tests Testing Structured Outputs Against Schemas Testing Tool-Calling Workflows Building a Small Evaluation Dataset Measuring Accuracy, Consistency, and Failure Rates Regression Testing Prompts and Model Changes Human Evaluation Versus Automated Evaluation Creating a Repeatable Evaluation Pipeline AI Request Monitoring Token Cost Management Usage Metrics Design Reducing Model Calls Prompt & Context Optimization Model Selection & Optimization AI Caching Strategies Interactive Latency Optimization Usage Dashboards & Budget Alerts Performance & Cost Checklist Every API Call Starts Fresh Fixing API Statelessness Server-Side Conversation Memory Limits of Response Chaining What We're Building Conversation Memory Challenges Preparing an OpenAI SDK Application for Deployment Environment-Specific Configuration for Development and Production Deploying a Python AI Service with Docker Container Health Checks and Startup Configuration Managing Secrets in Cloud Deployments Background Workers for Long-Running AI Tasks Queues and Asynchronous Job Architectures Scaling AI Workloads Horizontally Monitoring Production Incidents and Failures Production Deployment Checklist for OpenAI SDK Applications Reusable OpenAI Service Classes AI Client Dependency Injection Typed AI Responses Python Configuration Management AI Request Decorators Centralized AI Error Handling Clean SDK Abstractions Reusable OpenAI Utilities Internal AI Python Libraries SDK Integration Maintenance Production AI Chatbot Document Q&A System Web Research Assistant Customer Support Agent AI Data Analysis Assistant Image Analysis App Meeting Transcription & Summary Semantic Document Search Multi-Tool AI Agent Production OpenAI SDK App Why "It Looked Fine When I Tested It" Isn't Enough Timing Note Status Note Pre-Decision Status Note Current Availability Note
Ask about this post
AI Ask about this post

Ask questions about What Embeddings Are and When to Use Them and get answers drawn from it.

Signed-in readers only.