Как я научил LLM попадать в 1С с первого раза

Как я научил LLM попадать в 1С с первого раза

Пятничный кошмар в 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 для финального выбора + порог уверенности для разделения автомата и человека. И обязательно считайте метрики на реальных заявках, а не на синтетике.