EN UA RU
AI

Векторный поиск для e-commerce: почему ключевые слова не работают при масштабе и что строить вместо

A
Flexor Engineering
19 марта 2026
13 мин чтения

Мы перестраивали поиск с нуля на каталогах от 30 000 SKU до более двух миллионов — паттерн везде одинаковый: поиск по ключевым словам держится на малых объёмах, а потом рассыпается так, что сложно поставить диагноз. Запросы возвращают ноль результатов по товарам, которые точно есть. Синонимы не учитываются. Пользователь пишет "кроссовки для бега" и получает туфли, потому что слово "обувь" встречается в описании. Это не проблема конфигурации. Это фундаментальное ограничение инвертированного индекса — и векторный поиск здесь правильный инструмент, а не серебряная пуля, которую можно просто вставить не думая.

QUERY "running shoes" KEYWORD BRANCH BM25 Inverted index VECTOR BRANCH EMBED MODEL e5-large / ada-002 ANN INDEX HNSW · Qdrant MERGE / RRF Top-100 candidates RERANKER Cross-encoder + rules ↓ RESULTS HYBRID SEARCH PIPELINE · SIMPLIFIED

Гибридный пайплайн поиска: один и тот же запрос идёт двумя путями одновременно — ключевым (BM25/инвертированный индекс) и векторным (модель эмбеддингов → приближённый индекс ближайших соседей) — затем оба результата сливаются и проходят через reranker до того, как попасть к пользователю. Разделение происходит на этапе запроса; построение индекса — отдельный офлайн-процесс.

Что на самом деле делает BM25 и где ломается

BM25 — функция ранжирования под капотом Elasticsearch, OpenSearch и большинства Solr-установок — оценивает документы по частоте термина и обратной частоте документа. Термины, которые часто встречаются в документе и редко — в корпусе, получают высокий вес. Элегантный алгоритм с 30 годами производственного использования за плечами, и для многих задач это ровно то, что нужно.

Проблема проявляется при масштабе. Каталог из 50 000+ SKU накапливает длиннохвостовые запросы — нишевые названия, аббревиатуры брендов, разговорные термины, опечатки — которые индекс никогда не видел как буквальные строки. BM25 не может вывести, что "NB 574" и "New Balance 574" описывают одну и ту же кроссовку. Он не знает, что "зарядник" и "зарядный кабель" связаны. Это чисто лексический матчинг: если токен не в индексе, он ничего не добавляет к оценке.

Самые частые режимы отказа: страницы нулевых результатов по запросам, которые должны возвращать товары (слепота к синонимам); нерелевантные товары, потому что они делят один высокочастотный токен с запросом (несоответствие намерения); и результаты, которые технически корректны, но кладут самую релевантную позицию на третью страницу из-за различия терминологии. Все три убивают конверсию, и все три ухудшаются по мере роста каталога. Магазин с 5 000 товаров может залатать это агрессивными словарями синонимов. На 200 000 товарах этот подход становится неуправляемым.

Что на самом деле делает векторный поиск

Векторный поиск не матчит токены. Он конвертирует и запросы, и документы в плотные числовые векторы — обычно от 384 до 1536 измерений в зависимости от модели — и находит документы, чьи векторы близки к вектору запроса в этом многомерном пространстве. "Близко" — это косинусное сходство выше некоторого порога, или практически: top-K ближайших соседей.

Эмбеддинги берутся из языковой модели, обученной на огромных массивах текста, а это значит, что модель уже выучила: "кроссовки" и "беговая обувь" находятся рядом в пространстве векторов, "NB 574" отображается на бренд New Balance, а "зарядный кабель" связан с "USB-C зарядником". Это семантическое понимание достаётся практически бесплатно — без поддержки словарей синонимов.

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

Гибридный поиск: почему почти всегда нужны оба

Где чистый векторный поиск даёт сбой

Чистый векторный поиск имеет реальные слабые места в коммерции. Точный поиск — один из них. Если пользователь ищет конкретный артикул — "WD-40 300011" или "SKU-RF2291-BLK" — BM25 находит его тривиально. Векторная модель может не найти: она обучена на естественном языке, а не на каталожных кодах, и эмбеддинг SKU — по сути шум. Получаете ситуации, когда точный запрос возвращает семантически похожие, но неправильные результаты.

Редкие имена собственные — ещё одна большая дыра. Малоизвестные торговые марки, нишевые номера моделей, названия товаров, которые есть в вашем каталоге, но слабо представлены в обучающих данных модели — у всех них будут низкокачественные эмбеддинги. Модель не знает, что значит "Ridgid 18V", если видела это лишь несколько раз при предобучении.

