Архитектура и продвинутые концепции
Три уровня поиска — от простого к сложному
Сервис ищет товары на трех уровнях:
id, barcode, code, art. Скорость: 1-2 мс. Нашел → сразу возвращает результат.id/code/art/barcode. Однако в стратегиях hybrid и parallel семантический поиск запускается параллельно с полнотекстовым всегда, а их результаты объединяются по весам.
- Пользователи используют сленг ("шурик", "макака", "холодильник самсунг")
- В названиях товаров нет точных совпадений со словами пользователей
- Вы хотите поднять точность поиска с 85% до 92-95%
- Если все товары ищутся по артикулу/штрихкоду — эмбеддинги не дадут выигрыша
Принципиальная схема поиска
┌─────────────────────────────────────────────────────────────┐
│ sku-search │
├─────────────────────────────────────────────────────────────┤
│ │
│ Запрос пользователя: "шурик бош" │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Маршрутизатор запросов │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │ │
│ │ │ Точный поиск │ │ N-gram/Fuzzy │ │ Векторный │ │ │
│ │ │ (ID/артикул) │ │ (текст) │ │ поиск │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ └─────┬──────┘ │ │
│ │ │ │ │ │ │
│ │ └─────────────────┼─────────────────┘ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ Слияние и ранжирование │ │
│ │ └─────────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Результат: "шуруповерт бош" (score: 0.94) │
└─────────────────────────────────────────────────────────────┘
Семантический поиск (эмбеддинги) — как это работает
[pro.search] и подготовленной ONNX-модели.
Простая аналогия: Представьте, что каждый товар и каждый поисковый запрос получают свой «отпечаток пальца» — набор из 1024 чисел (для модели bge-m3; e5-small дает 384 числа). Похожие по смыслу товары имеют похожие отпечатки. Сервис умеет находить «отпечатки», которые ближе всего к отпечатку запроса, за 5-10 мс.
Как это работает на практике
multilingual-e5-small) в формат ONNX с INT8-квантованием. Получаем файл model.onnx (30-40 МБ).Гибридный поиск — как объединяются результаты
Когда включены оба движка (полнотекстовый + семантический поиск), сервис запускает их параллельно и объединяет результаты с весами:
Итоговый score = normalize(текстовый_score) × weight_tantivy + normalize(семантический_score) × weight_embedding
Перед объединением скоры обоих источников нормализуются (`min_max`, `robust`, `z_score`, `softmax` или `none`). Метод задается в `pro.search.orchestrator.score_normalization` (default: `robust`).
Упрощенный пример (значения до нормализации):
Полнотекстовый поиск нашел товар с score = 0.70
Семантический поиск нашел тот же товар с score = 0.92
Веса: weight_tantivy = 0.6, weight_embedding = 0.4
Итоговый score ≈ 0.70 × 0.6 + 0.92 × 0.4 = 0.42 + 0.368 = 0.788
Рекомендуемые веса
| Режим | tantivy_weight | embedding_weight | Когда использовать |
|---|---|---|---|
| Преимущество полнотекстового поиска (баланс) | 0.6 | 0.4 | Большинство кейсов, рекомендуется по умолчанию |
| Равные права | 0.5 | 0.5 | Текст и семантика одинаково важны |
| Преимущество семантики | 0.4 | 0.6 | Сленг и семантика важнее точных кодов |
| Только полнотекстовый поиск | 1.0 | 0.0 | Эмбеддинги выключены |
| Только семантический поиск | 0.0 | 1.0 | Экспериментальный режим |
Как подготовить ONNX-модель с INT8-квантованием
# 1. Установка зависимостей (одна команда)
pip install optimum[exporters] onnxruntime transformers
# 2. Конвертация E5-small в ONNX с INT8-квантованием
optimum-cli export onnx --model intfloat/multilingual-e5-small --task feature-extraction --optimize O2 ./models/e5
optimum-cli onnxruntime quantize --onnx_model ./models/e5 -o ./models/e5-int8 --avx512
# Готово! В папке ./models/e5-int8 вы найдете model.onnx (30-40 МБ)
Доступные модели для конвертации
| Модель | Размер | Скорость инференса | Точность |
|---|---|---|---|
| multilingual-e5-small | 30-40 МБ | Быстро (10-30 мс) | Хорошая |
| multilingual-e5-base | ~280 МБ | Средне (20-60 мс) | Очень хорошая |
| multilingual-e5-large | ~560 МБ | Медленно (50-150 мс) | Отличная |
Векторное хранилище — что хранит и как ищет
Векторы товаров хранятся во встраиваемой векторной базе данных. Она не требует отдельного сервера — это просто файлы на диске.
- Хранит пары:
(ID товара, вектор) - Тип индекса задается параметром
pro.search.embedding.vector_index_type(default:"auto"). Рекомендуется оставить"auto"— хранилище само выберет подходящий индекс. При смене типа нужно выполнитьsku-cli embedding clearи переиндексировать товары, иначе останется старый индекс. - Поиск: 1-10 мс для 1 млн векторов
- Не требует держать все векторы в RAM — данные на диске
# Что хранится в векторном хранилище (упрощенно)
┌─────────────────────────────────────┐
│ id: "DETAL-001" │
│ vector: [0.123, -0.456, 0.789...] │ ← 1024 числа (bge-m3)
├─────────────────────────────────────┤
│ id: "DETAL-002" │
│ vector: [0.111, -0.444, 0.777...] │
└─────────────────────────────────────┘
Как эмбеддинги работают с дополнительными полями
Простой принцип: перед отправкой в модель вы склеиваете ВСЕ значимые поля в один «супер-текст». Модель сама «поймет», где бренд, где название, где артикул.
Пример супер-текста для эмбеддинга:
"Шуруповерт аккумуляторный Bosch GSR 12V-15 электроинструмент"
Что дает:
• Запрос "шурик" → найдет этот товар (сленг)
• Запрос "bosch gsr" → найдет (артикул)
• Запрос "аккумуляторный инструмент" → найдет (категория)
Настройка весов для каждого поля не нужна — модель сама научилась определять важность разных частей текста.
Скорость работы: честные цифры
| Операция | Время | Примечание |
|---|---|---|
| Точный поиск (ID/штрихкод) | 1-2 мс | Всегда работает быстро |
| N-gram / Fuzzy поиск | 3-10 мс | Основной режим |
| Эмбеддинг (вектор запроса) | 10-30 мс | С INT8-квантованием |
| Поиск в векторном хранилище (1 млн векторов) | 5-10 мс | Автоматически выбранный индекс |
| Гибридный поиск (текст + векторы) | 20-50 мс | Параллельный запуск |
| Индексация 10 000 товаров (с эмбеддингами) | ~2 минуты | Фоновая пакетная обработка |
Сравнение подходов (для принятия решений)
| Функция | Только полнотекстовый поиск | Текст + эмбеддинги |
|---|---|---|
| Точный поиск (ID/штрихкод) | ✓ | ✓ |
| Fuzzy / опечатки | ✓ | ✓ |
| N-gram (короткие запросы) | ✓ | ✓ |
| Семантика / сленг ("шурик" → "шуруповерт") | — | ✓ |
| Синонимы без ручного ввода | — | ✓ |
| Скорость поиска | 2-10 мс | 15-50 мс |
| Память (RAM) | 200-500 МБ | 500-1000 МБ |
| Размер дистрибутива | 10-20 МБ | 60-80 МБ |
- Основная логика стабильна
- Есть реальные запросы пользователей, которые текущий поиск не покрывает
- Вы готовы добавить «AI-усиление» как PRO-фичу
Частые вопросы по архитектуре и эмбеддингам
1. Нужно ли переиндексировать все товары при смене модели?
Да. У каждой модели свое «понимание» смысла — векторы будут разными. Если вы меняете модель, векторное хранилище нужно перезаполнить.
2. А если я хочу попробовать несколько моделей?
Сервис хранит векторы только в одной таблице на инстанс (задается через lancedb.path + collection_name). Переход на другую модель делается в четыре шага:
- Удалите старые векторы:
sku-cli embedding clear(или вручную удалите файл векторного хранилища). - В
config.tomlизменитеactive_backend,model_pathиvector_dimensionна новую модель. - Перезапустите сервис.
- Заново загрузите все товары — сервис пересчитает эмбеддинги для каждого добавленного товара. Старые векторы при этом будут перезаписаны.
3. Как эмбеддинги влияют на время индексации?
Добавляет ~20-30 мс на товар (вычисление вектора). Для массовой загрузки используйте пакетную обработку (batch-инференс) — это ускорит процесс в 5-10 раз.
4. Мой сервер слабый — потянет ли он эмбеддинги?
Минимальные требования для E5-small с INT8:
- RAM: 500 МБ свободной (модель 30 МБ + векторное хранилище)
- CPU: любой с поддержкой AVX2 (Intel Core i3 / AMD Ryzen 3 и выше)
- Диск: SSD (для векторного хранилища)
Если сервер очень слабый — используйте только полнотекстовый поиск (эмбеддинги отключите).