ارزیابی سیستم RAG؛ آموزش Recall@K، MRR، nDCG و Faithfulness

در این راهنمای فنی یاد می‌گیرید کیفیت Retrieval و پاسخ نهایی سیستم RAG را با Precision@K، Recall@K، MRR، nDCG، Faithfulness و Golden Dataset ارزیابی و تغییرات آن را قبل از انتشار آزمایش کنید.

Share
ارزیابی سیستم RAG؛ آموزش Recall@K، MRR، nDCG و Faithfulness

ساخت یک سیستم RAG که پاسخ تولید کند آسان‌تر از ساخت سیستمی است که بتوان کیفیت پاسخ‌های آن را به‌صورت تکرارپذیر اندازه‌گیری کرد. ممکن است چند پرسش را به‌صورت دستی آزمایش کنید و پاسخ‌ها مناسب به نظر برسند، اما این روش نشان نمی‌دهد سیستم روی صدها سوال واقعی، اسناد دشوار، عبارت‌های فارسی یا نسخه بعدی Chunking چه عملکردی خواهد داشت.

وقتی پاسخ نهایی ضعیف است، باید بتوانید تشخیص دهید مشکل در کدام بخش قرار دارد:

  • سند مناسب بازیابی نشده است.
  • سند مناسب بازیابی شده اما رتبه پایینی دارد.
  • Chunk اطلاعات کافی ندارد.
  • Reranker ترتیب نتایج را خراب کرده است.
  • Prompt از Context درست استفاده نمی‌کند.
  • مدل بخشی از پاسخ را از خودش ساخته است.
  • پاسخ مستند است اما سوال کاربر را جواب نمی‌دهد.
  • پاسخ درست است اما کامل نیست.
  • اطلاعات منبع قدیمی یا متناقض هستند.

ارزیابی RAG باید Retrieval و Generation را جداگانه اندازه‌گیری کند. اگر فقط پاسخ نهایی را امتیازدهی کنید، محل واقعی خطا مشخص نخواهد شد.

در این مقاله یک چارچوب کامل برای ارزیابی RAG می‌سازیم؛ از Golden Dataset و معیارهای Precision@K، Recall@K، Hit Rate، MRR و nDCG تا Faithfulness، Answer Relevance، LLM as a Judge، Regression Test و مانیتورینگ محیط عملیاتی.

سیستم RAG از چه بخش‌هایی تشکیل می‌شود؟

یک Pipeline معمول RAG شامل مراحل زیر است:

  1. دریافت سوال کاربر
  2. بازنویسی یا توسعه Query
  3. بازیابی اسناد
  4. ترکیب جست‌وجوی برداری و کلمه‌ای
  5. Reranking
  6. انتخاب Context نهایی
  7. ساخت Prompt
  8. تولید پاسخ
  9. افزودن Citation
  10. ارائه پاسخ به کاربر

برای ارزیابی دقیق، باید خروجی مرحله‌های اصلی ذخیره شود:

{
  "question": "چگونه کلید API بسازم؟",
  "rewritten_query": "راهنمای ایجاد API Key در پنل",
  "retrieved_documents": [],
  "reranked_documents": [],
  "final_contexts": [],
  "generated_answer": "",
  "citations": [],
  "latency_ms": 0,
  "usage": {}
}

اگر فقط سوال و پاسخ نهایی را ذخیره کنید، در زمان خطا نمی‌دانید Retriever چه اسنادی به مدل داده است.

چرا ارزیابی دستی کافی نیست؟

ارزیابی دستی برای شروع مفید است، اما محدودیت‌هایی دارد:

  • نمونه‌ها معمولاً ساده انتخاب می‌شوند.
  • ارزیابی افراد مختلف یکسان نیست.
  • تغییرات کوچک میان نسخه‌ها دیده نمی‌شوند.
  • تست چندصد سوال زمان‌بر است.
  • امکان اجرای خودکار در CI وجود ندارد.
  • خطاهای مربوط به دسته‌ای خاص از سوال‌ها پنهان می‌مانند.
  • نتیجه به حافظه و برداشت ارزیاب وابسته می‌شود.

چارچوب مناسب باید ارزیابی خودکار و انسانی را ترکیب کند.

دو سطح اصلی ارزیابی RAG

ارزیابی Retrieval

بررسی می‌کند آیا سیستم اسناد مرتبط را پیدا کرده و در رتبه مناسب قرار داده است.

معیارهای مهم:

  • Precision@K
  • Recall@K
  • Hit Rate@K
  • MRR
  • MAP
  • DCG و nDCG
  • Context Precision
  • Context Recall

ارزیابی Generation

بررسی می‌کند پاسخ مدل چقدر درست، مرتبط، کامل و مبتنی بر Context است.

معیارهای مهم:

  • Faithfulness
  • Groundedness
  • Answer Relevance
  • Answer Correctness
  • Completeness
  • Citation Correctness
  • Citation Completeness
  • Abstention Accuracy

راهنمای ارزیابی RAG در LangSmith نیز این فرایند را به ساخت Dataset، اجرای سیستم روی سوال‌ها و اندازه‌گیری کیفیت Retrieval و پاسخ تقسیم می‌کند. راهنمای ارزیابی RAG در LangSmith

Golden Dataset چیست؟

Golden Dataset مجموعه‌ای از نمونه‌های تأییدشده است که برای هر سوال، خروجی موردانتظار را مشخص می‌کند.

حداقل ساختار:

{
  "id": "faq-001",
  "question": "چگونه کلید API ایجاد کنم؟",
  "reference_answer": "کاربر پس از ورود به پنل می‌تواند از بخش کلیدهای API یک کلید جدید ایجاد کند.",
  "relevant_document_ids": [
    "docs-api-keys-001"
  ],
  "reference_contexts": [
    "متن بخش مرتبط از مستندات"
  ],
  "category": "api_keys",
  "difficulty": "easy",
  "language": "fa",
  "answerable": true
}

ویژگی‌های Dataset مناسب

  • سوال‌ها از کاربرد واقعی محصول گرفته شده‌اند.
  • سوال ساده و دشوار دارد.
  • پرسش‌های مستقیم و محاوره‌ای دارد.
  • عبارت‌های فارسی و انگلیسی را پوشش می‌دهد.
  • سوال بدون پاسخ دارد.
  • سوال دارای چند سند مرتبط دارد.
  • موارد دارای اطلاعات مشابه یا متناقض دارد.
  • برای هر نمونه منبع تأییدشده مشخص است.
  • Dataset نسخه‌بندی شده است.

منابع ساخت Dataset

سوالات واقعی کاربران

سوال‌های پشتیبانی، جست‌وجوی داخلی و مکالمات واقعی بهترین منبع‌اند؛ البته باید اطلاعات شخصی و محرمانه حذف شوند.

مستندات

برای هر بخش مهم مستندات چند سوال طراحی کنید.

تیم پشتیبانی و فروش

این تیم‌ها سوال‌هایی را می‌شناسند که کاربران واقعاً بیان می‌کنند.

تولید مصنوعی سوال

مدل می‌تواند از روی اسناد سوال پیشنهادی بسازد، اما نمونه تولیدشده باید توسط انسان بررسی شود.

