Обучение на обратной связи

Сервис умеет учиться на ваших действиях. Не ML, а просто и понятно: вы отправляете обратную связь, сервис анализирует ее и предлагает улучшения.

Доступно только в PRO версии. Эндпоинты /admin/learn, /feedback и управление синонимами через API работают только в PRO-сборке. В Free-редакции /admin/learn вернет 404.
Текущий алгоритм анализирует действия accept и zero_result. Записи с reject сохраняются, но пока не используются для построения синонимов. При accept сервис учит, что искали одно, а выбрали другое, и предлагает синонимы; при zero_result — частые запросы, по которым ничего не нашлось.

Как это работает — по шагам

1
Оператор находит нужный товар или не находит ничего
В 1С после поиска сервис показал товары. Оператор либо выбирает подходящий и подтверждает (accept), либо сообщает, что ничего не нашлось (zero_result).
2
1С отправляет feedback в сервис
1С формирует запрос: что искали, какой товар выбрали, и действие оператора.
3
Сервис сохраняет этот случай в feedback.log
Каждая запись выглядит так:
{"timestamp":"2024-01-15T10:30:00Z","query_name":"болт м16 цинк","matched_1c_id_out":"1C-GUID-0001","product_name":"Болт М16х60 оцинкованный ГОСТ 7798-70","action":"accept"}
4
Администратор запускает анализ
Раз в неделю (или чаще) администратор выполняет команду:
# Через CLI:
sku-cli learn

# Или через API:
curl http://localhost:8080/admin/learn

Доступно только в PRO.

5
Сервис анализирует accept-записи и предлагает улучшения
Сервис ищет закономерности: если оператор искал слово "цинк", а в выбранном товаре встречается "оцинкованный", значит, это хорошая пара для синонима. Анализ строится по полю product_name, поэтому без него предложения не генерируются.
6
Администратор проверяет предложения
Сервис возвращает JSON с рекомендациями:
{
  "recommendations": [
    {
      "recommendation_type": "synonym",
      "title": "Синоним: цинк → оцинкованный",
      "description": "В 82% accept-запросов со словом 'цинк' выбран товар, содержащий 'оцинкованный'",
      "confidence": 0.82,
      "data": {
        "query_token": "цинк",
        "product_token": "оцинкованный"
      }
    },
    {
      "recommendation_type": "missing_product",
      "title": "Возможно отсутствующий товар: болт м16 din933",
      "description": "15 zero_result запросов за период",
      "confidence": 0.95,
      "data": {
        "query": "болт м16 din933",
        "count": 15
      }
    },
    {
      "recommendation_type": "check_product",
      "title": "Проверьте товар 1C-GUID-0001",
      "description": "Высокий reject rate (45%)",
      "confidence": 0.45,
      "data": {
        "id": "1C-GUID-0001",
        "reason": "Высокий reject rate (45%)"
      }
    },
    {
      "recommendation_type": "typo",
      "title": "Опечатка: перфоротар → перфоратор",
      "description": "Похоже на опечатку, найден канонический вариант",
      "confidence": 0.78,
      "data": {
        "query": "перфоротар",
        "canonical": "перфоратор"
      }
    }
  ],
  "processed_entries": 42
}
7
Администратор добавляет синонимы
Если предложение хорошее — добавляем его вручную через API:
# Через CLI:
sku-cli synonyms add --word "цинк" --expansions "оцинкованный,zn,zinc"

# Или через API:
curl -X POST http://localhost:8080/synonyms \
  -H "Content-Type: application/json" \
  -d '{"items":[{"word":"цинк","expansions":["оцинкованный","zn","zinc"]}]}'
8
Синоним начинает работать мгновенно
С этого момента все новые поиски "цинк" будут находить "оцинкованный". Перезапуск сервиса не нужен.

Как настроить 1С для отправки feedback

В 1С, после того как сервис вернул результаты поиска, добавьте кнопки "Выбрать этот товар" (accept) и "Ничего не нашлось" (zero_result). При нажатии вызывайте:

