Справочник10 сентября 2026 г.·обновлено 10 сентября 2026 г.

Векторный поиск: зачем он нужен и когда не нужен

Эмбеддинги, нарезка документов, ANN-индексы и гибрид с полнотекстовым поиском — механика целиком. Отдельно: список задач, где вектор проигрывает обычному поиску по словам, и как это померить на своём корпусе.

Что это решает

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

Как устроено

Конвейер состоит из шести шагов, и качество результата определяется на первом, а не на последнем.

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

Эмбеддинг. Фрагмент отдаётся модели, которая возвращает вектор фиксированной размерности — массив чисел. Тексты, близкие по смыслу, дают близкие векторы. Размерность задаётся моделью и напрямую влияет на объём хранения и скорость поиска.

Хранение. Векторы кладутся рядом с метаданными. В PostgreSQL это расширение pgvector с типом vector; существуют и специализированные хранилища, но отдельная система оправдана далеко не всегда — держать эмбеддинги в той же базе, где лежат бизнес-таблицы, проще для фильтров и прав.

Метрика близости. Косинусное расстояние, скалярное произведение, евклидово. В pgvector им соответствуют операторы <=>, <#> и <->. Метрику выбирают под модель: если модель обучалась на косинусной близости и выдаёт нормализованные векторы, брать евклидово расстояние смысла нет.

Индекс. Полный перебор точен, но линеен по числу фрагментов. На больших корпусах ставят приближённый индекс: HNSW строит многослойный граф соседей (настраивается параметрами построения и шириной поиска при запросе), IVFFlat делит пространство на списки и обходит только ближайшие. Оба индекса приближённые — часть правильных соседей теряется в обмен на скорость, и эта потеря настраивается.

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

select id, source, chunk
from documents
where tenant_id = $1
order by embedding <=> $2
limit 10;

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

Полезные сценарии

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

Ограничения

Точный поиск вектор проигрывает всегда. Артикул, номер заказа, код ошибки, фамилия, название версии — здесь нужен лексический индекс. Эмбеддинг размывает редкие токены, и EPERM-1073 окажется рядом с EPERM-1075, потому что для модели это похожие строки.

Отрицания и количественные условия не работают. «Всё, кроме тарифа Базовый», «договоры, подписанные после сентября», «сколько клиентов из Ростова» — векторное расстояние не умеет ни отрицать, ни считать, ни фильтровать по атрибуту. Такие условия выносятся в обычный where, и если фильтр отсекает большую часть корпуса, приближённый индекс начинает возвращать мусор: он обходит соседей до фильтрации и может не набрать нужное число подходящих строк. Лечится частичными индексами по разрезу или полным перебором внутри узкой выборки.

Поиск возвращает результат всегда. Даже на вопрос, ответа на который в корпусе нет, придут десять ближайших фрагментов с приличными на вид расстояниями. Абсолютное значение расстояния плохо переносится между запросами, поэтому порог отсечения «нет ответа» подбирается эмпирически и всё равно течёт.

Качество упирается в нарезку сильнее, чем в модель. Фрагмент, разрезанный посреди таблицы или оторванный от заголовка раздела, не спасёт никакая переранжировка. Основная работа по улучшению поиска почти всегда лежит в подготовке текста, а не в подборе модели.

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

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

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

Как проверить результат

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

На этом наборе считается recall@k — доля вопросов, для которых нужный фрагмент попал в первые k результатов. Именно эта метрика ограничивает качество итогового ответа: то, что не нашлось, модель не процитирует.

Векторный поиск внедряют только после того, как полнотекстовый померили на том же наборе и он проиграл. Базовая линия — обычный поиск по словам средствами базы. Часто выясняется, что он даёт сопоставимый recall на порядок дешевле, а выигрывает не вектор сам по себе, а гибрид.

Смотреть надо на сами фрагменты, а не только на финальный ответ модели. Каждый запрос логируется вместе со списком найденного и расстояниями; десять минут чтения этих логов объясняют про качество поиска больше, чем любая сводная метрика. Регулярный источник сюрпризов — фрагменты, обрезанные посреди списка или таблицы.

Использование индекса проверяется планом запроса: explain analyze должен показать индексный доступ, а не полный перебор с сортировкой. Заодно замеряется задержка по верхним перцентилям под реальной нагрузкой — среднее время здесь бесполезно.

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

После любой правки нарезки, модели или параметров индекса золотой набор прогоняется заново. Без этого замера каждое изменение остаётся вопросом веры.

  • векторный поиск
  • эмбеддинги
  • rag
  • pgvector