Почему ресторану предлагают не то
Я прочитал ваш каталог, оба отправленных КП и код — подряд, глазами. Сделано и проверено на боевых данных. Здесь: что было сломано, что теперь работает иначе, и цифры пересобранного предложения. Без терминов.
✓Что уже сделано
Решение построено и проверено на ваших данных. Ниже — что изменилось в отправленном КП, если пересобрать его заново.
| Что | Было | Стало |
|---|---|---|
| Разделов в документе | 100 | 78, каждый — под слово, для которого товар есть в нужном виде |
| Соус наполи | рыбный, чесночный, сырный | 3 настоящих соуса для пиццы |
| Сыр фета | рикотта, моцарелла, чеддер | 4 феты НАК |
| Сыр горгонзола | те же три | MonBlu Горгонзола |
| Тесто для пиццы | тайские спринг-роллы, 26 блюд | не печатается; менеджер видит пробел |
| Расширение меню, 8 пицц | спринг-роллы во всех, рыбный соус в 6 | моцарелла блоками, горгонзола, пармезан, пицца-соус — по каждой пицце |
| Рыбный соус за 0 ₽ | 11 раз в документе | 0 раз |
| Пробелы | исчезали молча | 159 видны менеджеру с пояснением и вариантами «на решение» |
| «Сыр» в Маргарите | «нет в номенклатуре» — при двухстах сырах | «есть, на решение менеджера», варианты рядом |
| «Сливки кокосовые» в том‑яме | обрезались до «сливки» — шли молочные | слово доходит целым, находятся кокосовые |
| «Курица» в Цезаре | варёно‑копчёная грудка печаталась как курица | на ваше решение, с пометкой «копчёное» (ваше правило 07.09) |
| «Огурцы» в греческом салате | маринованные из банки печатались как огурцы | на ваше решение; под «огурцы маринованные» — печатаются |
| «Соус томатный» для пиццы | кетчуп первым | Краснодарский и два пицца‑соуса |
| «Сироп ванильный» | любой из 33 сиропов, хоть арбузный | только ванильный; нет вкуса — пробел |
| Моцарелла под пиццу | шарики по 100 г первыми — дешевле за пачку | блоки 2–3,6 кг: цена сравнивается за килограмм |
| Покрытие ilpatio.ru | 72 % | 40 % с родственными, 39 % точное — честные |
Сколько это стоит в работе
| Что | Сколько |
|---|---|
| Сборка одного КП на 242 блюда | около 2 секунд, обращений наружу — ноль |
| Раскладка всех 1633 блюд меню заново, целыми словами | 15 минут машинного времени на прогон, денег 0 |
| Разовый разбор каталога моделью (черновик) | 37 минут машинного времени, денег 0 |
| Подбор под один ингредиент | 0,4 мс |
| То же, если каталог вырастет вдесятеро | 4,1 мс — рост ровный |
Что проверено на живой базе
Чего я не сделал
101 раскладка блюд осталась сломанной. «Мякоть молодого бычка одним отрубом» по-прежнему сжимается до слова «мякоть» ещё до того, как дело доходит до товаров. Со стороны каталога это не лечится — это отдельная работа.
Отдельного экрана справочника во фронте нет. Пробелы, пояснения и позиции «на ваше решение» менеджер видит в подборе; чтобы править разбор через интерфейс и одним нажатием брать такую позицию в документ, экран надо дорисовать.
Модель может назвать не тот ингредиент. Пример из IL Патио: пицца «Аль Сальмоне» разложена как ролл — с рисом для суши и нори. Под таким блюдом окажется настоящий, но не тот продукт. Это видно в раскладке на карточке заведения и правится один раз для всех заведений с таким блюдом. Правило «ничего не печатать молча» такую ошибку не ловит — ловит человек.
Винная карта без раздела «Напитки» проходит как блюда. Состав у них пустой, в покрытии они не участвуют, но в числе блюд считаются: у IL Патио 22 из 242.
На прод не выкладывал. Всё в отдельной ветке, проверено на копии боевых данных. Старая система из кода удалена целиком: модули обрезания названий, поиск по кускам слов, разбор по первому слову, корпус чужих рецептов, колонки обрезков в базе.
→Как это устроено
Про каждый товар один раз записано, что это такое. Подбор идёт по этой записи, а не по обрезкам названий.
Сейчас система при каждой сборке КП заново гадает по названию. Предложение — перестать гадать: завести таблицу, где про каждый товар 1С один раз записано, чем он является.
Что в записи
Четыре поля. Все выбираются из готового списка, а не пишутся свободным текстом — это принципиально, объясню ниже.
| Поле | Значение | Зачем оно |
|---|---|---|
| что это | наггетсы | Сам продукт. Больше не обрезок названия |
| из чего | курица | Ставит наггетсы под курицу — ваше правило |
| в каком виде | полуфабрикат | Не даёт им попасть под «куриную грудку» |
| заменитель | нет | Отделяет настоящий продукт от аналога |
Почему из списка, а не своими словами
Я замерил это на ваших данных. Когда модель выбирает из готового списка — она отвечает одинаково в 93–100 % случаев. Когда придумывает формулировку сама — в 76 %, и «йогурт греческий» с «йогуртом греческим натуральным» уже не склеиваются в одну группу. Поэтому списки закрытые.
Почему исправления перестанут стираться
Запись привязана к коду товара в 1С, а не к его названию. Сейчас при каждой выгрузке поле ingredient_canonical пересчитывается заново и затирает всё — это видно в коде, sync.py, строка 72. Новая таблица к названию не привязана, и выгрузка её не трогает вообще.
Что именно исчезнет из КП
Не список заплаток — каждый дефект уходит по механизму.
| Что происходит сейчас | Что это убирает |
|---|---|
| Паста карри и пралине под «Пасту» | «что это» у них — паста карри и кондитерская паста |
| Фасоль под «Соль», печенье под «Печень» | Поиска по кускам слов больше нет вообще |
| Мякоть помидора под мякоть бычка | «из чего» — томат, а не говядина |
| Картофель фри под оливье, кранч-лук под свежий лук | «в каком виде» — полуфабрикат и сушёное |
| Фета, горгонзола, грюйер, бри не находятся | «что это» записано, обрезок названия не участвует |
| Чёрный перец под «перец» в Примавере, где нужен болгарский | Общее слово ничего не печатает само — решает менеджер |
| 35 складов и строк кросс-докинга лежат в каталоге как еда | «это не еда» — модель определила без единой ошибки |
| 8 ключей на сливочное масло, 20 на шоколад | Одна запись «сливочное масло», одна «тёмный шоколад» |
| «Сливки кокосовые» в раскладке обрезались до «сливки» | Слово блюда хранится целым; что оно значит, решает та же запись |
| Любой из 33 сиропов под «сироп ванильный» | Вкус — тоже продукт: свой узел, нет вкуса — пробел |
| Маринованные огурцы под «огурцы», грудка в/к под «курицу» | У каждого слова есть естественная форма продукта; всё другое — на решение менеджера |
| Кетчуп первым под «соус томатный» для пиццы | Кетчуп — свой продукт, а не частный случай томатного соуса |
| Шарики моцареллы по 100 г впереди блоков | Цена сравнивается за килограмм, а не за пачку |
Чего это не решает
Рыбный соус по 0 ₽ останется. Он лезет вперёд не из-за названий, а из-за правила «показывай самое дешёвое» — select.py, строка 141. Чинится отдельно, за час, и починить стоит в любом случае.
101 раскладка блюд останется сломанной. «Мякоть молодого бычка одним отрубом» сжата до слова «мякоть» ещё до того, как дело доходит до товаров. Со стороны каталога это поправить нельзя — на вход подаётся слово «мякоть». Это надо включить в работу отдельным куском.
Как решён вопрос про аналоги
Взято из вашей же спецификации, контрольный набор: «все моцареллы, включая «Продукт белково-жировой Моцарелла» с пометкой, что это аналог». Аналог входит в группу, всегда с пометкой в документе, и никогда не встаёт выше настоящего продукта.
Ниже — как я к этому пришёл: что сломано, почему списками это не чинится, что я замерил и какие ещё пути рассматривал (раздел 06 — все четыре, включая те, что отверг, и почему).
01Что происходит
Система сравнивает не продукты, а первое слово названия. Совпало первое слово — товар едет клиенту.
Три примера из документа, который уже ушёл в IL Патио.
Паста
Пенне Арабьята, Мясная лазанья, Паппарделле Ресторану нужна паста — макароны. предложили Бельгийское пралине из фундука и тайскую пасту карри. Оба названия начинаются со слова «Паста».Соль
Мясная лазанья, Спагетти болоньезе, ещё 12 блюд Ресторану нужна соль. предложили Три банки красной фасоли. Потому что «соль» — это кусочек слова «фасоль».Печень
Куриная печень Ресторану нужна куриная печень. предложили Печенье Савоярди для тирамису. «Печень» — кусочек слова «печенье».Мякоть
Мякоть молодого бычка одним отрубом Ресторану нужен кусок говядины. предложили Мякоть помидора Pomi. Одно слово — два совершенно разных продукта.Таких мест в одном КП — 47 разделов из 100. А в блоке, где мы сами предлагаем ресторану добавить блюда, все восемь пицц собраны на тайской рисовой бумаге для спринг-роллов вместо теста.
02Почему так вышло
Внутри системы нет никакой записи о том, что такое товар. Есть только его название из 1С, и от названия отрезаются первые одно-два слова. Этот обрезок и считается «продуктом».
И то же самое происходит со стороны блюда. «Куриная печень» превращается в слово «печень» — курица отброшена. «Филе-миньон из фермерской говядины» превращается в слово «филе» — говядина отброшена. «Зелёный салат с баклажанами» превращается в слово «салат» — и совпадает с салатом из водорослей Чука.
Система сравнивает обрезок с обрезком. Ни на одной стороне никто не знает, что такое продукт.
И это не чинится списками. Можно добавить слово «белая» в список прилагательных, можно запретить искать «соль» внутри «фасоли». Но каждая новая выгрузка из 1С приносит написания, которых в списках нет. А главное — правку некуда записать: при каждой загрузке из 1С все обрезки считаются заново, и любое исправление стирается.
03Что я нашёл нового
Три вещи, которых в спецификации нет.
Фета есть. И горгонзола есть.
В спецификации написано, что их нет в каталоге. Это не так — они есть, просто система их не видит.
Это хорошая новость: у вас шире ассортимент, чем показывает система. Ресторану, который просил фету, мы предложили рикотту и чеддер, хотя фета лежала на складе.
Товары с нулевой ценой лезут вперёд всех
Тайский рыбный соус попал в одно КП одиннадцать раз. Не потому, что больше нечего было предложить. А потому, что когда система не может выбрать, она берёт самое дешёвое — а у этого соуса в базе стоит цена 0,00 ₽. Ноль дешевле всего.
Таких товаров с нулевой ценой — 28. Каждый лезет наверх своей группы. Из-за этого же «Картофельные дольки со специями» стоят первыми в разделе «Картофель».
Это чинится отдельно и быстро, и стоит починить в любом случае — даже когда всё остальное заработает.
Нужные товары часто лежат рядом — их просто не находят
| Что просил ресторан | Что предложили | Что было на складе |
|---|---|---|
| Томатный соус для римской пиццы | Тайский рыбный соус | 6 соусов для пиццы: SICA, MUTTI, Pomi, LOLI'K, LUNITO |
| Картофельное пюре к телячьим щёчкам | Пюре гуавы, инжира и тыквы | DINNER SERVICE, Картофельное пюре |
| Кунжутное масло в соус барбекю | Сливочное и подсолнечное масло | REAL TANG, Кунжутное масло, 2 фасовки |
| Соус демиглас к ростбифу | Рыбный, чесночный, сырный | DINNER SERVICE, Соус Деми-Гласс |
| Кленовый сироп к ромовой бабе | Апельсин, арбуз, базилик | AGROBAR, Сироп «Клен» (Maple Syrup) |
| Грюйер и бри в сырную тарелку | Моцарелла для пиццы ×3 | Грюйер EKONIVA, бри President и Schonfeld |
| Тёмный шоколад в фондан | 3 позиции, две из них «без сахара» | 47 настоящих шоколадов |
04Как это делают там, где решено
Я посмотрел, как эту задачу решают крупные сети, оптовые каталоги, продуктовые базы и закупочные системы. У всех одно и то же в основе:
- Никто не вычисляет продукт из названия в момент запроса. Продукт определяют один раз и записывают. Название — это то, что читают при записи, а не сам ответ.
- У записи свой номер, не из системы-источника. Поэтому перезагрузка каталога из 1С ничего не ломает: связь живёт отдельно от строки.
- Продукты стоят деревом, а не списком. «Бурый рис» — это частный случай «риса». Ровно ваше правило: наггетс — это курица, а курица — не наггетс.
- Когда система не уверена — она спрашивает человека, а не угадывает. Уверена — применяет сама. Неуверена — показывает вопрос. Исправления человека возвращаются обратно и больше не теряются.
Это стандартная связка, ей десятки лет: GS1 в торговле, FoodEx2 и LanguaL в пищевых базах, Open Food Facts в открытом виде.
05Что я проверил на вашей модели
У вас есть свой сервер с языковой моделью — llm.salam0nn.ru. Я взял самые трудные названия из каталога и спросил её, что это за продукты. Реальных денег это не стоит, только машинное время.
Что получилось хорошо
Из 24 самых трудных названий модель верно опознала 22. Нашла фету под «Сыр расс.», горгонзолу под «Сыр с гол. плесенью», вернула рыбу в «Дорадо с/м непотрош.». Пралине назвала кондитерской пастой, а не итальянской. Все склады и кросс-докинг пометила «не еда» — без единой ошибки. Все заменители сыра пометила заменителями.
Что получилось плохо
Модель не знает, когда она не уверена: на всех 24 ответах поставила «уверен», включая те, где ошиблась. И если задать один и тот же вопрос дважды, ответы расходятся: во втором прогоне наггетсы стали «точной курицей», а японский удон — «точной пастой».
Главное измерение
159 названий случайно прошли через модель по два раза. Вот насколько ответы совпали:
| Что спрашивали | Совпало | Вывод |
|---|---|---|
| Это еда или не еда? | 100 % | Полностью надёжно |
| Это заменитель? | 98 % | Надёжно |
| Из чего сделано? | 93 % | Надёжно |
| Сырьё, полуфабрикат или консервы? | 88 % | Требует проверки человеком |
| Название продукта своими словами | 76 % | Так нельзя — ответы разъезжаются |
Разница простая: когда модель выбирает из готового списка — она надёжна. Когда придумывает формулировку сама — «йогурт греческий» и «йогурт греческий натуральный» уже не склеиваются в одно.
Отсюда главный вывод: модель должна выбирать из готового справочника, а её ответ — записываться и замораживаться, а не пересчитываться каждый раз.
06Четыре пути
1. Завести справочник продуктов
советуюОдин раз проходим каталог: у каждого товара записываем, что это за продукт — из готового списка, а не своими словами. Спорное подтверждает человек. Запись живёт в отдельной таблице, привязанной к коду 1С.
Дальше выгрузка из 1С может приходить сколько угодно раз — запись не тронется. Новые товары попадают в очередь на разбор и до разбора клиенту не показываются.
- Что даёт
- Ответ объясним словами. Исправление живёт вечно. Пробел виден. Проверяется обычными тестами.
- Разовая цена
- Проход модели по 1080 товарам — машинное время вашего сервера, денег не стоит. Плюс подтверждение человеком спорных позиций.
- Каждый день
- Разбирать только новые коды из 1С. Это десятки позиций, а не тысячи.
- Где ломается
- У справочника должен быть хозяин. Без него он через год зарастёт.
2. Спрашивать модель в момент сборки КП
не советуюНичего не строим. Каждый раз при сборке КП отдаём модели весь каталог и спрашиваем, что подходит. Замерил: 26–63 секунды на одно КП.
Почему не советую. Я прогнал один и тот же вопрос дважды — ответы разошлись. Наггетсы стали «точной курицей», удон — «точной пастой». Именно та граница, которая защищает клиента от подмены, и поехала.
И главное: исправление некуда записать. Это тот же корень, что сейчас — ответ считается заново каждый раз и нигде не хранится. Меняется только тот, кто считает.
- Где ломается
- Ответы разъезжаются между прогонами. Модель недоступна — КП собрать нельзя. Объяснение каждый раз новое.
3. Описывать товар свойствами
хорошая добавка к первомуУ товара не одна пометка, а несколько: из чего сделан (курица, молоко, томат), в каком виде (сырьё, полуфабрикат, консервы, заморозка), заменитель или нет.
Тогда ваше правило записывается прямо: наггетсы — это «курица» + «полуфабрикат». Под ингредиент «курица» они годятся, под «куриную грудку» — нет. И объяснение выходит само: «предложили, потому что сделано из курицы, но это полуфабрикат».
Это же убрало бы картофель фри под оливье и сушёный кранч-лук под свежий лук.
- Где ломается
- Замерил: свойство «в каком виде» модель ставит верно только в 88 % случаев — бекон с пометкой ЗАМ назвала охлаждённым. Это свойство придётся брать из пометок в названии правилами, а не у модели.
4. Копить подтверждения менеджеров
не советуюНичего не размечаем заранее. Менеджер, собирая КП, отмечает: этот товар подходит, этот нет. Со временем накапливается.
Почему не советую. Пар «ингредиент — товар» больше миллиона. Первые месяцы КП не улучшаются вообще, а менеджер вместо работы занимается разметкой. И новый товар не наследует ничего — всё заново.
Показываю его потому, что это единственный путь совсем без модели, и вы вправе его увидеть.
07Что придётся тронуть сверх ожидаемого
Спецификация выносит качество раскладок блюд за границы задачи — «кроме случаев, когда исследователь докажет, что без этого задачу решить нельзя». Доказательство нашлось.
Со стороны товара это не лечится: системе на вход подаётся слово «мякоть», а не «говядина». Таких разборов 101 — немного, но они попадают в дорогие блюда.
Что предлагаю включить в работу: эти 101 разбор и восстановление уточнений, которые теряются («куриная печень» должна оставаться куриной). Всё остальное про раскладки — не трогаем.
08Что мне нужно от вас
Два вопроса вы уже решили, и я их записал:
Остались два. Оба про то, что считать правильным ответом — без них я не смогу сказать, верно работает вариант или нет.
1. «Белково-жировой продукт "Моцарелла"» — это моцарелла?
По закону это не сыр, а заменитель на растительном жире. Таких позиций в каталоге около сорока, и они дешевле настоящих. Предлагать их ресторану вместе с моцареллой, помечая «аналог»? Или называть своим именем отдельной позицией и не подставлять под моцареллу?
2. Кто разбирает каталог и сколько это допустимо?
Тысяча товаров разбирается человеком примерно за неделю — один раз. Дальше при каждой выгрузке из 1С разбирать надо только новые названия, это десятки позиций. Готовы на это, и кто этим займётся — вы, менеджер или кто-то нанятый?