Спецификация проблемы · Хорика · не содержит решений

Ингредиент как строка

Система решает, какой товар предложить ресторану, сравнивая обрезки названий. Обрезок совпал — товар попал в коммерческое предложение. В отправленном КП так оказались тайская паста карри для пенне, фасоль вместо соли и печенье вместо куриной печени.

Дата
7 сентября 2026
Статус
Открыта. Решение не выбрано и в этом документе не предлагается.
Кому
Владельцу продукта и агенту-исследователю, которому поручается поиск решения
Основание
Ручная сверка каталога 1С и полного текста КП‑2026‑0001, отправленного клиенту 6 сентября 2026

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. Отделение бренда

Название режется по точке с пробелом. Если отрезанный кусок короче четырёх слов и не содержит цифр, он объявляется брендом и выбрасывается.

Название → что осталось после шага 1
верно05.04.30. Oldenburger. Сыр Гойя 40%, БРУС 3кг«Oldenburger» — действительно бренд. Осталось: «Сыр Гойя 40%, БРУС 3кг».
ошибка874.40.04. Дорадо с/м непотрош. с головой 400-600, кор. 5кг (Турция. ЗАМ)Три слова, цифр нет — правило объявило брендом сам продукт. Осталось: «с головой 400-600…». Рыба из названия исчезла.

Шаг 2. Ведущий сегмент

Из остатка берётся всё до первой меры или размера.

Шаг 3. Базовый терм

Из сегмента выбрасываются ведущие маркировки по списку из двадцати (ГОСТ, с/м, охл, зам и прочие) и одно ведущее прилагательное по списку из тринадцати (молочный, белый, тёмный…). Из того, что осталось, берутся первые одно-два слова.

Списки закрытые. Прилагательное «белая» в них не входит — там есть только мужская форма «белый». Поэтому «Белая глазурь» становится ключом белая.

Шаг 4. Канон-ключ

Из терма берётся первое слово. Для восемнадцати семейств из зашитого списка (масло, соус, сыр, мука, паста, икра…) сохраняются два слова, потому что для них второе слово важно. Для всех остальных — одно.

Что получается на выходе шага 4 — трассировка выполнена на боевых названиях
мол90.70.386. Chocovic. Мол. шоколад «МАРКО» (сливоч. с карам.), дропсы 1кг«Мол.» — сокращение от «молочный», но в списке прилагательных стоит полная форма. Ключом стал обрывок «мол».
с874.40.02. Дорадо с/м непотрош. с головой 300-400, кор. 5кгПосле потери продукта на шаге 1 первым словом оказался предлог.
мясо875.04.03. Мясо Мидии варено-мороженные очищенные 200/300, кор. 10 кг«Мясо» не входит в список из восемнадцати, поэтому второе слово отброшено. Мидии стали просто «мясом».
паста90.70.226. Callebaut. Паста из фундука 24% и миндаля 24% (пралине), вед. 5кг«Паста» в списке есть, но второе слово — предлог «из», он отбрасывается. Кондитерское пралине получило тот же ключ, что итальянская паста.

Шаг 5. Поиск товаров под ингредиент блюда

Ингредиент блюда ищется по ключу тремя способами по очереди: точное совпадение, совпадение по первому слову, вхождение подстроки. Подстрочный поиск — последний рубеж, и именно он даёт самые заметные ошибки: «соль» входит в «фасоль», «печень» входит в «печенье».

Существенное следствие

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

05Доказательство: отправленное КП

КП‑2026‑0001 собрано для ilpatio.ru — сети итальянских ресторанов IL Патио — и выгружено клиенту 6 сентября 2026. Это единственное КП со статусом «экспортировано» в базе. Оно содержит 100 разделов, по одному на ингредиент, и 255 предложенных товаров.

Я прочитал все сто разделов и все 255 позиций подряд, глазами, а не выборкой. По моей оценке 47 разделов из 100 содержат хотя бы один дефект, и 59 из 255 предложенных товаров предлагать не следовало. Ниже — не выборка «самого яркого», а полный перечень того, что я счёл ошибочным, с указанием блюд, ради которых товар предложен.

