Talking about embeddings conceptually is one thing. In this lesson you will call real APIs, inspect raw vectors, and compute cosine similarity yourself — the exact code you will use in every RAG pipeline, semantic search system, and recommendation engine you build.
Most providers expose a dedicated embeddings endpoint that takes text and returns a vector. The Anthropic SDK (as of 2025) routes embedding requests to Voyage AI models. OpenAI exposes embeddings directly. Both follow the same pattern.
text-embedding-3-smallfrom openai import OpenAI
client = OpenAI(api_key="YOUR_OPENAI_API_KEY")
response = client.embeddings.create(
model="text-embedding-3-small", # 1536 dimensions, cheapest option
input="How do neural networks learn?",
)
vector = response.data[0].embedding
print(f"Dimensions: {len(vector)}") # → 1536
print(f"First 5 values: {vector[:5]}") # → [-0.023, 0.041, -0.105, ...]import anthropic
client = anthropic.Anthropic(api_key="YOUR_ANTHROPIC_API_KEY")
# voyage-3 is Anthropic's recommended general-purpose embedding model
response = client.beta.messages.batches # Embeddings use the Voyage endpoint:
# Direct Voyage API call (included with Anthropic API access):
import voyageai
vo = voyageai.Client(api_key="YOUR_VOYAGE_KEY") # same key as Anthropic
result = vo.embed(
["How do neural networks learn?"],
model="voyage-3", # 1024 dimensions
input_type="query", # "query" for search queries, "document" for corpus items
)
vector = result.embeddings[0]
print(f"Dimensions: {len(vector)}") # → 1024To compare two embeddings, compute the cosine similarity: the dot product of the two unit-length vectors. A score of 1.0 means identical direction (same meaning); 0.0 means perpendicular (unrelated); negative values are rare with well-trained models.
import numpy as np
def cosine_similarity(vec_a: list[float], vec_b: list[float]) -> float:
a = np.array(vec_a)
b = np.array(vec_b)
return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
# --- Example: compare two sentences ---
texts = [
"The cat sat on the mat.",
"A feline rested on a rug.",
"The stock market dropped 3% today.",
]
client = OpenAI(api_key="YOUR_KEY")
def embed(text: str) -> list[float]:
return client.embeddings.create(
model="text-embedding-3-small",
input=text,
).data[0].embedding
vecs = [embed(t) for t in texts]
print(cosine_similarity(vecs[0], vecs[1])) # → ~0.94 (cat ≈ feline)
print(cosine_similarity(vecs[0], vecs[2])) # → ~0.11 (cat ≠ stock market)input_type MattersMany embedding models are trained with separate encoders for queries and documents. If you use the same encoder for both, retrieval quality degrades. Always match the type:
input_type="query"— use when embedding a user's search questioninput_type="document" — use when embedding corpus chunks at index timeNone — symmetric tasks like clustering or deduplication (no retrieval asymmetry)For each sentence pair below, guess whether the similarity is high (≥0.8), moderate (0.5–0.8), or low (<0.5), then reveal the real value. These are real cosine similarity scores from text-embedding-3-small.
Embedding API calls have latency. When you need to embed hundreds or thousands of documents, send them in batches rather than one at a time:
def embed_batch(texts: list[str], batch_size: int = 100) -> list[list[float]]:
"""Embed a large list of texts in batches to respect API limits."""
all_embeddings = []
for i in range(0, len(texts), batch_size):
batch = texts[i : i + batch_size]
response = client.embeddings.create(
model="text-embedding-3-small",
input=batch,
)
# Response preserves order — safe to extend directly
all_embeddings.extend([item.embedding for item in response.data])
return all_embeddings
# Embed 500 chunks efficiently
chunks = ["chunk text..."] * 500
vectors = embed_batch(chunks)
print(f"Embedded {len(vectors)} chunks") # → 500cosine_similarity(a, b) using only NumPy (no sklearn). Verify it returns 1.0 when both inputs are the same vector.most_similar(query, corpus) that returns the index of the most similar document in a list, using cosine similarity.Next up: not all embedding models are equal. In Lesson 3 we compare the key models by dimension, cost, context length, and retrieval benchmark performance — so you can pick the right one for any job.