// Псевдокод 1С
Процедура ВыбранТовар(НайденныйТовар, ПоисковыйЗапрос)
    Данные = Новый Структура;
    Данные.Вставить("query_name", ПоисковыйЗапрос.Наименование);
    Данные.Вставить("matched_id_out", НайденныйТовар.Идентификатор);
    Данные.Вставить("product_name", НайденныйТовар.Наименование);
    Данные.Вставить("action", "accept");  // "accept" — выбрали этот товар

    // Отправка POST /feedback
    Ответ = ОтправитьHTTPЗапрос("/feedback", Данные);
КонецПроцедуры

Процедура НичегоНеНашлось(ПоисковыйЗапрос)
    Данные = Новый Структура;
    Данные.Вставить("query_name", ПоисковыйЗапрос.Наименование);
    Данные.Вставить("action", "zero_result");  // ничего не нашлось

    Ответ = ОтправитьHTTPЗапрос("/feedback", Данные);
КонецПроцедуры

Автоматизация обучения

Можно настроить cron для регулярного анализа:

# Запускать каждую неделю в понедельник в 9 утра
0 9 * * 1 cd /opt/sku-search && ./sku-cli learn >> /var/log/learn.log 2>&1

Полный цикл обучения на примере

  1. Оператор ищет "болт м16 цинк", сервис предлагает "Болт М16 черный"
  2. Оператор выбирает "Болт М16х60 оцинкованный ГОСТ 7798-70" и нажимает "Это тот товар", 1С отправляет feedback с action: "accept"
  3. В лог попадает запись: искали "цинк", выбрали товар с "оцинкованный"
  4. Администратор через неделю запускает sku-cli learn
  5. Сервис анализирует: в нескольких accept-запросах со словом "цинк" выбран товар, содержащий "оцинкованный"
  6. Сервис предлагает синоним: "цинк" → "оцинкованный"
  7. Администратор добавляет синоним через API
  8. Теперь "болт м16 цинк" находит "Болт М16х60 оцинкованный" с высоким score

Что происходит в feedback.log

Файл data/feedback.log накапливает все действия операторов. Пример содержимого:

{"timestamp":"2024-01-15T10:30:00.123Z","query_name":"болт м16 цинк","matched_1c_id_out":"1C-GUID-0001","product_name":"Болт М16х60 оцинкованный ГОСТ 7798-70","action":"accept"}
{"timestamp":"2024-01-15T10:32:15.456Z","query_name":"гайка нерж м16","matched_1c_id_out":"1C-GUID-0002","product_name":"Гайка М16 нержавеющая","action":"accept"}
{"timestamp":"2024-01-15T10:35:22.789Z","query_name":"шайба м8","matched_1c_id_out":"1C-GUID-0003","action":"zero_result"}

Вы можете анализировать его вручную:

# Сколько раз отклонили
grep '"action":"reject"' data/feedback.log | wc -l

# Сколько подтверждений
grep '"action":"accept"' data/feedback.log | wc -l

# Какие запросы чаще всего отклоняют
grep '"action":"reject"' data/feedback.log | jq -r '.query_name' | sort | uniq -c | sort -rn | head -10

Частые вопросы про обучение на фидбеке

1. Как часто нужно запускать sku-cli learn?

Зависит от объема данных. Если у вас 1000 новых feedback-записей в день — запускайте раз в неделю. Если 100 — раз в месяц. Сервис анализирует все записи с момента последнего запуска (он не хранит состояние, просто читает весь файл каждый раз).

2. Почему сервис предлагает синоним, который уже есть?

Алгоритм ищет пересечения токенов в query_name и названии выбранного товара (product_name). Если синоним уже есть в synonyms.json, он все равно может предложить его снова. Просто игнорируйте такие предложения.

3. Как удалить синоним, который добавили по ошибке?

Через API:

curl -X DELETE http://localhost:8080/synonyms/цинк
# или через CLI:
sku-cli synonyms remove --word цинк

Также можно отредактировать data/synonyms.json вручную и перезапустить сервис, но это не требуется.

4. Что означает confidence в предложениях?

Число от 0 до 1, которое показывает долю accept-запросов, содержащих query-токен, в которых выбран товар с product-токеном. 0.7 = в 70% таких accept-запросов выбран товар с предложенным токеном. Можете использовать как фильтр: добавлять только те, у кого confidence > 0.5. Внутренний порог генерации синонимов — 0.3.

5. А сервис сам добавит синоним, если я не проверю?

Нет. Сервис только предлагает. Решение всегда за администратором. Это сделано специально — чтобы вы случайно не испортили поиск автоматическими правками.

6. Почему после добавления синонима поиск не изменился?

Проверьте:

  • Синоним добавился (проверьте GET /health — там есть synonyms_count)
  • Ваш запрос содержит слово, которое совпадает с ключом синонима (регистр не важен, все приводится к нижнему)
  • Синоним не конфликтует со стоп-словом (стоп-слова фильтруются ДО применения синонимов)

7. Можно ли автоматизировать добавление синонимов?

Можно, но осторожно. Напишите скрипт, который раз в неделю:

  1. Вызывает GET /admin/learn
  2. Фильтрует предложения с confidence > 0.8
  3. Отправляет их через POST /synonyms

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

8. Что делать, если feedback.log слишком большой?

Сервис сам не ротирует этот файл. Добавьте в crontab:

# Каждую неделю архивировать и очищать
0 2 * * 0 mv /path/to/data/feedback.log /path/to/data/feedback.log.$(date +\%Y\%m\%d) && touch /path/to/data/feedback.log

9. Почему GET /admin/learn возвращает пустые предложения?

Причины:

  • Нет accept-записей с product_name в feedback.log (для синонимов)
  • Нет zero_result-записей (для missing_product)
  • Нет повторяющихся accept/reject по одному товару (для check_product)
  • Включен параметр enabled = false в конфиге (фидбек не пишется)
  • В запросах все токены короче learn_min_token_len (по умолчанию 3 символа). "М6", "шт", "уп" — игнорируются

10. Можно ли обучить сервис на "хороших" совпадениях (action: "accept")?

Да. Алгоритм синонимов как раз работает на accept-записях с заполненным product_name. Хорошие совпадения показывают, что искали одно, а выбрали другое — и это самый надежный источник для предложений синонимов.

11. Как проверить, что обучение реально помогло?

Возьмите 100 запросов, на которых раньше были ошибки. Запустите поиск до добавления синонимов и после. Сравните результаты. Эффект зависит от качества и полноты feedback.

12. Что делать, если операторы нажимают "неверно" случайно?

Алгоритм ищет повторяющиеся паттерны. Для синонима нужно минимум 2 accept-записи с одним query-токеном и confidence ≥ 0.3. Случайные нажатия не наберут статистику.

Продвинутая настройка обучения

В config.toml есть параметры:

[feedback]
enabled = true                      # Включить сбор фидбека
log_path = "./data/feedback.log"    # Путь к логу
learn_min_token_len = 3             # Игнорировать слова короче 3 букв
learn_max_suggestions = 20          # Максимум предложений за раз

Что изменить, если...

СитуацияРешение
Слишком много мусорных предложенийУвеличьте learn_min_token_len до 4-5
Сервис не видит короткие важные слова ("М6", "din")Уменьшите learn_min_token_len до 2
Приходит 100+ предложений, трудно читатьУменьшите learn_max_suggestions до 10
Нет предложений, хотя ошибки естьПроверьте, что длина токенов >= learn_min_token_len, и что в accept-записях передано product_name
Совет: Запускайте `sku-cli learn` раз в неделю и просматривайте предложения. Добавляйте синонимы, которые кажутся логичными. Через месяц точность поиска заметно вырастет.