Не тот продукт — ключ совпал, предмет другой
ПастаПаста из фундука (пралине) · Паста карри жёлтая · Паста Том ЯмРади блюд: Фрутти ди Маре, Пенне дель фатторе, Пенне Арабьята, Мясная лазанья, Паппарделле. Итальянскому ресторану предложены бельгийское пралине и тайские карри-пасты. Ни одной пасты в кулинарном смысле.
СольФасоль красная натуральная · Фасоль КРАСНАЯ · Сыр рассольныйРади 14 блюд, включая Мясную лазанью и Спагетти болоньезе. «Соль» — подстрока в «фасоль» и в «рассольный».
ПеченьПеченье Савоярди · Печенье Савоярди сахарное для тирамисуРади блюда «Куриная печень». «Печень» — подстрока в «печенье».
ТестоТесто для спринг-роллов 21,5 см (Таиланд)Ради 26 блюд, почти все — римские пиццы. Единственное предложение под тесто для пиццы — тайская рисовая бумага.
СалатСалат из водорослей «ЧУКА»Ради Цезаря с креветками, Цезаря с курицей и зелёных салатов. Единственное предложение.
ЗаправкаЗаправка для БорщаРади двух Цезарей. Единственное предложение.
Сыр фетаРикотта · Моцарелла МИНИ · Чеддер слайсы для бургеровРади Греческого салата и блюда «Фета и томаты с римским хлебом». Феты нет ни одной.
Сыр горгонзолаРикотта · Моцарелла МИНИ · Чеддер слайсы для бургеровРади «Мороженого с горгонзолой». Те же три сыра, что и под фету. Горгонзолы нет.
Сыр твёрдыйSardo 45% · Рикотта мягкий · Моцарелла МИНИРади Мясной лазаньи. Два из трёх — мягкие сыры под ключом «твёрдый».
Соус наполиСоус «Рыбный» · Соус чесночный · Соус сырныйРади двух римских пицц. Томатного соуса под неаполитанский нет ни одного, хотя в каталоге он есть.
МясоМясо мидии · Мясо морского гребешка · Мясо мидии очищенныеРади «Супа дня» и пиццы «Мексикана». Под мясо предложены моллюски.
РисРис для суши ×3Ради пяти ризотто. Сорта для ризотто в подборке нет.
ЛапшаЛапша УДОН ×2 · Лапша гречневая СобаРади Пенне Болоньезе и Спагетти нери.
ИмбирьИмбирь маринованный для суши ×3Ради соуса Барбекю и морковного напитка. Свежего имбиря нет.
ЛимонЛимонная кислота ×2 · Пюре «Лимон»Ради стейка из лосося и скалопини из курицы.
БазиликСироп «Базилик» 1лРади 20 блюд, включая Маргариту. Барный сироп вместо зелени. Единственное предложение.
АвокадоМасло АВОКАДО раф. 100%Ради «Салата с морепродуктами», где нужен плод. Единственное предложение.
БатонБатончик без сахара «Арахисовый»Ради «Чесночного хлеба». Единственное предложение.
ОрехСоус ОРЕХОВЫЙ (Кунжутный)Ради Мясной лазаньи, где нужен мускатный орех. Единственное предложение.
СмесьСмесь для мороженого молочная · Смесь для мороженого шоколадная · Смесь мучная ТемпураРади «Овощного салата».
СахарСахар ванильный 8гр · Сахар ванильный XXL · Сахарная пудраРади 20 блюд. Обычного сахара в подборке нет.
Масло кунжутноеМасло ТРАДИЦИОННОЕ 82,5% · Масло подсолнечное ×2Ради соуса Барбекю. Первым идёт сливочное масло, кунжутного нет.
Сок лимонныйСок яблочный · Сок томатный · Сок лимона концентрированныйРади «Салата с морепродуктами». Нужный товар — третий.
ПюреПюре «Гуава розовая» · Пюре «Инжир» · Пюре «Тыква»Ради «Щёчек телёнка с трюфельным пюре», где пюре картофельное.
Соус (4 раздела)Соус «Рыбный» — в разделах «Соус», «Соус бургер», «Соус барбекю», «Соус томатный»Тайский рыбный соус добирает третье место в четырёх разных разделах, потому что подходящих по ключу товаров не хватило.
Уксус бальзамическийУксус бальзамический МОДЕНЫ · Уксус столовый 9% ×2Ради Пиццы Капрезе. Два из трёх — столовый уксус.
Один ингредиент разошёлся по нескольким разделам — клиент видит один и тот же товар дважды под разными заголовками
масло«Масло» · «Масло масло» · «Масло сливочное»Три раздела про сливочное масло. Заголовок «Масло масло» напечатан в документе, ушедшем клиенту.
мука«Мука» · «Мука пшеничная»Два раздела, одни и те же три товара в разном порядке.
оливковое масло«Оливковое» · «Масло оливковое»Два раздела, одни и те же три товара.
какао«Какао» · «Какао-порошок»Два раздела, пересекающиеся товары.
шампиньоны«Шампиньон» · «Шампиньоны»Единственное и множественное число — два раздела, один и тот же товар.
лосось«Лосось» · «Лосося»Именительный и родительный падеж — два раздела.
соевый соус«Соус соевый» · «Соевый»Два раздела, пересекающиеся товары.
уксус«Уксус» · «Уксус бальзамический» · «Бальзамический»Три раздела.
сахар«Сахар» · «Сахарная»Сахарная пудра предложена в обоих.
печенье«Печень» · «Печенье»Два раздела с одними и теми же двумя пачками савоярди.
Заголовки, напечатанные в документе для клиента — это то, что человек читает в PDF
обрубок«Оливковое» · «Бальзамический» · «Сахарная» · «Соевый»Прилагательное без существительного как название раздела.
повтор«Масло масло»Слово продублировано.
падеж«Лосося»Родительный падеж как название раздела.
пусто«Соус»Категория вместо ингредиента: под ней рыбный, чесночный и сырный соусы для 12 блюд.

