Гибридный поиск: почему одних векторов мало
Вы подняли RAG на векторной базе, всё работает на демо-вопросах, а потом пользователь спрашивает про конкретный код ошибки или модель устройства — и система выдаёт похожий, но не тот ответ. Это не баг эмбеддингов, а их врождённое ограничение: они хорошо ловят смысл и плохо — точные редкие строки. Разбираемся, почему так происходит и как гибридный поиск закрывает эту дыру, не отказываясь от плюсов семантики.
Содержание
Почему чистый вектор проваливает точные термины
Эмбеддинг-модель превращает текст в точку в многомерном пространстве так, чтобы близкие по смыслу фрагменты оказывались рядом. Это отличный механизм для запросов вида «как перезапустить сервис после падения» — модель понимает, что «упал», «завис», «не отвечает» это про одно и то же, даже если слова разные. Но у этого же механизма есть обратная сторона: он усредняет.
Название модели вроде RTX 4070 Ti Super, код ошибки ORA-01555, артикул SM-A536B или аббревиатура вроде MTBF — для эмбеддинг-модели это просто редкие токены, которые она видела в обучающих данных считанные разы. Модель не выучила для них устойчивого «якоря» в пространстве смыслов, потому что не набрала статистики. В результате вектор такого термина может оказаться ближе по косинусному сходству к фрагменту, который просто похож по общей теме, чем к фрагменту, где этот термин реально встречается.
Показательный пример: у вас в базе знаний два фрагмента.
- «При ошибке
ORA-01555(«snapshot too old») нужно увеличитьUNDO_RETENTIONи размер undo-табличного пространства». - «Если запрос падает со старым снапшотом данных, проверьте настройки отмены транзакций и время хранения undo-логов».
Семантически второй фрагмент местами даже ближе к общему смыслу вопроса «что делать при ошибке снапшота», потому что использует более частотные слова, которые модель видела миллионы раз и хорошо для них откалибровала пространство. А первый — с точным кодом ORA-01555, который пользователь буквально скопировал из лога — может проиграть по векторному сходству, если код ошибки редкий и модель не научилась плотно упаковывать его вокруг релевантного контекста.
Это системная проблема, а не недоработка конкретной модели: чем реже токен встречается в обучающей выборке, тем менее надёжна его позиция в векторном пространстве. Собственные названия, номера версий, коды ошибок, партномера, ФИО, аббревиатуры отраслевого жаргона — вся эта категория токенов страдает одинаково, независимо от того, какую модель эмбеддингов вы выбрали. Мы подробнее разбирали механику того, как эмбеддинг превращает смысл в координаты — там видно, откуда берётся это усреднение.
Важно: это не значит, что векторный поиск «плохой». Он отлично решает свою задачу — находить смысловые парафразы и синонимы там, где точное совпадение слов невозможно найти по определению. Проблема возникает только на узком, но частом на практике классе запросов — тех, где пользователь либо уже знает точный термин, либо скопировал его буквально.
Почему лексический поиск закрывает именно эту дыру
Лексический (term-based, ключевой) поиск — это классика информационного поиска: BM25, TF-IDF, инвертированный индекс. Он не пытается понять смысл запроса. Он ищет буквальные вхождения слов и учитывает их частоту в документе относительно частоты во всей коллекции.
Это ровно то, что нужно для точных терминов. Если код ORA-01555 встречается в документе, лексический поиск найдёт его гарантированно — не потому что «понял» контекст, а потому что это простое совпадение строки (с учётом токенизации и, возможно, стемминга). Чем реже термин встречается в коллекции документов, тем выше его вес по IDF (обратная частота документа) — то есть лексический поиск устроен ровно наоборот к слабому месту вектора: редкий термин лексика вознаграждает, а вектор — штрафует за недостаток обучающей статистики.
Обратная сторона лексического поиска — он не понимает синонимов и парафразов. Запрос «как ускорить холодный старт» не найдёт документ, где написано «оптимизация времени инициализации приложения», если слова не совпадают буквально (разве что вы вручную поддерживаете словарь синонимов). Здесь как раз выигрывает вектор.
Отсюда простой вывод: у двух методов ортогональные сильные стороны.
| Критерий | Векторный (семантический) поиск | Лексический (ключевой) поиск |
|---|---|---|
| Парафразы и синонимы | Находит хорошо | Не находит без словаря синонимов |
| Точные коды, номера моделей, редкие термины | Часто промахивается | Находит надёжно |
| Запрос на естественном языке без спецтерминов | Сильная сторона | Слабее, зависит от пересечения слов |
| Запрос-цитата из лога/документации | Слабая сторона | Сильная сторона |
| Чувствительность к редкости токена в обучении | Высокая (проблема) | Низкая (IDF даже помогает) |
| Учёт релевантности всего документа | Через приближение в пространстве | Через частоты терминов (BM25) |
Ни один из методов не покрывает слабости другого — они именно взаимно дополняющие, а не конкурирующие.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть ИИ на сервереПринцип гибридного поиска: два движка, один запрос
Идея гибридного поиска простая: не выбирать один метод, а запускать оба параллельно по одному и тому же запросу пользователя, а затем объединять и переранжировать результаты.
Типичный пайплайн выглядит так:
- Запрос пользователя одновременно идёт в векторный индекс (например Qdrant, pgvector, Milvus) и в лексический индекс (например Elasticsearch, OpenSearch, Typesense, или встроенный BM25/
tsvectorв PostgreSQL). - Каждый движок возвращает top-N кандидатов со своей внутренней оценкой релевантности — векторный даёт косинусное сходство (или dot product), лексический — BM25-score. Эти шкалы несопоставимы напрямую, поэтому их нельзя просто сложить.
- Результаты объединяются одним из способов слияния (об этом ниже) в единый ранжированный список.
- Опционально — финальный реранкинг: более тяжёлая модель (cross-encoder или reranker) пересматривает top-20-50 кандидатов и расставляет финальный порядок с учётом полного текста запроса и документа, а не только эмбеддингов. Мы отдельно разбирали, зачем нужны reranker-модели в RAG — гибридный поиск и реранкинг обычно идут в связке, но это разные слои: гибрид отвечает за то, чтобы нужный кандидат вообще попал в top-N, реранкер — за финальную сортировку внутри этого набора.
Самый распространённый способ объединения оценок разных шкал — Reciprocal Rank Fusion (RRF). Вместо того чтобы сравнивать несопоставимые score, RRF смотрит только на позицию (ранг) документа в каждом из списков и считает:
RRF_score(d) = Σ 1 / (k + rank_i(d))
где rank_i(d) — позиция документа d в результатах метода i (векторного или лексического), а k — константа-сглаживание (обычно берут 60). Документ, который высоко стоит хотя бы в одном из списков, получает бонус; документ, который присутствует в обоих списках сразу, получает суммарный бонус и обычно выходит в топ. Плюс RRF в том, что он не требует нормализации score между методами и работает «из коробки» без подбора весов.
Более простой (но менее надёжный) вариант — линейное взвешивание: нормализовать оба score в диапазон [0, 1] и сложить с весами alpha * vector_score + (1 - alpha) * lexical_score. Этот подход требует подбора alpha под свои данные и чувствителен к тому, как именно вы нормализуете разные шкалы — RRF в большинстве случаев проще и стабильнее в продакшене.
Практическая реализация зависит от стека:
- Elasticsearch / OpenSearch имеют встроенную поддержку гибридного поиска: dense-векторные поля (
dense_vector) плюс обычный BM25-запрос, объединяемые черезrank_featureили встроенный hybrid-query API (в OpenSearch — через search pipeline с нормализацией и combination). - PostgreSQL может обойтись без отдельного поискового движка: pgvector для векторов плюс встроенный полнотекстовый поиск на
tsvector/GIN-индексе, а слияние результатов делается на уровне приложения через RRF по двум SQL-запросам. - Qdrant с версии, поддерживающей sparse-векторы, умеет комбинировать dense- и sparse-представления (sparse-вектор — по сути тот же принцип, что и BM25, только выраженный в векторном формате) прямо внутри одного запроса.
- Typesense изначально спроектирован как гибрид «из коробки» — в одном запросе можно указать и текстовый поиск, и векторное поле, с встроенным слиянием.
Как понять, нужен ли вам гибридный поиск
Гибридный поиск — это дополнительная инфраструктура: ещё один индекс, ещё один этап слияния, ещё один параметр для тюнинга. Прежде чем его внедрять, стоит честно посмотреть на свои реальные запросы пользователей, а не на гипотетические.
Практический тест: возьмите выборку из 50-100 реальных запросов (из логов, если система уже работает, или составьте вручную, если запускаетесь с нуля) и оцените долю запросов, где есть хотя бы одно из:
- точное название продукта, модели, версии ПО;
- код ошибки, артикул, номер детали;
- редкая аббревиатура или специфичный отраслевой термин;
- прямая цитата из документации или лога, которую пользователь скопировал буквально.
Если такая доля заметная (в технической документации, каталогах товаров, базах знаний поддержки это часто половина запросов и больше) — гибридный поиск почти наверняка даст ощутимое улучшение полноты найденных релевантных документов (recall), и сложность внедрения окупится. Именно этот сценарий типичен, когда вы поднимаете RAG по своим документам на сервере для внутренней техподдержки или каталога — там точные термины идут потоком.
Если же запросы преимущественно на естественном разговорном языке без специфичных точных терминов (например, FAQ по общим темам, консультационный чат-бот без узкой номенклатуры) — разница между чистым вектором и гибридом может быть небольшой, и тратить ресурсы на второй индекс не обязательно. В этом случае лучше сначала протестировать на своих же логах, чем добавлять сложность заранее «на всякий случай».
Важный нюанс: оценивать нужно не абстрактную точность, а recall на реальных проблемных запросах — тех самых, которые чистый вектор уже проваливал. Если у вас нет истории таких провалов (например, продукт ещё не запущен), придётся либо экстраполировать по составу базы знаний (много ли в ней точных номеров и кодов), либо запустить A/B на минимальной выборке пользователей.
Что теряется и что выигрывается при добавлении гибрида
Внедрение гибридного поиска — это не бесплатный апгрейд, у него есть измеримая цена.
Цена:
- Два индекса вместо одного — двойные требования к хранилищу и к процессу обновления при изменении документов (нужно поддерживать консистентность между векторным и лексическим индексом).
- Дополнительная задержка — два запроса вместо одного, пусть и параллельных, плюс шаг слияния. На практике это обычно единицы-десятки миллисекунд сверху, но при жёстких SLA это стоит измерить, а не предполагать.
- Ещё один параметр для тюнинга — вес RRF-константы
kили веса линейного слияния нужно подбирать под свои данные, а не оставлять дефолт вслепую. - Операционная сложность — нужно мониторить оба индекса, оба должны быть в актуальном состоянии после обновления документов.
Выигрыш:
- Устойчивость к запросам с точными терминами — та самая проблема, из-за которой чистый вектор молча выдаёт неправильный, но похожий результат.
- Более предсказуемое поведение на edge-case запросах — лексический поиск даёт понятную, объяснимую причину, почему документ нашёлся (совпало слово), а не «черный ящик» векторного сходства.
- Меньше зависимости от конкретной эмбеддинг-модели — если модель плохо обучена на вашей узкой предметной области (медицина, право, специфичное железо), лексика частично компенсирует этот пробел без переобучения модели.
Здесь же стоит вспомнить и обратную грань темы: если у вас, наоборот, RAG выдаёт нерелевантные ответы даже с хорошим поиском, часто причина не в самом ретривале, а в том, как документы нарезаны на чанки — это отдельная и не менее частая проблема в чанкинге, которую гибридный поиск не решает.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть ИИ на сервереНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Гибридный поиск заменяет реранкер или дополняет его?
Дополняет. Гибридный поиск отвечает за то, чтобы нужный документ вообще попал в короткий список кандидатов (высокий recall), реранкер — за то, чтобы внутри этого списка кандидатов расставить порядок точнее (высокая precision на топ-позициях). В продакшен-системах эти два слоя обычно работают вместе: сначала гибридное извлечение top-20-50, потом реранкинг до top-5.
Можно ли обойтись без отдельного поискового движка вроде Elasticsearch, если у меня уже PostgreSQL с pgvector?
Да, для многих нагрузок достаточно tsvector со встроенным полнотекстовым поиском PostgreSQL плюс pgvector в той же базе — не нужна отдельная инфраструктура. Проигрыш в качестве ранжирования по сравнению с полноценным BM25-движком есть, но для умеренных объёмов (до нескольких миллионов документов) это часто разумный компромисс по сложности эксплуатации.
Как выбрать константу k в Reciprocal Rank Fusion?
Стандартное значение k=60 взято из оригинальной статьи про RRF и работает как разумный дефолт в большинстве случаев без необходимости тюнинга. Уменьшение k увеличивает вклад самых верхних позиций каждого списка, увеличение — сглаживает разницу между позициями. Если нет времени на эксперименты — оставьте 60 и проверяйте результат на реальных запросах, а не гоняйтесь за идеальным значением с самого начала.
Нужен ли гибридный поиск, если у меня всего пара сотен документов в базе знаний?
На таком объёме векторный поиск обычно и так справляется прилично, потому что кандидатов мало и даже неточное сходство редко уводит слишком далеко. Выигрыш от гибрида становится заметнее по мере роста коллекции документов и разнообразия точных терминов в них — тестируйте на своих данных, не переносите чужой опыт с других объёмов один в один.
Гибридный поиск работает только для текста, или его можно применить к другим модальностям?
Принцип параллельного запуска нескольких методов извлечения и слияния результатов переносится и на другие сценарии (например, метаданные-фильтры плюс векторный поиск по изображениям), но классическая пара «векторный + лексический» специфична именно для текстового поиска, где есть понятие точного термина как отдельной единицы.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →