Embeddings

OpenAI API · Practical · 10 min

Embeddings power semantic search, recommendations, clustering and the retrieval step of RAG.

An embedding turns a piece of text into a long list of numbers (a vector) so that texts with similar meaning get similar vectors. "How do I reset my password?" and "I forgot my login" end up close together even though they share no words.

Call client.embeddings.create(model=..., input=...). input can be one string or a list of strings; each result is in response.data[i].embedding. Send lists in one call rather than one call per string.

The current models are text-embedding-3-small (cheap, 1,536 numbers per vector) and text-embedding-3-large (more accurate, 3,072 numbers). Each input can be up to 8,192 tokens. The dimensions parameter returns shorter vectors, which saves storage with a small loss in quality.

To compare two vectors use cosine similarity: a score near 1 means very similar meaning, near 0 unrelated. OpenAI's vectors are normalised (length 1), so cosine similarity is just the dot product, the sum of the pairwise products.

Rules that save pain:

  • Use the same model for your stored documents and your queries; vectors from different models can't be compared.
  • Store vectors once (in a database or a vector store) instead of recomputing.
  • If you shorten vectors yourself, normalise them again.

For a managed alternative where OpenAI stores and searches the vectors for you, see file search.

Example

from openai import OpenAI

client = OpenAI()
docs = ["How to reset your password", "Shipping times to Europe",
        "Changing the email on your account"]
query = "I forgot my login"

resp = client.embeddings.create(model="text-embedding-3-small",
                                input=docs + [query])
vectors = [d.embedding for d in resp.data]
doc_vecs, q = vectors[:-1], vectors[-1]

def cosine(a, b):                     # vectors are normalised
    return sum(x * y for x, y in zip(a, b))

ranked = sorted(zip(docs, doc_vecs), key=lambda p: cosine(q, p[1]), reverse=True)
for text, vec in ranked:
    print(f"{cosine(q, vec):.3f}  {text}")
# highest score: How to reset your password

Remember

  • client.embeddings.create returns one vector per input
  • text-embedding-3-small / -large; dimensions shortens vectors
  • Cosine similarity compares meaning; never mix models

Check yourself

  1. Documents were embedded with text-embedding-3-large. What model must the queries use?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developers.openai.com/api/docs/guides/embeddings