Часть разделов КП собрана правильно и хорошо выглядит: Бекон, Сливки, Грибы, Моцарелла, Пармезан, Ветчина, Кетчуп, Огурцы, Кукуруза, Творог, Желатин, Горчица, Сухари Панко. Проблема не в том, что система не работает никогда. Она в том, что по документу нельзя отличить те разделы от этих, не зная предметной области — а клиент их не различает и читает целиком.

06Классы дефектов в каталоге

КП — витрина. Ниже то, что стоит за ней, в самом каталоге из 1080 товаров и 367 канон-ключей. Все примеры выписаны при сплошном чтении, а не найдены поиском по образцу.

А. Омонимы: разные продукты под одним ключом

Ключ филе — три разных животных
филе85.62.32. Смолевичи бройлер. Филе нарезанное запеченное, 2кгКурица.
филе872.20.02. Филе лосося атлант н/к ХОЛ.КОПЧЕНИЯ трим А, в/уп. 2 кгЛосось.
филе874.50.02. Филе масляной рыбы на коже ХОЛ.КОПЧЕНИЯ, в/уп. 1,8 кгМасляная рыба. Любой запрос «филе» вернёт все три.

То же самое даёт ключ продукт, 16 товаров. Производители по закону называют сырные аналоги «Продукт сычужный» и «Продукт белково-жировой», поэтому под одним ключом лежат аналоги Тильзитера, Гауды, Голландского и Моцареллы, плавленый «Янтарь», варёная сгущёнка и растительно-творожная масса. Ключ напиток, 27 товаров, точно так же смешивает кокосовое, миндальное, овсяное и соевое «молоко» с апельсиновыми и вишнёвыми соками — все названия начинаются со слова «Напиток».

Б. Пропуски: товар есть, но найти его нельзя

Ключей курица, яйцо, говядина, свинина и соль в каталоге не существует вовсе. При этом куриные товары есть.

