В отчёте по остаткам — 180 строк одного и того же обручального кольца. Не потому, что учёт ведётся плохо, а потому, что он ведётся правильно: у каждой партии и размера своя карточка номенклатуры, свой код, свой вес и своя себестоимость. Для учёта это норма. Для аналитики — катастрофа: руководитель не может увидеть «этой модели на складах 180 штук», он видит сто восемьдесят разных наименований по одной штуке.
Эта статья — о том, как мы свернули 1 622 фотографии ювелирных изделий в 289 визуальных сегментов с помощью мультимодальной модели со зрением, не разворачивая никакой инфраструктуры: ни своих нейросетей, ни векторных БД, ни GPU. Со стороны 1С — только платформенные средства: БСП, СКД, внешние обработки и одно расширение на кнопку. Разберём промпты, инженерные грабли и то, почему самой ценной оказалась функция, которую никто не заказывал.
Задача, которую нельзя решить текстом
Первое, что пробует каждый, кто сталкивается с «размазанной» номенклатурой, — сгруппировать по наименованию или артикулу. В нашем случае это не работало по трём причинам:
- в наименованиях зашиты партия, размер и вес — «Кольцо <модель> (071024/1) (750, 16,5, 4,8)» и «Кольцо <модель> (080824/9) (750, 15, 4,7)» — это один дизайн, но разные строки;
- артикулы у каждой партии свои;
- группы справочника организованы по поставщикам и коллекциям, а не по внешнему виду.
Единственный признак, по которому изделия действительно одинаковые, — то, как они выглядят. То есть фотография.
Почему нельзя решить руками
«Найти среди фотографий одинаковые» — это не просмотр списка, это попарное сравнение. Для 1 622 фотографий честное «каждая с каждой» даёт:
C(1622, 2) = 1622 × 1621 / 2 = 1 314 631 пара
Больше 1,3 миллиона сравнений — и это на срезе, который составляет примерно восьмую часть базы: всего в ней 12 000+ фотографий. Даже более умный ручной подход — раскладывать по «кучкам» на глаз, около двух минут на фотографию, — даёт ≈ 54 часа на срез и ≈ 400 часов на всю базу. Два с половиной месяца работы.
И это не главная проблема. Главная — невоспроизводимость: два сотрудника разложат один и тот же ассортимент по-разному, и один и тот же сотрудник в понедельник и в пятницу — тоже по-разному. А каждое новое поступление требует раскладывать снова.
Архитектура: два EPF по краям, AI посередине
Пайплайн из пяти звеньев. Со стороны 1С — две внешние обработки, между ними — три скрипта:
1С ^72;^72;выгрузка^72;^72;> фото + метаданные ^72;^72;Vision-модель^72;^72;> категории
^74;
консолидация синонимов
^74;
выбор фото-обложки
^74;
1С <^72;^72;импорт^72;^72; сегменты + привязка <^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^72;^96;
Никакой инфраструктуры: ни векторной БД, ни эмбеддингов, ни обучения моделей. Классификация делается готовой мультимодальной моделью через API, промежуточные результаты — обычный JSON, который читается глазами и версионируется.
Этап 1. Выгрузка фотографий — штатное API БСП
Внешняя обработка обходит присоединённые файлы номенклатуры через подсистему БСП «Работа с файлами», достаёт двоичные данные изображений и складывает их в каталог. Рядом пишется файл соответствия: на каждое фото — код номенклатуры, наименование, артикул и полный путь в иерархии справочника.
Имя файла собирается как <КодНоменклатуры>_<ИсходноеИмя>.<расширение> — код в имени позволяет связать файл с номенклатурой, даже если файл соответствия потеряется. Путь в иерархии в классификации не участвует (об этом ниже — это принципиально), но незаменим при разборе спорных случаев: сразу видно, из какой коллекции пришёл товар.
Этап 2. Классификация: два правила, которые определили качество
Каждое изображение уходит в модель со зрением. Промпт задаёт роль эксперта по визуальной классификации ювелирных изделий и требует определить тип изделия (кольцо, серьги, колье, браслет, цепь…), форму и силуэт, вставки, плетение для цепей, особенности конструкции. Ответ — категория из 2–5 слов на русском: «Кольцо фасонное ажурное», «Цепь плетение Бисмарк».
Дальше — два правила, на которых держится весь результат.
Правило 1: «анализируй только визуал». Промпт прямо запрещает использовать текстовую информацию. Стоит дать модели наименование — и она начинает классифицировать по названию коллекции, а не по внешнему виду. Весь смысл теряется: одинаковые изделия из разных коллекций разъезжаются по разным сегментам.
Правило 2: «металл и пробу не указывать». Отличить белое золото 750 от серебра 925 по фотографии практически невозможно — модель будет уверенно угадывать, и уверенно ошибаться. Зато эти данные точно известны из учёта. Поэтому ответственность разделена:
Имя сегмента = <визуальный дизайн от AI> + <металл из 1С> + <проба из 1С> «Кольцо обручальное гладкое» + «Золото» + «750» → «Кольцо обручальное гладкое Золото 750»
Модель отвечает за то, что видит; учётная система — за то, что знает. Каждый источник делает то, в чём он надёжен. По нашему опыту это применимо к любой задаче связки «AI + 1С»: как только модели поручают угадывать то, что лежит в реквизитах, качество рушится.
Сам промпт батча выглядит так:
Классифицируй N изделий ТОЛЬКО по фотографиям.
Не используй текстовую информацию — анализируй только визуал.
Не указывай металл (золото/серебро) и пробу (585/750/925) в категории.
Фото пронумерованы от 1 до N.
Ответ строго в формате JSON-массив:
[{"index": 1, "segment": "Категория", "description": "Краткое описание"}, ...]
Только JSON, без markdown-блоков, без пояснений.
Плюс системная часть со списком из девяти эталонных примеров категорий. Примеры задают нужный уровень детализации — без них модель уходит либо в «Кольцо» (слишком грубо), либо в «Кольцо с четырьмя круглыми фианитами в белой оправе» (слишком дробно — каждое фото становится собственным сегментом).
Технические параметры: изображения уходят батчами по 5 штук за запрос, temperature = 0.3 (нужна воспроизводимость, а не креатив). Почему именно 5: по одному — квоты API сгорают впятеро быстрее, по 20 — ответ упирается в лимит выходных токенов и обрывается на середине, теряя весь батч.
Этап 3. Консолидация синонимов
Модель, вызванная на разных батчах, формулирует одно и то же по-разному: «Цепь Бисмарк» и «Цепь плетение Бисмарк», «Серьги-пусеты» и «Серьги пусеты». Второй проход — уже текстовый, без изображений — получает полный список категорий с количеством товаров в каждой и схлопывает синонимы в канонические названия.
Правила жёсткие и прописаны явно: объединять только очевидные синонимы; не объединять разные камни («с бриллиантами» ≠ «с фианитами»); не объединять разные типы («пусеты» ≠ «длинные»). temperature = 0.1 — здесь нужна максимальная предсказуемость.
Обязательный элемент — режим предпросмотра: сначала показать, что именно будет объединено, и только после подтверждения применять. Это спасло от пары ложных склеек ещё на пилоте.
Отдельная механика — склейка по хэшу изображения. Один и тот же снимок нередко привязан к нескольким карточкам номенклатуры; байтово идентичные файлы объединяются до AI-обработки. Это экономит запросы и гарантирует, что физически одинаковые фото не разъедутся по разным сегментам из-за случайности в ответе модели.
Этап 4. Выбор обложки
У сегмента из 180 позиций — 180 фотографий, а в интерфейсе нужна одна. Модель получает выборку фотографий сегмента и выбирает лучшую по критериям: резкость, освещение, изделие в кадре целиком, чистый фон, типичность для сегмента. Сегменты с единственной фотографией размечаются без обращения к модели — незачем тратить запрос там, где выбора нет.
Этап 5. Импорт в 1С: справочник + регистр сведений
Вторая обработка читает результаты и создаёт в базе:
- иерархический справочник сегментов — AI-результаты складываются в отдельную группу, чтобы не смешиваться с сегментами, заведёнными вручную; у каждого сегмента — фото-обложка;
- независимый регистр сведений «Номенклатура → Сегмент»: измерение «Номенклатура», ресурс «Сегмент».
Регистр сведений вместо реквизита справочника — осознанный выбор: конфигурация остаётся на поддержке, а привязка джойнится в любом запросе и подключается к любой СКД без доработок. Добавить разрез «сегмент» в существующий отчёт — это соединение с регистром и новое поле группировки, а не переписывание отчёта.
Металл и проба живут в дополнительных реквизитах номенклатуры (значения свойств из плана видов характеристик). Наивная реализация — запрос в цикле по каждой позиции — на полутора тысячах номенклатур работает минутами. Сделано иначе: один пакетный запрос по всем кодам сразу, результат — в соответствие «код → структура(Металл, Проба)», дальше подстановка идёт в памяти. Один заход к базе вместо полутора тысяч.
Форма выбора сегмента: фото-обложка, поиск, счётчик привязанных товаров. Имя сегмента собрано из двух источников: дизайн определила модель, металл и пробу подставила 1С.
Инженерные грабли: что ломалось и как закрыли
Самое интересное в связке с внешним API — не счастливый путь, а то, что происходит на четвёртом часу прогона.
| Проблема | Решение |
|---|---|
| Многочасовой прогон упирается в лимиты API и обрывается | Возобновление с места остановки: при старте читается уже готовый результат, обрабатываются только новые файлы. Ключ — имя файла. Результат сохраняется после каждого батча, а не в конце прогона |
| Модель возвращает обрезанный JSON, упёршись в лимит токенов ответа | Четырёхступенчатый разбор: снять markdown-обёртку → распарсить как есть → вырезать массив по первой «[» и последней «]» → достроить обрезанный JSON до последнего целого объекта и добавить «]» |
| Одна и та же категория сформулирована по-разному в разных батчах | Отдельный проход консолидации с предпросмотром замен |
| Один снимок привязан к нескольким карточкам | Склейка по хэшу файла до AI-обработки |
| Модель ошибается на нетипичных ракурсах и плохих фото | Товар остаётся без сегмента и попадает в инструмент ручной досегментации. Ошибки локализованы и видны, а не размазаны по базе |
Четвёртая ступень разбора JSON выглядит избыточной ровно до первого многочасового прогона: каждый спасённый батч — это запросы, которые не пришлось тратить повторно.
И правило, которое дороже всех остальных: перед каждым необратимым шагом — снимок состояния в отдельный файл. До консолидации, до склейки по хэшу, до ручного объединения. Любой шаг откатывается копированием файла. Весь пайплайн отрабатывается на копии базы; в продуктив отдельной обработкой переносится только принятый результат.
Ручная досегментация: AI закрывает массу, но не всё
Остаток — товары без фото, единичные изделия, неудачные ракурсы — разбирается вручную. Для этого сделана обработка с тремя вкладками: пакетное назначение сегмента выделенным строкам (с предпросмотром фото), переопределение обложки со сравнением «фото сегмента / фото товара» бок о бок, объединение сегментов-синонимов, которые не схлопнула консолидация.
Принципиально, что ручной разбор — это заложенная в процесс стадия, а не признак провала автоматики: модель честно оставляет спорное без сегмента, вместо того чтобы уверенно ошибаться.
Побочный эффект, который оказался ценнее отчётов
Сегменты делались ради аналитики. По ходу проекта выяснилось, что у них есть применение, которого никто не заказывал: сегмент — это готовый шаблон для заведения новых товаров.
Пришла новая партия колец той же модели. Раньше менеджер заполнял карточку руками: десяток реквизитов, дополнительные свойства, фотография. Теперь — создаёт карточку, жмёт «Заполнить из сегмента» (кнопка добавлена расширением конфигурации), выбирает сегмент по фото-обложке — и получает заполненный товар.
Кнопка «Заполнить из сегмента» в карточке создания номенклатуры.
Из образца переносятся наименование, вид кодировки, реквизиты карточки, дополнительные свойства и фотография — причём фото копируется ссылкой на те же двоичные данные: новая запись присоединённого файла указывает на существующий файл, дублирования в томах хранения не происходит. Побочный эффект, о котором пришлось предупредить в инструкции: удаление файла у образца уберёт картинку у всех товаров, заполненных из этого сегмента.
Самая недооценённая часть — обучаемый образец. У каждого свойства в форме параметров есть флаг «В сегмент»: отмеченные значения записываются не только в новый товар, но и в эталон сегмента — и подставляются автоматически при создании следующих товаров. Заполнили «Материал» у первого товара сегмента — у второго и всех последующих он подставится сам. Чем дольше живёт сегмент, тем меньше в нём ручного ввода.
Флаг «В сегмент»: значение свойства записывается в эталон и подставляется у следующих товаров.
Результаты
| Показатель | Значение |
|---|---|
| Обработано фотографий | 1 622 |
| Позиций номенклатуры | 1 594 |
| Получено визуальных сегментов | 289 |
| Коэффициент свёртки отчёта | 5,6× |
| Сегментов с 10 и более позициями | 34 |
| Ручная разметка того же среза (оценка) | ≈ 54 часа |
| Машинное время пайплайна | несколько часов |
Крупнейший сегмент — «Кольцо обручальное гладкое», 180 позиций: одна строка в отчёте по остаткам вместо ста восьмидесяти. Десять верхних сегментов свернули почти половину всего среза.
Про деньги на инференс: пилот уложился примерно в 475 запросов к модели и прошёл в рамках бесплатного тарифа провайдера. При переходе на всю базу в 12 000 фотографий платный тариф на модели класса Flash — величина порядка нескольких долларов. В экономике проекта стоимость инференса — величина пренебрежимая; вся стоимость — это инженерная работа.
Ограничения — честный список
- Фотографии должны быть в базе. Пайплайн работает с присоединёнными файлами номенклатуры. Если фото лежат в папке на диске или «у поставщика» — сначала их импорт, это отдельная задача.
- Модель ошибается на нетипичных ракурсах и фото с несколькими изделиями в кадре. Это заложено в процесс: спорное уходит в ручной разбор.
- Промпт настроен под ассортимент. Для одежды, автозапчастей или посуды нужен свой список типов и характеристик — это адаптация, а не «поменять одну строчку».
- Границу сегмента определяет заказчик. «Кольцо с бриллиантом 0,3 карата» и «0,5 карата» — один сегмент или два? Ответ зависит от того, как принимаются решения по остаткам, и согласуется до прогона.
- Товары без фотографий не сегментируются автоматически — только вручную.
- Зависимость от внешнего API заканчивается вместе с прогоном: сегменты и привязка — обычные объекты 1С, для отчётов и «Заполнить из сегмента» внешние сервисы не нужны. При запрете на передачу изображений наружу пайплайн переводится на локальную модель со зрением — меняется класс провайдера, не архитектура.
Исходный код
К статье приложен рабочий скелет пайплайна — classify_images.py, один читаемый файл на Python со всеми механиками, о которых шла речь: батчевая классификация с системным промптом и эталонными примерами, возобновление с места остановки, четырёхступенчатый разбор обрезанного JSON, склейка дублей по хэшу, консолидация синонимов с режимом --dry-run и выбор обложек. Провайдер модели изолирован в одной функции — заменяется на любой API со зрением, включая локальные модели. В комплекте README и пример файла соответствия.
Чего в архиве сознательно нет — 1С-стороны: обработок выгрузки и импорта, отчётов по сегментам и механики «Заполнить из сегмента». Их логика описана в статье, а реализация сильно зависит от конфигурации; выгрузка фотографий делается штатными средствами БСП «Работа с файлами» за вечер. Скрипт же универсален: промпт настраивается под любой визуально опознаваемый ассортимент — от одежды до автозапчастей.
Вместо заключения
Главный вывод проекта — не про ювелирку и даже не про конкретную модель. Он про разделение ответственности: AI хорош там, где нужно смотреть, и плох там, где нужно знать. Как только визуальную классификацию отделили от учётных данных — металла, пробы, реквизитов — качество стало предсказуемым, а ошибки локализуемыми.
Если у вас похожая задача — «одна модель, десятки карточек» в любом визуально опознаваемом ассортименте, — задавайте вопросы в комментариях: расскажу подробности про промпты, структуру данных или устойчивость пайплайна. И интересен опыт коллег: пробовал ли кто-то решать сегментацию номенклатуры эмбеддингами и векторным поиском вместо прямой классификации?
Вступайте в нашу телеграмм-группу Инфостарт