نمونه‌های شکست Production

هر خطای مهمی که در محیط واقعی مشاهده می‌شود باید به Regression Dataset اضافه شود.

Dataset را براساس نوع سوال متوازن کنید

اگر بیشتر نمونه‌ها سوال‌های کوتاه و مستقیم باشند، امتیاز بالا ممکن است گمراه‌کننده باشد.

دسته‌های پیشنهادی:

دستهمثال
سوال مستقیمBase URL چیست؟
سوال چندمرحله‌ایچگونه کلید بسازم و در Python استفاده کنم؟
مقایسهتفاوت دو روش چیست؟
عیب‌یابیچرا درخواست 401 برمی‌گردد؟
چندسندیقیمت و محدودیت مدل چگونه تعیین می‌شوند؟
بدون پاسخآیا سرویس قابلیتی دارد که در منابع نیست؟
دارای اصطلاح انگلیسیRate Limit چگونه محاسبه می‌شود؟
محاوره‌ایچرا API من جواب نمیده؟
مبهمکلیدم کار نمی‌کند
دارای پیش‌فرض غلطچرا همه مدل‌ها ورودی تصویر دارند؟

ارزیابی Retrieval با Ground Truth

برای محاسبه معیارهای قطعی Retrieval باید بدانیم کدام اسناد برای هر سوال مرتبط هستند.

نمونه:

{
  "question": "چگونه API Key ایجاد کنم؟",
  "relevant_document_ids": [
    "doc-api-key-create",
    "doc-api-key-security"
  ],
  "retrieved_document_ids": [
    "doc-auth-overview",
    "doc-api-key-create",
    "doc-billing",
    "doc-api-key-security",
    "doc-errors"
  ]
}

در این مثال، دو سند مرتبط در رتبه‌های دوم و چهارم قرار دارند.

Precision@K چیست؟

Precision@K نشان می‌دهد چه نسبتی از K نتیجه اول مرتبط بوده‌اند.

فرمول Ghost-compatible:

Precision@K =
تعداد اسناد مرتبط در K نتیجه اول
تقسیم بر K

مثال:

K = 5
نتایج مرتبط در پنج رتبه اول = 2

Precision@5 = 2 / 5 = 0.40

Precision بالا یعنی Contextهای بازیابی‌شده کمتر شامل اطلاعات نامرتبط هستند.

محدودیت Precision@K

اگر فقط یک سند مرتبط برای سوال وجود داشته باشد و K=5 باشد، بیشترین Precision ممکن برابر 0.20 خواهد بود. بنابراین این معیار باید با ساختار Dataset و تعداد اسناد مرتبط تفسیر شود.

Recall@K چیست؟

Recall@K نشان می‌دهد چه نسبتی از تمام اسناد مرتبط در K نتیجه اول بازیابی شده‌اند.

Recall@K =
تعداد اسناد مرتبط در K نتیجه اول
تقسیم بر کل اسناد مرتبط

مثال:

کل اسناد مرتبط = 4
اسناد مرتبط پیدا شده در پنج نتیجه اول = 3

Recall@5 = 3 / 4 = 0.75

Recall بالا یعنی سیستم بخش کمتری از اطلاعات مهم را از دست داده است.

Precision یا Recall؟

  • Precision بالا: نتایج اضافی و نامرتبط کمترند.
  • Recall بالا: اسناد مهم کمتری جا افتاده‌اند.

افزایش top_k معمولاً Recall را بیشتر می‌کند، اما ممکن است Precision را کاهش دهد و Context نهایی را شلوغ کند.

Hit Rate@K چیست؟

Hit Rate@K فقط بررسی می‌کند آیا حداقل یک سند مرتبط در K نتیجه اول وجود دارد یا نه.

Hit Rate@K = 1
اگر حداقل یک سند مرتبط در K نتیجه اول وجود داشته باشد

Hit Rate@K = 0
اگر هیچ سند مرتبطی وجود نداشته باشد

میانگین آن روی Dataset:

Mean Hit Rate@K =
تعداد سوال‌های دارای حداقل یک نتیجه مرتبط
تقسیم بر تعداد کل سوال‌ها

این معیار برای سیستم‌هایی مناسب است که وجود یک سند درست برای پاسخ کافی است.

MRR چیست؟

MRR مخفف Mean Reciprocal Rank است و رتبه اولین سند مرتبط را اندازه‌گیری می‌کند.

برای هر سوال:

Reciprocal Rank = 1 / رتبه اولین سند مرتبط

مثال‌ها:

اولین سند مرتبط در رتبه 1:
RR = 1 / 1 = 1

اولین سند مرتبط در رتبه 2:
RR = 1 / 2 = 0.5

اولین سند مرتبط در رتبه 5:
RR = 1 / 5 = 0.2

هیچ سند مرتبطی پیدا نشده:
RR = 0

سپس:

MRR =
میانگین Reciprocal Rank تمام سوال‌ها

MRR وقتی مفید است که پیدا شدن اولین پاسخ مرتبط در رتبه بالا اهمیت زیادی داشته باشد.

محدودیت MRR

MRR فقط اولین نتیجه مرتبط را در نظر می‌گیرد. اگر سوال به چند سند نیاز داشته باشد، MRR کیفیت بقیه رتبه‌بندی را نشان نمی‌دهد.

DCG و nDCG چیست؟

nDCG برای زمانی مناسب است که اسناد درجه‌های مختلفی از ارتباط دارند.

مثلاً:

0 = نامرتبط
1 = کمی مرتبط
2 = مرتبط
3 = بسیار مرتبط

DCG به اسناد مرتبطی که در رتبه بالاتر هستند امتیاز بیشتری می‌دهد.

فرمول متداول:

DCG@K =
مجموع از i=1 تا K:
(2 به توان relevance_i منهای 1)
تقسیم بر log2(i + 1)

سپس رتبه‌بندی ایده‌آل ساخته می‌شود و IDCG@K محاسبه می‌شود:

nDCG@K = DCG@K / IDCG@K

nDCG معمولاً بین صفر و یک است و مقدار نزدیک‌تر به یک نشان می‌دهد رتبه‌بندی به ترتیب ایده‌آل نزدیک‌تر است. مستندات scikit-learn نیز nDCG را حاصل مقایسه DCG رتبه‌بندی موجود با DCG ایده‌آل توضیح می‌دهد. مستندات nDCG در scikit-learn

چه زمانی از هر معیار استفاده کنیم؟

وضعیتمعیار مناسب
فقط وجود یک سند درست مهم استHit Rate@K
رتبه اولین سند مهم استMRR
تعداد نتایج نامرتبط مهم استPrecision@K
از دست ندادن اسناد مهم ضروری استRecall@K
اسناد درجات مختلف ارتباط دارندnDCG@K
چند سند برای پاسخ لازم استRecall@K و nDCG@K
Context محدود استPrecision@K و MRR
ارزیابی RerankerMRR و nDCG

پیاده‌سازی معیارهای Retrieval با Python

from collections.abc import Sequence
from math import log2