Все куриные товары каталога и их ключи — ни одного под ключом «курица»
грудка86.61.50. Турбаслинский бройлер. Грудка куриная Варено-Копченая, кор. 3кг
котлета85.62.41. Смолевичи бройлер. Котлета для бургера (курица в темпуре), 2кг
кусочки85.62.28. Смолевичи бройлер. Кусочки КУРИНЫЕ «ПОПКОРН» в панировке, 2кг
наггетсыЧеркизово: ФИРМЕННЫЕ · КЛАССИЧЕСКИЕ · ХРУСТЯЩИЕ; Смолевичи: ТЕМПУРНЫЕ
стрипсы85.61.51. Черкизово. Стрипсы ОРИГИНАЛЬНЫЕ, пак. 1,115кг
филе85.62.32. Смолевичи бройлер. Филе нарезанное запеченное, 2кг
шаурмаСмолевичи бройлер. Шаурма «Донер», втулка 40см и 50см

Одиннадцать куриных товаров разложены по семи ключам, названным по форме нарезки, а не по продукту. Ни один не найдётся по слову «курица». Отдельно отмечу: сводить их все к «курице» тоже нельзя — котлета для бургера и шаурма-полуфабрикат не заменяют куриное филе в блюде. Это ровно тот вопрос, на который у меня нет ответа и который входит в задание исследователю.

В. Дубликаты: один продукт под многими ключами

Сливочное масло в каталоге
Канон-ключТоваров
масло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. Почему итальянскому ресторану предложили пралине из фундука?Товар «Паста из фундука» и ингредиент блюда «паста» получили один и тот же ключ паста.
  2. Почему у них один ключ?Ключ — это первое слово названия (для восемнадцати семейств — два первых). Оба начинаются со слова «паста».
  3. Почему ключ — это первое слово?Потому что ингредиент в системе представлен строкой, вырезанной из названия по позиции, а не опознанным предметом.
  4. Почему по позиции?Потому что нет перечня того, какие ингредиенты вообще бывают, и нет записи о том, какому ингредиенту соответствует конкретный товар. Строка выполняет роль тождества.
  5. Почему нет такой записи?Потому что конструкция исходит из того, что первые слова названия надёжно называют продукт. Это допущение неверно — производители называют товары по своим правилам, — и в системе нет места, где неверность можно было бы зафиксировать: ключ пересчитывается заново при каждой загрузке выгрузки 1С, и любая правка исчезает.

Корень

Тождество ингредиента вычисляется из написания названия в момент загрузки, и в системе нет состояния, в котором это тождество могло бы быть задано или исправлено. Поэтому расхождение между написанием и действительностью не является отказом системы — это её штатный результат, и он бесшумно доходит до документа, который читает клиент.

Это описание причины, а не требование что-либо построить. Каким должен быть ответ — предмет исследования, а не этого документа.

Практическое следствие: правки в закрытые списки не сходятся. Каждый новый прайс-лист от нового поставщика приносит написания, которых в списках нет, и создаёт новые ключи-осколки. Список из тринадцати прилагательных, двадцати маркировок, восемнадцати семейств и двадцати пяти сортов сыра — это перечисление того, что кто-то однажды увидел в выгрузке.

08Масштаб

Боевые данные на 7 сентября 2026
ВеличинаЗначениеПроверено
Товаров в каталоге1080запрос к боевой базе
Канон-ключей367запрос, все прочитаны глазами
Ключей ровно с одним товаром186запрос
Записей-не-товаров35выписаны поимённо
Раскладок блюд129 085запрос
Разных ингредиентов в раскладках1073запрос
Заведений в базе1746запрос
Позиций меню4232запрос
Разделов в отправленном КП100прочитаны все
Товаров в отправленном КП255прочитаны все
Из них предлагать не следовало59ручная оценка, 23 %
Разделов КП с дефектом47ручная оценка

Соотношение 1073 ингредиента в раскладках против 367 ключей в каталоге само по себе ничего не доказывает — часть ингредиентов поставщик действительно не возит. Но оно задаёт размер пространства, в котором придётся принимать решения: соответствие между этими двумя множествами сейчас не описано нигде.

09Что это стоит

10Данность

Факты обстановки, которые исследователь обязан принять как есть. Это не требования к решению и не намёки на него.

11Границы

Входит в задачу

