Ингредиент как строка
Система решает, какой товар предложить ресторану, сравнивая обрезки названий. Обрезок совпал — товар попал в коммерческое предложение. В отправленном КП так оказались тайская паста карри для пенне, фасоль вместо соли и печенье вместо куриной печени.
00Как читать документ
Разделы 1–10 описывают проблему. Они намеренно не содержат ни одного предложения о том, как её чинить: любая догадка о решении, вписанная сюда, сузила бы поиск раньше времени.
Раздел 11 задаёт границы, 12 — критерии, по которым решение будет принято или отвергнуто, 13 — контрольный набор случаев. Раздел 14 — задание агенту-исследователю; это единственный раздел, обращённый к исполнителю, и он обязателен к исполнению целиком.
Каждая цифра и каждый пример в документе получены прямым просмотром боевых данных и текста отправленного КП. Ни один вывод не взят из агрегатов, посчитанных кодом, без последующей ручной проверки — почему это принципиально, сказано в разделе 14.
01Словарь
| Термин | Значение |
|---|---|
| Заведение | Ресторан или кафе, потенциальный покупатель. В базе 1746 штук. |
| Меню | Список блюд заведения, снятый с его сайта. Всего 4232 позиции. |
| Раскладка | Список ингредиентов блюда: «Карбонара» → сливки, бекон, яйцо, пармезан. 129 085 записей, 1073 разных ингредиента. |
| Каталог | Товары поставщика, выгруженные из 1С. 1080 позиций. |
| Ингредиент-терм | Строка, вырезанная из названия товара. Хранится в поле ingredient. |
| Канон-ключ | Эта же строка, урезанная до одного-двух слов. Поле ingredient_canonical. Ключ, по которому товар находят. Всего 367 разных. |
| Сопоставление | Поиск товаров под ингредиент блюда. Совпадение — точное по ключу, по первому слову или по вхождению подстроки. |
| Покрытие | Доля ингредиентов меню, под которые нашёлся хоть какой-то товар. Показывается менеджеру как процент. |
| КП | Коммерческое предложение. Письмо плюс подборка товаров под меню заведения. Отправляется клиенту в PDF. |
02Контекст
Хорика — внутренний инструмент оптового поставщика продуктов для ресторанов. Менеджер вводит адрес сайта заведения. Система обходит сайт, распознаёт меню, раскладывает каждое блюдо на ингредиенты, подбирает под эти ингредиенты товары из каталога поставщика и собирает коммерческое предложение.
Смысл продукта в одной фразе: прийти к ресторану с готовым списком того, что ему нужно, и показать, что мы это уже посчитали. Всё остальное — обход сайтов, распознавание фотографий, редактор КП — обслуживает этот момент.
Подбор товаров под ингредиенты и есть тот шаг, на котором продукт либо выполняет своё обещание, либо не выполняет. Именно он описан в этом документе.
03Формулировка проблемы
Проблема
Система определяет, какой ингредиент представляет собой товар, вырезая из его названия одно-два первых слова, и считает два предмета одним и тем же ингредиентом, когда эти обрезки совпали. Совпадение обрезков и совпадение продуктов — разные вещи, поэтому в подборку попадают товары, не имеющие отношения к блюду, а нужные товары не попадают.
Формулировка намеренно не содержит слов «баг», «неточность» и «качество сопоставления». Речь не о проценте ошибок, который можно снизить. Речь о том, что признак, по которому система принимает решение, не является признаком, по которому это решение должно приниматься.
04Как это устроено сейчас
Цепочка от строки в выгрузке 1С до товара в КП состоит из пяти шагов. Все пять — правила о позициях слов в строке. Ни на одном шаге не участвует ничего, что знало бы, что такое продукт.
Шаг 1. Отделение бренда
Название режется по точке с пробелом. Если отрезанный кусок короче четырёх слов и не содержит цифр, он объявляется брендом и выбрасывается.
Шаг 2. Ведущий сегмент
Из остатка берётся всё до первой меры или размера.
Шаг 3. Базовый терм
Из сегмента выбрасываются ведущие маркировки по списку из двадцати (ГОСТ, с/м, охл, зам и прочие) и одно ведущее прилагательное по списку из тринадцати (молочный, белый, тёмный…). Из того, что осталось, берутся первые одно-два слова.
Списки закрытые. Прилагательное «белая» в них не входит — там есть только мужская форма «белый». Поэтому «Белая глазурь» становится ключом белая.
Шаг 4. Канон-ключ
Из терма берётся первое слово. Для восемнадцати семейств из зашитого списка (масло, соус, сыр, мука, паста, икра…) сохраняются два слова, потому что для них второе слово важно. Для всех остальных — одно.
Шаг 5. Поиск товаров под ингредиент блюда
Ингредиент блюда ищется по ключу тремя способами по очереди: точное совпадение, совпадение по первому слову, вхождение подстроки. Подстрочный поиск — последний рубеж, и именно он даёт самые заметные ошибки: «соль» входит в «фасоль», «печень» входит в «печенье».
Существенное следствие
На всей цепочке из пяти шагов нет места, где решение о том, чем является товар, можно было бы записать, исправить или сохранить. Ключ вычисляется заново при каждой загрузке выгрузки 1С. Строка и есть тождество товара.
05Доказательство: отправленное КП
КП‑2026‑0001 собрано для ilpatio.ru — сети итальянских ресторанов IL Патио — и выгружено клиенту 6 сентября 2026. Это единственное КП со статусом «экспортировано» в базе. Оно содержит 100 разделов, по одному на ингредиент, и 255 предложенных товаров.
Я прочитал все сто разделов и все 255 позиций подряд, глазами, а не выборкой. По моей оценке 47 разделов из 100 содержат хотя бы один дефект, и 59 из 255 предложенных товаров предлагать не следовало. Ниже — не выборка «самого яркого», а полный перечень того, что я счёл ошибочным, с указанием блюд, ради которых товар предложен.
Часть разделов КП собрана правильно и хорошо выглядит: Бекон, Сливки, Грибы, Моцарелла, Пармезан, Ветчина, Кетчуп, Огурцы, Кукуруза, Творог, Желатин, Горчица, Сухари Панко. Проблема не в том, что система не работает никогда. Она в том, что по документу нельзя отличить те разделы от этих, не зная предметной области — а клиент их не различает и читает целиком.
06Классы дефектов в каталоге
КП — витрина. Ниже то, что стоит за ней, в самом каталоге из 1080 товаров и 367 канон-ключей. Все примеры выписаны при сплошном чтении, а не найдены поиском по образцу.
А. Омонимы: разные продукты под одним ключом
филе — три разных животныхТо же самое даёт ключ продукт, 16 товаров. Производители по закону называют сырные аналоги «Продукт сычужный» и «Продукт белково-жировой», поэтому под одним ключом лежат аналоги Тильзитера, Гауды, Голландского и Моцареллы, плавленый «Янтарь», варёная сгущёнка и растительно-творожная масса. Ключ напиток, 27 товаров, точно так же смешивает кокосовое, миндальное, овсяное и соевое «молоко» с апельсиновыми и вишнёвыми соками — все названия начинаются со слова «Напиток».
Б. Пропуски: товар есть, но найти его нельзя
Ключей курица, яйцо, говядина, свинина и соль в каталоге не существует вовсе. При этом куриные товары есть.
Одиннадцать куриных товаров разложены по семи ключам, названным по форме нарезки, а не по продукту. Ни один не найдётся по слову «курица». Отдельно отмечу: сводить их все к «курице» тоже нельзя — котлета для бургера и шаурма-полуфабрикат не заменяют куриное филе в блюде. Это ровно тот вопрос, на который у меня нет ответа и который входит в задание исследователю.
В. Дубликаты: один продукт под многими ключами
| Канон-ключ | Товаров |
|---|---|
| масло | 8 |
| масло сливочное | 9 |
| масло сл/сл | 7 |
| масло масло | 4 |
| масло традиционное | 4 |
| масло сливоч | 1 |
| масло сладкосливочное | 1 |
| масло кислосливочное | 1 |
Восемь ключей, 35 товаров, один продукт. Запрос по любому из них вернёт восьмую часть ассортимента. Твёрдый сыр так же разложен по сыр тв, сыр твердый и сыр твёрдый; творожный — по сыр творожный, сыр творож, сыр сливочный, сыр cream, сыр creamcheese и сыр c, где c — латинская буква.
Из 367 ключей 186 содержат ровно один товар. Это не ассортимент из 186 редких позиций, а в значительной части осколки групп, распавшихся на написания.
Г. Не-товары
35 записей каталога — вообще не продукты: 16 складов вида «КД1 Кросс-Док Москва 1», 17 строк кросс-докинга вида «Кросс Докинг ТД ЧЕРКИЗОВО ООО/Наш Партнёр», а также «Образцы $$$» и «Продукты питания». Они получили ключи кд1…кд16, кросс, кросс-док, кросс-докинг, образцы, продукты и лежат в каталоге наравне с едой.
Д. Потеря продукта на отделении бренда
Три товара потеряли продуктовое слово, которое правило приняло за бренд: «Дорадо с/м непотрош» дважды и «Филе атлант» один раз. Класс редкий, но показательный: сбой происходит молча и восстановить утраченное из ключа уже нельзя.
07Почему это не набор багов
Каждый пример выше можно закрыть точечно: внести «белая» в список прилагательных, добавить «мясо» в список тип-значимых семейств, запретить подстрочный поиск для слов короче шести букв. Ниже — рассуждение о том, почему такие правки не заканчиваются.
- Почему итальянскому ресторану предложили пралине из фундука?Товар «Паста из фундука» и ингредиент блюда «паста» получили один и тот же ключ
паста. - Почему у них один ключ?Ключ — это первое слово названия (для восемнадцати семейств — два первых). Оба начинаются со слова «паста».
- Почему ключ — это первое слово?Потому что ингредиент в системе представлен строкой, вырезанной из названия по позиции, а не опознанным предметом.
- Почему по позиции?Потому что нет перечня того, какие ингредиенты вообще бывают, и нет записи о том, какому ингредиенту соответствует конкретный товар. Строка выполняет роль тождества.
- Почему нет такой записи?Потому что конструкция исходит из того, что первые слова названия надёжно называют продукт. Это допущение неверно — производители называют товары по своим правилам, — и в системе нет места, где неверность можно было бы зафиксировать: ключ пересчитывается заново при каждой загрузке выгрузки 1С, и любая правка исчезает.
Корень
Тождество ингредиента вычисляется из написания названия в момент загрузки, и в системе нет состояния, в котором это тождество могло бы быть задано или исправлено. Поэтому расхождение между написанием и действительностью не является отказом системы — это её штатный результат, и он бесшумно доходит до документа, который читает клиент.
Это описание причины, а не требование что-либо построить. Каким должен быть ответ — предмет исследования, а не этого документа.
Практическое следствие: правки в закрытые списки не сходятся. Каждый новый прайс-лист от нового поставщика приносит написания, которых в списках нет, и создаёт новые ключи-осколки. Список из тринадцати прилагательных, двадцати маркировок, восемнадцати семейств и двадцати пяти сортов сыра — это перечисление того, что кто-то однажды увидел в выгрузке.
08Масштаб
| Величина | Значение | Проверено |
|---|---|---|
| Товаров в каталоге | 1080 | запрос к боевой базе |
| Канон-ключей | 367 | запрос, все прочитаны глазами |
| Ключей ровно с одним товаром | 186 | запрос |
| Записей-не-товаров | 35 | выписаны поимённо |
| Раскладок блюд | 129 085 | запрос |
| Разных ингредиентов в раскладках | 1073 | запрос |
| Заведений в базе | 1746 | запрос |
| Позиций меню | 4232 | запрос |
| Разделов в отправленном КП | 100 | прочитаны все |
| Товаров в отправленном КП | 255 | прочитаны все |
| Из них предлагать не следовало | 59 | ручная оценка, 23 % |
| Разделов КП с дефектом | 47 | ручная оценка |
Соотношение 1073 ингредиента в раскладках против 367 ключей в каталоге само по себе ничего не доказывает — часть ингредиентов поставщик действительно не возит. Но оно задаёт размер пространства, в котором придётся принимать решения: соответствие между этими двумя множествами сейчас не описано нигде.
09Что это стоит
- Первое впечатление о поставщике. КП — единственный документ, по которому ресторан судит о нас до разговора. Предложение тайской карри-пасты к пенне читается не как программная ошибка, а как незнание своего товара и товара клиента.
- Менеджер не может опереться на систему. Чтобы отправить КП, он должен вычитать сто разделов и знать, что рикотта — не фета. Работа, которую продукт обещал снять, возвращается человеку целиком, но теперь в форме вычитки чужого черновика.
- Покрытие вводит в заблуждение. Процент считается по факту нахождения товара, а не по его пригодности. Раздел «Соль» с тремя банками фасоли повышает покрытие. Цифра, по которой менеджер выбирает, за какое заведение браться, вознаграждает ложные совпадения.
- Пропуски не видны вообще. Отсутствие курицы, яиц, говядины и соли в подборке не отображается нигде: ингредиент, под который ничего не нашлось, просто не попадает в документ. Мы не предлагаем то, что у нас есть, и не знаем об этом.
- Правки не накапливаются. Исправление, внесённое менеджером сегодня, не переживает следующую загрузку выгрузки 1С.
10Данность
Факты обстановки, которые исследователь обязан принять как есть. Это не требования к решению и не намёки на него.
- Каталог приходит выгрузкой из 1С и перезагружается регулярно. Названия товаров задаёт поставщик, менять их в 1С мы не можем.
- Названия подчиняются правилам маркировки, а не удобству: «Продукт сычужный "ГАУДА оригин." 50%», «Напиток BARISTA МИНДАЛЬ». Форма названия не в нашей власти.
- Раскладки блюд имеют четыре источника: кулинарная база «Повaрёнок» (126 674 записи), языковая модель (2249), правила (101) и ручные заготовки (61). Качество и словарь у них разные.
- Меню снимается с сайтов заведений автоматически, названия блюд произвольны и часто маркетинговые: «Имя розы», «Моя гениальная подруга», «Почтальонша».
- Ассортимент поставщика — около тысячи позиций и меняется. Заведений 1746 и будет больше.
- КП собирает менеджер, а не инженер. Он видит подборку в редакторе перед экспортом.
- Система работает на проде, ей пользуются. Полная остановка подбора товаров недопустима, но временное ухудшение охвата ради достоверности обсуждаемо.
11Границы
Входит в задачу
- Определение того, какой ингредиент представляет собой товар каталога.
- Определение того, какой ингредиент имеется в виду в раскладке блюда.
- Решение о том, соответствуют ли они друг другу, и на каком основании.
- Судьба уже накопленных 367 ключей и 1080 товаров.
- Что происходит при загрузке новой выгрузки 1С с незнакомыми названиями.
- Что видит и что может сделать менеджер, когда система ошиблась или не уверена.
Не входит
- Качество обхода сайтов и распознавания меню. Это отдельная проблема, здесь не рассматривается.
- Качество самих раскладок блюд, кроме случаев, когда исследователь докажет, что без этого задачу решить нельзя. Тогда он выносит это отдельным вопросом владельцу.
- Вёрстка КП, редактор, PDF.
- Ценообразование и выбор конкретного товара из нескольких годных.
- Интерфейс в целом. Он переработан отдельно, см.
docs/UI_AUDIT.html.
12Критерии, по которым решение будет принято
Сформулированы как проверки результата, а не как свойства устройства. Способ достижения не задаётся.
- Контрольный набор проходит. Все случаи из раздела 13 дают правильный ответ. Ни один из них не должен решаться правилом, написанным специально под него.
- Ложное совпадение не может дойти до клиента молча. Если система не уверена, это видно в интерфейсе до отправки. Молчаливое «похоже, подойдёт» недопустимо.
- Ответ можно объяснить. Для любого товара в КП можно сказать, почему он там, словами, понятными менеджеру, а не ссылкой на совпадение подстроки.
- Пропуск видим. Ингредиент, под который ничего не нашлось, отображается как ненайденный, а не исчезает.
- Исправление переживает перезагрузку каталога. Решение, однажды принятое человеком, не отменяется следующей выгрузкой 1С.
- Новое название не ломает старое. Появление незнакомого товара не меняет ответы для уже разобранных.
- Стоимость понятна заранее. Известно, сколько стоит обработка тысячи товаров и одного меню, в деньгах и во времени, и что происходит при росте каталога вдесятеро.
- Отказ безопасен. Понятно, что происходит, когда внешняя зависимость недоступна: система не должна начать выдавать ответы худшего качества, не сообщая об этом.
13Контрольный набор
Случаи с известным ответом, собранные при ручной сверке. Любой вариант решения обязан быть прогнан по ним и показан владельцу с результатом по каждому пункту.
| Ингредиент блюда | Ожидается |
|---|---|
| курица (Цезарь с куриной грудкой) | Грудка куриная в/к; допустимо — наггетсы и стрипсы с явной пометкой, что это полуфабрикат |
| паста (Пенне Арабьята) | Ничего. Пасты в каталоге нет. Правильный ответ — пусто и видимая пометка о пропуске |
| тесто для пиццы | Ничего либо мука пшеничная. Тесто для спринг-роллов — неверно |
| соль | Ничего. Соли в каталоге нет |
| фета (Греческий салат) | Ничего. Феты в каталоге нет. Рикотта, моцарелла и чеддер — неверно |
| сливочное масло | Все 35 товаров восьми нынешних ключей, как одна группа |
| твёрдый сыр (лазанья) | Sardo и прочие твёрдые. Рикотта и моцарелла — неверно |
| оливковое масло | Три оливковых масла, одной группой, без второго раздела «Оливковое» |
| базилик | Ничего. Сироп «Базилик» — неверно |
| лимон (стейк из лосося) | Ничего либо сок лимона. Лимонная кислота — неверно |
| рис для ризотто | Ничего. Рис для суши — неверно |
| мидии | Мясо мидии и мидии в створках — одной группой |
| моцарелла | Все моцареллы, включая «Продукт белково-жировой "Моцарелла"» с пометкой, что это аналог |
| миндальное молоко | Напитки BARISTA МИНДАЛЬ, а не все 27 товаров ключа «напиток» |
| дорадо | Оба товара, ныне лежащие под ключом «с» |
| Ингредиент | Товар, который сейчас предлагается |
|---|---|
| соль | Фасоль красная · Сыр рассольный |
| печень | Печенье Савоярди |
| паста | Паста из фундука (пралине) · Паста карри · Паста Том Ям |
| салат | Салат из водорослей «ЧУКА» |
| заправка для Цезаря | Заправка для Борща |
| мясо | Мясо мидии · Мясо морского гребешка |
| батон | Батончик «Арахисовый» |
| авокадо | Масло авокадо |
| кунжутное масло | Масло сливочное традиционное 82,5% |
| любой ингредиент | «КД1 Кросс-Док Москва 1» и прочие 34 не-товара |
Набор неполон намеренно. Исследователь обязан расширить его собственными случаями, найденными при личном просмотре, и показать пополнение владельцу.
14Задание агенту-исследователю
Дальше — обращение к тому, кто будет искать решение. Этот раздел обязателен целиком.
Что требуется
Найти надёжный способ решения задачи в границах раздела 11, разобрать варианты и обсудить каждый с владельцем. Не выбрать вариант в одиночку и не приступить к реализации. Результат работы — разобранные варианты и принятое владельцем решение, а не код.
Смотреть своими глазами. Не поручать изучение проблемы коду. Открыть каталог и читать названия подряд. Открыть раскладки и читать ингредиенты подряд. Открыть КП и читать разделы подряд. Скрипт может привести данные к виду, удобному для чтения, — но выводы делаются из прочитанного человеком-читателем, а не из сводки, которую посчитал скрипт.
Изучить мировые практики онлайн. Как задачу «опознать товар и сопоставить его с позицией» решают там, где она решена: розничные сети, оптовые каталоги, рецептурные и нутрициологические базы, закупочные системы, маркетплейсы, отраслевые классификаторы. Найти, что именно применяют, где это ломается и чем за это платят. Ответ, придуманный из головы без обзора практики, задание не выполняет.
Обсудить каждый вариант с владельцем. По каждому: что он даёт, где ломается, сколько стоит в деньгах и во времени, что требуется от людей ежедневно, что происходит при незнакомом названии, как проверять правильность. Прогнать по контрольному набору раздела 13 и показать результат по каждому пункту. Обсуждение ведётся простым русским языком, без жаргона.
Разобрать не меньше трёх принципиально разных подходов, включая тот, который сам считает неудачным, и сказать, почему. Один вариант — не выбор.
Проверять утверждения на боевых данных. Каждое «это сработает» подкрепляется прогоном на реальном каталоге и реальном меню, а результат прочитывается глазами. Цифра совпадений без прочитанных примеров ничего не подтверждает.
Сказать, чего не знает. Открытые вопросы выносятся владельцу отдельно и явно, а не закрываются догадкой.
Предлагать затычку. Пополнение списка прилагательных, ещё один словарь синонимов, порог похожести строк, запрет подстрочного поиска для коротких слов — всё это меняет распределение ошибок, не устраняя их источник. Такой ответ не принимается, даже если на контрольном наборе он даёт хороший результат.
Начинать с реализации. До решения владельца код не пишется, кроме небольших проб, нужных чтобы измерить вариант, — и о них говорится прямо как о пробах.
Считать вопрос закрытым, пока владелец не выбрал. Молчаливый переход к любимому варианту — невыполнение задания.
Экономить время и токены. Ограничений нет. Единственный критерий — достоверность результата. Быстрый обзор хуже медленного, если медленный точнее.
Почему требование смотреть глазами вынесено первым
При подготовке этого документа я дважды получил неверную картину именно потому, что доверился коду.
Первый раз: поиск подстрок по каталогу выдал «ошибочные» совпадения, которых не было. Слово «мороженое» нашлось внутри «варено-мороженного кальмара», слово «специи» — внутри «картофельных долек со специями». Список дефектов оказался наполовину выдуманным.
Второй раз: разбор КП скриптом показал девять разделов без единого товара. Я открыл HTML руками — товары там были. «Базилик» содержал сироп, «Авокадо» — масло авокадо, «Шоколад» — три настоящих шоколада. Регулярное выражение просто не добирало строки. Если бы я не открыл файл, в спецификацию попало бы утверждение о пустых разделах, которого нет в действительности.
Оба раза сводка выглядела убедительно и была неверна — в обе стороны. Задача такова, что правильность ответа определяется знанием предмета, а не формой строки; проверяющий код обладает ровно тем же изъяном, что и проверяемый. Отсюда требование: читать самому.
Порядок работы
- Знакомство с предметом. Прочитать каталог и раскладки своими глазами. Расширить контрольный набор. Прийти к владельцу с тем, что увидел, и сверить понимание проблемы до всякого поиска решений.
- Обзор практики. Изучить, как эту задачу решают в мире. Принести владельцу разбор: что применяется, при каких условиях работает, чего стоит.
- Варианты. Разобрать не менее трёх подходов по общей схеме, прогнать по контрольному набору, показать реальные примеры ответов — верных и неверных.
- Обсуждение и выбор. Обсудить с владельцем каждый вариант. Выбор делает владелец.
- План перехода. Только после выбора: как переходить, что делать с накопленными 367 ключами, как проверить, что стало лучше, а не иначе.
Что считается невыполнением
- Ответ, полученный только из чтения кода и агрегатов, без личного просмотра данных.
- Один вариант вместо разбора нескольких.
- Вариант, улучшающий цифру покрытия, но оставляющий возможность молчаливого ложного совпадения.
- Решение, принятое исполнителем, а не владельцем.
- Готовый код вместо разобранных вариантов.
15Открытые вопросы
На них у меня нет ответа. Они не риторические и требуют решения владельца — исследователь обязан их поднять, а не додумать.
- Что считать правильным ответом, когда точного товара нет? Под «пасту» у поставщика нет пасты. Предлагать близкое, предлагать ничего или показывать это менеджеру как повод для разговора с клиентом — разные продукты, и выбор здесь деловой, а не технический.
- Заменяют ли полуфабрикаты продукт? Наггетсы и шаурма-полуфабрикат — это курица для ресторана, который жарит куриное филе, или нет? Ответ определяет, будет ли каталог казаться шире или уже, чем он есть.
- Считать ли аналоги тем же продуктом? «Продукт белково-жировой "Моцарелла"» — это моцарелла в предложении ресторану или отдельная позиция, которую нужно называть своим именем?
- Сколько ручной работы допустимо? Тысяча товаров разбирается человеком за обозримое время однократно. Готовы ли мы к этому, и кто это делает при каждой новой выгрузке?
- Что показывать менеджеру при неуверенности? Ответ определяет, сколько ложных совпадений дойдёт до клиента.
16Приложение: где смотреть
| Файл | Что там |
|---|---|
| app/core/onec/files.py | split_onec_title — отделение кода группы и бренда, шаг 1 |
| app/core/nomenclature/ingredients.py | _head_segment, _base_term — шаги 2 и 3; закрытые списки _LEAD_MARKERS, _LEAD_ADJECTIVES, _FILLER_W2 |
| app/core/nomenclature/canon.py | canonicalize — шаг 4; списки TYPE_SIGNIFICANT, _CHEESES, FAMILY_SYNONYMS, SYNONYMS |
| app/core/nomenclature/matching_core.py | Index.classify — шаг 5, три способа поиска по очереди |
| app/core/nomenclature/matching.py | _decompose — порядок источников раскладки блюда |
| app/core/onec/sync.py | Место, где при загрузке выгрузки вызывается цепочка и записывается ingredient_canonical |
| app/core/nomenclature/dishes.py | Ручные заготовки раскладок |
Данные для личного просмотра: таблица client_products (поля title, brand, ingredient, ingredient_canonical), таблица dish_decomposition (поля dish_name, ingredients, source), таблица proposals (поле selection_html — полный текст отправленного КП).