Объединение BM25 и вектора через reciprocal rank fusion

Стандартный подход — запустить оба ретривера параллельно и смержить списки результатов. Reciprocal rank fusion (RRF) — стратегия слияния, к которой мы обращаемся первой: для каждого документа вычисляем оценку на основе его ранга в каждом списке — 1/(k + rank), где k обычно 60 — и суммируем. Устойчив к разнице в масштабе оценок BM25 и косинусного сходства, не требует подбора весов под каждый тип запроса, хорошо работает на широком диапазоне конфигураций каталогов. Мы сравнивали его с подходами линейной комбинации — RRF стабильно выходит вперёд или на одном уровне при гораздо меньшей чувствительности к параметрам.

Относительный вес и маршрутизация запросов

Одно улучшение, которое стоит реализовать: маршрутизируйте разные типы запросов на разные смеси. Запросы, похожие на точные идентификаторы (буквенно-цифровые со специфическими паттернами), уходят в режим с преобладанием BM25. Запросы на естественном языке — полные предложения, разговорные формулировки, вопросы — сильно смещаются в сторону вектора. Для этого не нужен сложный классификатор; несколько regex-правил и эвристики по длине запроса дают 80% результата. Остальное — настройка по реальным логам запросов.

RERANKING LAYER · DETAIL CANDIDATES CROSS-ENCODER BGE-Reranker / Cohere query ⊕ doc → relevance score ~80–120ms on CPU BIZ RULES × stock penalty × margin boost × promo flag × freshness window RESULTS

Reranker стоит между ретривалом и итоговым списком результатов. Cross-encoder видит полную пару запрос-документ, что медленнее, но гораздо точнее, чем косинусное сходство эмбеддингов само по себе. Бизнес-правила (маржа, остатки, промоакции) применяются как финальный множитель поверх семантической оценки.

Reranking: где релевантность встречается с бизнес-логикой

Ретривал — BM25 и ANN вместе — даёт набор кандидатов, обычно top-50 до 200 документов. Reranking переоценивает этот набор более затратной моделью и применяет сверху бизнес-правила. Это вычислительно реализуемо, потому что вы оцениваете десятки результатов, а не сканируете миллионы.

  • Cross-encoder rerankers — модели вроде BGE-Reranker или Cohere Rerank видят полную конкатенированную пару запрос-документ, а не отдельные эмбеддинги. Медленнее, но намного точнее: модель может рассуждать о связи конкретного запроса с конкретным текстом документа, а не только об усреднённой семантической близости.
  • Персонализационные сигналы — кликабельность, конверсия и оценки аффинитета из рекомендательной системы можно подмешать как мультипликативные или аддитивные факторы поверх семантической оценки.
  • Инвентарь и наличие — товары, которых нет в наличии, нужно убирать или опускать в хвост. Ноль остатков = почти нулевой ранг, вне зависимости от семантической релевантности. Мы видели магазины, которые направляли трафик с высоким намерением на OOS-товары, потому что релевантность игнорировала остатки.
  • Маржа и промо-флаги — если товар акционный или несёт более высокую маржу, чуть приподнять его — законное бизнес-правило. Держите это тонко, чтобы не перебивать настоящую релевантность, иначе подорвёте доверие.
  • Свежесть — новинки часто выигрывают от раннего буста трафика, но этот сигнал затухает по мере накопления данных кликов и покупок, и товар начинает ранжироваться по собственным заслугам.

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

Проблема холодного старта для новых товаров

У новых товаров нет данных кликов, истории покупок, отзывов. Для систем на основе коллаборативной фильтрации это известная проблема с известными решениями. Для поиска — другое: товар сразу появится в векторном поиске (с первого дня у него есть эмбеддинг), но у него нет поведенческого сигнала для буста в reranker. Свежие товары могут ранжироваться ниже устаревших, менее релевантных позиций просто потому, что те накопили данные кликов.

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

Обогащение каталога тоже важно. Товары с тонким контентом — только название и SKU, без описания — дают слабые эмбеддинги. Перед индексацией мы делаем лёгкий проход обогащения: подтягиваем доступные атрибуты из PIM, генерируем короткое описание, если его нет, добавляем хлебные крошки категорий и синонимы тегов. Одно это ощутимо улучшает recall по новым товарам. Качество контента в ваших данных о товарах прямо пропорционально качеству поисковых эмбеддингов.

Задержка и инфраструктура: между чем вы реально выбираете

