Embeddings
Embeddings
Zet tekst om in vectoren voor semantisch zoeken, retrieval (RAG), aanbevelingen, clustering en deduplicatie, met OpenAI's text-embedding-3-modellen tegen de helft van OpenAI's prijs. Dezelfde model-ID's, dezelfde request en response, dezelfde vectoren: code die je voor OpenAI schreef werkt zodra je de base URL en de key aanpast.
/v1/embeddingsJe betaalt voor de tekst die je verstuurt, en de vectoren zijn gratis. Beide modellen lezen tot 8,192 tokens per input. Text Embedding 3 Small is het model om mee te beginnen: goedkoop, snel en goed voor de meeste zoek- en RAG-taken. Text Embedding 3 Large is het grotere, nauwkeurigere model, voor als retrievalkwaliteit zwaarder weegt dan opslag en prijs.
Snel aan de slag
import osfrom openai import OpenAIclient = OpenAI( base_url="https://api.zurelay.com/v1", api_key=os.environ["ZURELAY_API_KEY"],)response = client.embeddings.create( model="text-embedding-3-small", input="The food was delicious and the waiter was friendly.",)vector = response.data[0].embedding # a list of 1,536 floatsprint(len(vector), response.usage.prompt_tokens) # 1536 10Elke OpenAI SDK werkt: wijs hem naar https://api.zurelay.com/v1, gebruik je Zurelay-key en roep de embeddings-methode aan. Het antwoord is van OpenAI:
{ "object": "list", "data": [ { "object": "embedding", "index": 0, "embedding": [-0.0022748, -0.0093183, 0.0158532, ..., -0.0028842] } ], "model": "text-embedding-3-small", "usage": { "prompt_tokens": 10, "total_tokens": 10 }}data bevat één embedding per input, in de volgorde waarin je ze stuurde, elk met een index. usage.prompt_tokens is waarop de request is gefactureerd. De headers zeggen het ook: x-zurelay-model noemt het model en x-zurelay-input-tokens de tokens, en met x-request-id vind je de request terug in je requestlog.
Parameters
modelstringverplichttext-embedding-3-smalloftext-embedding-3-large.inputstring of arrayverplicht- Wat je kunt embedden: een string, een array van maximaal 2,048 strings, een array met token-ID's (integers uit OpenAI's
cl100k_base-tokenizer), of een array van maximaal 2,048 van zulke arrays. Elke input is maximaal 8,192 tokens, en geen enkele mag een lege string zijn. encoding_formatstringfloat(de standaard): elke embedding is een reeks getallen.base64: elk is een base64-string van little-endian float32-getallen, kleiner en sneller te downloaden. Zie Base64.dimensionsinteger- Maak de vector korter: 1 tot 1,536 bij
text-embedding-3-small, 1 tot 3,072 bijtext-embedding-3-large. Laat je het weg, dan krijg je de volledige lengte. Zie Kortere vectors. userstring- Optioneel: een ID voor de eindgebruiker voor wie het verzoek bedoeld is, zoals in OpenAI’s API. Wordt geaccepteerd, maar verandert niets aan de vectors.
Batchen
Stuur veel teksten in één verzoek: input neemt een array van maximaal 2,048, en ze komen in dezelfde volgorde terug. Eén verzoek met duizend teksten is veel sneller dan duizend verzoeken, en kost hetzelfde: je betaalt per token, niet per verzoek. Houd elk verzoek in totaal onder 300,000 tokens.
# pip install tiktokenimport tiktoken# The tokenizer text-embedding-3 counts with, to keep each request under the limitsenc = tiktoken.get_encoding("cl100k_base")def batches(texts, max_inputs=2048, max_tokens=300_000): batch, tokens = [], 0 for text in texts: count = len(enc.encode(text)) if batch and (len(batch) == max_inputs or tokens + count > max_tokens): yield batch batch, tokens = [], 0 batch.append(text) tokens += count if batch: yield batchvectors = []for batch in batches(texts): response = client.embeddings.create(model="text-embedding-3-small", input=batch) vectors += [item.embedding for item in response.data] # in the order sentSplits documenten langer dan 8,192 tokens in stukken voordat je ze embedt. Voor zoeken en RAG vinden stukken van een paar honderd tokens, een alinea van twee, meestal betere matches dan hele documenten.
Kortere vectors met dimensions
De text-embedding-3-modellen zijn zo getraind dat een verkorte vector nog werkt: vraag je om dimensions, dan krijg je het eerste deel van de volledige vector, opnieuw genormaliseerd naar lengte 1. Kortere vectors kosten minder opslag en geheugen en zoeken sneller, voor iets minder nauwkeurigheid. Text Embedding 3 Large op 1,024 dimensies neemt een derde van de ruimte van de volledige 3,072 in.
response = client.embeddings.create( model="text-embedding-3-large", input="The food was delicious and the waiter was friendly.", dimensions=1024,)print(len(response.data[0].embedding)) # 1024Base64
Met "encoding_format": "base64" komt elke embedding terug als een base64-string van little-endian float32-getallen in plaats van een JSON-array: kleiner, en sneller te downloaden en te parsen. Laat je encoding_format weg, dan vraagt OpenAI’s Python SDK zelf om base64 en decodeert die het voor je, dus je krijgt hoe dan ook gewone floats. Vraag je zelf om base64, dan krijg je de string, om zo te decoderen:
import base64import numpy as npresponse = client.embeddings.create( model="text-embedding-3-small", input=["First text", "Second text"], encoding_format="base64",)# Each embedding is a base64 string of little-endian float32 numbersvectors = [np.frombuffer(base64.b64decode(item.embedding), dtype="<f4") for item in response.data]Semantisch zoeken
Embed je documenten één keer en sla de vectors op. Om te zoeken embed je de query met hetzelfde model en rangschik je de documenten op cosinusgelijkenis: hoe dichter bij 1, hoe dichter bij elkaar in betekenis, zelfs zonder gemeenschappelijke woorden. Vectors van text-embedding-3 hebben lengte 1, dus het inwendig product geeft dezelfde rangschikking.
# pip install openai numpyimport osimport numpy as npfrom openai import OpenAIclient = OpenAI(base_url="https://api.zurelay.com/v1", api_key=os.environ["ZURELAY_API_KEY"])MODEL = "text-embedding-3-small"docs = [ "To change your password, open Settings, choose Security and click Change password.", "Invoices are emailed on the first day of each month and can be downloaded under Billing.", "Invite teammates from the Team page; each person signs in with their own email.", "API keys can be limited to a monthly budget, a rate limit and a list of models.",]def embed(texts): response = client.embeddings.create(model=MODEL, input=texts) return np.array([item.embedding for item in response.data])doc_vectors = embed(docs) # embed once, and store the vectors beside the textdef search(query, k=2): query_vector = embed([query])[0] # The vectors have length 1, so the dot product is the cosine similarity scores = doc_vectors @ query_vector best = np.argsort(-scores)[:k] return [(docs[i], float(scores[i])) for i in best]for text, score in search("How can I change my password?"): print(f"{score:.3f} {text}") # the password article comes firstVoor meer dan een paar duizend documenten houd je de vectors in een vector-database van een vector-index (pgvector, Qdrant, Pinecone, Weaviate, Chroma en dergelijke) en laat je die de rangschikking doen.
Retrieval (RAG)
Retrieval-augmented generation beantwoordt een vraag uit je eigen documenten: zoek de passages die het dichtst bij de vraag liggen en geef ze samen met de vraag aan een chatmodel. Dezelfde Zurelay-key doet beide.
# After the search example above: retrieve, then answer from what was foundquestion = "How do I stop a key from spending too much?"notes = "\n".join(text for text, _ in search(question))answer = client.chat.completions.create( model="gpt-6-luna", messages=[ { "role": "system", "content": "Answer from these notes only. If they don't say, say so.\n\n" + notes, }, {"role": "user", "content": question}, ],)print(answer.choices[0].message.content)LangChain, LlamaIndex en de AI SDK
Frameworks met een OpenAI-embeddingsklasse werken met de base-URL en key van Zurelay, en de model-ID’s blijven die van OpenAI.
LangChain
# pip install langchain-openaiimport osfrom langchain_openai import OpenAIEmbeddingsembeddings = OpenAIEmbeddings( model="text-embedding-3-small", base_url="https://api.zurelay.com/v1", api_key=os.environ["ZURELAY_API_KEY"],)query_vector = embeddings.embed_query("How can I change my password?")doc_vectors = embeddings.embed_documents(["First document", "Second document"])LangChain’s Python OpenAIEmbeddings tokeniseert teksten standaard zelf en stuurt de token-ID’s. Zurelay accepteert token-arrays, dus de standaardinstellingen werken zoals ze zijn.
LlamaIndex
# pip install llama-index-embeddings-openaiimport osfrom llama_index.core import Settingsfrom llama_index.embeddings.openai import OpenAIEmbeddingSettings.embed_model = OpenAIEmbedding( model="text-embedding-3-small", api_base="https://api.zurelay.com/v1", api_key=os.environ["ZURELAY_API_KEY"],)vector = Settings.embed_model.get_text_embedding("How can I change my password?")Met Settings.embed_model ingesteld embedt elke index en retriever die je bouwt via Zurelay. Voor het chatmodel zie de LlamaIndex-gids.
Vercel AI SDK
// npm install ai @ai-sdk/openaiimport { createOpenAI } from "@ai-sdk/openai";import { cosineSimilarity, embed, embedMany } from "ai";const zurelay = createOpenAI({ baseURL: "https://api.zurelay.com/v1", apiKey: process.env.ZURELAY_API_KEY,});const model = zurelay.embedding("text-embedding-3-small");const { embeddings } = await embedMany({ model, values: ["First document", "Second document"] });const { embedding } = await embed({ model, value: "How can I change my password?" });console.log(cosineSimilarity(embedding, embeddings[0]));Apps en vectorstores
Apps die OpenAI’s embeddings aanroepen met een base-URL die je kunt instellen, werken op dezelfde manier: zet die op https://api.zurelay.com/v1, gebruik je Zurelay-key en een van de model-ID’s hierboven. Dat geldt voor de OpenAI-embeddingengine van Open WebUI voor documenten, de RAG API van LibreChat, de Embeddings OpenAI-node van n8n en de OpenAI-compatibele modelprovider van Dify.
Dezelfde vectors als OpenAI
Verzoeken naar text-embedding-3-small en text-embedding-3-large draaien op OpenAI’s eigen modellen. We hebben elke route tegen OpenAI’s API gecontroleerd: voor dezelfde tekst en hetzelfde model komen de vectors overeen met die van OpenAI, met een cosinusgelijkenis van 0.9999 of meer. Een index die je met OpenAI’s API hebt gebouwd blijft dus werken wanneer je je query’s via Zurelay embedt, en een index die via Zurelay is gebouwd werkt met die van OpenAI. Wisselen tussen de twee vraagt geen nieuwe embedding.
Van model wisselen betekent opnieuw embedden
Vectors van verschillende modellen kun je niet vergelijken
text-embedding-3-small zegt niets naast een vector van text-embedding-3-large, of van welk ander model dan ook: elk model plaatst tekst in zijn eigen ruimte. Om van model te wisselen embed je dus alles wat je hebt opgeslagen opnieuw met het nieuwe model, en query je daarna met dat model.Om dezelfde reden wordt een embeddings-verzoek altijd beantwoord door het model dat je hebt opgegeven. Slimme routering geeft het nooit aan een ander model, wat je instellingen ook zijn: is het model down, dan krijg je 503 model_unavailable, om het opnieuw te proberen, in plaats van vectors die niet bij je index passen.
Limieten
- Maximaal 2,048 inputs per verzoek.
- Maximaal 8,192 tokens per input, en 300,000 in het hele verzoek.
- Geen lege strings.
dimensionsvan 1 tot de volledige lengte van het model: 1,536 bijtext-embedding-3-small, 3,072 bijtext-embedding-3-large.- Een verzoek boven een limiet wordt geweigerd met een
400die de limiet noemt, en kost niets. - Een key die tot bepaalde modellen beperkt is, moet het embedding-model op de lijst hebben. Zie Limieten en regels voor keys.
Betalen voor embeddings
Embeddings worden alleen op input-tokens afgerekend, geteld zoals OpenAI ze telt, en de vectors zijn gratis. Text Embedding 3 Small kost $0.01 per 1M tokens en Text Embedding 3 Large $0.065, de helft van OpenAI's prijs. Een miljoen teksten van elk 100 tokens, 100M tokens, kosten $1.00 om te embedden met Text Embedding 3 Small en $6.50 met Text Embedding 3 Large, tegenover $2.00 en $13.00 bij OpenAI.
Er is geen minimum per request: een request van 10 tokens wordt als 10 tokens afgerekend. Kortere dimensions en base64 kosten hetzelfde. De usage- en x-zurelay-input-tokens-header van elk antwoord zeggen waarop is afgerekend, elk request staat in je requestlog, en mislukte requests kosten niets.
Fouten
Fouten komen in OpenAI's vorm, dus de SDK's van OpenAI geven ze door zoals ze dat voor OpenAI doen.
Keys, tegoed, budgetten en rate limits geven dezelfde fouten als elk ander endpoint: zie Fouten. De SDKs van OpenAI proberen de fouten opnieuw waarbij dat zinvol is.
Probeer het eerst uit
Vragen, of ontbreekt er iets? Vraag het ons supportteam en je krijgt antwoord per e-mail, meestal binnen een minuut.