بازیابی
هشدار
Retrieval میزبانیشده، vector_stores مدیریتشده توسط ارائهدهنده و ابزار hosted file_search در AvalAI در حال توسعه هستند. برای production امروز، مرحله retrieval را در برنامه خودتان با /v1/embeddings، vector/search store خودتان و /v1/responses یا /v1/chat/completions بسازید.
Retrieval یعنی پیدا کردن مرتبطترین دانش خصوصی یا محصولی پیش از درخواست پاسخ از مدل. مستندات hosted Retrieval و File Search شرکت OpenAI برای طراحی مفیدند، اما پروژههای AvalAI در حال حاضر باید مرحله retrieval را خودشان پیادهسازی کنند.
ماتریس دسترسی در AvalAI
| قابلیت | وضعیت در AvalAI | مسیر پیشنهادی امروز |
|---|---|---|
/v1/embeddings | در دسترس | chunkها و query کاربر را embed کنید. |
/v1/responses | برای مدلهای پشتیبانیشده در دسترس | پاسخ grounded را از context بازیابیشده بسازید. |
/v1/chat/completions | در دسترس | workflowهای chat فعلی را نگه دارید و context بازیابیشده را در messages بفرستید. |
vector_stores میزبانیشده | در حال توسعه | chunkها و embeddingها را در database، vector DB یا search index خودتان ذخیره کنید. |
ابزار hosted file_search | در حال توسعه | retrieval را در برنامه اجرا کنید و پس از اعلام پشتیبانی، به file_search مهاجرت کنید. |
الگوی اصلی Retrieval
- آمادهسازی سندها: متن را استخراج کنید، بر اساس موضوع chunk کنید و source ID پایدار نگه دارید.
- Embedding chunkها: برای هر chunk و query کاربر از
/v1/embeddingsاستفاده کنید. - بازیابی: ابتدا فیلترهای tenant/permission را اعمال کنید، سپس chunkهای مشابه را جستجو کنید.
- Rerank و کوتاهسازی: فقط chunkهای با اعتماد بالا را نگه دارید که در بودجه context مدل جا میشوند.
- تولید پاسخ: context انتخابشده را به مدل بدهید و citation به source IDها را اجباری کنید.
retrieved_chunks = [
{
"source_id": "refund-policy.md#section=returns",
"text": "Customers can request a refund within 30 days of purchase.",
"score": 0.86,
},
{
"source_id": "refund-policy.md#section=exceptions",
"text": "Digital goods are refundable only when access has not started.",
"score": 0.79,
},
]
def format_sources(chunks):
return "\n\n".join(
f"[{chunk['source_id']} score={chunk['score']}]\n{chunk['text']}"
for chunk in chunks
)
sources = format_sources(retrieved_chunks)const retrievedChunks = [
{
source_id: "refund-policy.md#section=returns",
text: "Customers can request a refund within 30 days of purchase.",
score: 0.86,
},
{
source_id: "refund-policy.md#section=exceptions",
text: "Digital goods are refundable only when access has not started.",
score: 0.79,
},
];
function formatSources(chunks) {
return chunks
.map((chunk) => `[${chunk.source_id} score=${chunk.score}]\n${chunk.text}`)
.join("\n\n");
}
const sources = formatSources(retrievedChunks);تولید پاسخ با Chat Completions
این مسیر را برای مدلها و integrationهایی نگه دارید که از قبل با /v1/chat/completions کار میکنند.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AVALAI_API_KEY"],
base_url="https://api.avalai.ir/v1",
)
question = "Can a customer refund a digital purchase?"
completion = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[
{
"role": "developer",
"content": (
"Answer only from the provided sources. Cite source IDs in brackets. "
"If the sources are insufficient, say what is missing."
),
},
{
"role": "user",
"content": f"Sources:\n{sources}\n\nQuestion: {question}",
},
],
)
print(completion.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.AVALAI_API_KEY,
baseURL: "https://api.avalai.ir/v1",
});
const question = "Can a customer refund a digital purchase?";
const completion = await client.chat.completions.create({
model: "claude-sonnet-4-6",
messages: [
{
role: "developer",
content:
"Answer only from the provided sources. Cite source IDs in brackets. If the sources are insufficient, say what is missing.",
},
{
role: "user",
content: `Sources:\n${sources}\n\nQuestion: ${question}`,
},
],
});
console.log(completion.choices[0].message.content);curl https://api.avalai.ir/v1/chat/completions \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"messages": [
{
"role": "developer",
"content": "Answer only from the provided sources. Cite source IDs in brackets."
},
{
"role": "user",
"content": "Sources: [refund-policy.md#section=returns] Customers can request a refund within 30 days. Question: Can a customer refund a digital purchase?"
}
]
}'نسخه معادل Responses API
وقتی مدل انتخابی از /v1/responses پشتیبانی میکند، از این نسخه استفاده کنید. منابع بازیابیشده داخل input قرار میگیرند و متن نهایی از response.output_text خوانده میشود.
response = client.responses.create(
model="gpt-5.5",
instructions=(
"Answer only from the provided sources. Cite source IDs in brackets. "
"If the sources are insufficient, say what is missing."
),
input=f"Sources:\n{sources}\n\nQuestion: {question}",
)
print(response.output_text)const response = await client.responses.create({
model: "gpt-5.5",
instructions:
"Answer only from the provided sources. Cite source IDs in brackets. If the sources are insufficient, say what is missing.",
input: `Sources:\n${sources}\n\nQuestion: ${question}`,
});
console.log(response.output_text);curl https://api.avalai.ir/v1/responses \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"instructions": "Answer only from the provided sources. Cite source IDs in brackets.",
"input": "Sources: [refund-policy.md#section=returns] Customers can request a refund within 30 days. Question: Can a customer refund a digital purchase?"
}'چکلیست مهاجرت:
messages→input- راهنمای developer/system →
instructionsیا input item با نقش developer choices[0].message.content→response.output_text- tool callها، citationها و itemهای ساختاریافته → بررسی
response.outputبر اساسtype
بازنویسی Query
بعضی سؤالهای کاربر مکالمهای هستند، اما vector search با عبارتهای کوتاه و قابل جستجو بهتر کار میکند. در pipeline دستی AvalAI، سؤالهای مبهم را پیش از retrieval بازنویسی کنید، اما query اصلی و بازنویسیشده را لاگ کنید.
| سؤال اصلی | Query برای retrieval |
|---|---|
| "Can I get my money back if I never used the product?" | refund policy unused digital product |
| "What do we do when a customer is in Europe?" | EU customer data handling policy |
| "Which plan lets me invite the whole team?" | team invitation plan limits |
برای hosted vector store search آینده، شکل مرجع OpenAI شامل rewrite_query=true است. search_query برگشتی یا لاگشده را telemetry برای retrieval بدانید، نه جایگزین پیام اصلی کاربر: هر دو را ذخیره کنید تا بعدا بتوانید توضیح دهید چرا یک سند match شده است.
فیلتر و رتبهبندی
فیلترهای deterministic را پیش از similarity search اعمال کنید. برای کنترل دسترسی به مدل تکیه نکنید.
{
"tenant": "acme",
"language": "fa",
"document_type": [
"policy",
"faq"
],
"published_after": "2026-01-01"
}پس از retrieval، رفتار ranking را تنظیم کنید:
top_k/max_num_results: مقدار کمتر latency و هزینه context را کاهش میدهد؛ مقدار بیشتر recall را بهتر میکند.- Score threshold: chunkهای کماعتماد را حذف کنید و اجازه دهید مدل بگوید context کافی نیست.
ranker: برای hosted search باautoشروع کنید تا provider ranker فعلی را انتخاب کند؛ فقط برای evalهای تکرارپذیر ranker را pin کنید.hybrid_search.embedding_weight: وقتی semantic similarity باید غالب باشد، این وزن را افزایش دهید.hybrid_search.text_weight: وقتی نام محصول، ID یا اصطلاح policy دقیق مهم است، این وزن را افزایش دهید.- Reranking: ابتدا جستجوی ارزانتر انجام دهید، سپس candidateهای برتر را پیش از synthesis rerank کنید.
مرجع Retrieval میزبانیشده
وقتی AvalAI پشتیبانی hosted vector store را اعلام کند، شکل سازگار با OpenAI احتمالا شبیه این خواهد بود:
import os
from pathlib import Path
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AVALAI_API_KEY"],
base_url="https://api.avalai.ir/v1",
)
file_obj = client.files.create(
file=Path("refund-policy.md").open("rb"),
purpose="assistants",
)
vector_store = client.vector_stores.create(
name="support-knowledge",
expires_after={"anchor": "last_active_at", "days": 14},
)
client.vector_stores.files.create_and_poll(
vector_store_id=vector_store.id,
file_id=file_obj.id,
attributes={"tenant": "acme", "document_type": "policy"},
chunking_strategy={
"type": "static",
"max_chunk_size_tokens": 1000,
"chunk_overlap_tokens": 200,
},
)
results = client.vector_stores.search(
vector_store_id=vector_store.id,
query="refund policy unused digital product",
max_num_results=5,
rewrite_query=True,
ranking_options={
"ranker": "auto",
"score_threshold": 0.25,
"hybrid_search": {"embedding_weight": 0.7, "text_weight": 0.3},
},
)import fs from "node:fs";
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.AVALAI_API_KEY,
baseURL: "https://api.avalai.ir/v1",
});
const file = await client.files.create({
file: fs.createReadStream("refund-policy.md"),
purpose: "assistants",
});
const vectorStore = await client.vectorStores.create({
name: "support-knowledge",
expires_after: { anchor: "last_active_at", days: 14 },
});
await client.vectorStores.files.createAndPoll(vectorStore.id, {
file_id: file.id,
attributes: { tenant: "acme", document_type: "policy" },
chunking_strategy: {
type: "static",
max_chunk_size_tokens: 1000,
chunk_overlap_tokens: 200,
},
});
const results = await client.vectorStores.search(vectorStore.id, {
query: "refund policy unused digital product",
max_num_results: 5,
rewrite_query: true,
ranking_options: {
ranker: "auto",
score_threshold: 0.25,
hybrid_search: { embedding_weight: 0.7, text_weight: 0.3 },
},
});برای batch ingestion، وقتی چند فایل باید همزمان searchable شوند از file_batches.create_and_poll استفاده کنید. یک batch میتواند file_ids با تنظیمات مشترک داشته باشد، یا از آرایه files برای attributes و chunking_strategy مخصوص هر فایل استفاده کند؛ این دو شکل را در یک request همزمان نفرستید.
نکات طراحی از مستندات Retrieval شرکت OpenAI
- جستجوی معنایی میتواند chunkهای مرتبط را حتی با اشتراک کم در keywordها پیدا کند.
- vector storeهای میزبانیشده، فایلهای attachشده را parse، chunk، embed و index میکنند.
- جستجوی معنایی مستقیم بهصورت پیشفرض تا ۱۰ نتیجه برمیگرداند و با
max_num_resultsتا ۵۰ قابل تنظیم است. - attributeهای فایل از فیلتر metadata پشتیبانی میکنند؛ محدودیت مرجع OpenAI برابر ۱۶ کلید با ۲۵۶ کاراکتر برای هر کلید است.
- جستجوی hosted مستقیم میتواند query را rewrite کند و یک
search_queryبرای debug نشان دهد؛ آن را کنار filterها، ranker و scoreها لاگ کنید. ranking_optionsشاملranker،score_thresholdو وزنهایhybrid_searchاست. وقتی hybrid search تنظیم میشود، حداقل یکی از وزنهای hybrid باید بیشتر از صفر باشد.- chunking مرجع OpenAI بهصورت پیشفرض ۸۰۰ توکن با overlap برابر ۴۰۰ توکن است. اندازه chunk سفارشی باید بین ۱۰۰ تا ۴۰۹۶ توکن باشد و overlap نباید از نصف اندازه chunk بیشتر شود.
- محدودیتهای مرجع OpenAI برای فایلهای vector store برابر ۵۱۲ MB و ۵٬۰۰۰٬۰۰۰ توکن برای هر فایل است. تا زمان اعلام پشتیبانی میزبانیشده در AvalAI، این موارد را فقط مرجع برنامهریزی بدانید، نه تعهد AvalAI.
- batch ingestion برای تعداد زیاد فایل مفید است؛ مرجع OpenAI تا ۵۰۰ (500) فایل در هر vector store batch را پشتیبانی میکند.
expires_afterهنگام انقضای hosted vector store، objectهای مرتبطvector_store.fileرا حذف میکند؛ برای uploadهای موقت از آن استفاده کنید و برای manual RAG storeها retention policy خودتان را نگه دارید.
ایمنی و ارزیابی
- permissionهای tenant، user و document را در لایه retrieval نگه دارید.
- source ID را با هر chunk ذخیره کنید و citation را در instructionهای مدل اجباری کنید.
- برای کاهش خطر prompt injection و data exfiltration، لینکهای retrieveشده و argumentهای tool را validate کنید.
- query rewriteها، filterها، scoreها، chunkهای انتخابشده، پاسخ مدل و feedback کاربر را لاگ کنید.
- کیفیت retrieval را جدا از کیفیت پاسخ بسنجید: source recall، source precision، first-correct-rank، MRR و MAP را روی مجموعه کوچکی از سؤالهای واقعی کاربران اندازهگیری کنید.
- حذف فایل و انقضای vector store را از نظر ایمنی برنامه eventual consistent در نظر بگیرید؛ حتی پس از درخواست cleanup، authorization checkها را در لایه retrieval نگه دارید.
- eval setهایی با question، پاسخ مورد انتظار، source IDهای مورد انتظار و hallucinationهای غیرقابلقبول بسازید.