Часто задаваемые вопросы

Что делать, если поиск не работает?

Пошаговый чек-лист:

1
Проверьте, что сервис запущен
curl http://localhost:8080/health | jq .status — ожидаем "ok".
Через CLI: sku-cli health | jq .status
2
Проверьте, что в индексе есть данные
curl http://localhost:8080/health | jq .db_record_count — ожидаем > 0. Если 0 — загрузите данные через POST /add.
Через CLI: sku-cli health | jq .db_record_count
3
Проверьте API-ключ (если включен)
В config.toml: api_key = "" — если не пустой, нужен заголовок X-API-Key. Ошибка 401 → проверьте ключ и заголовки.
4
Проверьте rate limiting
В config.toml: rate_limit_per_minute = 0 — временно отключите для теста. Ошибка 429 → увеличьте лимит.

Поиск не находит то, что должен

Запрос без name возвращает пустой результат

Причина: require_name_for_fuzzy = true.

[search]
require_name_for_fuzzy = false   # ← измените на false

При false можно искать только по динамическим полям (поставщик, бренд, ТН ВЭД).

Поиск по динамическим полям (supplier, brand) не работает

Поиск по динамическим полям доступен только в PRO-редакции. В Free-сборке расширенные поля не регистрируются в схеме индекса и не могут быть включены ни через какую настройку.

Причина 1 (PRO): extended_search_enabled = false.

[search]
extended_search_enabled = true    # ← включите

Причина 2 (PRO): поле не описано в [[search.extended_fields]].

[[search.extended_fields]]
field = "supplier"   # ← добавьте поле
weight = 0.7
stemmed = true
fuzzy = true

Сервис находит только идеальные совпадения, ничего "похожего"

Причина: слишком высокий порог auto_match_threshold.

[search]
auto_match_threshold = 0.85      # ← попробуйте снизить до 0.75-0.80
relative_score_threshold = 0.55

Слишком высокий порог (0.95+) — находим только идеальные совпадения. Слишком низкий (0.5) — много мусора.

Короткие запросы ("перф", "цинк") ничего не находят

Причина: edge_ngram.enabled = false или слишком высокий min_token_length.

[search]
min_token_length = 2           # ← должно быть 2 или меньше

[search.edge_ngram]
enabled = true                 # ← включите N-gram поиск
min_gram = 3
max_gram = 6
Включение N-gram увеличивает размер индекса на 30-40%.

Опечатки не исправляются ("блот" не находит "болт")

Причина: fuzzy_max_distance = 0.

[index]
fuzzy_max_distance = 2         # ← разрешить до 2 опечаток

Расстояние 1 уже находит "блот" → "болт" (перестановка соседних букв). Расстояние 2 дополнительно находит "блоот" → "болт", но делает поиск в 1.5-2 раза медленнее.

Поиск в другом падеже не работает ("болтом" не находит "болт")

Причина: при fuzzy_max_distance = 1 сервис не успевает «согласовать» стеммированный корень с исходной формой в документе. Русский стеммер приводит «болтом» и «болт» к одному корню, но пост-фильтр ориентируется на расстояние Дамерау-Левенштейна (Damerau-Levenshtein), которое между этими формами равно 2.

[index]
fuzzy_max_distance = 2         # ← позволяет находить разные падежи и числа

Рекомендация:

  • Используйте 1, если основная задача — исправлять опечатки и искать по точным артикулам/штрихкодам.
  • Используйте 2, если пользователи часто вводят названия в разных падежах или числах ("болтом", "болты", "краской" → "краска"), а также при работе с кириллическими названиями, где встречаются опечатки в два символа: "краскаа" → "краска", "крска" → "краска", "телефоон" → "телефон".
  • Значения >2 не рекомендуются — значительно растет задержка (+50–100% latency) и количество ложных срабатываний.

Товар с 2 совпавшими словами из 3 не показывается

Причина: слишком высокий min_token_match_ratio.

[search]
min_token_match_ratio = 0.5    # ← 50% совпадений достаточно
# Если 0.7 — нужно 70% (3 из 3 или 2 из 3 не хватит)

Сервис падает при старте с ошибкой SchemaError

Ошибка: SchemaError("An index exists but the schema does not match.")

Причина: поисковый индекс был создан другой версией сервиса или с другой схемой полей.

Решение: удалите старый индекс и перезапустите сервис. Данные в индексе будут потеряны — перезагрузите их через POST /add.

# Остановите сервис
rm -rf ./data/index
# Перезапустите сервис — индекс создастся заново
В production не удаляйте индекс без бэкапа. Данные в ./data/index можно восстановить только из исходного источника.
Чтобы избежать ошибки после обновления исполняемого файла: используйте одну и ту же версию сервиса для создания и открытия индекса, или настройте автоматическое пересоздание индекса при несовпадении схемы.

PRO: почему не работает PRO-версия?

В /health показывает edition: "free" или license.active: false.

Возможные причины:

  1. Используется Free-версия вместо PRO. Редакция определяется поставляемым исполняемым файлом и не переключается через config.toml. Проверьте:

    curl http://localhost:8080/health | jq .edition
    # Ожидаем: "pro"
    
    # Или через CLI:
    sku-cli health | jq .edition
    

    Если "free" — вы запустили Free-версию вместо PRO.

  2. Нет файла лицензии. PRO требует валидный файл ./data/license.lic. Получите его через сервер активации или админ-панель.

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