def precision_at_k(
    retrieved_ids: Sequence[str],
    relevant_ids: set[str],
    k: int,
) -> float:
    if k <= 0:
        raise ValueError("k must be positive")

    top_k = list(retrieved_ids[:k])

    if not top_k:
        return 0.0

    relevant_retrieved = sum(
        1
        for document_id in top_k
        if document_id in relevant_ids
    )

    return relevant_retrieved / k


def recall_at_k(
    retrieved_ids: Sequence[str],
    relevant_ids: set[str],
    k: int,
) -> float:
    if k <= 0:
        raise ValueError("k must be positive")

    if not relevant_ids:
        return 0.0

    top_k = set(retrieved_ids[:k])

    relevant_retrieved = len(
        top_k.intersection(relevant_ids)
    )

    return relevant_retrieved / len(relevant_ids)


def hit_rate_at_k(
    retrieved_ids: Sequence[str],
    relevant_ids: set[str],
    k: int,
) -> float:
    top_k = set(retrieved_ids[:k])

    return float(
        bool(top_k.intersection(relevant_ids))
    )


def reciprocal_rank(
    retrieved_ids: Sequence[str],
    relevant_ids: set[str],
) -> float:
    for rank, document_id in enumerate(
        retrieved_ids,
        start=1,
    ):
        if document_id in relevant_ids:
            return 1.0 / rank

    return 0.0


def dcg_at_k(
    relevance_scores: Sequence[float],
    k: int,
) -> float:
    score = 0.0

    for rank, relevance in enumerate(
        relevance_scores[:k],
        start=1,
    ):
        gain = (2 ** relevance) - 1
        discount = log2(rank + 1)
        score += gain / discount

    return score


def ndcg_at_k(
    relevance_scores: Sequence[float],
    k: int,
) -> float:
    actual_dcg = dcg_at_k(
        relevance_scores,
        k,
    )

    ideal_scores = sorted(
        relevance_scores,
        reverse=True,
    )

    ideal_dcg = dcg_at_k(
        ideal_scores,
        k,
    )

    if ideal_dcg == 0:
        return 0.0

    return actual_dcg / ideal_dcg

آزمایش توابع

retrieved = [
    "doc-a",
    "doc-b",
    "doc-c",
    "doc-d",
    "doc-e",
]

relevant = {
    "doc-b",
    "doc-d",
    "doc-x",
}

print(
    "Precision@5:",
    precision_at_k(
        retrieved,
        relevant,
        5,
    ),
)

print(
    "Recall@5:",
    recall_at_k(
        retrieved,
        relevant,
        5,
    ),
)

print(
    "Hit Rate@3:",
    hit_rate_at_k(
        retrieved,
        relevant,
        3,
    ),
)

print(
    "Reciprocal Rank:",
    reciprocal_rank(
        retrieved,
        relevant,
    ),
)

graded_relevance = [
    0,
    3,
    1,
    2,
    0,
]

print(
    "nDCG@5:",
    ndcg_at_k(
        graded_relevance,
        5,
    ),
)

محاسبه میانگین معیارها روی Dataset

ساختار retrieval-results.jsonl:

{"id":"q1","retrieved_ids":["d1","d2","d3"],"relevant_ids":["d2"]}
{"id":"q2","retrieved_ids":["d4","d5","d6"],"relevant_ids":["d4","d7"]}
{"id":"q3","retrieved_ids":["d8","d9"],"relevant_ids":["d10"]}

کد:

import json
from pathlib import Path
from statistics import mean


def load_jsonl(path: Path) -> list[dict]:
    rows = []

    with path.open(
        "r",
        encoding="utf-8",
    ) as file:
        for line_number, line in enumerate(
            file,
            start=1,
        ):
            line = line.strip()

            if not line:
                continue

            try:
                rows.append(json.loads(line))
            except json.JSONDecodeError as error:
                raise ValueError(
                    f"Invalid JSON on line {line_number}"
                ) from error

    return rows


rows = load_jsonl(
    Path("retrieval-results.jsonl")
)

k_values = [1, 3, 5]

report = {}

for k in k_values:
    precisions = []
    recalls = []
    hit_rates = []

    for row in rows:
        retrieved_ids = row["retrieved_ids"]
        relevant_ids = set(row["relevant_ids"])

        precisions.append(
            precision_at_k(
                retrieved_ids,
                relevant_ids,
                k,
            )
        )

        recalls.append(
            recall_at_k(
                retrieved_ids,
                relevant_ids,
                k,
            )
        )

        hit_rates.append(
            hit_rate_at_k(
                retrieved_ids,
                relevant_ids,
                k,
            )
        )

    report[f"precision@{k}"] = mean(
        precisions
    )
    report[f"recall@{k}"] = mean(recalls)
    report[f"hit_rate@{k}"] = mean(
        hit_rates
    )

report["mrr"] = mean(
    reciprocal_rank(
        row["retrieved_ids"],
        set(row["relevant_ids"]),
    )
    for row in rows
)

print(json.dumps(
    report,
    ensure_ascii=False,
    indent=2,
))

Chunk-level یا Document-level؟

یک سند ممکن است به چند Chunk تقسیم شده باشد. در ارزیابی باید مشخص کنید مرتبط بودن در چه سطحی تعریف می‌شود.

Chunk-level

شناسه دقیق Chunk مرتبط ثبت می‌شود:

document-12#chunk-08

مزایا:

  • دقیق‌تر است.
  • کیفیت Chunking را بهتر نشان می‌دهد.
  • برای ارزیابی Reranker مناسب است.

محدودیت:

  • برچسب‌گذاری زمان بیشتری می‌برد.
  • تغییر Chunking شناسه‌ها را عوض می‌کند.

Document-level

فقط شناسه سند اصلی ثبت می‌شود.

مزایا:

  • در برابر تغییر Chunking پایدارتر است.
  • برچسب‌گذاری ساده‌تر است.

محدودیت:

  • ممکن است سند درست پیدا شود اما Chunk اشتباه بازیابی شده باشد.

روش حرفه‌ای این است که هر دو را ذخیره کنید:

{
  "relevant_document_ids": [
    "docs-api-keys"
  ],
  "relevant_chunk_ids": [
    "docs-api-keys#chunk-03"
  ]
}

Context Precision چیست؟

Context Precision بررسی می‌کند آیا Contextهای بازیابی‌شده برای پاسخ دادن مفید هستند و موارد مرتبط در رتبه‌های بالاتر قرار گرفته‌اند.

Ragas برای این معیار نسخه‌های دارای Reference و بدون Reference ارائه می‌کند. در حالت دارای پاسخ مرجع، Context با Reference Answer مقایسه می‌شود. مستندات Context Precision در Ragas

Context Precision با Precision@K قطعی یکسان نیست:

  • Precision@K به برچسب‌های انسانی اسناد متکی است.
  • Context Precision مبتنی بر مدل ممکن است سودمندی Context را با LLM ارزیابی کند.

Context Recall چیست؟

Context Recall بررسی می‌کند آیا Context بازیابی‌شده اطلاعات لازم برای پاسخ مرجع را پوشش می‌دهد.