Не входит

12Критерии, по которым решение будет принято

Сформулированы как проверки результата, а не как свойства устройства. Способ достижения не задаётся.

  1. Контрольный набор проходит. Все случаи из раздела 13 дают правильный ответ. Ни один из них не должен решаться правилом, написанным специально под него.
  2. Ложное совпадение не может дойти до клиента молча. Если система не уверена, это видно в интерфейсе до отправки. Молчаливое «похоже, подойдёт» недопустимо.
  3. Ответ можно объяснить. Для любого товара в КП можно сказать, почему он там, словами, понятными менеджеру, а не ссылкой на совпадение подстроки.
  4. Пропуск видим. Ингредиент, под который ничего не нашлось, отображается как ненайденный, а не исчезает.
  5. Исправление переживает перезагрузку каталога. Решение, однажды принятое человеком, не отменяется следующей выгрузкой 1С.
  6. Новое название не ломает старое. Появление незнакомого товара не меняет ответы для уже разобранных.
  7. Стоимость понятна заранее. Известно, сколько стоит обработка тысячи товаров и одного меню, в деньгах и во времени, и что происходит при росте каталога вдесятеро.
  8. Отказ безопасен. Понятно, что происходит, когда внешняя зависимость недоступна: система не должна начать выдавать ответы худшего качества, не сообщая об этом.

13Контрольный набор

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

Должно совпасть
Ингредиент блюдаОжидается
курица (Цезарь с куриной грудкой)Грудка куриная в/к; допустимо — наггетсы и стрипсы с явной пометкой, что это полуфабрикат
паста (Пенне Арабьята)Ничего. Пасты в каталоге нет. Правильный ответ — пусто и видимая пометка о пропуске
тесто для пиццыНичего либо мука пшеничная. Тесто для спринг-роллов — неверно
сольНичего. Соли в каталоге нет
фета (Греческий салат)Ничего. Феты в каталоге нет. Рикотта, моцарелла и чеддер — неверно
сливочное маслоВсе 35 товаров восьми нынешних ключей, как одна группа
твёрдый сыр (лазанья)Sardo и прочие твёрдые. Рикотта и моцарелла — неверно
оливковое маслоТри оливковых масла, одной группой, без второго раздела «Оливковое»
базиликНичего. Сироп «Базилик» — неверно
лимон (стейк из лосося)Ничего либо сок лимона. Лимонная кислота — неверно
рис для ризоттоНичего. Рис для суши — неверно
мидииМясо мидии и мидии в створках — одной группой
моцареллаВсе моцареллы, включая «Продукт белково-жировой "Моцарелла"» с пометкой, что это аналог
миндальное молокоНапитки BARISTA МИНДАЛЬ, а не все 27 товаров ключа «напиток»
дорадоОба товара, ныне лежащие под ключом «с»
Не должно совпасть ни при каких условиях
ИнгредиентТовар, который сейчас предлагается
сольФасоль красная · Сыр рассольный
печеньПеченье Савоярди
пастаПаста из фундука (пралине) · Паста карри · Паста Том Ям
салатСалат из водорослей «ЧУКА»
заправка для ЦезаряЗаправка для Борща
мясоМясо мидии · Мясо морского гребешка
батонБатончик «Арахисовый»
авокадоМасло авокадо
кунжутное маслоМасло сливочное традиционное 82,5%
любой ингредиент«КД1 Кросс-Док Москва 1» и прочие 34 не-товара

Набор неполон намеренно. Исследователь обязан расширить его собственными случаями, найденными при личном просмотре, и показать пополнение владельцу.

14Задание агенту-исследователю

Дальше — обращение к тому, кто будет искать решение. Этот раздел обязателен целиком.

Что требуется

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

Обязан

Смотреть своими глазами. Не поручать изучение проблемы коду. Открыть каталог и читать названия подряд. Открыть раскладки и читать ингредиенты подряд. Открыть КП и читать разделы подряд. Скрипт может привести данные к виду, удобному для чтения, — но выводы делаются из прочитанного человеком-читателем, а не из сводки, которую посчитал скрипт.

