Evaluating retrieval quality: recall@k and faithfulness
Separate retrieval from generation evaluation using recall@k, MRR, nDCG, context precision, and regression gates in CI.
Lesson content is currently available in Russian.
What you will learn
- →Собирать golden-набор «запрос → релевантные чанки» и поддерживать его при переиндексациях.
- →Считать и интерпретировать recall@k, MRR и nDCG и выбирать k под свой конвейер.
- →Разворачивать end-to-end оценку (faithfulness, answer relevancy, context precision) и превращать её в CI-гейт.
Два контура оценки: retrieval отдельно, генерация отдельно
Сквозная оценка «вопрос → финальный ответ» не говорит, ЧТО чинить: плохой ответ бывает и из-за retrieval (нужного чанка нет в контексте), и из-за генерации (чанк есть, модель его исказила или проигнорировала). Поэтому продакшен-оценка RAG всегда двухконтурная. Контур retrieval отвечает на вопрос «попали ли нужные фрагменты в top-k» и меряется классическими IR-метриками по golden-набору — быстро, дёшево, детерминированно, можно гонять на каждый коммит. Контур генерации отвечает на «правильно ли модель использовала найденное» и меряется LLM-judge-метриками — дороже и шумнее, гоняется реже.
Диагностическая таблица, которая экономит недели: retrieval хороший + ответ плохой → чинить промпт/grounding (урок 5); retrieval плохой + лексические запросы проваливаются → гибрид/BM25 (урок 2); retrieval плохой + запросы сформулированы иначе, чем документы → rewriting (урок 3); нужный чанк существует, но разрезан или без контекста → chunking (урок 1). Без раздельных метрик команда крутит промпт, когда сломан индекс, — самый дорогой анти-паттерн в RAG-разработке.
Золотое правило приоритета: сначала доведите retrieval-контур до стабильного уровня (recall@k на eval-наборе не растёт от дальнейших правок), и только потом вкладывайтесь в генерацию. Идеальный промпт не спасёт контекст, в котором нет ответа, — а хороший контекст прощает средний промпт.
Golden-набор и retrieval-метрики
Golden-набор для retrieval — это записи «запрос → id релевантных чанков». Источники, по убыванию ценности: реальные запросы из логов с ручной разметкой релевантности; вопросы, написанные экспертами домена по документам; синтетика — LLM генерирует вопросы к случайным чанкам (тогда исходный чанк автоматически становится релевантным). Синтетика дешёвая, но смещённая: вопросы «слишком похожи» на текст чанка, и метрики на ней систематически завышены на 10–20 пунктов относительно реальных запросов. Рабочий компромисс: 150–300 синтетических кейсов для быстрого старта и регрессий + 50–100 размеченных реальных для калибровки. Ragas TestsetGenerator умеет генерировать разнотипные вопросы (простые, многошаговые, сравнительные) прямо из вашей коллекции.
Метрики поверх набора:
- recall@k — доля golden-чанков, попавших в top-k. Главная метрика первой ступени: если ответа нет в top-50, никакой reranker его не вернёт.
- MRR (mean reciprocal rank) — 1/позиция первого релевантного, усреднённая по запросам. Чувствительна к «нашли, но на 37-м месте».
- nDCG@k — учитывает и позиции, и градации релевантности; нужна, когда релевантность не бинарная.
- precision@k / context precision — доля релевантного в top-k; это метрика чистоты контекста, за неё отвечает reranker.
k выбирается по архитектуре: recall@50…100 для первой ступени (кандидаты для reranker’а), recall@5…10 и precision@5 — для финального контекста. Важная гигиена: golden-набор версионируется вместе с параметрами индексации, потому что id чанков меняются при переиндексации — храните привязку к содержимому (хеш текста), а не только к id.
End-to-end: триада Ragas и её ловушки
Для контура генерации де-факто стандартом стала триада метрик (Ragas, TruLens):
- Faithfulness — подкреплён ли ответ контекстом (из прошлого урока).
- Answer relevancy — отвечает ли ответ на заданный вопрос.
- Context precision/recall — принёс ли retrieval то, что нужно, и без мусора.
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision
result = evaluate(
dataset, # вопрос, контекст, ответ, эталон
metrics=[faithfulness, answer_relevancy, context_precision],
)
print(result) # {faithfulness: 0.87, answer_relevancy: 0.93, ...}
Три ловушки интерпретации. Первая: judge-метрики шумные — разница 0.87 против 0.89 на 50 кейсах ничего не значит; сравнивайте на 150+ кейсах и смотрите на доверительные интервалы, а не на вторую цифру после запятой. Вторая: faithfulness 1.0 при context recall 0.4 — это «честно отвечаем по мусору»: система вежливо перевирает задачу, метрики по отдельности выглядят прилично. Смотрите триаду целиком. Третья: judge наследует свои bias (длинные ответы нравятся больше) — калибруйте его по 30–50 кейсам с человеческой разметкой и перепроверяйте после смены модели-судьи.
Отдельно про агентские конвейеры из урока 4: у них оценивается ещё и траектория — число хопов, доля отказов от ответа там, где ответ в базе есть (over-refusal), и наоборот. Логи трасс из observability-системы — прямой источник этих метрик.
Eval как CI-гейт: регрессии ловятся до деплоя
Ценность метрик раскрывается, когда они встроены в процесс изменений. Любая правка пайплайна — размер чанка, модель эмбеддингов, параметр alpha гибрида, промпт, версия reranker’а — проходит через один и тот же контур: прогон retrieval-метрик на golden-наборе (минуты, копейки) и, для изменений, затрагивающих генерацию, прогон триады на подвыборке (десятки минут, единицы долларов). Результат сравнивается с базовой линией: recall@10 упал больше чем на 2 пункта — сборка красная, изменение не едет в прод.
Это буквально тот же паттерн, что юнит-тесты в CI, с одной особенностью: метрики стохастические, поэтому гейт формулируется с допуском (например, «не хуже базовой линии минус 1 std на 200 кейсах»), а не как точное равенство. Для промптов и конфигов удобно держать eval-прогон в pull request checks; для смены модели эмбеддингов — отдельный ритуал с полной переиндексацией стейджинга и сравнением по всем срезам.
Вторая половина контура — продакшен-мониторинг: сэмплированный faithfulness на живом трафике, доля отказов, доля ответов с провалившейся верификацией цитат, латентность по перцентилям и разбивка метрик по типам запросов из роутера. Дрейф этих кривых — ранний сигнал деградации (обновили документацию → чанки устарели → recall пополз вниз) задолго до жалоб пользователей. Плюс постоянный приток провальных кейсов из мониторинга в golden-набор: каждый инцидент становится регрессионным тестом, и система накапливает иммунитет — тот же принцип «копилки выученных ошибок», что и в курсе по эвалам.
Practice
Eval-контур с базовой линией
Соберите golden-набор: 100+ синтетических кейсов через Ragas TestsetGenerator (или свой промпт генерации вопросов к чанкам) плюс 20 реальных размеченных запросов. Посчитайте recall@10, recall@50, MRR для своего конвейера. Затем сломайте один параметр (например, отключите BM25-компоненту) и покажите, что контур ловит регрессию.
Deliverable
Скрипт eval-прогона, таблица базовой линии (recall@10/50, MRR, по желанию faithfulness на подвыборке) и диф метрик после намеренной поломки с порогом, при котором сборка считалась бы красной.
Checkpoint
Ответы бота стали хуже после последнего релиза, но сквозная метрика «доля хороших ответов» упала всего на 2%. Опишите план диагностики: какие метрики смотреть, в каком порядке и как локализовать слой поломки.
- ✓ Предложено разделение контуров: retrieval-метрики (recall@k, MRR) на golden-наборе отдельно от генерации (faithfulness, answer relevancy).
- ✓ Названа диагностическая логика: retrieval хороший + ответ плохой → промпт/grounding; retrieval плохой → chunking/гибрид/rewriting по типу проваленных запросов.
- ✓ Упомянуты срезы по типам запросов и сравнение с версионированной базовой линией, а не абсолютные цифры одного прогона.
Check yourself
Sources