КЛАСТЕР 05 · RAG РЕРАНКИНГ И ФИЛЬТРЫ ~18 МИНУТ
День 24 · из 35

Реранкинг
и фильтры

В Дне 23 ты построил базовый RAG и увидел: top-N по эмбеддингам — не значит «лучшие». Сегодня — про второй этап, где из найденного выбирается реально релевантное. И почему хорошая RAG-система — это не «поиск», а «воронка».

Суть урока

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

Почему одного эмбеддинг-поиска мало

Базовый RAG из Дня 23 устроен просто: текст запроса → вектор → ищем top-5 ближайших векторов в БД → суём в контекст. И ты увидел, что результат часто разочаровывает: в топ попадают «похожие, но не те», действительно нужное оказывается на 8-м месте.

Это не баг — это фундаментальное свойство эмбеддинг-поиска. Чтобы понять почему, надо вспомнить, как он работает.

Что делает эмбеддинг-модель и что она пропускает

Эмбеддинг — это вектор фиксированной длины (768, 1536, 3072 чисел), который пытается сжать весь смысл текста в одну точку. Это очень мощное сжатие. Но и очень лоссовое: что-то всегда теряется.

Конкретно, эмбеддинги хорошо ловят общий смысл и тему, но плохо различают:

  • Точные сущности. «Платёж не прошёл у клиента Acme» и «Платёж не прошёл у клиента Beta» имеют почти одинаковые эмбеддинги. Различие между Acme и Beta — критическое, но в векторах оно почти не видно.
  • Числовые значения. «Скидка 10%» и «скидка 50%» — похожие векторы. Эмбеддинг видит, что обе строки про скидки, а не их размер.
  • Отрицания. «Не используйте этот API в проде» и «Используйте этот API в проде» — векторы будут очень похожи. Слово «не» — это маленькое возмущение в пространстве 1500 измерений.
  • Логические отношения. «X является причиной Y» и «Y является причиной X» — модели сложно различить.

Поэтому даже когда top-50 «правильные по теме», внутри этих 50 нужный документ редко стоит первым. Хорошие документы перемешаны с «похоже выглядящими» неправильными.

Эмбеддинги — это инструмент recall (полнота): «среди всех документов найди те, что про эту тему». Но они плохо справляются с precision (точность): «из похожих по теме выбери именно правильный».

Это и есть формальная причина, почему RAG нужно две модели: одна для recall (эмбеддинги), вторая для precision (реранкер).

Воронка: recall → rerank

Серьёзный RAG-pipeline устроен как воронка. Каждый этап делает свою работу:

Двухступенчатый поиск · воронка
Документы всё, что у тебя есть 1M+ чанков
фильтры
Кандидаты подмножество по метаданным ~100K чанков
эмбеддинги
Top-N recall «похожие по теме» 50 чанков
реранкер
Top-K precision «реально подходящие» 5 чанков

Каждая ступень делает то, что плохо делает следующая:

Ступень 1 · Recall

Эмбеддинг-поиск

«Найди мне всё, что про это»

Берёт твою базу из 1M чанков и быстро (за миллисекунды) находит top-50, которые «похожи по теме» на запрос. Не точные, не отранжированные — просто множество кандидатов, среди которых наверняка есть правильные.

Цель этого этапа — не упустить. Лучше взять 50 кандидатов, из которых 80% мусор, чем 5, из которых нужное не попало.

скорость10-50ms на запрос
стоимость1 вызов эмбеддинг-API + поиск в БД
Ступень 2 · Rerank

Реранкер

«Из этих 50 выбери 5 действительно правильных»

Берёт top-50 от первой ступени и пропускает каждый через специальную модель-реранкер. Реранкер читает (запрос + один документ) вместе и выдаёт число — насколько они совпадают. Сортирует по этому числу, отбирает top-5.

Ключевое отличие от эмбеддингов: реранкер видит конкретные слова и связи запроса с документом. Он замечает, что «Acme» в запросе и «Beta» в документе — это разные сущности. Видит отрицания. Видит числа.

скорость50-500ms (зависит от N)
стоимость1 вызов API реранкера на каждые ~20 пар

Откуда берётся реранкер