Ragas آن را معیاری برای اندازه‌گیری میزان بازیابی اطلاعات مرتبط معرفی می‌کند؛ یعنی چه بخش‌هایی از اطلاعات لازم جا افتاده‌اند. مستندات Context Recall در Ragas

برای محاسبه دقیق‌تر می‌توانید پاسخ مرجع را به Claimهای مستقل تقسیم و بررسی کنید کدام Claimها در Context پشتیبانی می‌شوند.

مثال پاسخ مرجع:

کاربر می‌تواند از پنل یک کلید API ایجاد کند.
کلید کامل فقط یک بار نمایش داده می‌شود.
کلید را می‌توان غیرفعال کرد.

Claimها:

[
  "کاربر می‌تواند از پنل کلید API ایجاد کند.",
  "کلید کامل فقط یک بار نمایش داده می‌شود.",
  "کلید API قابل غیرفعال‌سازی است."
]

اگر Context فقط دو Claim را پوشش دهد:

Context Recall = 2 / 3 = 0.67

Faithfulness چیست؟

Faithfulness یا وفاداری به منبع بررسی می‌کند چند درصد ادعاهای پاسخ توسط Context بازیابی‌شده پشتیبانی می‌شوند.

Faithfulness =
تعداد ادعاهای پشتیبانی‌شده
تقسیم بر کل ادعاهای قابل بررسی پاسخ

مثال:

پاسخ:

کلید API از پنل ساخته می‌شود.
کلید فقط یک بار نمایش داده می‌شود.
ساخت کلید رایگان است.

Context فقط دو ادعای اول را پشتیبانی می‌کند:

Faithfulness = 2 / 3 = 0.67

Ragas نیز Faithfulness را میزان سازگاری واقعی پاسخ با Context بازیابی‌شده تعریف می‌کند و برای محاسبه، ادعاهای پاسخ را استخراج و پشتیبانی آن‌ها را در Context بررسی می‌کند. مستندات Faithfulness در Ragas

Faithfulness با Correctness متفاوت است

ممکن است پاسخ از نظر Context وفادار باشد اما Context قدیمی یا نادرست باشد. در این حالت Faithfulness بالا و Correctness پایین خواهد بود.

همچنین پاسخ ممکن است از نظر دنیای واقعی درست باشد اما اطلاعات آن در Context وجود نداشته باشد؛ در این حالت برای RAG یک پاسخ Ungrounded محسوب می‌شود.

Groundedness چیست؟

Groundedness معمولاً مفهومی نزدیک به Faithfulness دارد و بررسی می‌کند پاسخ به شواهد ارائه‌شده متصل است یا نه.

تعریف دقیق این معیار میان Frameworkها یکسان نیست. بنابراین در گزارش ارزیابی باید مشخص کنید:

  • واحد ارزیابی Claim است یا کل پاسخ؟
  • Context کامل استفاده شده یا هر Chunk جداگانه؟
  • پاسخ‌های عمومی چگونه امتیاز می‌گیرند؟
  • Citation جزو معیار است یا نه؟
  • داور کدام مدل و Prompt بوده است؟

Answer Relevance چیست؟

Answer Relevance بررسی می‌کند پاسخ واقعاً سوال کاربر را جواب داده است یا نه.

مثال:

سوال:

چگونه کلید API را غیرفعال کنم؟

پاسخ:

کلید API برای احراز هویت درخواست‌ها استفاده می‌شود و باید در محیط امن نگهداری شود.

این پاسخ ممکن است درست و مستند باشد، اما سوال را جواب نمی‌دهد؛ بنابراین Answer Relevance پایین است.

Answer Correctness چیست؟

Correctness پاسخ را با Reference Answer یا واقعیت تأییدشده مقایسه می‌کند.

معیارهای ممکن:

  • تطابق دقیق برای مقدار مشخص
  • F1 برای لیست‌ها
  • Semantic Similarity
  • بررسی Claimها
  • داوری مدل
  • بازبینی انسانی

برای پاسخ آزاد، مقایسه رشته‌ای ساده معمولاً مناسب نیست؛ زیرا دو پاسخ با واژگان متفاوت ممکن است معنی یکسان داشته باشند.

Completeness چیست؟

Completeness بررسی می‌کند پاسخ چه مقدار از نکات ضروری را پوشش داده است.

مثال، پاسخ مرجع چهار مرحله دارد و پاسخ مدل فقط دو مرحله را ذکر می‌کند:

Completeness = 2 / 4 = 0.50

پاسخ می‌تواند Faithful و Correct باشد، اما کامل نباشد.

Citation Correctness و Citation Completeness

اگر سیستم Citation تولید می‌کند، دو معیار جدا لازم است.

Citation Correctness

آیا منبع ارجاع‌شده واقعاً ادعای پاسخ را پشتیبانی می‌کند؟

Citation Completeness

آیا تمام ادعاهای مهم پاسخ Citation مناسب دارند؟

مثال:

{
  "claim": "کلید فقط یک بار نمایش داده می‌شود.",
  "citation_id": "docs-api-key-create#chunk-3",
  "supported": true
}

صرف وجود Citation به معنی درست بودن آن نیست.

Abstention Accuracy

سیستم RAG باید بداند چه زمانی پاسخ کافی در منابع ندارد.

نمونه سوال بدون پاسخ:

آیا درواره مدل اختصاصی شرکت من را بدون قرارداد جداگانه فاین‌تیون می‌کند؟

اگر چنین اطلاعاتی در منابع وجود ندارد، پاسخ مناسب باید نبود اطلاعات کافی را اعلام کند، نه اینکه یک فرایند فرضی بسازد.

برای Dataset بدون پاسخ، این موارد را اندازه‌گیری کنید:

  • پاسخ ندادن صحیح
  • پاسخ ساختگی
  • ارجاع مناسب به پشتیبانی
  • Citation نامرتبط
  • میزان اطمینان غیرواقعی

طراحی Dataset دارای سوال‌های بدون پاسخ

حداقل بخشی از Dataset باید شامل موارد answerable=false باشد:

{
  "id": "unanswerable-001",
  "question": "آیا سرویس از قابلیت X پشتیبانی می‌کند؟",
  "reference_answer": null,
  "relevant_document_ids": [],
  "answerable": false,
  "expected_behavior": "abstain"
}

اگر تمام سوال‌های Dataset پاسخ داشته باشند، توانایی سیستم برای خودداری از پاسخ ساختگی اندازه‌گیری نمی‌شود.

ساخت LLM as a Judge با API درواره

LLM as a Judge از یک مدل برای ارزیابی پاسخ مدل دیگر استفاده می‌کند. این روش سریع و مقیاس‌پذیر است، اما باید با نمونه‌های انسانی کالیبره شود.

فایل .env:

DARVAREH_API_KEY=YOUR_API_KEY
DARVAREH_JUDGE_MODEL=YOUR_MODEL_ID

YOUR_MODEL_ID باید با Model ID مدل داور در درواره جایگزین شود.

نصب:

pip install openai python-dotenv pydantic

مدل خروجی:

from pydantic import BaseModel, Field


