Архитектура и продвинутые концепции

Для кого этот раздел: Для технических специалистов, разработчиков и всех, кто хочет понять, как сервис работает «под капотом». Здесь мы разбираем три уровня поиска, семантический поиск (PRO), гибридные стратегии и подготовку моделей.

Три уровня поиска — от простого к сложному

Сервис ищет товары на трех уровнях:

1
Точный поиск — по id, barcode, code, art. Скорость: 1-2 мс. Нашел → сразу возвращает результат.
2
N-gram + Fuzzy поиск — по названию и динамическим полям. N-gram находит по короткому началу: "перф" → "Перфоратор". Fuzzy находит с опечатками: "блот" → "болт". Скорость: 3-10 мс.
3
Семантический поиск (PRO only) — ищет по смыслу. "шурик" → "шуруповерт", "дрель-шуруповерт" → "шуруповерт". Скорость: 15-50 мс (с INT8-квантованием).
Уровни не являются строгим каскадом «предыдущий не дал результат → включаем следующий». Точный поиск действительно имеет ранний выход при совпадении по id/code/art/barcode. Однако в стратегиях hybrid и parallel семантический поиск запускается параллельно с полнотекстовым всегда, а их результаты объединяются по весам.
Когда эмбеддинги (семантический поиск) реально нужны?
  • Пользователи используют сленг ("шурик", "макака", "холодильник самсунг")
  • В названиях товаров нет точных совпадений со словами пользователей
  • Вы хотите поднять точность поиска с 85% до 92-95%
  • Если все товары ищутся по артикулу/штрихкоду — эмбеддинги не дадут выигрыша

Принципиальная схема поиска

┌─────────────────────────────────────────────────────────────┐
│                        sku-search                            │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Запрос пользователя: "шурик бош"                            │
│         │                                                    │
│         ▼                                                    │
│  ┌──────────────────────────────────────────────────────┐   │
│  │         Маршрутизатор запросов                        │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌────────────┐  │   │
│  │  │ Точный поиск │  │ N-gram/Fuzzy │  │ Векторный  │  │   │
│  │  │ (ID/артикул) │  │  (текст)     │  │  поиск     │  │   │
│  │  └──────┬───────┘  └──────┬───────┘  └─────┬──────┘  │   │
│  │         │                 │                 │         │   │
│  │         └─────────────────┼─────────────────┘         │   │
│  │                           ▼                           │   │
│  │              ┌─────────────────────┐                  │   │
│  │              │  Слияние и ранжирование               │   │
│  │              └─────────────────────┘                  │   │
│  └──────────────────────────────────────────────────────┘   │
│                           │                                  │
│                           ▼                                  │
│              Результат: "шуруповерт бош" (score: 0.94)       │
└─────────────────────────────────────────────────────────────┘

Семантический поиск (эмбеддинги) — как это работает

Доступно только в PRO версии! Требует включенных эмбеддингов в секции [pro.search] и подготовленной ONNX-модели.

Простая аналогия: Представьте, что каждый товар и каждый поисковый запрос получают свой «отпечаток пальца» — набор из 1024 чисел (для модели bge-m3; e5-small дает 384 числа). Похожие по смыслу товары имеют похожие отпечатки. Сервис умеет находить «отпечатки», которые ближе всего к отпечатку запроса, за 5-10 мс.

Как это работает на практике

1
Подготовка модели (один раз) — конвертируем модель (например, multilingual-e5-small) в формат ONNX с INT8-квантованием. Получаем файл model.onnx (30-40 МБ).
2
При добавлении товара — берем супер-текст (название + бренд + категория + артикул), ставим построение вектора в фоновую очередь и сразу возвращаем ответ. Фоновые воркеры асинхронно превращают текст в вектор и сохраняют его в векторное хранилище вместе с ID товара.
3
При поиске — пользовательский запрос превращаем в вектор, ищем в векторном хранилище ближайшие векторы, получаем ID товаров.
4
Объединение — результаты полнотекстового поиска (с весом) + результаты векторного поиска (с весом) → финальная сортировка.
Важно: Эмбеддинги не заменяют полнотекстовый поиск, а дополняют. Полнотекстовый поиск отвечает за точные совпадения (артикул, код). Эмбеддинги — за смысл (сленг, синонимы).