На рынке есть готовые модели-реранкеры, которые ты подключаешь как сервис:

  • Cohere Rerank — один из самых популярных, есть API и опенсорсные версии. Вход: запрос + список документов. Выход: пересортированный список со скорами.
  • Voyage Rerank — конкурент Cohere, тоже API.
  • Jina Reranker — опенсорсный, можно запускать локально.
  • BGE Reranker — опенсорс от BAAI, бесплатный, неплохое качество.
  • LLM как реранкер — иногда можно использовать обычную LLM с промптом типа «оцени релевантность 1-10». Дороже, но иногда даёт лучшее качество, особенно на сложных доменных запросах.

Технически реранкер устроен как cross-encoder: он подаёт пару (запрос, документ) через одну модель и выдаёт скор. Эмбеддинг-модель — это bi-encoder: запрос и документ кодируются отдельно, потом меряется расстояние. Это разница объясняет, почему реранкер точнее, но медленнее.

i
Почему двух этапов хватает
Можно было бы добавить и третий, и четвёртый. На практике — после реранкера top-5 в большинстве случаев действительно хороший. Добавление новых этапов даёт прирост 2-5% при удвоении сложности. Двухступенчатый RAG — это оптимальная точка для большинства задач.

Фильтры по метаданным

Эмбеддинги и реранкеры — это семантический поиск. Он отлично ловит смысл, но плохо ограничивает по фактам: «только за последний месяц», «только этого автора», «только в этом проекте». Для таких ограничений нужны фильтры по метаданным.

Что такое метаданные в RAG

Когда ты индексировал документы (День 22), помимо самого текста и эмбеддинга у каждого чанка можно (и нужно) хранить метаданные — структурированные поля:

  • doc_type: статья / тикет / письмо / спецификация
  • author: кто создал документ
  • created_at: дата создания
  • tags: ручная разметка («billing», «onboarding», «api»)
  • project_id: к какому проекту относится
  • language: язык документа
  • access_level: кто имеет право видеть
  • любые другие поля, специфичные для твоего домена

Современные векторные БД (Pinecone, Weaviate, Qdrant, pgvector + jsonb) умеют делать гибридный запрос: «найди ближайшие векторы среди тех, у кого tags содержит «billing» и created_at > 2024-01-01».

Где фильтр в pipeline

Фильтр — это первая ступень воронки. До эмбеддинг-поиска. До реранкера. Это сильно меняет экономику системы:

Скорость

Сократил кандидатов

Из 1M чанков → 100K по фильтрам → эмбеддингам нужно сравнивать в 10 раз меньше. Реранкер обрабатывает 50 «хороших по теме», а не «50 случайных».

Качество

Убил неверные совпадения

В результатах не появятся документы из «не того» проекта или старее чем нужно. Реранкеру не приходится их отсеивать — их просто нет.

Безопасность

Контроль доступа

Фильтр по access_level = роль пользователя. Чужие документы физически не попадут в результаты, даже семантически релевантные.

Откуда брать значения фильтров

Главный практический вопрос: пользователь не пишет «покажи мне статьи 2024 года в проекте Acme». Он пишет «что у нас с Acme?». Откуда система знает, что нужно фильтровать?

Три типичных подхода:

Из контекста сессии

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

Например: пользователь зашёл в проект Acme → все RAG-запросы в этой сессии автоматически фильтруются по project_id = "acme". Это самый частый и надёжный подход.

Преимущество: работает 100% времени, без ошибок LLM, без зависимости от формулировки запроса.

Через LLM-извлечение из запроса

Перед RAG-поиском запускаешь отдельный маленький LLM-вызов: «извлеки из этого запроса фильтры». Запрос «что у нас по Acme за последний квартал?» → {project: "acme", date_from: "2024-Q4"}.

Этот промпт идёт быстро, на дешёвой модели, с structured output (День 2). Результат — JSON с фильтрами, которые применяются на этапе поиска.

Когда использовать: когда пользователь часто пишет запросы с явными фильтрами в тексте («за прошлую неделю», «от Ивана», «только баги»).

Через UI

Самый простой и часто недооценённый: дать пользователю явные фильтры в интерфейсе. Дропдауны «проект», «дата», «автор», «тип документа». Перед запросом он их выставляет, фильтры идут в RAG-pipeline.

