エンベディング
エンベディング
テキストをベクトルに変換して、セマンティック検索、検索拡張(RAG)、レコメンド、クラスタリング、重複排除に利用できます。OpenAI の text-embedding-3 モデルを OpenAI の半額でご利用いただけます。モデルIDもリクエストとレスポンスもベクトルも同じなので、base URL とキーを変更するだけで、OpenAI 向けに書いたコードがそのまま動きます。
/v1/embeddings課金対象は送信したテキストのみで、ベクトルは無料です。どちらのモデルも、入力1件あたり最大8,192トークンまで処理できます。まずは Text Embedding 3 Small から始めてください。安価で高速、大半の検索や RAG に十分です。Text Embedding 3 Large はより大きく高精度なモデルで、ストレージやコストよりも検索品質を重視する場合に適しています。
クイックスタート
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 10どの OpenAI SDK でも使えます。https://api.zurelay.com/v1 を指定し、Zurelay のキーを使って、embeddings メソッドを呼び出すだけです。返ってくるのは 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 には入力ごとに1つのエンベディングが、送信した順に並びます。それぞれに index が付きます。usage.prompt_tokens が課金対象となったトークン数です。ヘッダーにも記載されています。x-zurelay-model はモデル名、x-zurelay-input-tokens はトークン数、x-request-id を使えばリクエストログからそのリクエストを特定できます。
パラメータ
modelstring必須text-embedding-3-smallまたはtext-embedding-3-large。input文字列または配列必須- 埋め込めるもの:文字列、最大2,048個の文字列の配列、トークンID(OpenAI の
cl100k_baseトークナイザーによる整数)の配列、またはそうした配列を最大2,048個含む配列です。各入力は最大8,192トークンで、空文字列は指定できません。 encoding_formatstringfloat(デフォルト):各埋め込みは数値の配列です。base64:各埋め込みはリトルエンディアンの float32 数値を base64 にした文字列で、サイズが小さくダウンロードも高速です。Base64 を参照してください。dimensionsinteger- ベクトルを短くします。
text-embedding-3-smallでは 1〜1,536、text-embedding-3-largeでは 1〜3,072。省略すると最大長になります。ベクトルの短縮 を参照してください。 userstring- 任意:リクエストの対象となるエンドユーザーの ID です(OpenAI の API と同じ)。受け付けますが、ベクトルの内容は変わりません。
バッチ処理
1 回のリクエストで多数のテキストを送信できます。input は最大 2,048 件の配列を受け取り、同じ順序で返ってきます。1,000 件のテキストを 1 回のリクエストで送るほうが、1,000 回のリクエストよりはるかに速く、コストは同じです。課金はリクエスト単位ではなくトークン単位です。1 リクエストあたり合計 300,000 トークン未満に収めてください。
# 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 sent8,192 トークンを超えるドキュメントは、埋め込む前にチャンクに分割してください。検索や RAG では、数百トークン程度(1〜2 段落)のチャンクのほうが、ドキュメント全体よりも良い一致が得られるのが一般的です。
dimensions によるベクトルの短縮
text-embedding-3 モデルは、短縮したベクトルでもそのまま使えるように学習されています。dimensions を指定すると、完全なベクトルの先頭部分を長さ 1 に再正規化したものが返ります。短いベクトルはストレージとメモリの使用量が少なく検索も高速ですが、精度はわずかに下がります。Text Embedding 3 Large を 1,024 次元にすると、最大の 3,072 次元の 3 分の 1 の容量になります。
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
"encoding_format": "base64" を指定すると、各埋め込みは JSON 配列ではなく、リトルエンディアンの float32 数値を base64 にした文字列で返ります。サイズが小さく、ダウンロードとパースも高速です。encoding_format を省略した場合、OpenAI の Python SDK が自身で base64 を要求してデコードするため、どちらでも通常の float 値が得られます。自分で base64 を要求した場合は文字列が返るので、次のようにデコードします:
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]セマンティック検索
ドキュメントは一度だけ埋め込み、ベクトルを保存します。検索時には同じモデルでクエリを埋め込み、クエリとのコサイン類似度でドキュメントをランク付けします。1 に近いほど意味が近く、共通する単語がなくても一致します。text-embedding-3 のベクトルは長さが 1 なので、内積でも同じランキングになります。
# 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 first数千件を超えるドキュメントの場合は、ベクトルデータベースやベクトルインデックス(pgvector、Qdrant、Pinecone、Weaviate、Chroma など)にベクトルを保存し、ランキングはそちらに任せてください。
検索拡張(RAG)
検索拡張生成(RAG)は、自社のドキュメントから質問に答えます。質問に最も近い箇所を検索し、その箇所と質問をチャットモデルに渡します。どちらも同じ Zurelay のキーで実行できます。
# 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、AI SDK
OpenAI の embeddings クラスを持つフレームワークは、Zurelay のベース URL とキーでそのまま動作し、モデル ID も 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 の Python OpenAIEmbeddings は、デフォルトでテキストを自身でトークン化し、トークン ID を送信します。Zurelay はトークン配列を受け付けるため、デフォルト設定のままで動作します。
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?")Settings.embed_model を設定すると、作成するすべてのインデックスとリトリーバーが Zurelay 経由で埋め込みを行います。チャットモデルについては、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]));アプリとベクトルストア
ベース URL を設定できる形で OpenAI の embeddings を呼び出すアプリも同様に動作します。https://api.zurelay.com/v1 を設定し、Zurelay のキーと上記のモデル ID のいずれかを使用してください。対象には、Open WebUI のドキュメント用 OpenAI embedding エンジン、LibreChat の RAG API、n8n の Embeddings OpenAI ノード、Dify の OpenAI 互換モデルプロバイダーが含まれます。
OpenAI と同じベクトル
text-embedding-3-small と text-embedding-3-large へのリクエストは OpenAI 自身のモデルで実行されます。当社はすべてのルートを OpenAI の API と照合しました。同じテキストと同じモデルでは、ベクトルは OpenAI のものと一致し、コサイン類似度は 0.9999 以上です。そのため、OpenAI の API で構築したインデックスは、Zurelay 経由でクエリを埋め込んでもそのまま動作し、Zurelay で構築したインデックスも OpenAI で動作します。両者の移行に再埋め込みは不要です。
モデルを切り替えるときは再埋め込みが必要
異なるモデルのベクトルは比較できません
text-embedding-3-small のベクトルは、text-embedding-3-large のベクトルや他のモデルのベクトルと並べても意味を持ちません。モデルごとにテキストを独自の空間に配置するためです。モデルを変更する場合は、保存済みのすべてを新しいモデルで再埋め込みし、以降はそのモデルでクエリを実行してください。同じ理由から、embeddings リクエストは常に指定したモデルが処理します。スマートルーティング は、設定にかかわらず他のモデルに振り分けることはありません。モデルが停止している場合は、インデックスと一致しないベクトルではなく、再試行用の 503 model_unavailable が返ります。
制限
- 1 リクエストあたり最大 2,048 件の入力。
- 各入力は最大 8,192 トークン、リクエスト全体で最大 300,000 トークン。
- 空文字列は不可。
dimensionsは 1 からモデルの最大長まで。text-embedding-3-smallでは 1,536、text-embedding-3-largeでは 3,072。- 制限を超えたリクエストは、制限名を含む
400で拒否され、課金も発生しません。 - 一部のモデルに制限されたキーでは、埋め込みモデルをそのリストに含める必要があります。キーの制限とルール を参照してください。
埋め込みの料金
エンベディングの課金は入力トークンのみで、数え方はOpenAIと同じです。ベクトルは無料です。 Text Embedding 3 Smallは1Mトークンあたり$0.01 および Text Embedding 3 Large $0.065で、OpenAIの半額です。100トークンのテキスト100万件、つまり100MトークンをText Embedding 3 Small および Text Embedding 3 Large の $6.50で埋め込むと$1.00、OpenAIでは$2.00 および $13.00かかります。
1リクエストあたりの最低額はありません。10トークンのリクエストは10トークン分の課金です。短いdimensionsもbase64も同じ料金です。各レスポンスのusageとx-zurelay-input-tokensヘッダーに課金対象が示され、すべてのリクエストはリクエストログに記録され、失敗したリクエストは課金されません。
エラー
エラーはOpenAIと同じ形式で返るため、OpenAIのSDKはOpenAIに対するのと同じように例外を発生させます。
キー、クレジット、予算、レート制限に関するエラーは、ほかのエンドポイントと同じです。詳しくはエラーをご覧ください。OpenAI の SDK は、再試行する価値のあるエラーを自動で再試行します。
まず試してみる
ご質問や不足している情報はありますか? サポートチームにお問い合わせください。通常1分以内にメールで回答します。