امبدینگ (Embedding) متن را به یک بردار عددی تبدیل میکند؛ متنهایی که معنای نزدیک دارند، بردارهای نزدیک میگیرند، حتی اگر واژههایشان فرق کند. با آن جستوجوی معنایی میسازید («چند روزه میرسه؟» متن «ارسال یک تا دو روز کاری طول میکشد» را پیدا میکند)، پرسشهای تکراری را دستهبندی میکنید و پایهٔ RAG را میگذارید: مدل از روی اسناد خودتان جواب بدهد.
درخواست و پاسخ#
curl https://gisoo.pro/api/v1/embeddings \
-H "Authorization: Bearer $GISOO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "text-embedding-3-small",
"input": ["How long does shipping take?", "What is your return policy?"]
}'
{
"object": "list",
"data": [
{ "object": "embedding", "index": 0, "embedding": [0.0182, -0.0041, 0.0333, "…"] },
{ "object": "embedding", "index": 1, "embedding": [-0.0107, 0.0219, 0.0045, "…"] }
],
"model": "text-embedding-3-small",
"usage": { "prompt_tokens": 14, "total_tokens": 14 }
}
GET /models?type=embedding.float (پیشفرض) یا base64 برای پاسخ کمحجمتر.مدلها#
text-embedding-3-small: ارزان و سریع؛ برای بیشتر جستوجوهای معنایی کافی است.text-embedding-3-large: دقیقتر، با بردار بزرگتر.- مدلهای
gemini-embeddingهم در فهرست هستند.
بردارهای دو مدل مختلف با هم مقایسهپذیر نیستند. سندها و پرسشها را با یک مدل امبدینگ کنید؛ اگر مدل را عوض کردید، همهٔ سندها را دوباره امبدینگ کنید.
نمونهٔ کامل: جستوجوی معنایی فارسی#
سندها را یک بار امبدینگ و ذخیره کنید؛ برای هر پرسش فقط خود پرسش امبدینگ میشود و نزدیکترین سندها با شباهت کسینوسی پیدا میشوند:
# pip install openai numpy
import numpy as np
from openai import OpenAI
client = OpenAI(base_url="https://gisoo.pro/api/v1", api_key="sk-gisoo-v1-...")
MODEL = "text-embedding-3-small"
docs = [
"ارسال سفارش به تهران یک تا دو روز کاری طول میکشد.",
"کالای سالم را تا هفت روز پس از تحویل میتوانید مرجوع کنید.",
"پرداخت با همهٔ کارتهای عضو شتاب انجام میشود.",
]
def embed(texts):
res = client.embeddings.create(model=MODEL, input=texts)
vectors = np.array([d.embedding for d in res.data])
return vectors / np.linalg.norm(vectors, axis=1, keepdims=True) # normalise once
doc_vectors = embed(docs) # do this once and store it
def search(question: str, k: int = 2):
q = embed([question])[0]
scores = doc_vectors @ q # cosine similarity (vectors are normalised)
best = np.argsort(-scores)[:k]
return [(docs[i], float(scores[i])) for i in best]
for text, score in search("چند روزه میرسه؟"):
print(round(score, 3), text)
const docs = ["Shipping to Tehran takes 1–2 business days.", "Items can be returned within 7 days.", "All Shetab cards are accepted."];
const embed = async (input) =>
(await client.embeddings.create({ model: "text-embedding-3-small", input })).data.map((d) => d.embedding);
const cosine = (a, b) => {
let dot = 0, na = 0, nb = 0;
for (let i = 0; i < a.length; i++) { dot += a[i] * b[i]; na += a[i] ** 2; nb += b[i] ** 2; }
return dot / Math.sqrt(na * nb);
};
const docVectors = await embed(docs);
const [q] = await embed(["How long is delivery?"]);
const ranked = docs
.map((text, i) => ({ text, score: cosine(q, docVectors[i]) }))
.sort((a, b) => b.score - a.score);
console.log(ranked[0]);
از جستوجو تا پاسخ (RAG)#
نزدیکترین سندها را بهعنوان زمینه به یک مدل گفتوگو بدهید تا فقط از روی آنها جواب بدهد:
def answer(question: str) -> str:
context = "\n".join(text for text, _ in search(question, k=3))
reply = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Answer ONLY from the context below, in Persian. If the answer is not there, say you don't know.\n\nContext:\n" + context},
{"role": "user", "content": question},
],
max_tokens=300,
)
return reply.choices[0].message.content
برای چند هزار سند، بهجای آرایهٔ ساده از یک پایگاه برداری مثل pgvector، Qdrant یا Chroma استفاده کنید. برای وصل کردن LangChain و LlamaIndex، یکپارچهسازیها را ببینید.
کوتاه کردن بردار#
res = client.embeddings.create(model="text-embedding-3-large", input="...", dimensions=768)
print(len(res.data[0].embedding)) # 768 instead of the full size: smaller index, a little less precise
هزینه و نکتهها#
- امبدینگ فقط توکن ورودی دارد و هزینهٔ خروجی ندارد. قیمت هر مدل در
GET /modelsآمده است. - متنهای بلند را پیش از امبدینگ به تکههای چندصد کلمهای بشکنید؛ هر مدل سقف ورودی دارد (
context_length) و تکههای کوتاهتر نتیجهٔ جستوجو را دقیقتر میکنند. - امبدینگ هر سند را یک بار حساب و ذخیره کنید؛ دوبارهکاری فقط هزینه است.
پاسخ پرسشتان را پیدا نکردید؟
شناسهٔ درخواست (هدر X-Request-Id) را با پرسشتان در تیکت بفرستید تا دقیق بررسی کنیم.