Как проверить точность эмбеддингов для поиска в RAG на своих данных

Разбираем, как сравнить модели эмбеддингов на собственном корпусе: подготовить запросы и эталонные документы, измерить Recall@k и проверить, помогает ли новый вариант поиску.

Схема проверки эмбеддингов для RAG: запрос сопоставляется с документами корпуса в векторном поиске
Схема проверки эмбеддингов для RAG: запрос сопоставляется с документами корпуса в векторном поиске
Fırtına Haber.png | by Fırtına Haber | wikimedia_commons | CC BY-SA 4.0

Эмбеддинги для RAG стоит выбирать не по одному числу из карточки модели, а по тому, находит ли поиск нужные фрагменты именно в вашем корпусе. Проверить это можно на небольшом размеченном наборе запросов: для каждого запроса указать документы, которые должны попасть в выдачу, а затем сравнить модели по Recall@k и качеству ответа с извлечённым контекстом.

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

Что именно измеряет тест эмбеддингов для RAG

Модель эмбеддингов преобразует текст в вектор, чтобы система могла искать близкие по смыслу фрагменты. Сам по себе вектор не говорит, полезен ли результат для ответа. Это проверяется на связке «запрос — корпус — правила поиска».

Для каждого тестового запроса подготовьте список релевантных фрагментов или документов. Затем выполните поиск и проверьте, сколько из размеченных целей оказалось среди первых *k* результатов. Например, Recall@5 показывает долю релевантных материалов, найденных в первой пятёрке. Если у запроса два подходящих фрагмента, а поиск вернул один, Recall@5 для него равен 0,5.

Метрика не оценивает полноту и точность окончательного ответа генеративной модели. Она отвечает на более узкий вопрос: попала ли нужная информация в контекст, который система передаст LLM? Это полезное разделение: если правильного фрагмента нет в выдаче, проблему следует искать до генерации.

Соберите небольшой, но репрезентативный набор запросов

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

Для каждого запроса запишите:

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

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

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

Зафиксируйте корпус и одинаковые условия поиска

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

Запишите и остальные параметры:

  • способ построения векторов и размерность;
  • метрику расстояния — например, косинусное сходство или скалярное произведение;
  • значение *k* и настройки поискового индекса;
  • применяется ли отдельная обработка запросов и документов;
  • включены ли фильтры по дате, языку, доступу или типу документа.

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

Например, карточка `sentence-transformers/all-MiniLM-L6-v2` описывает модель, обученную для сопоставления коротких предложений, и указывает максимальную длину последовательности 256 токенов. Для длинных документов это не означает, что модель автоматически сохранит смысл текста целиком: требуется контролировать нарезку и усечение. Сверяйте параметры с актуальной карточкой модели и документацией, поскольку настройки и доступные варианты могут меняться.

Посчитайте Recall@k и проверьте выдачу вручную

Для каждого запроса найдите первые *k* результатов, сравните их с эталонным списком и посчитайте долю найденных релевантных фрагментов. Затем усредните значения по запросам. Рядом с общей метрикой сохраняйте результаты по группам: точные термины, вопросы естественным языком, редкие названия и запросы с фильтрами.

Упрощённая функция для расчёта выглядит так:

python
def recall_at_k(retrieved_ids, relevant_ids, k):
relevant = set(relevant_ids)
if not relevant:
return None
found = set(retrieved_ids[:k]) & relevant
return len(found) / len(relevant)

scores = [
recall_at_k(results[q], relevant[q], k=5)
for q in relevant
]
valid_scores = [score for score in scores if score is not None]
mean_recall = sum(valid_scores) / len(valid_scores)

Здесь `results[q]` — упорядоченный список идентификаторов, возвращённых поиском, а `relevant[q]` — эталонные идентификаторы. Если для запроса нет размеченных релевантных документов, лучше исключить его из метрики и выяснить, почему разметка отсутствует.

Не делайте вывод только по среднему числу. Откройте запросы, где модели расходятся: часто именно они показывают, что изменилось. Новая модель может улучшить поиск по перефразированным вопросам, но хуже находить точные коды, версии или имена. Смотрите на сам текст фрагментов, порядок результатов и то, достаточно ли найденного контекста для ответа.

Сравнивайте модели по одному изменению за раз

Сначала прогоните одинаковые запросы через текущую модель, затем — через кандидата. Для каждой версии пересоздайте векторы всего корпуса; сравнивать векторы разных моделей напрямую нельзя. Оставьте неизменными корпус, чанки, фильтры, метрику и *k*. Иначе будет невозможно понять, что вызвало сдвиг.

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

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

Отделите качество эмбеддингов от чанкинга и ранжирования

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

Если релевантный фрагмент есть среди первых результатов, но находится слишком низко, возможно, дело в ранжировании или поисковых параметрах. Сравните порядок выдачи и проверьте, меняется ли результат при увеличении *k*. Это диагностический шаг, а не основание постоянно расширять контекст: большое число слабосвязанных фрагментов может затруднить генерации поиск нужного доказательства.

Практический разбор ошибок удобно вести по категориям:

нужный текст отсутствует в индексе;

нужная информация разрезана между чанками;
3. подходящий фрагмент не попал в первые *k*;
4. похожий, но неверный документ ранжирован выше;
5. корректный контекст найден, но итоговый ответ его использует неправильно.

Только третья и четвёртая категории напрямую указывают на поисковую выдачу. Для последней нужен отдельный тест генерации с проверкой опоры ответа на источник.

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

Улучшение Recall@k — полезный сигнал, но не гарантия более точных ответов. Для финальной проверки используйте один и тот же набор вопросов и сравните ответы с контекстом от каждой модели. Проверяйте, содержит ли контекст необходимые факты, не добавляет ли система неподтверждённые сведения и может ли читатель проследить утверждения до документа-источника.

Сохраняйте версии промпта генерации, модели, параметров и формата контекста. Иначе изменение ответа может быть вызвано не поиском. Для небольшого набора полезна ручная проверка по заранее установленной шкале: достаточность контекста, корректность ответа, соответствие источникам. Автоматическая оценка LLM может ускорить сортировку примеров, но спорные и высокорисковые случаи стоит просматривать вручную.

У разных метрик разные задачи. Recall@k отвечает, удалось ли найти отмеченные материалы. Метрики ранжирования, такие как nDCG, учитывают позицию и градации релевантности; их можно включить, если для результатов размечена степень полезности. Не смешивайте эти оценки в одно число без объяснения.

Решите, оправдывает ли прирост замену

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

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

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

Источники