Гибридный поиск — как объединяются результаты

Когда включены оба движка (полнотекстовый + семантический поиск), сервис запускает их параллельно и объединяет результаты с весами:

Итоговый 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_weightembedding_weightКогда использовать
Преимущество полнотекстового поиска (баланс)0.60.4Большинство кейсов, рекомендуется по умолчанию
Равные права0.50.5Текст и семантика одинаково важны
Преимущество семантики0.40.6Сленг и семантика важнее точных кодов
Только полнотекстовый поиск1.00.0Эмбеддинги выключены
Только семантический поиск0.01.0Экспериментальный режим

Как подготовить ONNX-модель с INT8-квантованием

Это нужно сделать один раз на любом Linux-компьютере. Готовую модель вы кладете рядом с исполняемым файлом сервиса.
# 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-small30-40 МББыстро (10-30 мс)Хорошая
multilingual-e5-base~280 МБСредне (20-60 мс)Очень хорошая
multilingual-e5-large~560 МБМедленно (50-150 мс)Отличная
Рекомендация: начните с multilingual-e5-small — баланс скорости и качества для большинства задач.

Векторное хранилище — что хранит и как ищет

Векторы товаров хранятся во встраиваемой векторной базе данных. Она не требует отдельного сервера — это просто файлы на диске.

  • Хранит пары: (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 минутыФоновая пакетная обработка
Зависит от железа: Все цифры приблизительные и сильно зависят от мощности вашего сервера (CPU, RAM, диск), размера базы и сложности запроса. На слабом железе время может быть в 2-3 раза выше, на мощном — ниже.
Что это значит для пользователя: Даже самый сложный гибридный поиск занимает меньше времени, чем вы моргаете (100-150 мс). Пользователь не заметит разницы.

Сравнение подходов (для принятия решений)

ФункцияТолько полнотекстовый поискТекст + эмбеддинги
Точный поиск (ID/штрихкод)✓✓
Fuzzy / опечатки✓✓
N-gram (короткие запросы)✓✓
Семантика / сленг ("шурик" → "шуруповерт")—✓
Синонимы без ручного ввода—✓
Скорость поиска2-10 мс15-50 мс
Память (RAM)200-500 МБ500-1000 МБ
Размер дистрибутива10-20 МБ60-80 МБ
Вердикт: Начинайте с классического полнотекстового поиска. Эмбеддинги подключайте, когда:
  1. Основная логика стабильна
  2. Есть реальные запросы пользователей, которые текущий поиск не покрывает
  3. Вы готовы добавить «AI-усиление» как PRO-фичу

Частые вопросы по архитектуре и эмбеддингам

1. Нужно ли переиндексировать все товары при смене модели?

Да. У каждой модели свое «понимание» смысла — векторы будут разными. Если вы меняете модель, векторное хранилище нужно перезаполнить.

2. А если я хочу попробовать несколько моделей?

Сервис хранит векторы только в одной таблице на инстанс (задается через lancedb.path + collection_name). Переход на другую модель делается в четыре шага:

  1. Удалите старые векторы: sku-cli embedding clear (или вручную удалите файл векторного хранилища).
  2. В config.toml измените active_backend, model_path и vector_dimension на новую модель.
  3. Перезапустите сервис.
  4. Заново загрузите все товары — сервис пересчитает эмбеддинги для каждого добавленного товара. Старые векторы при этом будут перезаписаны.

3. Как эмбеддинги влияют на время индексации?

Добавляет ~20-30 мс на товар (вычисление вектора). Для массовой загрузки используйте пакетную обработку (batch-инференс) — это ускорит процесс в 5-10 раз.

4. Мой сервер слабый — потянет ли он эмбеддинги?

Минимальные требования для E5-small с INT8:

  • RAM: 500 МБ свободной (модель 30 МБ + векторное хранилище)
  • CPU: любой с поддержкой AVX2 (Intel Core i3 / AMD Ryzen 3 и выше)
  • Диск: SSD (для векторного хранилища)

Если сервер очень слабый — используйте только полнотекстовый поиск (эмбеддинги отключите).