class JudgeResult(BaseModel):
    faithfulness: float = Field(
        ge=0.0,
        le=1.0,
    )
    answer_relevance: float = Field(
        ge=0.0,
        le=1.0,
    )
    correctness: float = Field(
        ge=0.0,
        le=1.0,
    )
    completeness: float = Field(
        ge=0.0,
        le=1.0,
    )
    unsupported_claims: list[str] = Field(
        default_factory=list
    )
    missing_points: list[str] = Field(
        default_factory=list
    )
    explanation: str

فراخوانی داور:

import json
import os

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

api_key = os.getenv("DARVAREH_API_KEY")
judge_model = os.getenv("DARVAREH_JUDGE_MODEL")

if not api_key:
    raise RuntimeError("DARVAREH_API_KEY is not configured")

if not judge_model:
    raise RuntimeError(
        "DARVAREH_JUDGE_MODEL is not configured"
    )

client = OpenAI(
    api_key=api_key,
    base_url="https://api.darvareh.ir/v1",
)


def evaluate_rag_answer(
    question: str,
    contexts: list[str],
    generated_answer: str,
    reference_answer: str | None,
) -> JudgeResult:
    prompt = f"""
یک پاسخ تولیدشده توسط سیستم RAG را ارزیابی کن.

سوال:
{question}

Contextهای بازیابی‌شده:
{json.dumps(contexts, ensure_ascii=False, indent=2)}

پاسخ تولیدشده:
{generated_answer}

پاسخ مرجع:
{reference_answer or "پاسخ مرجع موجود نیست"}

معیارها:
- faithfulness: چه نسبتی از ادعاهای پاسخ توسط Context پشتیبانی می‌شوند؟
- answer_relevance: پاسخ چقدر مستقیماً سوال را جواب می‌دهد؟
- correctness: پاسخ چقدر با پاسخ مرجع سازگار است؟
- completeness: پاسخ چه مقدار از نکات لازم را پوشش می‌دهد؟

خروجی فقط JSON معتبر باشد:
{{
  "faithfulness": 0.0,
  "answer_relevance": 0.0,
  "correctness": 0.0,
  "completeness": 0.0,
  "unsupported_claims": ["ادعای بدون پشتیبانی"],
  "missing_points": ["نکته حذف‌شده"],
  "explanation": "توضیح کوتاه"
}}

قواعد:
- تمام امتیازها بین صفر و یک باشند.
- اطلاعات خارج از Context و پاسخ مرجع وارد ارزیابی نکن.
- تفاوت نگارشی را خطا حساب نکن.
- ادعای بدون پشتیبانی را دقیق نقل کن.
- اگر پاسخ مرجع وجود ندارد، correctness را فقط
  براساس Context ارزیابی کن و این محدودیت را توضیح بده.
""".strip()

    response = client.chat.completions.create(
        model=judge_model,
        messages=[
            {
                "role": "system",
                "content": (
                    "تو داور دقیق سیستم RAG هستی. "
                    "امتیاز را فقط براساس شواهد ورودی بده."
                ),
            },
            {
                "role": "user",
                "content": prompt,
            },
        ],
        temperature=0.0,
    )

    raw_content = (
        response.choices[0]
        .message.content
        .strip()
    )

    if raw_content.startswith("```json"):
        raw_content = raw_content[7:]

    if raw_content.startswith("```"):
        raw_content = raw_content[3:]

    if raw_content.endswith("```"):
        raw_content = raw_content[:-3]

    parsed = json.loads(raw_content.strip())

    return JudgeResult.model_validate(parsed)

استفاده:

result = evaluate_rag_answer(
    question="چگونه کلید API ایجاد کنم؟",
    contexts=[
        (
            "کاربر پس از ورود به پنل می‌تواند از بخش "
            "کلیدهای API یک کلید جدید ایجاد کند."
        ),
        (
            "کلید کامل فقط هنگام ایجاد نمایش داده می‌شود."
        ),
    ],
    generated_answer=(
        "از بخش کلیدهای API در پنل، کلید جدید بسازید. "
        "مقدار کامل آن فقط هنگام ساخت نمایش داده می‌شود."
    ),
    reference_answer=(
        "پس از ورود به پنل، به بخش کلیدهای API بروید "
        "و کلید جدید ایجاد کنید."
    ),
)

print(result.model_dump_json(
    ensure_ascii=False,
    indent=2,
))

مشکلات LLM as a Judge

سوگیری به پاسخ طولانی

داور ممکن است پاسخ مفصل‌تر را بهتر ارزیابی کند، حتی اگر محتوای اضافه ارزش نداشته باشد.

سوگیری به سبک نگارش

پاسخ رسمی یا ساختاریافته ممکن است امتیاز بالاتری بگیرد.

خودترجیحی مدل

مدل ممکن است سبک یا پاسخ‌های شبیه خروجی خودش را ترجیح دهد.

نوسان امتیاز

با اجرای مجدد ممکن است نتیجه کمی تغییر کند.

خطای داوری فارسی

مدل داور باید در زبان فارسی و اصطلاحات حوزه شما ارزیابی شود.

تکیه بر Context ناقص

داور نمی‌تواند درستی اطلاعاتی را که در ورودی ندارد تأیید کند.

کالیبراسیون داور

برای اطمینان از کیفیت LLM Judge:

  1. حداقل مجموعه‌ای از پاسخ‌ها را انسان امتیازدهی کند.
  2. همان پاسخ‌ها توسط مدل داوری شوند.
  3. همبستگی و موارد اختلاف بررسی شوند.
  4. Prompt داور اصلاح شود.
  5. مرزهای امتیاز تعریف شوند.
  6. نمونه‌های اختلاف شدید به Dataset اضافه شوند.
  7. مدل و Prompt داور نسخه‌بندی شوند.

نباید امتیاز 0.82 یک داور را بدون دانستن روش کالیبراسیون، حقیقت قطعی فرض کرد.

استفاده از چند داور

برای نمونه‌های مهم می‌توانید:

  • دو مدل داور استفاده کنید.
  • رأی اکثریت بگیرید.
  • اختلاف زیاد را به انسان ارجاع دهید.
  • یک مدل ارزان را برای همه نمونه‌ها اجرا کنید.
  • مدل قوی‌تر را فقط برای موارد مرزی به کار ببرید.

نمونه سیاست:

اگر اختلاف دو داور کمتر از 0.15 بود:
میانگین گرفته شود.

اگر اختلاف بین 0.15 و 0.30 بود:
داور سوم اجرا شود.

اگر اختلاف بیشتر از 0.30 بود:
بازبینی انسانی انجام شود.

ارزیابی Reference-based و Reference-free

Reference-based

پاسخ تولیدشده با پاسخ یا Context مرجع مقایسه می‌شود.

مزایا:

  • امکان محاسبه Correctness و Completeness بهتر
  • مناسب Regression Test
  • قابل‌تکرارتر

محدودیت:

  • ساخت Reference هزینه دارد.
  • پاسخ مرجع ممکن است فقط یکی از چند پاسخ صحیح باشد.

Reference-free

بدون پاسخ مرجع، کیفیت براساس سوال، Context و پاسخ ارزیابی می‌شود.

مزایا:

  • مناسب Traceهای Production
  • آماده‌سازی ساده‌تر
  • قابلیت اجرا روی داده زیاد

