Пятничный кошмар в 17:48
Представьте: в пятницу вечером менеджер получает письмо — «нужно 200 метров ВВГ 3х2.5, не плоский». Открывает 1С, вбивает запрос и видит 47 позиций. Какой из них круглый, а какой плоский? Какой с оболочкой «ок-0.66», а какой по ТУ? Ошибёшься — и на стройке машину развернут.
На одну позицию уходит 4–7 минут. Пять позиций в заявке, двадцать заявок в день. К концу недели уже не в номенклатуру смотришь, а в стену.
Это классическая задача, на которой ломаются «универсальные» AI-агенты. Расскажу, как мы сопоставляем свободный текст клиента со справочником на 12 тысяч позиций.
Почему регулярки и Левенштейн сразу сдают
Первый порыв — написать парсер: вытащить марку, сечение, исполнение и через ILIKE найти кандидатов. На бумаге красиво, на реальных заявках — нет.
Клиенты пишут как хотят: «ВВГ-3*2,5», «провод ВВГнг 3 на 2.5», «ВВГ 3×2,5 не круглый». При этом «не плоский» у нас означает как раз круглый, потому что плоский — это ВВГ-П. Левенштейн путает 3х2.5 с 3х4 (разница в цене в два раза), а полнотекстовый поиск отдаёт те же 47 вариантов.
Двухступенчатая схема
Шаг 1. Векторное отсеивание
Прогоняем всю номенклатуру через эмбеддинг-модель, нормализуем строки (убираем лишние пробелы, приводим «х» и «×» к одному виду, разворачиваем сокращения) и складываем в pgvector. Запрос клиента проходит ту же обработку.
Ищем по косинусной близости, берём топ-15. Задача — просто отсеять 11 985 заведомо не тех позиций.
Шаг 2. LLM как дешифратор
Топ-15 отдаём GigaChat Pro с чётким промптом: «Клиент написал X. Вот 15 позиций. Выбери одну или верни null. Объясни выбор по атрибутам, а не по похожести строк».
Важно — жёсткий JSON Schema. Модель не может придумать новый SKU, только выбирает из списка. Если ничего не подошло — заявка идёт менеджеру.
Порог уверенности
Самое интересное было не с моделью, а с порогом confidence. Разметили 1200 реальных заявок и получили такую картину:
- 0.95+ → 98.4% точности (71% заявок)
- 0.80–0.94 → 91.2%
- 0.60–0.79 → 73%
- ниже 0.60 → 41.5%
Порог автоподтверждения поставили на 0.90. В итоге 76% позиций уходит автоматически, 20% — в полуавтомат, 4% — на ручной разбор.
Почему первый подход провалился
Сначала попробовали просто эмбеддинги и топ-1 по близости. На синтетике было 78%, в проде упало до 61%. Модель не различала, что сечение 2.5 и 4 — это два разных товара.
Пришлось явно прописать в промпте приоритеты: «Сечение, количество жил и исполнение должны совпадать буквально». После этого точность прыгнула до 94%.
Что получилось в цифрах
- Время на позицию: 4–7 минут → 15 секунд (при высоком confidence)
- Доля заявок за 24 часа: 64% → 97%
- Ошибок с «привезли не то»: 11 в месяц → 2
ROI окупился за два месяца.
Главный вывод
Одним LLM-вызовом на 12 тысяч позиций не обойдёшься — либо контекст не влезет, либо дорого. Один pgvector без LLM будет путать то, что путать нельзя.
Рабочая схема: эмбеддинги для грубой фильтрации + LLM для финального выбора + порог уверенности для разделения автомата и человека. И обязательно считайте метрики на реальных заявках, а не на синтетике.