Это паттерн «как в продвинутом поиске почты». Не очень модный для AI-чатов, но работает идеально — нет ошибок LLM, нет догадок.

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

Решения AI-инженера

Развилка 1. Сколько брать на каждом этапе

Два числа определяют твою воронку: N — сколько берём после эмбеддинг-поиска, K — сколько отдаём модели после реранкера.

  • N слишком малое (5-10) — реранкеру нечего ранжировать, нужный документ часто не попадает в выборку.
  • N слишком большое (200+) — реранкер становится медленным и дорогим, дополнительные кандидаты не несут пользы.
  • K слишком малое (1-2) — модели не хватает контекста, она хочет всё знать из одного-двух чанков.
  • K слишком большое (20+) — токены тают, контекст забит шумом, lost-in-the-middle.

Практическое правило: N=30-50, K=3-7. Подстраивается под задачу: для сложных вопросов с фактами — K=5-7, для простых — K=2-3.

Развилка 2. Нужен ли тебе реранкер

Реранкер — это дополнительная стоимость и latency. Когда он действительно нужен:

  • Размер базы — большой (10K+ чанков). На маленьких базах разница между recall и precision не критична.
  • Запросы пользователей — разнообразные и непредсказуемые. Если все запросы шаблонные — recall справится сам.
  • Качество ответов важнее цены и скорости. Для прода с реальными пользователями — почти всегда да.

Когда без реранкера обойтись: маленькая база (<5K чанков), узкая предметная область, требования к скорости больше требований к точности.

Концептуальные грабли

«Сначала запустим простой RAG, потом оптимизируем реранкером». На практике без реранкера качество настолько отличается, что «простой RAG» иногда невозможно пустить даже в бету. Лучше с первого дня закладывать двухступенчатую архитектуру.

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

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

Правило: всё, что можно точно зафильтровать (даты, проекты, теги) — фильтруй. Семантический поиск оставляй на то, что без него никак.

«Сначала проиндексирую тексты, метаданные потом добавлю». На практике добавлять метаданные потом — это переиндексировать всё заново: тысячи чанков, десятки минут. Дорого и больно.

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

«Везде использую N=30 K=5». В реальности разные запросы требуют разных воронок. «Покажи последний инцидент» — нужен 1 чанк, фильтр по дате. «Что мы знаем про Acme?» — нужно 10 чанков, разные стороны.

Продвинутый паттерн: классификация запроса перед поиском (маленьким LLM-вызовом), и на основе типа — выбор стратегии поиска. Это уже близко к agentic RAG.

Практика

Эксперимент 01 · Сравни recall с rerank вживую

Перплекcity или похожий продукт

Открой Perplexity, ChatGPT с web search, или похожий продукт. Задай вопрос на тему, в которой ты — эксперт. Внимательно посмотри на список найденных источников: какие из них действительно по делу, какие — «вообще про эту тему, но не отвечают на вопрос»? Часто 2 из 8 источников — правильные, 6 — рядом-стоящий шум. Это и есть «recall без хорошего реранкинга». Чувство этой проблемы — половина понимания темы.

Эксперимент 02 · Распиши метаданные для своего домена

На бумаге, 20 минут

Возьми реальный набор документов из своей работы (база знаний компании, твои заметки в Obsidian, корпоративная wiki). Распиши: какие метаданные у каждого документа есть или должны быть? Что бы помогало фильтровать перед семантическим поиском? Минимум 5 полей. Это и есть фундамент твоей будущей RAG-архитектуры.

Эксперимент 03 · Реранкер в Cohere playground

Прямо в браузере

Cohere предоставляет демо-страницу с реранкером (cohere.com / dashboard). Загрузи 10-15 коротких текстовых фрагментов и запрос. Посмотри, как они ранжируются. Затем — поменяй порядок текстов и снова запусти: ранжировка такая же. Это даёт ощущение, что реранкер работает стабильно и независимо от исходного порядка. Хорошее интуитивное понимание того, как он отличается от эмбеддинг-поиска.

Что в Дне 25
У нас есть воронка, находим хорошие чанки. Финальная проблема: модель всё равно может соврать, даже когда контекст правильный. Завтра — про цитаты, привязку ответа к источникам, борьбу с галлюцинациями. Финал кластера RAG.