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

بازیابی

هشدار

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-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)
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-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);
bash
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 خوانده می‌شود.

python
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)
javascript
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);
bash
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?"
  }'

چک‌لیست مهاجرت:

  • messagesinput
  • راهنمای developer/system → instructions یا input item با نقش developer
  • choices[0].message.contentresponse.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های غیرقابل‌قبول بسازید.

منابع مرتبط