[!NOTE] Автоматический перевод Эта статья была автоматически переведена с оригинальной английской версии.

Оценка RAG: показатели для каждого этапа работы производственной системы RAG

Часть 1 серии для производственной среды RAG

Система RAG с повреждёнными фильтрами может функционировать в течение месяцев, не вызывая уведомлений о сбоях в работе. Она по-прежнему возвращает ответы и соответствует своей латентность цели, однако эти ответы основаны на неполных данных. Проверка с использованием исходного «золотого» набора выявляет наличие ошибок; при этом панели контроля латентность и доступности их не обнаруживают.

Оценка качества может выявить наличие сбоя лишь в том случае, если у каждого этапа пайплайн имеются собственные метрики. В данной статье распространённые модели сбоев связываются с этими метриками на протяжении всего цикла — от парсинга документов до мониторинга в режиме работы.

!!! Совет: «Хотите пропустить описание и сразу запустить код?»

Запускаемый код [`slavadubrov/rag-evals-demo`](https://github.com/slavadubrov/rag-evals-demo) репозиторий применяет эти метрики к SciFact. `make eval` запускает набор тестов, и `make benchmark` Сравниваются конфигурации чанкинг, эмбеддинг и LLM. Ноутбуки с номерами от 00 до 09 предназначены для изоляции каждого из этих показателей. В демонстрации используется встроенная реализация Qdrant, поэтому нет необходимости в использовании Docker.

Кратко:

Разделы расположены в соответствии с порядком пайплайн. Сначала приводится таблица решений, после чего более поздние разделы используются в качестве справочных материалов для каждого этапа.


Таблица решений для оценки RAG

Используйте эту таблицу в качестве отправной точки перед тем, как выбрать фреймворк. Подходящая метрика определяется способом возникновения сбоя, который необходимо выявить, а не названием инструмента.

ВопросСемейство метрикИспользуйте это в тех случаях, когдаОстерегайтесь
Сохранялась ли структура исходного текста в процессе парсинга?Степень полноты извлечения, охват таблиц и рисунковPDF-файлы, слайды, сканы и HTML-страницы попадают в корпус данных.Даже текст с оптимизированным внешним видом может содержать пропущенные подписи к изображениям, сноски или некорректную структуру таблиц.
Нашел ли retrieval подходящие доказательства?Recall@k, nDCG@k, MRR, точность/память в контекстеВы можете пометить соответствующие чанки или документыАгрессивный фильтр метаданных способен исключить соответствующий документ ещё до начала процесса ранжирования.
Улучшил ли реранкинг список кандидатов?Реранкер повышение, точность@1, дельта nDCGКросс-энкодеры или ранжировщики LLM располагаются после retrieval.Измеряйте латентность и затраты с учётом улучшения качества
Использовалось ли в ответе соответствующее доказательство?Степень точности, основанность на реальных данных, поддержка цитированияВ ответе приводятся ссылки на документы или указываются факты непосредственно из контекста.Проверка на верность данных не может выявить некорректную обработку или ошибки парсинга retrieval
Является ли система стабильной в режиме производства?Дрейф, регенерация, резервный режим, p95 латентность, стоимость ответаИзменения в трафике после запускаДля поддержания калибровки телеметрии производственных систем требуется периодический человеческий анализ выборочных данных.

Для более краткого сравнения инструментов см. Лучшие инструменты и метрики оценки RAG в 2026 году.

Часть 1: Определите критерии успеха до разработки архитектуры

Составьте набор эвал перед диаграммой архитектуры. Он позволяет задать измеримую цель для каждого последующего выбора компонентов.

Вы не можете выбирать между алгоритмами BM25 и плотными retrieval, рекурсивными и семантическими чанкинг, а также между методами Cohere Rerank и BGE, пока не определите, что именно вы пытаетесь оптимизировать. Понятие «лучших ответов» само по себе не является критерием оценки. Хорошим примером формулировки целей может служить требование к точности: «точность ≥ 0.85 для набора из 200 тестовых запросов, охватывающего три основные цели использования системы, при этом время обработки на уровне p95 латентность должно быть менее 1.5 секунд, а коэффициент ложных исключений — менее 2%». Указанные здесь числа являются лишь примерами; главное — наличие четких критериев для оценки качества, степени охвата, латентность и эффективности фильтрации.

Сначала определите харнесс, прежде чем писать код retrieval. Первая версия харнесс окажется некорректной, и её придётся перерабатывать. Исправление метрики стоит гораздо дешевле, чем корректировка уже выпущенной системы.

Три слоя пайплайн и два режима работы

Современные RAG являются пайплайн, поэтому процесс оценки должен выполняться с использованием пайплайн. Ни одно единичное число не может учесть все возможные сценарии сбоев.

Оценка в режиме производства включает три уровня пайплайн. Оценка этапа поступления данных проверяет, сохраняются ли в корпусе и индексе исходные данные без искажений. Оценка во время выполнения запроса анализирует, позволили ли операции переписывания, фильтрации, retrieval, реранкинг и сбора контекста найти подходящие доказательства. Оценка ответа и работы в реальном времени определяет, использовался ли найденный материал в ответе и сохраняется ли его качество при работе с нагрузкой от реальных пользователей. Объединив результаты всех уровней в один единый балл, можно скрыть последствия ошибок нормализации в рамках допустимого значения оценки ответа.

Три сценария, при которых система RAG может потерять доказательства

Эти слои определяют место возникновения сбоя. Термины offline и online указывают на момент выполнения проверки и на набор данных, по которым она производится. При оффлайн-оценке используется фиксированный датасет с известными эталонными значениями; такой подход обеспечивает воспроизводимость результатов и применяется при выборе компонентов, проведении сравнений типа A/B и на этапах контроля качества в рамках CI. Онлайн-оценка анализирует данные из реального трафика, учитывая процессы регенерации, время пребывания на странице, явную обратную связь и дрейф запросов. Этот метод более шумный и требует более сложной инструментализации.

Каждый слой пайплайн может обеспечивать проверку как в офлайн-режиме, так и в режиме онлайн. Фиксированный набор данных для обработки позволяет выявлять регрессии парсера ещё до выпуска продукта, в то время как мониторы свежести данных и ошибок парсинга отслеживают изменения в реальном времени. Фиксированный набор запросов используется для оценки retrieval перед релизом, а выборочные данные из рабочей среды трейсы помогают выявлять отклонения в производственной среде. Работа исключительно в офлайн-режиме не позволяет учитывать изменения в реальном времени, а работа исключительно в онлайн-режиме затрудняет воспроизведение регрессий.

На уровне компонентов против полного цикла обработки

Существуют две распространённые ошибки. Оценка исключительно в режиме «от конца до конца» позволяет сделать вывод о неисправности системы, но не указывает на место сбоя. Оценка только отдельных компонентов может показать, что все части работают корректно, при этом вся система всё равно терпит неудачу. Решением является использование нескольких ключевых метрик в режиме «от конца до конца» для принятия решений о допустимости или отклонении, наряду с метриками компонентов для постановки диагноза. Метрики Retrieval позволяют выявлять ухудшение качества работы модуля поиска информации, а метрики генерации — ухудшение качества работы модуля генерации текста. Проверка корректности ответа в режиме «от конца до конца» помогает обнаруживать проблемы с интеграцией.

Справочный вариант фреймворки (тур с жёстко заданным маршрутом)

ФреймворкЛучшие результаты показывает приГде происходит сбой
RAGASМетрики RAG без использования внешних референций (степень верности, релевантность ответа, точность/покрытие контекста); фактический словарный запасLLM-джадж — затраты; непрозрачные компоненты оценки во время отладки; параметры по умолчанию, ориентированные на англоязычную среду.
ARESОбученный классификатор джаджи с использованием пайплайн; количество аннотаций меньше, чем в подходах типа RAGAS; высокая точность для близких по характеристикам системБолее громоздкая конфигурация; для её работы необходимо провести обучение модели.
TruLensКомпозиционные функции обратной связи с высокой степенью объяснимости; OpenTelemetry трейсы; пригодные для применения в продакшенеВ метриках, специфичных для RAG, указано меньше количества встроенных батарей по сравнению с RAGAS.
DeepEvalЕдиничные тесты в стиле Pytest для выводов LLM; G-Эвал, пользовательские метрики, интеграция с пайплайнами CI/CDИнтенсивное использование LLM-джадж приводит к резкому скачку затрат
Arize PhoenixЭффективная визуализация трейсинг и эмбеддинг; визуальное выявление смещений эмбеддинг; нативно для OTEL
Трек TREC 2024 RAGОбщедоступный бенчмарк для оценки качества фрагментов (AutoNuggetizer), поддержки процесса оценки и измерения уровня плавности в MS MARCO Segment v2.1Это не инструмент рантайм, а бенчмарк для калибровки по отношению к нему.

Мой стандартный набор инструментов включает RAGAS для работы с лексиконом метрик, DeepEval для реализации этапов контроля качества в процессе CI, Phoenix для эксплуатации в продакшене трейсинг, а также пользовательский код для расчёта метрик, специфичных для конкретной онтологии. Любая начальная конфигурация со временем окажется недостаточной. Выбирайте фреймворк, который облегчит разработку собственных метрик.

Для бенчмарки используйте BEIR (Thakur et al., NeurIPS 2021) — для реализации обобщения без обучения на примерах retrieval. MTEB для обеспечения общего качества эмбеддинг. MIRACL для мультиязычного retrieval, а также TREC 2024 RAG дорожка для оценки в режиме «от начала до конца» RAG.


Часть 2: Соотнесение точек оценки с пайплайн

Промышленная система RAG значительно сложнее, чем простая задача встраивания документов, получения чанки и вызова LLM. На любом этапе пути от получения документа до передачи ответа может возникнуть сбой.

Полная реализация RAG пайплайн с метрическими индикаторами на каждом этапе

В каждом этапе диаграммы предусмотрен хотя бы один показатель эффективности. Этап, не имеющий такого показателя, может выйти из строя незамеченным.

Три канала обработки соответствуют этапам, на которых может произойти потеря данных. Канал входных данных отвечает за парсинг, очистку, чанкинг, эмбеддинг и индексацию. Канал обработки запросов включает в себя переписывание формулировок, фильтрацию, retrieval, реранкинг и сбор контекста. Канал генерации ответов и их релиза занимается сохранением точности информации, проверкой цитат, анализом сигналов от пользователей, выявлением дрейфа качества, латентность и оценкой затрат.

Ошибки накапливаются по всей цепочке обработки. Некорректный парсинг ограничивает возможности чанкинг. Неправильная работа чанкинг сказывается на эффективности retrieval. Некорректное функционирование retrieval влияет на качество реранкинг. Проблемы с реранкинг препятствуют процессу генерации результата. Показатель точности отражает лишь конечный ответ, но никак не первопричины, возникшие на более ранних этапах.


Часть 3: Оценка процесса поступления данных

Большинство сбоев RAG в продакшене возникают на этапе загрузки данных. Система корректно обрабатывает чистые тестовые документы, но терпит неудачу при работе с реальными PDF-файлами, сканами, таблицами и страницами из «грязных» корпусов данных.

Получение и парсинг документов

Что измерять:

Сравните различные семейства парсеров: например, базовую версию Tesseract, реализацию на основе VLM типа OCR модель, а также вариант от вашего поставщика. Для тестирования используйте стратифицированный набор реальных документов с фиксированным разрешением DPI, включающий чистые сканы, фотографии, таблицы, многоязычный текст, математические формулы и рукописный текст. Предоставьте показатели CER или WER для каждого типа документов, а также значение TEDS для страниц с таблицами.

Очистка и нормализация

Чанкинг регулирует качество retrieval

Чанкинг может приводить к появлению разрыва в многоточечном восстановлении данных даже при неизменности значения эмбеддинг-модель. В Поставщик чипов NVIDIA для 2024 года бенчмарк, На уровне страницы чанкинг показал наивысшую точность и минимальную дисперсию для документов с разделением на страницы. Следует рассматривать этот результат как свидетельство эффективности данного метода в рамках тестируемого корпуса, а не как универсально оптимальное решение.

Семантические чанкинг группируют соседние предложения на основе эмбеддинг сходства и разделяют их в местах с низким уровнем сходства. В фреймворке LangChain SemanticChunker и LlamaIndex’s SemanticSplitterNodeParser Реализуйте эту стратегию. Она позволяет повысить точность восстановления данных при использовании фиксированных окон в тех случаях, когда важны границы тематических областей.

Рекурсивное разделение символов начинается с пробования разбиения на абзацы, затем — на предложения, и далее — на слова, пока каждый чанк не будет соответствовать заданному размеру. LangChain’s RecursiveCharacterTextSplitter реализуется последовательность обработки. Выберите подходящие значения параметров окна и перекрытия в зависимости от структуры вашего документа, после чего позвольте алгоритму «золотого набора» определить окончательные значения.

Метрики для отслеживания:

Мое мнение: структурный чанкинг (разделение по заголовкам, таблицам и разделам — реализуемое парсерами вроде unstructured.io Использование таких методов, как просмотр синтаксического дерева, уже сгенерированного парсером, остаётся недостаточно распространённым. Если ваши документы имеют определённую структуру, следует воспользоваться ею до того, как применять гистерики оценки сходства. Рекурсивное разделение символов является базовым подходом; применение семантического чанкинг оправдано лишь в случае неструктурированных текстов из-за связанной с этим дополнительной нагрузки.

Извлечение и обогащение метаданных

Генерация Эмбеддинг

Построение индекса


Часть 4: Оценка во время выполнения запроса

В сегменте времени запроса хранятся метрики, позволяющие диагностировать путь retrieval. Одного только показателя Recall@k недостаточно для определения того, привело ли к сбою переписывание данных, фильтрация, реранкинг или формирование контекста.

Понимание запроса и его переписывание

Метрики Retrieval

Это базовые метрики. Если вы не отслеживаете их, вы не сможете определить, улучшается ли retrieval.

МетрикаЧто оно измеряетКогда использовать
Recall@kдоля релевантных документов запроса, возвращаемых в топ-kиспользуется в тех случаях, когда отсутствие любого элемента соответствующего набора имеет значимое влияние
Precision@kдоля элементов из набора top-k, являющихся релевантнымиэтот подход полезен, когда контекстное окно является боттлнек
среднее значение 1/ранг первого релевантного документакогда пользователи рассматривают лишь топ-1 или топ-3
nDCG@kвзвешенная прибыль с учетом позиционного дисконта и оценок релевантностистандартная метрика retrieval для оценки степени релевантности
MAPсреднее значение точности по всем запросамкогда важен весь полный рейтинговый список
Hit Rate@kприсутствует ли хотя бы один релевантный документ в топ‑kВычисляется среднее значение бинарного результата по всем запросам в качестве быстрой метрики проверки корректности работы.
Покрытиедоля «золотых» документов, когда-либо извлечённых в ходе всех запросовобнаруживает систематические пробелы в индексе

Формулы для справки (бинарная релевантность с множеством релевантных документов RqR_q для запроса qq, причём reli=1\text{rel}_i = 1, если ii-й полученный документ находится в RqR_q):

Recall@k=Rqd1,,dkRq,Precision@k=количество верных предсказаний из первых kобщее количество предсказанийRqd1,,dkk\text{Recall@k} = \frac{|R_q ∩ {d_1, …, d_k}|}{|R_q|}, \quad \text{Precision@k} = \frac{\text{количество верных предсказаний из первых } k}{\text{общее количество предсказаний}}|R_q ∩ {d_1, …, d_k}|{k} RRq=1ранг первого релевантного документа,MRR=1ВопросqQRRq\text{RR}_q = \frac{1}{\text{ранг первого релевантного документа}}, \quad \text{MRR} = \frac{1}{|Вопрос|} \sum_{q \in Q} \text{RR}_q DCG@k=i=1k2reli1log2(i+1),nDCG@k=DCG@kIDCG@k\text{DCG@k} = \sum_{i=1}^{k} \frac{2^{\text{rel}_i} - 1}{\log_2(i + 1)}, \quad \text{nDCG@k} = \frac{\text{DCG@k}}{\text{IDCG@k}}

Для оценки степени релевантности reli{0,1,2,}\text{rel}_i \in \{0, 1, 2, \dots\}; бинарный nDCG представляет собой специальный случай, используемый в приведённом ниже коде. MAP — это среднее по всем запросам значений \text{AP}_q = \frac{1}{|R_q|\sum_{i: \text{rel}_i = 1} \text{Precision@}i. См. Мэннинг, Рагхаван, Шютце, «Введение в поиск информации», Глава 8 — развёртывание доказательств.

Для кода, выпускаемого в продакшн, используйте ranx, pytrec_eval, или ir_measures — они реализуют весь набор метрик TREC и корректно обрабатывают понятие степени релевантности. Устанавливайте цели для выпуска на основе реалистичного золотого набора данных, качества ответов на выходном уровне и стоимости ошибочного результата. Не берите пороговые значения из учебных материалов.

Тест харнесс для этих решений имеет небольшую продолжительность. Его можно запустить прямо из ноутбука ещё до того, как будет выбрана база данных векторов.

from math import log2
from statistics import mean

# synthetic gold set: query_id -> set of relevant doc ids
gold = {
    "q1": {"d3"},
    "q2": {"d7", "d2"},
    "q3": {"d11"},
    "q4": {"d5"},
}

# ranked retrieval results: query_id -> ranked list of doc ids (top-10)
runs = {
    "q1": ["d8", "d3", "d1", "d4", "d2", "d9", "d6", "d10", "d12", "d13"],
    "q2": ["d2", "d6", "d4", "d7", "d1", "d3", "d8", "d11", "d5", "d9"],
    "q3": ["d11", "d2", "d3", "d4", "d1", "d6", "d7", "d8", "d10", "d12"],
    "q4": ["d1", "d2", "d3", "d6", "d8", "d9", "d10", "d12", "d13", "d14"],
}

def recall_at_k(ranked, gold_set, k):
    if not gold_set:
        return 0.0
    hit = sum(1 for d in ranked[:k] if d in gold_set)
    return hit / len(gold_set)

def reciprocal_rank(ranked, gold_set):
    # MRR contribution per query: 1/rank of the first relevant doc.
    for rank, d in enumerate(ranked, start=1):
        if d in gold_set:
            return 1.0 / rank
    return 0.0

def ndcg_at_k(ranked, gold_set, k):
    # binary relevance: rel ∈ {0, 1}
    gains = [1.0 if d in gold_set else 0.0 for d in ranked[:k]]
    dcg = sum(g / log2(i + 2) for i, g in enumerate(gains))
    # ideal DCG: all gold docs ranked first, capped by k
    n_gold_in_topk = min(k, len(gold_set))
    idcg = sum(1.0 / log2(i + 2) for i in range(n_gold_in_topk))
    return dcg / idcg if idcg else 0.0

K = 5
print(f"Recall@{K}: {mean(recall_at_k(runs[q], gold[q], K) for q in gold):.3f}")
print(f"MRR:       {mean(reciprocal_rank(runs[q], gold[q]) for q in gold):.3f}")
print(f"nDCG@{K}:  {mean(ndcg_at_k(runs[q], gold[q], K) for q in gold):.3f}")
# Recall@5: 0.750
# MRR:       0.625
# nDCG@5:    0.627

Это и есть ваша CI-шлюзовая точка retrieval. Подключите её к быстрому поднабору тестов, ориентированному на измерение уровня покрытия кода, при каждом создании PR, а полный набор тестов — к более медленной шлюзовой точке сборки для релизов. Заблокируйте процесс слияния, если значение заранее заданного метрического показателя превысит установленный лимит на возникновение регрессий.

Сопутствующий репозиторий фиксирует именно эти значения выше.Recall@5 = 0.750, MRR = 0.625, nDCG@5 = 0.627) в качестве теста единицы в tests/test_retrieval_metrics.py; журнал 01 проводятся анализы Recall@k / MRR / nDCG на реальном индексе SciFact, причём в продакшене используется харнесс, соответствующий реальным условиям работы evaluation/retrieval.py.

Гибридное слияние рангов retrieval и взаимных рангов

BM25 это редкий лексический оценщик, объединяющий точное совпадение терминов, взвешивание терминов и нормализацию длины. Он доступен в rank_bm25Elasticsearch, OpenSearch и большинство других поисковых движков.

Слияние рекурсивных рангов (Cormack, Clarke, and Buettcher, SIGIR 2009) объединяет алгоритм BM25 с методом плотных ранжировок по положению. Оригинальная k=60 Заданные параметры служат удобной отправной точкой для настройки. Метод RRF не зависит от используемой оценочной функции, что позволяет избежать нормализации между каналами, необходимой при линейной интерполяции. При наличии достаточно большого набора с метками для определения стабильного значения дельты также рекомендуется протестировать использование конвексной комбинации и отрегулировать параметр α.

Использование гибридной модели retrieval в сочетании с кросс-кодером реранкер зачастую способствует улучшению качества обработки технических текстов, логов и кодовых корпусов. Однако эффект может оказаться незначительным для корпусов с высокой степенью семантической насыщенности. Рекомендуется проводить сравнение с результатами обработки только на основе плотных данных и только на основе разреженных данных, поскольку неоптимальная конфигурация слияния информации может привести к худшим показателям по сравнению с любым из исходных вариантов.

Реализация занимает всего несколько строк.

from collections import defaultdict

# two retrieval lanes: dense embeddings and BM25.
dense  = ["d3", "d7", "d1", "d4", "d2", "d9", "d10"]
sparse = ["d2", "d3", "d8", "d1", "d11", "d4", "d6"]

def rrf(rankings: list[list[str]], k: int = 60) -> list[tuple[str, float]]:
    """Reciprocal Rank Fusion (Cormack et al., SIGIR 2009).

    score(d) = sum over rankings of 1 / (k + rank(d))
    Score-agnostic: only rank position matters. k=60 is the canonical default.
    """
    scores: dict[str, float] = defaultdict(float)
    for ranking in rankings:
        for rank, doc in enumerate(ranking, start=1):
            scores[doc] += 1.0 / (k + rank)
    return sorted(scores.items(), key=lambda kv: kv[1], reverse=True)

fused = rrf([dense, sparse], k=60)
for doc, score in fused[:5]:
    print(f"{doc}  score={score:.5f}")
# d3  score=0.03252   <- rank 1 dense, rank 2 sparse
# d2  score=0.03178   <- rank 5 dense, rank 1 sparse
# d1  score=0.03150

Обратите внимание на то, чего не делает RRF: он никогда не анализирует сырые значения показателей сходства. Результиры работы инструмента типа dense retriever с коэффициентом косинуса 0.98 и результаты работы алгоритма BM25 с показателем 17.4 нельзя сравнивать напрямую. Если нормализовать эти значения с помощью z-статистик или метода минимум-максимум, это может привести к тому, что будет отдаваться предпочтение тому способу поиска, у которого наибольшая дисперсия в данной группе данных.

RRF использует исключительно ранг. Если ретривер помещает документ на позицию 2, этот голос имеет такую же ценность 1 / (60 + 2), независимо от исходного балла, который его сгенерировал.

Гибридный подход + RRF в SciFact: журнал 02 сравнивает алгоритмы dense, BM25 и RRF с учётом дельт на уровне каждого запроса. Фьюзер, адаптированный для производственных условий, находится в retrieval/hybrid_rrf.py; tests/test_rrf.py фиксирует каноническую версию d3 / d2 / d1 формирование заказа k=60.

Реранкинг

from sentence_transformers import CrossEncoder

reranker = CrossEncoder("BAAI/bge-reranker-v2-m3")

query = "How do I rotate database credentials in production?"
candidates = [
    "Production database credentials are rotated via Vault every 30 days.",
    "The new logo was unveiled at the all-hands meeting.",
    "To rotate prod DB creds, run the `rotate-secrets` GitHub Action.",
]

scores = reranker.predict([(query, c) for c in candidates])
ranked = sorted(zip(candidates, scores), key=lambda x: -x[1])
for doc, score in ranked:
    print(f"{score:+.3f}  {doc}")

реранкер представляет собой кандидата с высоким потенциалом для применения базовой техники RAG пайплайн, однако это не гарантирует успеха. Необходимо измерить его значения ΔPrecision@1 и ΔnDCG на золотом наборе данных, после чего оставить такой вариант лишь в том случае, если полученная прибавка превышает установленные лимиты латентность и бюджет расходов. Перед выбором следующей оптимизации сравните эту измеренную прибавку с менее значительными изменениями, достигаемыми с помощью retrieval.

ΔnDCG и ΔPrecision@1, рассчитываемые с помощью кросс-энкодера в SciFact: журнал 03; модуль: retrieval/reranker.py.

Построение контекста и проблема «потери в середине»

Именно здесь возникает множество сбоев типа «хороший retrieval, плохой ответ».


Часть 5: Уровень ложного исключения фильтра

Этот показатель имеет отдельный раздел, поскольку суммарные оценки retrieval не позволяют приписать неудачу конкретному фильтру.

жёсткий фильтр метаданных вроде tenant_id = X AND product = Y AND locale = en-US Этот показатель может довести эффективную воспроизводимость до нуля. При правильной реализации метрики Recall@k удается зафиксировать такую потерю, поскольку её знаменатель остаётся прежним набором релевантных документов. Однако она не позволяет определить, была ли причиной пропуска фильтрация, механизм поиска или алгоритм ранжирования. Показатель верности всё равно может казаться в порядке, поскольку он оценивает ответ на основе неполного полученного контекста; модель верно заявил: «Я не знаю».

Красная ветвь на диаграмме отражает наиболее частую проблему: соответствующий документ действительно существует, но фильтр удаляет его ещё до retrieval.

Таксономия тихих сбоев с метрикой, фиксирующей каждый из режимов их возникновения

Метрика

filter_false_exclusion_rate =
    (# queries where all gold docs were excluded by metadata filter) /
    (# queries with at least one gold doc)

В данном определении на уровне запроса учитываются катастрофические случаи исключения: при таких сценариях не остается ни одного релевантного документа. При работе с многокритериальными запросами метод Recall@k по‑прежнему приводит к частичной потере информации; если этот показатель имеет важное значение, следует ввести показатель скорости исключения для каждого документа отдельно. Для расчёта любого из этих показателей требуются: (а) идентификаторы документов, соответствующие истинному состоянию, для каждого запроса эвал, и (б) инструментарий для записи условий фильтрации, применяемых на этапе обработки, а не только окончательных результатов. Целевое значение следует определять на основе стоимости исключения корректного ответа и диапазона доверия вашей производственной выборки.

Вот рабочая реализация. В ней сравнивается стандартный показатель воспроизведения с некорректным эвалуатор, который изменяет критерии релевантности после фильтрации.

# A small worked example where hard filters remove relevant documents.
docs = [
    {"id": "d1", "tenant": "acme",   "locale": "en-US"},
    {"id": "d2", "tenant": "acme",   "locale": "en-GB"},
    {"id": "d3", "tenant": "globex", "locale": "en-US"},
    {"id": "d4", "tenant": "acme",   "locale": "en-US"},
    {"id": "d5", "tenant": "acme",   "locale": "de-DE"},
]

queries = [
    # the gold doc lives in en-GB but the dynamic filter forced en-US
    {"qid": "q1", "gold": {"d2"}, "filter": lambda d: d["locale"] == "en-US"},
    # the gold doc is correctly within the tenant filter
    {"qid": "q2", "gold": {"d4"}, "filter": lambda d: d["tenant"] == "acme"},
    # the gold doc is in a different tenant and gets dropped
    {"qid": "q3", "gold": {"d3"}, "filter": lambda d: d["tenant"] == "acme"},
    # the gold doc passes the filter (de-DE locale match)
    {"qid": "q4", "gold": {"d5"}, "filter": lambda d: d["locale"] == "de-DE"},
]

def filter_false_exclusion_rate(queries, docs):
    n_with_gold, n_excluded = 0, 0
    for q in queries:
        if not q["gold"]:
            continue
        n_with_gold += 1
        survivors = {d["id"] for d in docs if q["filter"](d)}
        if not (q["gold"] & survivors):
            n_excluded += 1
    return n_excluded / n_with_gold if n_with_gold else 0.0

rate = filter_false_exclusion_rate(queries, docs)
print(f"filter_false_exclusion_rate = {rate:.2%}")
# filter_false_exclusion_rate = 50.00%

# Correct Recall@k keeps the original gold set as its denominator.
def standard_recall_at_k(queries, docs, k=10):
    recalls = []
    for q in queries:
        survivors = [d for d in docs if q["filter"](d)][:k]
        survivor_ids = {d["id"] for d in survivors}
        recalls.append(len(q["gold"] & survivor_ids) / len(q["gold"]))
    return sum(recalls) / len(recalls) if recalls else 0.0

print(f"standard recall@10 = {standard_recall_at_k(queries, docs):.2%}")
# standard recall@10 = 50.00%

# INVALID: rebuilding the gold set after filtering changes the question.
# It drops queries whose relevant documents did not survive, then scores 100%.
def invalid_recall_over_filtered_gold(queries, docs, k=10):
    recalls = []
    all_doc_ids = {d["id"] for d in docs}
    for q in queries:
        all_survivors = {d["id"] for d in docs if q["filter"](d)}
        filtered_gold = q["gold"] & all_doc_ids & all_survivors
        if not filtered_gold:
            continue
        top_k_ids = set(list(all_survivors)[:k])
        recalls.append(len(filtered_gold & top_k_ids) / len(filtered_gold))
    return sum(recalls) / len(recalls) if recalls else 0.0

invalid = invalid_recall_over_filtered_gold(queries, docs)
print(f"INVALID recall (filtered gold) = {invalid:.2%}")
# INVALID recall (filtered gold) = 100.00%

assert rate == 0.5
assert standard_recall_at_k(queries, docs) == 0.5
assert invalid == 1.0

Половина запросов теряют свой идеальный документ из-за применения фильтра, в результате чего показатель Recall@10 снижается до 50%. Этот показатель выявляет симптом проблемы, но не позволяет определить её причину. Уровень ложных исключений указывает на то, что предикат удалял два ответа ещё до того, как началась процедура поиска. Намеренно созданные некорректные данные эвалуатор показывают 100% только потому, что они исключают такие сбои из своего набора идеальных документов. Никакой модель не может восстановить документ, который был отфильтрован.

Указанный выше коэффициент 50% реализован в виде теста на единицу в сопутствующем репозитории: tests/test_filter_exclusion.py::test_50_percent_exclusion_rate. Ноутбук 04 запускается на SciFact с синтетическими метаданными, что позволяет наблюдать, как реальный фильтр полностью устраняет рекал; метрика рантайм (вместе с сопутствующими показателями точности/рекал) присутствует в evaluation/filter_exclusion.py.

Вспомогательная метрика: точность и воспроизводимость предиката

Когда фильтрация динамическая (например, LLM извлекает предикаты фильтрации из запроса), необходимо рассматривать механизм извлечения предикатов как модель классификации модель и оценивать его соответственно. Точность и воспроизводимость предикатов следует измерять на основе помеченного набора данных. (query, correct predicate) Пары. Уровень ошибок предиката не соответствует напрямую такому же показателю потерь в части воспроизведения retrieval; необходимо измерять, с какой частотой эти ошибки приводят к исключению эталонного документа. Как только жесткий фильтр исключает эталонный документ, никакое количество реранкинг уже не может помочь.

Мягкое усиление против жесткого фильтра

Этот показатель вынуждает принимать определённое архитектурное решение. Необходимо использовать строгие фильтры в тех случаях, когда критерий корректности имеет два чётких состояния — например, юрисдикция, границы ACL или статус документа «опубликовано» против «в черновике». В то же время следует применять более гибкие механизмы усиления значимости, когда оценка релевантности производится по шкале: предпочтения локали, дата обновления, версия. Без измерения коэффициента исключения ошибочный выбор остаётся практически незаметным.

Правило принятия решений, измеримое:

For each filter predicate F:
  hard_recall_F  = retrieval_recall@k with F as a hard filter
  soft_recall_F  = retrieval_recall@k with F as a +0.X rerank boost
  hard_precision = relevant_in_top_k / k under hard filter
  soft_precision = relevant_in_top_k / k under soft boost
  exclusion_rate = % of queries where the gold doc was filtered out (hard)

Use hard filter only if exclusion_rate < ε AND hard_precision >> soft_precision.
Otherwise prefer soft boost.

Необходимо выбрать значение ε с учётом рисков, связанных с ложным исключением, преимуществ повышения точности и размера выборки для оценки. В отдельной статье этой серии подробнее рассматривается именно этот компромисс.


Часть 6: Оценка генерации

Метрики Retrieval показывают, что система возможно смогла дать правильный ответ. Однако они не подтверждают, что она действительно это сделала. Метрики генерации заполняют этот пробел.

Верность оригиналу и основанность на реальных данных

верность RAGAS разбивает ответ на атомарные утверждения (короткие, самодостаточные фактические заявления), после чего проверяет каждое из них по отношению к полученному контексту с помощью LLM джадж:

faithfulness=Утверждения подтверждаются контекстомобщее количество заявок\text{faithfulness} = \frac{|\text{Утверждения подтверждаются контекстом}|}{|\text{общее количество заявок}|}

Процент поддерживаемых утверждений и является этим показателем. Такая структура полезнее любого отдельного числа, поскольку она позволяет определить, какие утверждения не имеют поддержки. Код, используемый в производстве, находится в ragas package — способ использования выглядит так:

from datasets import Dataset
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision

samples = Dataset.from_dict({
    "question": ["How many moons does Mars have?"],
    "answer":   ["Mars has two moons, Phobos and Deimos."],
    "contexts": [["Mars has two moons named Phobos and Deimos."]],
    "ground_truth": ["Mars has two moons."],
})

result = evaluate(samples, metrics=[faithfulness, answer_relevancy, context_precision])
print(result)

Ниже приведена та же самая итерация, развернутая с использованием детерминистичного заместителя джадж, чтобы было возможно увидеть её структуру от начала до конца.

def extract_claims(answer: str) -> list[str]:
    # Production: an LLM call that decomposes the answer.
    # Demo: split on sentence-final punctuation.
    return [c.strip() for c in answer.replace("?", ".").replace("!", ".").split(".") if c.strip()]

def verify_claim(claim: str, context: str) -> bool:
    # Production: an NLI (natural-language inference) model or LLM judge.
    # Demo: a deterministic stand-in so the example runs offline.
    entailed_pairs = {
        "Mars has two moons": True,
        "Phobos and Deimos orbit Mars": True,
        "Mars has a thick atmosphere": False,  # unsupported by context
        "Curiosity landed in 2012": True,
    }
    for k, v in entailed_pairs.items():
        if k.lower() in claim.lower() or claim.lower() in k.lower():
            return v
    words = [w.lower() for w in claim.split() if len(w) > 3]
    return all(w in context.lower() for w in words) if words else False

context = (
    "Mars has two moons, Phobos and Deimos. NASA's Curiosity rover "
    "landed on Mars in 2012."
)
answer = (
    "Mars has two moons. Phobos and Deimos orbit Mars. "
    "Mars has a thick atmosphere. Curiosity landed in 2012."
)

claims = extract_claims(answer)
verdicts = [(c, verify_claim(c, context)) for c in claims]
faithfulness = sum(1 for _, ok in verdicts if ok) / len(verdicts)
for c, ok in verdicts:
    print(f"  [{'✓' if ok else '✗'}] {c}")
print(f"faithfulness = {faithfulness:.2f}")
# faithfulness = 0.75   (one unsupported claim about the atmosphere)

Структура играет ключевую роль. В продакшене, verify_claim Это приводит к вызову механизма NLI модель или функции LLM. Остальные этапы процесса харнесс остаются без изменений: извлечение, проверка и агрегация.

Энд-то-энд извлечение утверждений и их верификация для генерируемых ответов SciFact: журнал 05; модуль: evaluation/faithfulness.py. В этом репозитории в том же цикле также запускается верификатор межсемейного типа HHEM, что позволяет определить, какая семья джадж совпадает с какой другой.

специально разработанная альтернатива LLM в роли джадж HHEM-2.1-Open (Hughes Галлюцинация Evaluation Модель, Vectara) — это классификатор, отточенный для обнаружения галлюцинация. В его модель-карте заданы чекпоинт, стандартные границы принятия решений, а также результаты тестирования на данных AggreFact и RAGTruth. Считайте эти данные доказательствами в формате модель-карт, а не гарантией качества всего вашего корпуса: откалибруйте пороговое значение с использованием локальных меток и сравните его с выбранным вами джадж перед началом деплой.

Оценка атомарных фактов

FActScore (Min и др., EMNLP 2023) разбивают процесс генерации длинных текстов на атомарные факты, извлекают соответствующие доказательства для каждого факта и присваивают каждому из них метку supported / not-supportedи сообщает поддерживаемую дробь:

FActScore=поддерживаемые атомарные фактыобщее количество атомарных фактов\text{FActScore} = \frac{|\text{поддерживаемые атомарные факты}|}{|\text{общее количество атомарных фактов}|}

Пример реализации: shmsw25/FActScore. Этот инструмент эффективно справляется с генерацией биографий, кратких резюме и другого текста длинной формы. Однако следует быть осторожным: чрезмерное количество повторяющихся тривиальных фактов может привести к завышению оценки, а атаки типа «MontageLie» (представление достоверных фактов в обманчивом порядке) способны сделать его недостоверным. VeriScore обрабатывает утверждения с необходимыми модификаторами; Ядро filter способствует предотвращению дополнения данных фактами.

Точность цитирования

Отслеживайте точность цитирования (то есть случаи, когда реально цитируемые спаны действительно подтверждают соответствующее утверждение) и воспроизводимость цитирования (то есть утверждения, которые должны быть процитированы, фактически присутствуют в списках цитат):

cite_precision=цитируемые спаны, подтверждающие данное утверждениецитируемый спаны,cite_recall=Утверждения, содержащие как минимум один опорный цитируемый спанутверждения, которые следует цитировать\text{cite\_precision} = \frac{|\text{цитируемые спаны, подтверждающие данное утверждение}|}{|\text{цитируемый спаны}|}, \quad \text{cite\_recall} = \frac{|\text{Утверждения, содержащие как минимум один опорный цитируемый спан}|}{|\text{утверждения, которые следует цитировать}|}

В рамках тракта TREC 2024 RAG определён протокол оценки поддержки, обеспечивающий воспроизводимость результатов. Упадхьяй и др. (SIGIR 2025) В отчёте указано, что GPT-4o соглашается с мнением человека джаджи в 56% случаев при ручной оценке с нуля, а этот показатель повышается до 72% после постобработки предсказаний LLM. Такой результат полезен в качестве инструмента для усиления производительности в определённых условиях, но не может заменить человеческую оценку в критически важных ситуациях. Речь идёт лишь об автоматизированной приближённой оценке. ALCE (Gao et al., EMNLP 2023) предлагает методы определения точности/полноты цитирования с использованием проверки, основанной на задачах NLI.

Правильность ответа, полнота, отказ

Проверка после генерации

Самые экономичные улучшения надежности обычно достигаются за счет детерминистичных постконтрольных операций, а не за счет более масштабных модели.


Часть 7: Оценка на основе онтологии RAG

Указанные выше стандартные метрики охватывают работу с открытыми корпусами RAG. Системам, основанным на онтологиях, требуются дополнительные показатели. Если ваш RAG выполняет поиск в структурированной онтологии, таксономии или графе знаний (товары в каталоге, условия в SNOMED, компоненты в BOM, методы защиты в MITRE ATT&CK), стандартные RAG метрики необходимы, но недостаточны. В таких случаях обязательно требуется измерять параметры самого слоя онтологии.

Точность связывания сущностей

Первая задача заключается в сопоставлении упоминания запроса с сущностью онтологии («Aspirin» → wikidata:Q18216”737” aircraft:Boeing_737).

Оценка с учётом иерархии

Простая точность рассматривает случай «прогнозирование Sedan, когда на самом деле это Hatchback» как эквивалентный ситуации «прогнозирование Sedan, когда на самом деле это Submarine». Однако эти ошибки не являются равноценными.

Уровень фильтрации ложных исключений (повтор, теперь критично)

В системах, основанных на онтологиях, жесткие фильтры зачастую определяются самой онтологией («выводить только документы с меткой категории X»). Метрика скорости исключения (определённая в Часть 5) Это превращается в основной сигнал корректности. Неверная предсказание категории может полностью свести на ноль показатель воспроизводимости; коэффициент исключения отвечает за приписывание этой потери фильтру.

Соответствие ограниченному генерированию

При необходимости соблюдения ортогональности вывода к определённой онтологии (когда каждое имя сущности в ответе должно быть допустимым элементом этой онтологии, а каждый предикат — выходить из замкнутого словаря), необходимо измерять:

Constrained decoding фреймворки (Основные положения, XGrammar, Рекомендации, OpenAI Structured Outputs) предназначены для обеспечения валидности схемы. JSONSchemaBench Сравнивает эффективность, уровень покрытия и качество между различными реализациями. Перезапустите его тесты, соответствующие вашим схемам, а также сервинг бэкенд, поскольку уровень покрытия и латентность напрямую зависят от обоих факторов.

Проверяемость

Для систем, основанных на онтологиях, в которых ответы подлежат проверке:


Часть 8: Оценка на уровне системы

Качество целостного ответа

LLM в роли джадж действительно обладает смещениями:

Практический алгоритм: выбирается джадж среди калибровочные данные, помеченных людьми, производится случайная перестановка порядка ответов, маскируются идентификаторы модель, а в рубрике указывается правило определения длины набора данных. Повторять тестирование следует только тогда, когда добавленные примеры существенно снижают уровень неопределённости. При оценках с высокими ставками необходимо сравнивать джаджи из разных семейств модель и анализировать расхождения с учётом лейблов, присвоенных людьми.

Руководство схемой Ризонинг для джаджи

Свободный формат вывода является одной из причин различий в результатах выполнения джадж. Два запуска с одним и тем же ответом могут по-разному интерпретировать критерии оценки, что приводит к разным баллам. Руководство схемой Ризонинг (SGR) Чтобы сделать эту структуру оценки явной, необходимо определить этапы анализа в виде схемы Pydantic, а затем обеспечить форматирование ограниченного вывода с помощью Outlines, XGrammar, vLLM structured outputs, или OpenAI. response_format Поэтому при каждом запуске возвращаются те же поля в том же порядке.

Для RAG эвал схема разбивает результат оценки на явные, поддающиеся аудиту поля, вместо того чтобы позволять модель сразу получать числовое значение:

from pydantic import BaseModel, Field
from typing import Literal

class FaithfulnessJudgment(BaseModel):
    extracted_claims: list[str] = Field(
        description="Atomic factual claims in the answer, one per item."
    )
    supported_claims: list[str] = Field(
        description="Subset of extracted_claims that are entailed by the context."
    )
    unsupported_claims: list[str] = Field(
        description="Subset that is NOT entailed by the context."
    )
    failure_mode: Literal[
        "none", "fabrication", "overgeneralization", "wrong_entity", "stale_fact"
    ]
    score: float = Field(ge=0.0, le=1.0)
    rationale: str

Благодаря структурированным полям оценка может быть восстановлена. len(supported) / len(extracted) Кроме того, отображается точный список утверждений, по которым джаджи пришли к разногласию. Инструмент Pydantic модель также позволяет видеть изменения в структуре классификации в виде различий кода. Благодаря ограничениям на формат вывода гарантируется строгое соблюдение структуры, но не обеспечивается беспристрастное суждение; поэтому методы случайной перестановки позиций, использование джаджи из разных семейств и участие людей калибровка по-прежнему применяются.

Этот подход эффективен для любых систем оценки, основанных на рубриках джадж, а не только для оценки степени соответствия исходному материалу. Параметры вроде взаимной предпочтительности, поддержки цитирования и корректности отклонений также получают преимущества от применения одинаковых правил обработки.

G-Эвал / двойное сравнение / смещение по позиции / межсемейное джадж харнесс присутствует в журнал 07; модуль: evaluation/llm_judge.py. Сканирование бенчмаркmake benchmark в репозитории) подключает три устройства высшего уровня модели — gpt-5-mini, claude-haiku-4-5, gemini-2.5-flash — во вращающуюся парную схему типа A/B джадж, при которой каждый модель джаджи сравнивается с остальными двумя, что позволяет численно выявить самопредпочтения.

Латентность и затраты

Встроена возможность расчёта показателей p50/p95/p99 по этапам с детальным разбивкой по стадиям. журнал 08 а также компонент выполнения в evaluation/latency.py; Отчёт бенчмарк объединяет латентность и показатели точности в одну матрицу, которую можно перезапустить для повторного анализа. make benchmark.

Тестирование A/B


Часть 9: Создание набора тестирования

Качество метрики полностью определяется набором тестовых данных, на которых она проверяется. Если ваш золотой набор включает три интента, а реальный трафик содержит спаны двенадцать, то метрика Recall@10 будет учитывать только первые три интента. Что ещё хуже, тестовый набор, слишком хорошо подходящий для простых запросов вроде «Какова политика возврата средств компании?», может дать положительную оценку системе, которая терпит неудачу при обработке сложных случаев, таких как «Является ли возможным возврат средств за частичное отменение заказа в соответствии с Законом ЕС об цифровых услугах 2023 года, если счёт выставлен в евро и заказ исходит из Ирландии?». В результате общий балл повышается, хотя система по-прежнему не справляется с значительной частью реального трафика.

Та же проблема возникает и с эталонными данными. Если специалисты среднего уровня помечают очевидные документы, но упускают менее распространённые, но релевантные варианты, метрика Recall@k будет занижать оценку поискового агента, который фактически их нашёл. Оптимизация проводится с учётом меток, а не с учётом самой истины.

Сначала сформируйте набор тестов, основываясь на реальном распределении запросов и уровне сложности. Затем выберите метрики, отражающие целевые модели сбоев, и настройте систему с учётом этих факторов.

Генерация синтетических запросов

Для генерации вопросов на основе вашего корпуса данных используйте LLM:

RАГИ имеет встроенное распределение типов вопросов (ризонинг, условное, многоконтекстное). DataMorgana генерирует настраиваемые синтетические бенчмарки в разрезе категорий пользователей и запросов. Синтетические данные полезны для решения проблемы «холодного старта» и проведения тестов на полноту покрытия. Они не могут заменить реальные запросы пользователей.

Метод построения «золотого» датасет

Данные, отобранные вручную, служат основой для золотого набора.

  1. Примеры реальных запросов пользователей (или симулированных в период до запуска), сгруппированные по целям использования.
  2. Наёмные специалисты должны ответить на каждый вопрос и определить, в каком(ых) документе(ах) содержится ответ.
  3. Размер набора определяется на основе матрицы покрытия и диапазона уверенности, необходимого для принятия решений о выпуске; показатель покрытия имеет большее значение, чем количество обработанных запросов.
  4. Пересмотр набора данных проводится при наличии оснований, связанных с частотой выпусков, сигналами отклонения, рисками в конкретной области и возможностями аннотации.

Наборы данных для адверсарских тестов

Уровень покрытия и непрерывная оценка


Часть 10: Мониторинг в продакшене

Сборка эвал, которую вы выпускаете, отражает состояние системы на момент запуска. После этого нагрузка в продакшене начинает меняться.

Косвенная и прямая обратная связь

Обнаружение дрейфа

Теневая оценка и участие человека в процессе

Запустите кандидатскую версию системы параллельно с продакшн-версией, сравните результаты в оффлайне и не предоставляйте их пользователям. Такой подход позволяет выявить регрессии ещё до выпуска продукта. Это требует дополнительных затрат инференс, однако не влияет на клиентов.

Для ревизии с участием человека (HITL):

Минимальный набор гардрейл

Предупреждение по этим пунктам в порядке приоритета:

  1. Оценка точности/HHEM ниже установленного порога в роллингой выборке из продакшена.
  2. p95 латентность превышает заданные показатели SLO.
  3. Уровень ложных исключений выше установленного порога (определяется на основе выборки).
  4. Частота регенерации находится за пределами локально откалиброванной диапазонной зоны, учитывающей размер окна, объём трафика, сезонность и лимит ложных сигналов.
  5. Затраты/запрос превышают установленный бюджет.

Если сигнал тревоги генерируется без соответствующего кода или изменения модель, скорее всего, наблюдается дрифт параметров системы. Если же сигнал появляется после внесения изменений, это, вероятно, свидетельствует о регрессии. В любом случае вы получаете уведомление ещё до того, как поступят заявки на поддержку.


Ограничения и замечания


Что будет далее в этой серии

Это был индекс. Следующие материалы — планирование:


Список литературы

Фреймворки и бенчмарки

Retrieval и ранжирование

Генерация, верность оригиналу, джаджи

Дрейф и производственная среда

Вспомогательный код