محدودیت:

  • وابستگی بیشتر به داور
  • اندازه‌گیری Correctness دشوارتر
  • کالیبراسیون پیچیده‌تر

LangSmith نیز در راهنمای ارزیابی کاربردی توضیح می‌دهد که RAG را می‌توان با پاسخ مرجع یا با روش‌های Reference-free ارزیابی کرد. رویکردهای ارزیابی LangSmith

ارزیابی RAG با Ragas

Ragas مجموعه‌ای از معیارهای ارزیابی برای برنامه‌های مبتنی بر LLM و RAG ارائه می‌کند. معیارهای RAG آن شامل Context Precision، Context Recall و معیارهای مربوط به پاسخ است. فهرست معیارهای Ragas

ساختار Dataset متداول:

from datasets import Dataset

evaluation_dataset = Dataset.from_list(
    [
        {
            "question": "چگونه کلید API ایجاد کنم؟",
            "answer": (
                "از بخش کلیدهای API در پنل، "
                "یک کلید جدید بسازید."
            ),
            "contexts": [
                (
                    "کاربر پس از ورود به پنل می‌تواند "
                    "از بخش کلیدهای API کلید بسازد."
                )
            ],
            "ground_truth": (
                "پس از ورود به پنل، به بخش کلیدهای API "
                "بروید و یک کلید جدید ایجاد کنید."
            ),
        }
    ]
)

API و نام کلاس‌های Ragas ممکن است با نسخه کتابخانه تغییر کنند. قبل از استفاده، مستندات نسخه نصب‌شده را بررسی کنید. تابع evaluate() در Ragas Dataset و فهرست Metricها را دریافت و نتیجه ارزیابی را برمی‌گرداند. مستندات evaluate در Ragas

ارزیابی فارسی در سیستم RAG

RAG فارسی مشکلات ویژه‌ای دارد که باید در Dataset پوشش داده شوند.

شکل‌های مختلف حروف

ی و ي
ک و ك

نیم‌فاصله

می‌شود
می شود
میشود

عدد فارسی و انگلیسی

۱۲۳۴
1234

ترکیب فارسی و انگلیسی

کلید API
API Key
ای‌پی‌آی

املای محاوره‌ای

نمیده
نمی‌ده
نمی‌دهد

فاصله و علائم

کاربران ممکن است فاصله یا نشانه‌گذاری درستی نداشته باشند.

اصطلاحات فنی

Rate Limit
ریت لیمیت
محدودیت درخواست

نرمال‌سازی فارسی برای Retrieval

نمونه تابع ساده:

import re
import unicodedata


PERSIAN_CHAR_MAP = str.maketrans(
    {
        "ي": "ی",
        "ك": "ک",
        "ۀ": "ه",
        "ة": "ه",
    }
)

DIGIT_MAP = str.maketrans(
    {
        "۰": "0",
        "۱": "1",
        "۲": "2",
        "۳": "3",
        "۴": "4",
        "۵": "5",
        "۶": "6",
        "۷": "7",
        "۸": "8",
        "۹": "9",
    }
)


def normalize_persian(text: str) -> str:
    text = unicodedata.normalize(
        "NFKC",
        text,
    )

    text = text.translate(
        PERSIAN_CHAR_MAP
    )

    text = text.translate(DIGIT_MAP)

    text = text.replace("\u200c", " ")

    text = re.sub(
        r"\s+",
        " ",
        text,
    )

    return text.strip().lower()

تبدیل نیم‌فاصله به فاصله همیشه برای همه کاربردها مناسب نیست. رفتار Normalizer باید همراه Retriever و Dataset آزمایش شود.

تست A/B برای Retrieval

برای مقایسه دو تنظیم:

نسخه A

Chunk Size: 400
Overlap: 60
Dense Retrieval
Top K: 5

نسخه B

Chunk Size: 700
Overlap: 100
Hybrid Retrieval
Top K: 10
Reranker Top N: 5

برای هر نسخه این معیارها را محاسبه کنید:

  • Recall@5
  • MRR
  • nDCG@5
  • Faithfulness
  • Answer Relevance
  • Latency
  • هزینه متوسط
  • میانگین طول Context

فقط یک امتیاز کلی نسازید. ممکن است نسخه B کیفیت را افزایش دهد اما زمان پاسخ و هزینه را بیش از حد بالا ببرد.

جدول ثبت آزمایش‌ها

نسخهChunkRetrievalRerankerRecall@5MRRFaithfulnessLatency
A400Denseندارد0.710.630.781.8s
B700Hybridدارد0.840.760.882.7s

اعداد این جدول صرفاً نمونه ساختار گزارش هستند و معیار هدف باید براساس Dataset واقعی شما تعیین شود.

تحلیل خطا مهم‌تر از میانگین است

دو سیستم ممکن است میانگین یکسانی داشته باشند اما روی دسته‌های متفاوتی خطا کنند.

گزارش را براساس Slice تقسیم کنید:

  • زبان
  • دسته محتوا
  • دشواری
  • طول سوال
  • تعداد اسناد موردنیاز
  • سوال دارای عدد
  • سوال بدون پاسخ
  • پرسش محاوره‌ای
  • اسناد قدیمی
  • PDF، HTML یا Markdown
  • سوال دارای اصطلاح انگلیسی

نمونه:

{
  "overall": {
    "recall@5": 0.82
  },
  "by_category": {
    "billing": {
      "recall@5": 0.91
    },
    "api_errors": {
      "recall@5": 0.63
    },
    "models": {
      "recall@5": 0.86
    }
  }
}

میانگین 0.82 مشکل جدی بخش api_errors را پنهان می‌کند.

ثبت Trace مناسب برای ارزیابی

برای هر درخواست ذخیره کنید:

{
  "trace_id": "trace-001",
  "question": "سوال کاربر",
  "normalized_query": "سوال نرمال‌شده",
  "retrieval_query": "Query نهایی",
  "retrieved_chunks": [
    {
      "chunk_id": "chunk-1",
      "document_id": "doc-1",
      "retrieval_score": 0.84,
      "rerank_score": 0.76,
      "text": "متن Chunk"
    }
  ],
  "answer": "پاسخ مدل",
  "citations": [
    "chunk-1"
  ],
  "model_id": "YOUR_MODEL_ID",
  "prompt_version": "rag-answer-v4",
  "index_version": "kb-2026-07-26",
  "latency_ms": 2450
}

بدون ثبت نسخه Index، Prompt و مدل، بازتولید نتیجه دشوار خواهد بود.

Regression Test برای RAG

هر تغییر در این موارد ممکن است کیفیت را تغییر دهد:

  • مدل Embedding
  • Chunk Size
  • Chunk Overlap
  • Normalizer
  • Metadata Filter
  • Vector Database
  • Hybrid Search
  • Reranker
  • Top K
  • Prompt
  • مدل مولد
  • ترتیب Context
  • طول Context

پیش از Deploy، Dataset ثابت را روی نسخه قبلی و جدید اجرا کنید.

تعریف Threshold

نمونه فایل quality-gates.json:

{
  "retrieval": {
    "hit_rate@5_min": 0.90,
    "recall@5_min": 0.78,
    "mrr_min": 0.70
  },
  "generation": {
    "faithfulness_min": 0.85,
    "answer_relevance_min": 0.82,
    "abstention_accuracy_min": 0.80
  },
  "performance": {
    "p95_latency_ms_max": 4500
  }
}

این اعداد فقط نمونه‌اند. Threshold واقعی باید براساس Baseline، حساسیت محصول و کیفیت Dataset تعیین شود.

مقایسه نسخه جدید با Baseline

def check_regression(
    baseline: dict,
    candidate: dict,
    max_drop: dict,
) -> list[str]:
    failures = []

    for metric, allowed_drop in max_drop.items():
        baseline_value = baseline[metric]
        candidate_value = candidate[metric]

        drop = (
            baseline_value
            - candidate_value
        )

        if drop > allowed_drop:
            failures.append(
                f"{metric} dropped by {drop:.4f}"
            )

    return failures

استفاده:

baseline = {
    "recall@5": 0.82,
    "mrr": 0.74,
    "faithfulness": 0.88,
}

candidate = {
    "recall@5": 0.84,
    "mrr": 0.71,
    "faithfulness": 0.80,
}

max_drop = {
    "recall@5": 0.02,
    "mrr": 0.03,
    "faithfulness": 0.03,
}

failures = check_regression(
    baseline,
    candidate,
    max_drop,
)

if failures:
    raise RuntimeError(
        "RAG quality regression:\n"
        + "\n".join(failures)
    )

تست با Pytest

def test_retrieval_quality():
    metrics = run_retrieval_evaluation(
        dataset_path="golden-dataset.jsonl",
        top_k=5,
    )

    assert metrics["hit_rate@5"] >= 0.90
    assert metrics["recall@5"] >= 0.78
    assert metrics["mrr"] >= 0.70

اجرای ارزیابی کامل LLM در هر Commit ممکن است زمان‌بر و پرهزینه باشد. می‌توانید:

  • مجموعه کوچک Smoke Test را در هر Pull Request اجرا کنید.
  • Dataset کامل را شبانه اجرا کنید.
  • داوری LLM را فقط روی پاسخ‌های تغییرکرده اجرا کنید.
  • نتیجه‌های تکراری را Cache کنید.

ارزیابی Offline و Online

Offline Evaluation

پیش از انتشار روی Dataset ثابت اجرا می‌شود.

کاربردها:

  • مقایسه نسخه‌ها
  • Regression Test
  • انتخاب مدل
  • تنظیم Chunking
  • انتخاب Top K
  • ارزیابی Reranker

Online Evaluation

روی نمونه‌هایی از ترافیک واقعی اجرا می‌شود.

کاربردها:

  • شناسایی تغییر رفتار کاربران
  • کشف سوال‌های جدید
  • بررسی Drift
  • جمع‌آوری بازخورد
  • یافتن دسته‌های ضعیف
  • اضافه کردن نمونه‌های جدید به Dataset

LangSmith نیز Offline Evaluation را برای تست پیش از انتشار و Online Evaluation را برای پایش تعاملات واقعی تفکیک می‌کند. مفاهیم Evaluation در LangSmith

نمونه‌گیری Production

لازم نیست تمام درخواست‌ها با مدل داور ارزیابی شوند. می‌توانید:

  • درصدی از درخواست‌ها را تصادفی انتخاب کنید.
  • پاسخ‌های بدون Citation را بیشتر نمونه‌گیری کنید.
  • درخواست‌های دارای بازخورد منفی را کامل ارزیابی کنید.
  • پاسخ‌های بسیار کوتاه یا طولانی را علامت بزنید.
  • سوال‌های بدون نتیجه Retrieval را ذخیره کنید.
  • موارد با امتیاز پایین Retriever را اولویت دهید.

بازخورد کاربر

سیگنال‌های مفید:

  • مفید بود یا نبود
  • مشکل پاسخ چه بود؟
  • منبع اشتباه بود
  • پاسخ ناقص بود
  • سوال را نفهمید
  • اطلاعات قدیمی بود
  • پاسخ بیش از حد طولانی بود
  • نیاز به تماس با پشتیبانی داشت

بازخورد کاربر Ground Truth کامل نیست. ممکن است کاربر به پاسخ درست امتیاز منفی بدهد یا برعکس، اما برای کشف موارد مسئله‌دار بسیار ارزشمند است.

داشبورد ارزیابی RAG

شاخص‌های پیشنهادی:

کیفیت Retrieval

  • Hit Rate@K
  • Recall@K
  • MRR
  • nDCG
  • درصد Retrieval خالی
  • توزیع Score
  • تفاوت قبل و بعد از Reranking

کیفیت Generation

  • Faithfulness
  • Answer Relevance
  • Completeness
  • Correctness
  • Abstention Accuracy
  • Citation Correctness

عملکرد

  • p50 Latency
  • p95 Latency
  • زمان Retrieval
  • زمان Reranking
  • زمان Generation
  • تعداد Token
  • هزینه متوسط
  • نرخ خطا

محصول

  • بازخورد مثبت
  • بازخورد منفی
  • نرخ سوال مجدد
  • نرخ ارجاع به پشتیبانی
  • دسته‌های پرتکرار
  • سوال‌های بدون پاسخ

کاهش هزینه ارزیابی با API

روش‌های کاربردی:

  • Metricهای قطعی Retrieval را محلی محاسبه کنید.
  • فقط Generation را با LLM داوری کنید.
  • نتیجه داوری ورودی تکراری را Cache کنید.
  • Dataset را به Smoke، Full و Production تقسیم کنید.
  • داور قوی‌تر را فقط برای موارد مرزی اجرا کنید.
  • پاسخ و Context را بیش از حد لازم به داور ندهید.
  • Prompt داور را کوتاه و ثابت نگه دارید.
  • ارزیابی را به‌صورت Batch برنامه‌ریزی کنید.
  • نمونه‌های بدون تغییر را دوباره ارزیابی نکنید.

برای مشاهده قیمت مدل‌های قابل‌استفاده به‌عنوان Generator یا Judge، صفحه مدل‌های درواره را بررسی کنید.

نسخه‌بندی اجزای ارزیابی

برای هر Run این اطلاعات را ثبت کنید:

{
  "experiment_id": "rag-exp-042",
  "dataset_version": "golden-v7",
  "index_version": "kb-2026-07-26",
  "chunking_version": "semantic-v3",
  "embedding_model": "EMBEDDING_MODEL_ID",
  "retriever_version": "hybrid-v2",
  "reranker_model": "RERANKER_MODEL_ID",
  "generator_model": "YOUR_MODEL_ID",
  "judge_model": "YOUR_JUDGE_MODEL_ID",
  "answer_prompt_version": "rag-answer-v6",
  "judge_prompt_version": "rag-judge-v3"
}

اگر هرکدام از این موارد ثبت نشوند، مقایسه آزمایش‌ها قابل‌اعتماد نخواهد بود.

خطاهای رایج در ارزیابی RAG

فقط ارزیابی پاسخ نهایی

در این حالت مشخص نیست مشکل از Retrieval است یا Generation.

