Text size
Dark mode
RecallRunGuides & benchmarks
Learn to build real systemsGuides•Benchmarks•Experiments•Community
Search the site/Free checklist

Guides/OpenAI SDK

OpenAI SDK: Embeddings & semantic search

An embedding is a list of numbers (a vector) that represents the meaning of a piece of text.

In this post

What this post covers
  1. 1. The API
  2. 2. Cosine similarity
  3. 3. A working semantic search
  4. 4. From a NumPy array to a vector database
  5. 5. Other uses
  6. Recap

An embedding is a list of numbers (a vector) that represents the meaning of a piece of text. Texts with similar meaning get vectors that point in similar directions, even with no words in common: “laptop won’t turn on” lands near “notebook computer has no power”. Comparing vectors with cosine similarity gives you semantic search, deduplication, clustering and recommendations, and it is the “retrieval” in RAG (the AI Engineering guide).

🗺️

A map of meaning

Imagine every sentence placed on a huge map, where nearby places mean similar things. "Refund my order" and "I want my money back" are neighbours; "delivery van" is in another district. An embedding model is the surveyor that gives each sentence its coordinates, with 1,536 of them instead of two.

1. The API

real API

from openai import OpenAI

client = OpenAI()
result = client.embeddings.create(
    model="text-embedding-3-small",          # 1,536 dimensions by default; -large has 3,072
    input=["laptop won't turn on", "notebook computer has no power", "delivery was late"],
)
vectors = [d.embedding for d in result.data]
print(len(vectors), len(vectors[0]), result.usage.total_tokens)

Batch inputs

Send many texts per request (a list). It is much faster than one request per text.

Shorter vectors

dimensions=256 returns smaller vectors that keep most of the quality: less storage, faster search.

Cheap

Embeddings cost a small fraction of text generation. Embed once, store the vectors, and search many times.

2. Cosine similarity

"laptop won't turn on" "notebook has no power" "delivery was late" cos(θ) = (a · b) / (|a| |b|) small angle → cos ≈ 1 → similar large angle → cos ≈ 0 → unrelated OpenAI embeddings are unit length, so cosine similarity = a simple dot product
import numpy as np
from fake_openai import fake_client

products = ["Ergonomic office chair with lumbar support", "Standing desk, electric height adjust",
            "Noise cancelling wireless headphones", "Bluetooth earbuds with charging case",
            "LED desk lamp with USB port", "Mesh back task chair for home office"]

client = fake_client()                                           # fake: toy embeddings (see note)
emb = client.embeddings.create(model="text-embedding-3-small", input=products)
matrix = np.array([d.embedding for d in emb.data])              # one row per product

def search(query, k=3):
    q = np.array(client.embeddings.create(model="text-embedding-3-small", input=query).data[0].embedding)
    scores = matrix @ q                                          # dot product = cosine for unit vectors
    best = np.argsort(-scores)[:k]
    return [(products[i], round(float(scores[i]), 2)) for i in best]

for query in ["chair for my back", "wireless audio"]:
    print(query, "→", search(query))

Output

chair for my back → [('Mesh back task chair for home office', 0.55), ('Ergonomic office chair with lumbar support', 0.28), ('Bluetooth earbuds with charging case', 0.1)]
wireless audio → [('Noise cancelling wireless headphones', 0.38), ('Mesh back task chair for home office', 0.17), ('Standing desk, electric height adjust', 0.11)]
About these numbers

The fake server's embeddings are a toy: hashed words and word fragments, so they only match on shared vocabulary. They let the code run offline. Real embeddings capture meaning, so "wireless audio" would also match "Bluetooth earbuds" and "noise cancelling headphones" with no shared words. Run the same code with OpenAI() to see the difference.

4. From a NumPy array to a vector database

Number of vectorsApproach
up to ~100ka NumPy matrix in memory, as above: exact, simple, milliseconds
~100k to many millionsa vector index (FAISS, pgvector, Chroma, Qdrant, Databricks Vector Search) with approximate nearest neighbours
documents you never want to manageOpenAI vector stores + the file_search tool (part 14)

Store the model name with your vectors. Vectors from different embedding models are not comparable, so switching models means re-embedding everything.

5. Other uses

Deduplication

Pairs with similarity above a threshold (say 0.92) are probably the same ticket, product or question.

Classification without training

Embed one example per category, and assign each new text to the nearest category.

Recap

  • Embeddings map text to vectors; similar meaning → similar direction.
  • Cosine similarity (a dot product for unit vectors) ranks results.
  • Embed in batches, store vectors with the model name, and search many times.
  • NumPy for small sets, a vector database for large ones; use dimensions to shrink vectors.

Get the free RAG production checklist

25 checks to run before real users see your RAG feature: exact-match misses, invented citations, prompt injection through documents, quality that slips after a prompt change. Confirm your email and the PDF comes with the welcome email, plus each new experiment.

One email to confirm. Unsubscribe any time. What's in it · Privacy

Want the production version of this? See ShipRAG: A production RAG + MCP starter kit for FastAPI: hybrid search, validated citations, evals in CI, Docker, 32 tests.

Written by

Alby Tomy

Python/FastAPI Engineer | Data Engineer | AI Engineer building RAG and MCP systems.

X · GitHub · Contact & enquiries