ANN-индексы — графы Hierarchical Navigable Small World (HNSW) стали стандартом — обменивают recall на скорость. Точный поиск ближайших соседей по миллиону векторов слишком медленен для интерактивного поиска; HNSW даёт 95-99% recall при миллисекундных задержках, исследуя структуру графа вместо полного сканирования. Этот компромисс почти всегда оправдан при коммерческих масштабах.

По вариантам инфраструктуры: pgvector — разумный выбор для каталогов до ~500k векторов, когда хотите минимизировать операционную сложность и уже работаете с Postgres. До миллионов векторов и высокого QPS без read-реплик и агрессивной настройки индекса не дотянет. Qdrant и Weaviate — специализированные векторные базы данных с нормальными HNSW, фильтрацией по метаданным и горизонтальным масштабированием — их операционные накладные оправданы после ~200k товаров или ~100 запросов в секунду. Elasticsearch с dense_vector — хороший выбор, если вы уже работаете с ES и хотите избежать нового сервиса; реализация HNSW зрелая, гибридный путь BM25+вектор хорошо задокументирован. Хостинговые сервисы (Pinecone, Zilliz) снимают инфраструктурные заботы, но добавляют стоимость за вектор и за запрос, которая быстро набегает на больших каталогах.

Целевая задержка: результаты поиска менее 200 мс на p95, не более 400 мс на p99. Этот бюджет должен покрыть эмбеддинг запроса, оба ретривера, слияние и reranking. Если добавляете cross-encoder reranker, ожидайте 80-120 мс на CPU — стоит прогревать модель заранее и рассмотреть GPU-инференс для высоконагруженных магазинов.

Что строить первым: версия 80/20

Прежде чем трогать архитектуру индекса, исправьте слой понимания запросов. Уберите стоп-слова (кроме значимых: "без" в "белое платье без рукавов" важно). Исправьте очевидные опечатки — простой spell checker на основе редакционного расстояния ловит большинство из них. Разверните аббревиатуры и нормализуйте названия брендов. Определите намерение: это навигационный запрос ("магазин Nike"), продуктовый ("белые Nike Air Force 1 размер 43") или информационный ("как чистить замшу")? Маршрутизируйте их по-разному. Мы видели, как улучшения препроцессинга запросов в одиночку снижали долю нулевых результатов с 8% до менее 2% — без изменений в индексе.

Когда понимание запросов стабилизируется, добавьте векторный поиск как параллельный путь к существующему keyword-поиску. Не заменяйте BM25 — дополняйте. Измерьте NDCG и MRR на тестовой выборке запросов с известными хорошими результатами до и после. Отслеживайте конверсию на поисковый клик, а не просто CTR: CTR может расти при падении конверсии, если вы показываете нерелевантные результаты, которые выглядят релевантно. Проведите нормальный A/B-тест — holdout 10-20% трафика — прежде чем полностью переходить. Последнее, во что инвестировать, — reranker. Начните с RRF плюс базовые бизнес-правила. Cross-encoder добавляйте, когда базовый гибридный ретривал устоится и объём запросов достаточен (5 000+ поисков в день) для точного измерения прироста.

Типичные ошибки, которых стоит избежать

Дрейф эмбеддингов — недооценённый режим отказа. Ваша модель эмбеддингов — это снимок. Если вы индексировали каталог с помощью text-embedding-ada-002 в 2024-м, а запрашиваете через text-embedding-3-large в 2026-м — векторы несовместимы. Выберите модель и держитесь её, или стройте пайплайн для повторного эмбеддинга всего каталога при апгрейде. То же касается дообученных моделей: дообучите на доменных данных, переиндексируйте всё, потом деплойте. Не смешивайте.

Однояязычные модели на многоязычных каталогах создают больше проблем, чем команды ожидают. Модель, обученная преимущественно на английском, даёт плохие эмбеддинги для французских, немецких или арабских описаний товаров. Для мультиязычных магазинов используйте мультиязычные модели (multilingual-e5-large, LaBSE или paraphrase-multilingual-mpnet-base-v2) и тестируйте качество ретривала отдельно для каждого языка. Разрыв в качестве между языком, на котором модель интенсивно обучалась, и тем, на котором нет, может быть разительным.

Переинжиниринг индекса до исправления препроцессинга запросов — пожалуй, самая частая ошибка, которую мы видим. Команды неделями сравнивают Weaviate, Qdrant и Pinecone, пока их spell checker выдаёт мусор на входе. Индекс стоит после всей остальной логики — мусор на входе, мусор на выходе. Сначала исправьте вход, измерьте, где реально падает recall, и только потом инвестируйте в индексный слой, который решает именно эти провалы.

Share Post on X LinkedIn
Назад к блогу

Следующий шаг

Работаете над сложной commerce-системой?

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

Поговорим