Embeddings
Embeddings
Transforme texto em vetores para busca semântica, recuperação (RAG), recomendações, agrupamento e deduplicação, com os modelos text-embedding-3 da OpenAI pela metade do preço da OpenAI. Mesmos IDs de modelo, mesma requisição e mesma resposta, mesmos vetores: código feito para a OpenAI funciona assim que você troca a base URL e a chave.
/v1/embeddingsVocê paga pelo texto que envia, e os vetores são de graça. Os dois modelos leem até 8,192 tokens por entrada. O Text Embedding 3 Small é o indicado para começar: barato, rápido e bom para a maioria dos casos de busca e RAG. O Text Embedding 3 Large é o maior e mais preciso, para quando a qualidade da recuperação importa mais do que armazenamento e preço.
Início rápido
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 10Qualquer SDK da OpenAI funciona: aponte para https://api.zurelay.com/v1, use sua chave da Zurelay e chame o método de embeddings dele. A resposta é a da 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 traz um embedding por entrada, na ordem em que você as enviou, cada um com seu index. usage.prompt_tokens é o que foi cobrado na requisição. Os headers também dizem isso: x-zurelay-model indica o modelo e x-zurelay-input-tokens os tokens, e x-request-id localiza a requisição no seu log de requisições.
Parâmetros
modelstringobrigatóriotext-embedding-3-smalloutext-embedding-3-large.inputstring ou arrayobrigatório- O que enviar: uma string, um array de até 2,048 strings, um array de IDs de token (inteiros do tokenizer
cl100k_baseda OpenAI) ou um array de até 2,048 arrays desses. Cada entrada tem no máximo 8,192 tokens, e nenhuma pode ser uma string vazia. encoding_formatstringfloat(o padrão): cada embedding é um array de números.base64: cada um é uma string base64 de números float32 little-endian, menor e mais rápido de baixar. Veja Base64.dimensionsinteger- Encurte o vetor: de 1 a 1,536 em
text-embedding-3-small, de 1 a 3,072 emtext-embedding-3-large. Deixe de fora para o comprimento total. Veja Vetores mais curtos. userstring- Opcional: um ID do usuário final para quem é a requisição, como na API da OpenAI. É aceito e não muda nada nos vetores.
Envio em lote
Envie muitos textos em uma requisição: input aceita um array de até 2,048, e eles voltam na mesma ordem. Uma requisição com mil textos é bem mais rápida que mil requisições, e custa o mesmo: você paga por token, não por requisição. Mantenha cada requisição com menos de 300,000 tokens no total.
# 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 sentDivida documentos com mais de 8,192 tokens em chunks antes de fazer o embedding. Para busca e RAG, chunks de algumas centenas de tokens, um ou dois parágrafos, costumam encontrar resultados melhores do que documentos inteiros.
Vetores mais curtos com dimensions
Os modelos text-embedding-3 são treinados para que um vetor encurtado ainda funcione: peça dimensions e você recebe a primeira parte do vetor completo, renormormalizada para comprimento 1. Vetores mais curtos ocupam menos armazenamento e memória e buscam mais rápido, com um pouco menos de precisão. O Text Embedding 3 Large com 1,024 dimensões ocupa um terço do espaço do seu 3,072 completo.
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
Com "encoding_format": "base64", cada embedding volta como uma string base64 de números float32 little-endian em vez de um array JSON: menor e mais rápido de baixar e processar. Quando você deixa encoding_format de fora, o SDK Python da OpenAI pede base64 por conta própria e decodifica para você, então você recebe floats normais de qualquer jeito. Se você mesmo pedir base64, recebe a string, para decodificar assim:
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]Busca semântica
Faça o embedding dos seus documentos uma vez e armazene os vetores. Para buscar, faça o embedding da consulta com o mesmo modelo e ordene os documentos por similaridade de cosseno com ela: quanto mais perto de 1, mais próximos em significado, mesmo sem nenhuma palavra em comum. Os vetores text-embedding-3 têm comprimento 1, então o produto escalar dá a mesma ordenação.
# 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 firstPara mais do que alguns milhares de documentos, guarde os vetores em um banco de dados vetorial ou um índice vetorial (pgvector, Qdrant, Pinecone, Weaviate, Chroma e afins) e deixe que ele faça a ordenação.
Recuperação (RAG)
A geração aumentada por recuperação responde a uma pergunta a partir dos seus próprios documentos: busque os trechos mais próximos da pergunta e então entregue-os a um modelo de chat junto com a pergunta. A mesma chave da Zurelay faz as duas coisas.
# 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 e o AI SDK
Frameworks com uma classe de embeddings da OpenAI funcionam com a base URL e a chave da Zurelay, e os IDs dos modelos continuam sendo os da 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"])O OpenAIEmbeddings do LangChain em Python tokeniza os textos por conta própria por padrão e envia os IDs dos tokens. A Zurelay aceita arrays de tokens, então as configurações padrão funcionam como estão.
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?")Com Settings.embed_model definido, todo índice e retriever que você criar faz o embedding pela Zurelay. Para o modelo de chat, veja o guia do LlamaIndex.
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 e bancos de vetores
Apps que chamam os embeddings da OpenAI com uma base URL que você pode definir funcionam do mesmo jeito: defina-a como https://api.zurelay.com/v1, use sua chave da Zurelay e um dos IDs de modelo acima. Isso inclui o motor de embeddings da OpenAI do Open WebUI para documentos, a RAG API do LibreChat, o nó Embeddings OpenAI do n8n e o provedor de modelos compatível com OpenAI do Dify.
Os mesmos vetores da OpenAI
As requisições para text-embedding-3-small e text-embedding-3-large rodam nos próprios modelos da OpenAI. Conferimos cada rota contra a API da OpenAI: para o mesmo texto e modelo, os vetores batem com os da OpenAI, com similaridade de cosseno de 0.9999 ou mais. Então um índice que você criou com a API da OpenAI continua funcionando quando você faz o embedding das suas consultas pela Zurelay, e um criado pela Zurelay funciona com a da OpenAI. Mudar de uma para a outra não exige refazer o embedding.
Trocar de modelo significa refazer o embedding
Vetores de modelos diferentes não podem ser comparados
text-embedding-3-small não significa nada ao lado de um de text-embedding-3-large, ou de qualquer outro modelo: cada modelo posiciona o texto em seu próprio espaço. Então, para trocar de modelo, refaça o embedding de tudo o que você armazenou com o novo modelo, e consulte com esse modelo daí em diante.Pelo mesmo motivo, uma requisição de embeddings é sempre respondida pelo modelo que você indicou. O Roteamento inteligente nunca a entrega a outro modelo, sejam quais forem suas configurações: se o modelo estiver fora do ar, você recebe 503 model_unavailable, para tentar de novo, em vez de vetores que não batem com o seu índice.
Limites
- Até 2,048 entradas por requisição.
- Até 8,192 tokens em cada entrada, e 300,000 na requisição inteira.
- Nenhuma string vazia.
dimensionsde 1 até o comprimento total do modelo: 1,536 emtext-embedding-3-small, 3,072 emtext-embedding-3-large.- Uma requisição acima de um limite é recusada com um
400que indica o limite, e não custa nada. - Uma chave limitada a alguns modelos precisa ter o modelo de embeddings na lista dela. Veja Limites e regras de chaves.
Pagar por embeddings
Embeddings são cobrados só pelos tokens de entrada, contados do jeito que a OpenAI conta, e os vetores são grátis. Text Embedding 3 Small custa $0.01 por 1M de tokens e Text Embedding 3 Large $0.065, metade do preço da OpenAI. Um milhão de textos de 100 tokens cada, 100M de tokens, custa $1.00 para gerar embeddings com Text Embedding 3 Small e $6.50 com Text Embedding 3 Large, contra $2.00 e $13.00 na OpenAI.
Não há mínimo por requisição: uma requisição de 10 tokens é cobrada como 10 tokens. Um dimensions mais curto e base64 custam o mesmo. Os headers usage e x-zurelay-input-tokens de cada resposta dizem sobre o que foi cobrado, toda requisição fica no seu request log, e requisições que falham não custam nada.
Erros
Os erros vêm no formato da OpenAI, então os SDKs da OpenAI os lançam como lançariam para a OpenAI.
Chaves, crédito, orçamentos e limites de taxa geram os mesmos erros de todos os outros endpoints: veja Erros. Os SDKs da OpenAI repetem automaticamente as requisições que vale a pena repetir.
Experimente primeiro
Tem dúvidas ou falta algo? Fale com nossa equipe de suporte e responderemos por e-mail, geralmente em até um minuto.