PRO: Как активировать PRO-версию?

  1. Получите пин-код PRO-лицензии (после оплаты или от администратора).

  2. Откройте в браузере страницу активации:

    • в сервисе — корень /, если PRO-версия еще не активирована;
    • на сайте — /license/activate/pro.
  3. Введите полученный пин-код и нажмите кнопку активации.

  4. Сервис проверит пин-код и сам установит лицензию по пути license_path (обычно ./data/license.lic). Ручное скачивание или копирование файла не требуется.

  5. Проверьте статус:

    curl http://localhost:8080/health | jq .license.active
    # Ожидаем: true
    
    # Или через CLI:
    sku-cli health | jq .license.active
    
Пин-код привязывается к вашей установке (fingerprint). Если вы переустановите сервис на другой машине — понадобится новая активация или пин с достаточным лимитом активаций.
Если после активации license.active: false — проверьте, что папка data/ (параметр license_path в config.toml) доступна сервису на запись и примонтирована как постоянный volume в Docker.

PRO: Кросс-коды и аналоги не показываются

Проверьте настройки кросс-кодов:

[pro.cross_codes]
enabled = true                  # ← должно быть true
include_in_response = true      # ← должно быть true
max_group_size = 30
Кросс-коды используют поле group_id. Если в ваших данных оно не заполнено — заполните его и перезагрузите товары. Переиндексация только из-за включения флага enabled не требуется.

PRO: Иерархия (/hierarchy) не работает

Проверьте настройки плагина:

[pro.plugins.hierarchy]
enabled = true                  # ← должно быть true
default_max_depth = 5

Также проверьте, что у товаров заполнено поле parent_id.

PRO: Эмбеддинги (семантический поиск) не работают

Чек-лист для эмбеддингов:

  1. Включен PRO-режим:

    [pro]
    enabled = true
    
  2. Включен оркестратор:

    [pro.search.orchestrator]
    embedding_enabled = true
    
  3. Выбран активный бэкенд и указан путь к модели:

    [pro.search.embedding]
    active_backend = "bge_m3"
    
    [pro.search.embedding.bge_m3]
    enabled = true
    model_path = "./models/bge-m3-int8/model.onnx"
    vector_dimension = 1024
    
  4. Модель существует и валидна:

    ls -la ./models/bge-m3-int8/model.onnx
    # Должен быть файл размером ~343 МБ (INT8)
    
  5. Проверьте health-статус:

    curl http://localhost:8080/health | jq .embedding
    # Ожидаем: "enabled": true, "model_loaded": true
    
    # Или через CLI:
    sku-cli health | jq .embedding
    
  6. Проверьте, что векторы построены:

    curl http://localhost:8080/health | jq .embedding.vector_count
    # Должно совпадать с .db_record_count
    
    # Или через CLI:
    sku-cli health | jq .embedding.vector_count
    
Если model_loaded = false, но файл модели есть — проверьте логи сервиса: возможна несовместимость версии ONNX-модели или ошибка инициализации runtime.

Частые ошибки при загрузке данных

Ошибка: id missing or empty

Причина: поле id обязательно для каждого товара.

{
  "name": "Болт М16",
  "id": "1C-GUID-001"   // ← обязательно!
}

Ошибка: rate limit exceeded (429)

Причина: слишком много запросов с одного IP.

[security]
rate_limit_per_minute = 0      # ← временно отключите для загрузки
Для пакетной загрузки отключайте rate limiting, после загрузки возвращайте обратно.

Данные не сохраняются после перезапуска

Причина: неправильный путь к индексу или volume не примонтирован.

[index]
path = "./data/index"          # ← относительный путь
В Docker используйте named volume или bind-mount для ./data. В production используйте абсолютный путь: path = "/var/lib/sku/index".

Фоновая индексация эмбеддингов идет слишком долго

Причина: маленький batch_size или мало worker_count.

[pro.background]
worker_count = 2
batch_size = 500               # ← для e5-small можно увеличить
lance_write_buffer_size = 1000
Для bge-m3 оставьте batch_size = 100 — он медленнее, но точнее. Для e5-small можно использовать batch_size = 500.

Как работают синонимы?

Синонимы хранятся в файле data/synonyms.json и загружаются при старте.

# Добавить синоним
sku-cli synonyms add --word "цинк" --expansions "оцинкованный,zn,zinc"

# Посмотреть список
sku-cli synonyms list

# Удалить синоним
sku-cli synonyms remove --word "цинк"
После изменения синонимов через CLI перезагрузка не требуется — изменения применяются сразу.

Общие рекомендации по настройке

Если сервис ничего не находит — снижайте пороги:

[search]
auto_match_threshold = 0.7
min_token_match_ratio = 0.4
relative_score_threshold = 0.4

Если сервис находит много мусора — повышайте пороги:

[search]
auto_match_threshold = 0.92
min_token_match_ratio = 0.7
relative_score_threshold = 0.6

Для массовой загрузки (100 000+ товаров):

[security]
rate_limit_per_minute = 0
rate_limit_burst = 0

[server]
max_batch_size = 500

[search]

Как получить поддержку

Если не нашли ответа на свой вопрос:

  • Email: kadr78job@gmail.com
  • Telegram: @kadr78job
  • Документация: полный перечень эндпоинтов и параметров — в этом документе
Перед обращением подготовьте:
  • Версию сервиса: sku-cli version
  • Логи: из папки ./data/logs/
  • Пример запроса, который не работает
  • Конфиг (без sensitive данных)