エンベディング

エンベディング

テキストをベクトルに変換して、セマンティック検索、検索拡張(RAG)、レコメンド、クラスタリング、重複排除に利用できます。OpenAI の text-embedding-3 モデルを OpenAI の半額でご利用いただけます。モデルIDもリクエストとレスポンスもベクトルも同じなので、base URL とキーを変更するだけで、OpenAI 向けに書いたコードがそのまま動きます。

POST/v1/embeddings
モデルID入力100万トークンあたりOpenAI の価格次元数
Text Embedding 3 Smalltext-embedding-3-small$0.01$0.021,536以下
Text Embedding 3 Largetext-embedding-3-large$0.065$0.133,072以下

課金対象は送信したテキストのみで、ベクトルは無料です。どちらのモデルも、入力1件あたり最大8,192トークンまで処理できます。まずは Text Embedding 3 Small から始めてください。安価で高速、大半の検索や RAG に十分です。Text Embedding 3 Large はより大きく高精度なモデルで、ストレージやコストよりも検索品質を重視する場合に適しています。

クイックスタート

import os
from openai import OpenAI
client = 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 floats
print(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_formatstring
float(デフォルト):各埋め込みは数値の配列です。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 tiktoken
import tiktoken
# The tokenizer text-embedding-3 counts with, to keep each request under the limits
enc = 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 batch
vectors = []
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 sent

8,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)) # 1024
比較するものには、すべて同じモデルと同じサイズを使ってください。長さの異なるベクトルは比較できず、多くのベクトルデータベースではインデックス作成時に長さが固定されます。短いベクトルもコストは同じです。料金は入力トークン単位です。

Base64

"encoding_format": "base64" を指定すると、各埋め込みは JSON 配列ではなく、リトルエンディアンの float32 数値を base64 にした文字列で返ります。サイズが小さく、ダウンロードとパースも高速です。encoding_format を省略した場合、OpenAI の Python SDK が自身で base64 を要求してデコードするため、どちらでも通常の float 値が得られます。自分で base64 を要求した場合は文字列が返るので、次のようにデコードします:

import base64
import numpy as np
response = 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 numbers
vectors = [np.frombuffer(base64.b64decode(item.embedding), dtype="<f4") for item in response.data]

ドキュメントは一度だけ埋め込み、ベクトルを保存します。検索時には同じモデルでクエリを埋め込み、クエリとのコサイン類似度でドキュメントをランク付けします。1 に近いほど意味が近く、共通する単語がなくても一致します。text-embedding-3 のベクトルは長さが 1 なので、内積でも同じランキングになります。

# pip install openai numpy
import os
import numpy as np
from openai import OpenAI
client = 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 text
def 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 found
question = "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-openai
import os
from langchain_openai import OpenAIEmbeddings
embeddings = 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-openai
import os
from llama_index.core import Settings
from llama_index.embeddings.openai import OpenAIEmbedding
Settings.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/openai
import { 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に対するのと同じように例外を発生させます。

ステータスコード発生条件
400invalid_requestフィールドの欠落または不正です。空文字列、2,048件を超える入力、整数でないdimensions、floatまたはbase64以外のencoding_formatなどが該当します。どれに該当するかはメッセージに示されます。
400invalid_valuedimensionsがモデルの最大次元数を超えています。
400context_length_exceeded入力が8,192トークンを超えているか、リクエストが300,000を超えています。テキストを分割するか、一度に送る入力を減らしてください。
503model_unavailableモデルが一時的に利用できません。retry-after ヘッダーに示された秒数が経過してから再試行してください。

キー、クレジット、予算、レート制限に関するエラーは、ほかのエンドポイントと同じです。詳しくはエラーをご覧ください。OpenAI の SDK は、再試行する価値のあるエラーを自動で再試行します。

まず試してみる

ダッシュボードの プレイグラウンド で埋め込みモデルを選び、テキストをいくつか書いて埋め込んでみてください。各ベクトル、課金されたトークン数、すべてのペアのコサイン類似度が表示されます。「コードを取得」ボタンは、同じリクエストを Python、Node.js、cURL 用に書き出します。

ご質問や不足している情報はありますか? サポートチームにお問い合わせください。通常1分以内にメールで回答します。