# Семантический поиск по сайту на эмбеддингах

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

## Чем семантический поиск отличается от полнотекстового

Обычный поиск по сайту ищет совпадение слов: запрос «как вернуть деньги» не найдёт статью «политика возврата средств», если в тексте нет точных слов из запроса. Семантический поиск сравнивает смысл, а не буквы — он превращает запрос и текст статей в числовые векторы (эмбеддинги) и находит ближайшие по смыслу совпадения, даже если слова разные.

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

## Как работают эмбеддинги и какую модель выбрать

Модель эмбеддингов превращает текст в вектор фиксированной длины — обычно от 384 до 1536 чисел. Тексты с похожим смыслом получают близкие векторы, а расстояние между векторами (косинусное сходство) показывает степень похожести.

| Модель | Размерность | Языки | Где запускать |
| --- | --- | --- | --- |
| all-MiniLM-L6-v2 | 384 | в основном английский | CPU, легко |
| multilingual-e5-base | 768 | множество, включая русский | CPU или GPU |
| text-embedding-3-small | 1536 | множество | только через API |

Для сайта с русским и украинским контентом подходит multilingual-e5-base — она запускается локально на VDS без API-ключей и обходится без затрат на внешний сервис.

## Установка окружения и генерация эмбеддингов

Установите библиотеку sentence-transformers и посчитайте векторы для всех статей базы знаний.

```
python3.11 -m venv /opt/search-env
source /opt/search-env/bin/activate
pip install sentence-transformers
```

```
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("intfloat/multilingual-e5-base")
texts = ["Как настроить резервное копирование", "Что делать при ошибке 500"]
vectors = model.encode(texts, normalize_embeddings=True)
print(vectors.shape)
```

Каждый вызов `encode` возвращает массив чисел для каждого текста. Нормализация векторов упрощает дальнейшее сравнение через скалярное произведение вместо полного косинусного расчёта.

## Где хранить векторы и искать ближайшие совпадения

Готовые векторы нужно где-то хранить и быстро искать среди них ближайшие к запросу пользователя. Есть два основных пути: добавить векторный тип прямо в существующую реляционную базу или поднять отдельную специализированную базу.

- Для проекта, который уже работает на PostgreSQL, проще всего подключить расширение — см. [pgvector в PostgreSQL](https://www.zevshost.net/ru/kb/pgvector-postgresql/): один SQL-запрос ищет ближайшие статьи без отдельного сервиса.
- Для крупных каталогов с миллионами документов стоит сравнить специализированные векторные базы — обзор дан в статье [сравнение векторных баз данных](https://www.zevshost.net/ru/kb/vektornye-bd-sravnenie/).
- Для готовой связки поиска и ответа языковой модели пригодится схема из статьи [RAG на VDS](https://www.zevshost.net/ru/kb/rag-sistema-nastroika/).

## Как встроить поиск в сайт

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

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

Порог обычно подбирают опытным путём: для multilingual-e5-base хорошим стартом служит косинусное сходство выше 0.75.

## Типичные ошибки при внедрении

Большинство проблем связаны не с самой моделью, а с организацией пайплайна вокруг неё:

- Разные модели для индексации и для поиска — векторы из разных моделей несравнимы, результат всегда бессмысленный.
- Отсутствие пересчёта эмбеддингов после редактирования статьи — поиск находит устаревшую версию текста.
- Слишком длинные тексты без разбивки на абзацы — модель усредняет смысл всей статьи и теряет детали.
- Игнорирование порога релевантности — без отсечки поиск всегда возвращает что-то, даже когда подходящего ответа нет.

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

---

Источник: https://www.zevshost.net/ru/kb/semanticheskiy-poisk-embeddings/
