داشبورد توسعه‌دهنده
پرسش از هوش مصنوعی
پرسش از هوش مصنوعی

بازیابی ​

هشدار

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 ​

  1. آماده‌سازی سندها: متن را استخراج کنید، بر اساس موضوع chunk کنید و source ID پایدار نگه دارید.
  2. Embedding chunkها: برای هر chunk و query کاربر از /v1/embeddings استفاده کنید.
  3. بازیابی: ابتدا فیلترهای tenant/permission را اعمال کنید، سپس chunkهای مشابه را جستجو کنید.
  4. Rerank و کوتاه‌سازی: فقط chunkهای با اعتماد بالا را نگه دارید که در بودجه context مدل جا می‌شوند.
  5. تولید پاسخ: context انتخاب‌شده را به مدل بدهید و citation به source IDها را اجباری کنید.
python
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)
javascript
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 کار می‌کنند.

python
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-5",
    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)
javascript
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-5",
  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);
bash
curl https://api.avalai.ir/v1/chat/completions \
  -H "Authorization: Bearer $AVALAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "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 خوانده می‌شود.

python
response = client.responses.create(
    model="gpt-5.6-luna",
    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)
javascript
const response = await client.responses.create({
  model: "gpt-5.6-luna",
  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);
bash
curl https://api.avalai.ir/v1/responses \
  -H "Authorization: Bearer $AVALAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.6-luna",
    "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 اعمال کنید. برای کنترل دسترسی به مدل تکیه نکنید.

json
{
  "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 احتمالا شبیه این خواهد بود:

python
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},
    },
)
javascript
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های غیرقابل‌قبول بسازید.

منابع مرتبط ​