Эмбеддинги и векторный поиск: как искать по смыслу

Обычный поиск ищет слова. Если в регламенте написано «срок гарантии», а сотрудник спросил «сколько действует гарантия», совпадений может не найтись. Векторный поиск решает эту задачу иначе — он сравнивает смысл.

ВЕКТОРЫПохожие по смыслу тексты оказываются рядомСовпадение слов при этом не обязательноФрагментыСрок гарантии на серию XСколько действует гарантияКак оформить возвратРежим работы складаПространство смысловПервые два фрагмента близки, хотя слова в них разные
Близкие по смыслу фрагменты оказываются рядом, даже когда слова в них разные

Что такое эмбеддинг

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

Дальше всё сводится к арифметике: вы превращаете вопрос в такой же набор чисел и ищете среди сохранённых фрагментов те, что ближе всего.

Чем это отличается от обычного поиска

Каждый способ хорош в своём.

Что ищемОбычный поискВекторный
Точное название или артикулЛучшеХуже
Формулировка своими словамиХужеЛучше
Опечатки и синонимыХужеЛучше
Понятность результатаВидно, что совпалоСовпадение не объяснить

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

Что решать при внедрении

**Размер фрагмента.** Слишком мелкие куски теряют контекст, слишком крупные размывают смысл и тянут лишнее в запрос. Абзац или два — разумное начало.

**Перекрытие.** Соседние фрагменты должны немного накладываться, иначе ответ, лежащий на границе, не найдётся.

**Что кладём в текст фрагмента.** Полезно добавлять заголовок раздела и название документа: без них фрагмент «не более 24 месяцев» бесполезен.

**Размерность вектора.** Больше не значит лучше.

РАЗМЕРЧем платит длинный векторТочность поискарастётОбъём хранилищарастётСкорость поискападаетСтоимость индексациирастётНа базе до сотни тысяч фрагментов разница в точности между размерностями обычно незаметна.
На базе до сотни тысяч фрагментов разница в точности между размерностями обычно незаметна

Где хранить

Вариантов три, и выбор зависит от объёма.

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

От сотни тысяч имеет смысл смотреть на расширения для вашей базы, которые умеют индексировать вектора.

Отдельное векторное хранилище оправдано на миллионах фрагментов или когда нужны специфические возможности вроде фильтрации по метаданным на больших объёмах.

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

Метаданные обязательны

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

Фильтрация по правам делается на этапе поиска. Надеяться, что модель сама не проговорится о содержимом закрытого документа, нельзя.

Как проверить качество

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

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

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

Совмещать обычный и векторный поиск проще, чем кажется. Оба запускаются параллельно, каждый возвращает свои десять результатов, дальше списки объединяются.

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

Такая схема заметно надёжнее любого из способов по отдельности. Артикул и точное название находит обычный поиск, вопрос своими словами — векторный, а спорные случаи разрешаются суммой.

Настраивать веса имеет смысл только после того, как схема заработала. Обычно достаточно равных.

Перезапрос и уточнение

Ещё один приём, который заметно поднимает качество, — переформулировать вопрос перед поиском.

Пользователь пишет коротко и с местоимениями: «а по нему какой срок?». Такой текст плохо ищется. Модель может превратить его в самостоятельный запрос с учётом предыдущих реплик — «срок гарантии на серию X», — и уже он отправляется в поиск.

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

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

Переиндексация

Вектора устаревают вместе с документами. Пересчёт должен запускаться при изменении документа, а не по расписанию раз в квартал.

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

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

Написать