Обязан

Изучить мировые практики онлайн. Как задачу «опознать товар и сопоставить его с позицией» решают там, где она решена: розничные сети, оптовые каталоги, рецептурные и нутрициологические базы, закупочные системы, маркетплейсы, отраслевые классификаторы. Найти, что именно применяют, где это ломается и чем за это платят. Ответ, придуманный из головы без обзора практики, задание не выполняет.

Обязан

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

Обязан

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

Обязан

Проверять утверждения на боевых данных. Каждое «это сработает» подкрепляется прогоном на реальном каталоге и реальном меню, а результат прочитывается глазами. Цифра совпадений без прочитанных примеров ничего не подтверждает.

Обязан

Сказать, чего не знает. Открытые вопросы выносятся владельцу отдельно и явно, а не закрываются догадкой.

Запрещено

Предлагать затычку. Пополнение списка прилагательных, ещё один словарь синонимов, порог похожести строк, запрет подстрочного поиска для коротких слов — всё это меняет распределение ошибок, не устраняя их источник. Такой ответ не принимается, даже если на контрольном наборе он даёт хороший результат.

Запрещено

Начинать с реализации. До решения владельца код не пишется, кроме небольших проб, нужных чтобы измерить вариант, — и о них говорится прямо как о пробах.

Запрещено

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

Запрещено

Экономить время и токены. Ограничений нет. Единственный критерий — достоверность результата. Быстрый обзор хуже медленного, если медленный точнее.

Почему требование смотреть глазами вынесено первым

При подготовке этого документа я дважды получил неверную картину именно потому, что доверился коду.

Первый раз: поиск подстрок по каталогу выдал «ошибочные» совпадения, которых не было. Слово «мороженое» нашлось внутри «варено-мороженного кальмара», слово «специи» — внутри «картофельных долек со специями». Список дефектов оказался наполовину выдуманным.

Второй раз: разбор КП скриптом показал девять разделов без единого товара. Я открыл HTML руками — товары там были. «Базилик» содержал сироп, «Авокадо» — масло авокадо, «Шоколад» — три настоящих шоколада. Регулярное выражение просто не добирало строки. Если бы я не открыл файл, в спецификацию попало бы утверждение о пустых разделах, которого нет в действительности.

Оба раза сводка выглядела убедительно и была неверна — в обе стороны. Задача такова, что правильность ответа определяется знанием предмета, а не формой строки; проверяющий код обладает ровно тем же изъяном, что и проверяемый. Отсюда требование: читать самому.

Порядок работы

  1. Знакомство с предметом. Прочитать каталог и раскладки своими глазами. Расширить контрольный набор. Прийти к владельцу с тем, что увидел, и сверить понимание проблемы до всякого поиска решений.
  2. Обзор практики. Изучить, как эту задачу решают в мире. Принести владельцу разбор: что применяется, при каких условиях работает, чего стоит.
  3. Варианты. Разобрать не менее трёх подходов по общей схеме, прогнать по контрольному набору, показать реальные примеры ответов — верных и неверных.
  4. Обсуждение и выбор. Обсудить с владельцем каждый вариант. Выбор делает владелец.
  5. План перехода. Только после выбора: как переходить, что делать с накопленными 367 ключами, как проверить, что стало лучше, а не иначе.

Что считается невыполнением

15Открытые вопросы

На них у меня нет ответа. Они не риторические и требуют решения владельца — исследователь обязан их поднять, а не додумать.

16Приложение: где смотреть

Места в коде, реализующие описанный механизм
ФайлЧто там
app/core/onec/files.pysplit_onec_title — отделение кода группы и бренда, шаг 1
app/core/nomenclature/ingredients.py_head_segment, _base_term — шаги 2 и 3; закрытые списки _LEAD_MARKERS, _LEAD_ADJECTIVES, _FILLER_W2
app/core/nomenclature/canon.pycanonicalize — шаг 4; списки TYPE_SIGNIFICANT, _CHEESES, FAMILY_SYNONYMS, SYNONYMS
app/core/nomenclature/matching_core.pyIndex.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 — полный текст отправленного КП).