Проверяем факты в ИИ-карточке товара: Python-валидатор и ошибка, которую он пропускает
В карточке средства защиты растений указаны правильные идентификатор, культура и единица измерения. В описании добавлено: «Средство подходит для любой культуры, ограничений нет». Если программа сравнивает только поля JSON с каталогом, такая карточка пройдёт проверку.
Именно это получилось в небольшом стенде для этого разбора. На 16 учебных карточках валидатор отметил все 12 намеренных ошибок в структурированных данных, принял три корректных варианта и пропустил одну неверную фразу в описании. Результат не измеряет качество какой-либо языковой модели: карточки составлены вручную, а ошибки специально внесены в копии утверждённых данных. Здесь испытывается проверяющий код.
Разберём, что такой код может взять на себя и почему его результат нельзя назвать ready_to_publish.
Что считаем источником
В стенде три позиции: учебный препарат, партия семян и запасная часть. Все идентификаторы начинаются с DEMO-; регистрационные сведения, документы, модели и числа вымышлены. Они не описывают товар на рынке и не предназначены для выбора препарата или нормы применения.
Для каждой позиции подготовлены facts и sources. Первое содержит утверждённые значения, второе — идентификатор версии документа для каждого поля. Код не читает оригинальные документы и не определяет, правдивы ли они. Эталон должен быть утверждён до проверки карточек; иначе программа будет последовательно воспроизводить ошибку в самом каталоге.
Источник привязан к полю, а не ко всей карточке. Ссылка на верную этикетку рядом с неверной культурой не должна скрыть несовпадение значения. Обратная ситуация тоже требует внимания: значение совпало, но указан документ старой версии. Это отдельное замечание source_mismatch.
Для семян в примере проверяются гибрид, партия и идентификатор документа качества; для детали — артикул и список совместимых моделей. Здесь нет попытки свести отраслевые требования к одной универсальной государственной системе.
Какие сравнения допустимы
Для артикулов и идентификаторов используется точное равенство. В учебной детали буква I заменена цифрой 1: DEMO-17I превратился в DEMO-171. Исправлять это автоматически опасно. В реальном каталоге оба артикула могут существовать.
У списка моделей порядок несущественен, но состав важен. ['DEMO-M1', 'DEMO-M2'] и переставленный список равны; появление DEMO-M3 — изменение совместимости. Дубли элементов тоже отмечаются, чтобы случайное повторение не терялось при превращении списка в множество.
Десятичное число передаётся строкой. 1.20 и 1.2 сравниваются через Decimal, поэтому разное оформление не считается изменением значения. При этом единица измерения хранится отдельным полем: совпадение числа не оправдывает замену л/га на мл/га. Валидатор не выполняет пересчёт единиц. Булево true, NaN и бесконечность десятичным значением не признаются.
Вот используемая логика сравнения:
В этом маленьком примере type(...) выбран намеренно: в Python bool является подклассом int, и слишком широкая проверка числового типа может принять его за число. Для рабочего каталога типы и ограничения удобнее описывать отдельной схемой. Это проверит форму входа; сравнение с актуальным эталоном всё равно понадобится.
Проверка карточки
Функция ниже возвращает список замечаний, а не разрешение на публикацию. Все поля из эталона обязательны. Неизвестный SKU и новое поле останавливают проверку. Смысл произвольного описания не анализируется.
Минимальный пример можно запустить без библиотек и обращения к ИИ-сервису: сохранить приведённый JSON как approved.json, а оба определения функций и следующий фрагмент — в Python-файл рядом с ним.
Что получилось на 16 случаях
Для полного прогона использованы файлы approved.json, candidates.json, validator.py и run_demo.py. Результаты записываются в CSV и JSON, поэтому замечания можно посмотреть по каждой карточке, а не только по итоговому счётчику.
Вывод запуска:
К стенду добавлены 14 тестов: они проверяют эти границы и некорректные типы входа. Все прошли. Число 12 из 12 здесь не следует превращать в «точность 100%». Ошибки выбраны самим разработчиком теста; неизвестных типов ошибок и реальных ответов моделей в наборе нет. Набор подтверждает поведение функций на перечисленных случаях, включая их известный пропуск.
Что делать с пропущенной фразой
Самый узкий вариант — не разрешать генератору писать произвольные заявления о применении и совместимости. Такие предложения собираются шаблоном из утверждённых полей. Модель может готовить общий вводный текст, но поля с последствиями для покупателя остаются вне свободной генерации.
Если свободное описание нужно, его следует проверять отдельно. Сравнение текста второй моделью может помочь найти спорные фразы, но её согласие не является подтверждением. Для публикации критичного утверждения нужны источник, понятная связь с товаром и решение ответственного специалиста.
В результате процесса полезно сохранять три разных состояния: поля сопоставлены; описание проверено; публикация утверждена. В стенде реализовано только первое. Название structured_fields_match оставлено длинным именно затем, чтобы его не приняли за итоговый статус карточки.
У кода есть ещё несколько границ. Он не узнает, что документ отозван: актуальность эталона должна обновляться отдельным процессом. Не проверит, что поставляемая партия соответствует карточке. Не распознает корректный синоним модели и не пересчитает единицы. Не рассчитает предельно допустимый размер входного файла и права доступа. Эти задачи нужны рабочей системе, но добавлять их незаметно в обещание «проверили ИИ» нельзя.
Для каталога такой валидатор — небольшой, понятный участок проверки. Он полезен тем, что одинаково обнаружит подмену поля, пришедшую от модели, из импорта CSV или после ручного редактирования. Случай C16 показывает, где этот участок заканчивается.