Dataset بسیار کوچک

چند سوال ساده نمی‌توانند کیفیت کل سیستم را نشان دهند.

استفاده از سوال‌های مصنوعی بدون بازبینی

این سوال‌ها ممکن است شبیه رفتار واقعی کاربران نباشند.

نداشتن سوال بدون پاسخ

سیستم توانایی Abstain کردن را یاد نمی‌گیرد یا ارزیابی نمی‌کند.

گزارش فقط میانگین

میانگین ممکن است ضعف شدید یک دسته مهم را پنهان کند.

تغییر هم‌زمان چند جزء

اگر Chunking، Embedding و Prompt هم‌زمان تغییر کنند، علت بهبود یا افت مشخص نیست.

استفاده از یک داور بدون کالیبراسیون

امتیاز LLM Judge باید با ارزیابی انسانی مقایسه شود.

بی‌توجهی به Latency و هزینه

کیفیت بالاتر با زمان پاسخ یا هزینه غیرقابل‌قبول ممکن است برای محصول مناسب نباشد.

نادیده گرفتن Citation

پاسخ مستند باید Citation صحیح و کامل داشته باشد.

نداشتن نسخه Dataset

تغییر Dataset می‌تواند مقایسه دو Experiment را بی‌اعتبار کند.

Leakage میان تولید و ارزیابی

اگر مدل پاسخ مرجع را هنگام تولید پاسخ ببیند، ارزیابی واقعی نخواهد بود.

انتخاب K نامتناسب

معیارهای @K باید با تعداد واقعی Contextهایی که وارد Prompt می‌شوند هماهنگ باشند.

چک‌لیست ارزیابی سیستم RAG

پیش از انتشار بررسی کنید:

  • Golden Dataset نسخه‌بندی شده است.
  • سوال‌های واقعی در Dataset وجود دارند.
  • نمونه‌های ساده، دشوار و بدون پاسخ پوشش داده شده‌اند.
  • Relevant Document و Relevant Chunk مشخص‌اند.
  • Retrieval جدا از Generation ارزیابی می‌شود.
  • Precision@K و Recall@K محاسبه می‌شوند.
  • رتبه اولین سند با MRR بررسی می‌شود.
  • کیفیت ترتیب با nDCG ارزیابی می‌شود.
  • Faithfulness در سطح Claim سنجیده می‌شود.
  • Answer Relevance و Completeness جدا هستند.
  • Citation Correctness بررسی می‌شود.
  • Abstention Accuracy اندازه‌گیری می‌شود.
  • LLM Judge با انسان کالیبره شده است.
  • Prompt داور نسخه‌بندی شده است.
  • نتایج براساس دسته و دشواری Slice شده‌اند.
  • Latency و هزینه همراه کیفیت گزارش می‌شوند.
  • Baseline مشخص است.
  • Threshold یا حداکثر افت مجاز تعریف شده است.
  • Regression Test قبل از Deploy اجرا می‌شود.
  • Trace شامل Query، Chunk و نسخه Index است.
  • خطاهای Production به Dataset اضافه می‌شوند.
  • ارزیابی فارسی و شکل‌های مختلف نوشتار پوشش داده شده‌اند.

سوالات متداول

بهترین معیار برای ارزیابی RAG چیست؟

یک معیار واحد کافی نیست. Retrieval را با Recall@K، MRR و nDCG و پاسخ را با Faithfulness، Relevance، Correctness و Completeness ارزیابی کنید.

تفاوت Precision@K و Recall@K چیست؟

Precision@K نسبت نتایج مرتبط میان K نتیجه اول را اندازه می‌گیرد. Recall@K نشان می‌دهد چه مقدار از تمام اسناد مرتبط در K نتیجه اول پیدا شده‌اند.

MRR چه چیزی را اندازه‌گیری می‌کند؟

MRR رتبه اولین نتیجه مرتبط را اندازه می‌گیرد. هرچه سند درست زودتر ظاهر شود، MRR بیشتر خواهد بود.

nDCG چه زمانی مفید است؟

وقتی اسناد چند سطح ارتباط دارند و ترتیب تمام نتایج مهم است، nDCG مناسب‌تر از MRR خواهد بود.

Faithfulness چه تفاوتی با Correctness دارد؟

Faithfulness بررسی می‌کند پاسخ توسط Context پشتیبانی می‌شود یا نه. Correctness بررسی می‌کند پاسخ واقعاً درست و با Reference Answer سازگار است یا نه.

آیا Ragas برای ارزیابی RAG مناسب است؟

Ragas معیارهای آماده‌ای مانند Context Precision، Context Recall و Faithfulness ارائه می‌کند. بااین‌حال، Metricها باید با نیاز محصول و داده انسانی کالیبره شوند.

آیا می‌توان بدون پاسخ مرجع RAG را ارزیابی کرد؟

بله. معیارهایی مانند Faithfulness و Answer Relevance را می‌توان با سوال، Context و پاسخ ارزیابی کرد، اما Correctness بدون Reference دشوارتر است.

Dataset ارزیابی باید چند نمونه داشته باشد؟

عدد ثابتی وجود ندارد. از یک مجموعه کوچک اما متنوع شروع کنید و آن را با خطاهای واقعی گسترش دهید. پوشش دسته‌ها مهم‌تر از تعداد خام نمونه‌هاست.

آیا می‌توان از API درواره برای LLM as a Judge استفاده کرد؟

بله. می‌توانید یک مدل متنی مناسب را از طریق API درواره فراخوانی کنید و خروجی داوری را به شکل JSON اعتبارسنجی کنید.

هزینه ارزیابی چقدر است؟

معیارهای قطعی Retrieval به فراخوانی مدل نیاز ندارند. هزینه LLM Judge به مدل، تعداد نمونه‌ها، طول Context و پاسخ بستگی دارد. قیمت‌های به‌روز در صفحه مدل‌های درواره قرار دارند.

جمع‌بندی

ارزیابی RAG باید به یک فرایند مهندسی تکرارپذیر تبدیل شود، نه بررسی چند پاسخ به‌صورت چشمی. ابتدا کیفیت Retrieval را با معیارهایی مانند Precision@K، Recall@K، Hit Rate، MRR و nDCG اندازه‌گیری کنید. سپس Generation را از نظر Faithfulness، Relevance، Correctness، Completeness و Citation بررسی کنید.

Golden Dataset، ثبت Trace، نسخه‌بندی اجزا و Regression Test کمک می‌کنند هر تغییر در Chunking، Embedding، Reranking، Prompt یا مدل مولد قبل از انتشار ارزیابی شود.

با API درواره می‌توانید مدل‌های مختلف را برای تولید پاسخ یا LLM as a Judge آزمایش کنید و کیفیت، هزینه و زمان پاسخ آن‌ها را روی Dataset واقعی خود مقایسه کنید.

برای شروع، در درواره ثبت‌نام کنید، مدل Generator و Judge مناسب را از صفحه مدل‌ها انتخاب کنید و اولین Benchmark قابل‌تکرار RAG خود را بسازید.

مقالات مرتبط

برای مطالعه شرایط استفاده و محدودیت‌های مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.

Read more