• Обложка генерируется из названия и категории события за один вызов, около 10 секунд
  • Промпт собирается по осям A.O.C. поверх фиксированного стилевого префикса листинга
  • Невидимый суффикс держит композицию full-bleed, а белую обводку срезает авто-кроп

Задача

В zdes.events события приходят двумя потоками: организаторы создают их руками, а агрегатор подтягивает публичные анонсы из открытых афиш и каналов организаторов. У большой части нет картинки вообще, у остальных она случайная: скриншот афиши, фото с телефона, чужой постер с текстом. Листинг из таких карточек выглядит как доска объявлений, а не как продукт с собственным лицом.

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

Приём

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

flowchart LR
    L{Листинг} -->|блог, подборки| R[Ризография<br/>в фирменных цветах]
    L -->|события руками| B[Случайное из ~14<br/>арт-направлений]

Второй слой — содержательный промпт — пишет языковая модель по мета-промпту арт-директора, построенному на осях A.O.C.: что в кадре (одна ситуативная виньетка из темы события), как скомпоновано (фокус-субъект и негатив-спейс), как освещено (физический свет и ограниченная палитра, mood-слова вроде «moody» и «epic» запрещены). Мета-промпт получает локаль страницы и пишет промпт на её языке; статический фолбэк на случай недоступности модели тоже двуязычный.

[стилевой префикс листинга]
ANCHOR: одна ситуативная виньетка из темы события
COMPOSITION: один фокус-субъект, негатив-спейс
CHEMISTRY: физический свет, ограниченная палитра, без mood-слов
STYLE: в духе {направление из банка}
[невидимый суффикс: the image IS the artwork, bleeds to all four
 edges, not a photo of a poster, no mockup / frame / margin,
 no text, no logos, no watermarks]

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

flowchart TB
    subgraph before[Первая версия суффикса]
        direction LR
        V1[Слово poster<br/>в промпте] --> R1[Лист на серой<br/>стене с тенью]
    end
    subgraph after[Рабочая версия]
        direction LR
        V2[Изображение и есть<br/>работа, до краёв] --> R2[Арт на весь кадр,<br/>без рамок и мокапов]
    end
    before ~~~ after
    classDef key stroke:#FF3600,stroke-width:2px
    class R1 key

Генерация one-shot: нажал кнопку, через ~10 секунд обложка стоит на форме, диалог закрылся. Превью, «Заново» и «Использовать» убраны намеренно: цикл выбора из вариантов превращает быстрый инструмент в редактор, а если результат не нравится, проще нажать ещё раз. Из той же логики и бюджет: 5 генераций в месяц на пользователя, слот возвращается, если вызов упал. Модель, Nano Banana 2, выбрана после замера: предыдущий кандидат отдавал картинку примерно за 45 секунд — это в четыре с лишним раза дольше.

flowchart LR
    K[Кнопка] --> G[Nano Banana 2:<br/>~10 секунд]
    G --> O[Обложка на форме,<br/>диалог закрыт]
    O -. не нравится .-> K
    G -. вызов упал .-> S[Слот из 5<br/>возвращается]

Последний узел — автоматический кроп. Модель, несмотря на запрет в промпте, периодически добавляет тонкую кремовую «печатную» рамку по периметру. Усиление промпта («zero print margin, no keyline») помогало ненадёжно, поэтому кроп сделан в браузере на шаге конвертации. Каждая сторона проверяется на near-white (яркий пиксель с низкой насыщенностью, чтобы закатное небо не считалось рамкой); если хотя бы две стороны согласны, срезается максимальная найденная ширина плюс 2 пикселя равномерно со всех четырёх сторон. Первая версия усредняла цвет четырёх углов и на частичной рамке получала грязный референс, ничего не кропала; абсолютный порог решил это. На контрольном примере 928×1152 с рамкой стало 872×1096 с артом до краёв.

flowchart LR
    S[Каждая сторона:<br/>near-white?] --> Q{Согласны<br/>хотя бы две?}
    Q -->|да| C[Срез: макс. ширина<br/>+2 px со всех сторон]
    Q -->|нет| N[Без кропа]
    C --> R[928×1152 стало<br/>872×1096]
    classDef key stroke:#FF3600,stroke-width:2px
    class C key

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

flowchart LR
    V1[Западный город<br/>по умолчанию] --> V2[Советская<br/>открытка]
    V2 --> V3[Перегруженные<br/>панорамы с текстом]
    V3 --> V4[Одна виньетка +<br/>негативный гард]
    classDef key stroke:#FF3600,stroke-width:2px
    class V4 key

Где ломается

  • Текст в картинке. Запрет в суффиксе снижает частоту, но не исключает буквы и псевдонадписи, особенно когда название события само похоже на вывеску. Фильтра на выходе нет — ловит глаз организатора.
  • Повторяемость. Фиксированный префикс даёт единый язык, но при одинаковой категории модель тянется к одним и тем же сюжетам. В подборках обложки несколько раз подряд показывали одно животное, потому что сюжет брался из заголовка выпуска; пришлось строить его по разбросу историй и банить маскотов.
  • Стоимость и бюджет. Генерация заметно дороже классификации текстом, отсюда лимит на пользователя и доступ только авторизованным. Для тысяч агрегированных событий это означает выбор между обложкой и её отсутствием, а не между вариантами.
  • Кроп по эвристике. Порог 50% и требование двух согласных сторон настроены на конкретную модель. Светлый арт с белыми краями может потерять пару пикселей, а рамка нестандартного цвета пройдёт.

Обновление, сентябрь 2026. В zdes.media тот же приём — фиксированная ризография плюс сюжет по разбросу историй — в сентябре отступил на роль запасного. Сгенерированная обложка дороже подбора, а единый стиль делает ленту однотипной: выпуски на взгляд перестают отличаться. Теперь обложкой становится фото из материалов источника, со ссылкой на первоисточник, а ризография рисуется, только если в источнике ничего не нашлось. Генерация закрывает дыру там, где картинки нет вообще, но не заменяет настоящую.

Итог

  • Обложка из названия и категории закрывает главную дыру листинга: событие без картинки.
  • Стиль вынесен в префикс, содержание — в A.O.C.-промпт; их можно менять независимо.
  • Модель нарушает «no border» и «no poster» достаточно часто, чтобы страховать их кодом, а не только промптом.
  • Один вызов без выбора из вариантов быстрее и честнее для быстрого инструмента, чем галерея превью.

© Александр Никулин. Цитирование — с указанием автора и ссылкой · Версия для LLM