• Выпуск целиком по расписанию: сбор, отбор фактов, дедуп, текст, обложка, публикация
  • Первый прогон: 45 фактов из 10 источников, 3 выпуска по 5 сюжетов; к сентябрю источников стало 23
  • Голос держат запреты, но бан-список не спас, пока не сменили модель и режим рассуждения

Задача

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

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

Метод

Единица публикации теперь — выпуск, а не ссылка. Всё остальное следует из этого решения.

flowchart LR
    C[Сборщик:<br/>9 каналов + RSS] --> G{Редакционный<br/>шлюз}
    G --> F[Пул фактов:<br/>150–300 слов + фото]
    F --> W[Выпуск: кластеры,<br/>текст, обложка]
    G -. реклама, колонки .-> X[Отсев]

Два направления. События и город: дайджесты рендерятся из базы событий zdes.events, тем же голосом и той же дизайн-системой, с каноническим адресом на исходном домене, чтобы не плодить SEO-дубли. Креатив и технологии: собственный конвейер от источников до пермалинков и рубрик. Оба направления делят голос, обложки и расписание, но не делят данные.

flowchart LR
    E[База<br/>zdes.events] --> D[Дайджесты<br/>событий]
    S[Каналы<br/>и RSS] --> P[Креатив<br/>и технологии]
    D --> O[Общие голос,<br/>обложки, расписание]
    P --> O

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

собрать    ежедневно   факты 150–300 слов, фото с атрибуцией
дневной    вечером     5 сюжетов за день
недельный  четверг     5 сюжетов недели, кластеризация
релизы     пятница     только сюжеты с отметкой релиза

Источники и отбор фактов. Сборщик читает 9 каналов по ИИ, технологиям и медиа плюс трендовый RSS-фид. Дальше редакционный шлюз: срезаются реклама, авторские колонки, спецпроекты, викторины и слишком локальные истории. На выходе не текст, а факты на 150–300 слов с официальной ссылкой и фотографиями, у каждой из которых сохранён автор и источник. Первый прогон дал 45 свежих фактов из 10 источников, из них собрались 3 выпуска по 5 сюжетов.

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

flowchart LR
    N[Новости] --> M1{Модель<br/>ответила?}
    M1 -->|да| T[Выпуск]
    M1 -->|нет| X[Выпуск<br/>не выходит]
    E[События] --> M2{Модель<br/>ответила?}
    M2 -->|да| T2[Дайджест]
    M2 -->|нет| S[Статичный<br/>список событий]
    classDef stop stroke:#FF3600,stroke-width:2px
    class X stop

Голос на запретах. Голос «острый рассказчик» дистиллирован из 13 промптов о человеческом письме: журналистская точность, сухая ирония, пост как сюжет с нитью и ударной концовкой, редакционное «мы» вместо «я». Но держится он не описанием характера, а списком запрещённого: бан-лексикон («погрузимся», «уникальный», «стоит отметить», «в заключение»), запрет на искусственные выводы и передоз связок, метрики ритма (25–35% коротких предложений, 20–25% длинных, одно очень короткое на два-три абзаца). Это тот же принцип, что в персоне через запреты: предписание «пиши живо» модель трактует как угодно, запрет «без слова уникальный» проверяем.

Решающий фикс оказался не промптом. Прозу писала быстрая модель с принудительно отключённым режимом рассуждения, и штампы лезли вопреки бан-листу. Пишущие вызовы перевели на сильную модель с включённым рассуждением, классификация осталась на быстрой. Слепое сравнение на трёх форматах сделал человек.

flowchart LR
    B[Бан-лексикон] --> V[Голос<br/>острого рассказчика]
    R[Метрики ритма] --> V
    M[Сильная модель<br/>с рассуждением] --> V
    classDef key stroke:#FF3600,stroke-width:2px
    class M key

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

Обновление, сентябрь 2026. Иллюстрация перестала быть обложкой по умолчанию. Решение 18 сентября: реального контента больше, чем генерированного. Сгенерированные обложки дорогие, а единый стиль делает ленту однотипной, выпуски на взгляд перестают отличаться. Теперь обложкой становится фото из материалов источника, со ссылкой на первоисточник: кадр без текста и интерфейса, иначе первый ролик, и только если в источнике ничего нет — ризография. Отбор картинок двухшаговый: vision-модель сначала описывает кадр, потом дешёвый судья оставляет не больше двух на сюжет. Подпись ведёт на первоисточник, а не на канал, который новость пересказал. То, что судья пропускает, от заглушек ленивой загрузки до аватарок из соцсетей, нашёл визуальный аудит всех картинок сайта, и на каждый такой класс теперь стоит фильтр в коде.

Дедуп. В старом конвейере одна новость приходила тремя ссылками: анонс, репозиторий, демо. Каждая становилась отдельным постом с одинаковым контекстом. Сверху два независимых крона репостили в канал, и подписчик видел одно и то же дважды. Трёхслойный дедуп (нормализованный URL, хеш первых 2000 символов контента, уникальный индекс) ловил точные повторы, но не «одну новость в десяти пересказах». Радикальное решение: публикация по ссылкам удалена целиком. Факты складываются в пул, дедуп идёт на уровне кластеризации сюжетов перед выпуском, и повтор просто некуда положить. Заодно закрылся дрейф: сборщик переехал в репозиторий медиа и пишет напрямую в его базу, вместо того чтобы жить в соседнем деплое и отправлять ссылки через API.

flowchart TB
    subgraph before[Было: публикация по ссылкам]
        direction LR
        N1[Одна новость] --> L[Анонс, репозиторий,<br/>демо: 3 ссылки]
        L --> P3[3 поста и<br/>репосты дважды]
    end
    subgraph after[Стало: выпуск из пула фактов]
        direction LR
        F[Факты в пул] --> K[Кластер<br/>сюжета]
        K --> O[Один сюжет<br/>в выпуске]
    end
    before ~~~ after
    classDef bad stroke:#FF3600,stroke-width:2px
    class P3 bad

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

flowchart LR
    C[Сборщик] --> M[Маркер<br/>завершения]
    M --> W[Писатель]
    W --> P[Новый выпуск<br/>замещает старый]
    L[Блокировка] -.-> C
    L -.-> W

Обновление, сентябрь 2026. Кроме выпусков у медиа появились отдельные новости. После каждого из пяти ежедневных сборов отбирается до трёх, у каждой своя страница, и она встаёт в очередь Telegram-канала, около 12 постов в день; вечерний рекап ссылается на готовые сюжеты, а не пишет их заново. В Дзен новости уходят через RSS-фид с полными статьями.

flowchart LR
    C[Сбор<br/>5 раз в день] --> P[Отбор<br/>до 3 новостей]
    P --> N[Страница<br/>новости]
    N --> Q[Очередь канала:<br/>свежее и бэклог]
    N --> D[Дзен<br/>через RSS]
    classDef key stroke:#FF3600,stroke-width:2px
    class N key

Что делает человек. Выбирает голос (три кандидата на одном реальном посте, слепое сравнение). Решает, куда деплоить и когда пушить. Смотрит выпуски и даёт обратную связь раундами: «слишком пластиково», «обложки повторяются». Ведёт список источников. Не пишет, не публикует, не следит за расписанием.

Где ломается

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

Дрейф. Документация описывала сборщик, который никогда не был закоммичен, а рабочий жил в чужом деплое. Модельный дрейф тише: у одного провайдера бюджет рассуждения делит лимит выходных токенов, JSON обрезался посреди строки, и выпуск молча падал в фолбэк. Это видно только по базе, не по коду выхода.

Обновление, сентябрь 2026. Тихий пропуск оказался главным режимом отказа. В новостном конвейере zdes.events выпуск почти два месяца не выходил: лимит «не больше двух сюжетов от одного медиа» срабатывал уже после отбора и вырезал единственный ежедневный источник, а крон рапортовал успех. Лимит перенесли в промпт отборщика. В zdes.media на самом тяжёлом выпуске обрывался JSON отборщика, и выпуск без сюжетов молча пропускался; теперь парсер спасает целые объекты из обрезанного ответа, а вместо тишины появляется видимое предупреждение. Расходы держит выбор модели под задачу: массовые JSON-задачи ушли на лёгкую модель, проза — на среднюю, и ночной прогон стал примерно в 15–20 раз дешевле.

Источники. Девять каналов и один фид, который не чисто про тему, а про трендовый tech вообще. Сайты с рендером на клиенте простым фетчем не читаются, публичные страницы каналов отдают по несколько постов. Шлюз — тоже модель: реклама, оформленная как новость, проходит.

Обновление, сентябрь 2026. Шлюз пришлось учить не только отсеву, но и курсу. Замер за 30 дней показал перекос в разработку, и курс сменили на креативные индустрии. Он живёт в одной константе, которую импортируют сборщик, отборщик сюжетов и классификатор архива, и в ней же разведены коллизии: реклама как тема принимается, рекламный материал — нет; рынок труда как тема принимается, вакансия — нет. Источников стало 23. Классификатор прошёлся и по старому архиву и скрыл около 15% записей: портфолио, вакансии, рекламу; записи помечены, а не удалены, и откатываются одним флагом.

Итог

  • Единица публикации — выпуск, а не ссылка. Это одновременно убирает дубли, даёт формат и делает расписание осмысленным.

  • Голос держится запретами и метриками, но промпт не спасает слабую модель: бан-лист работает только вместе с режимом рассуждения.

  • Без модели выпуск не выходит; без человека — не меняется голос, источники и стиль. Автономия — в исполнении, не в редакционной политике.

  • Первый прогон: 45 фактов из 10 источников, 3 выпуска, голос держится; к сентябрю источников 23. Дальше вопрос не техники, а того, кто и как читает.

  • Самый опасный отказ автономного конвейера — тихий: крон рапортует успех, а выпуска нет. Лимиты ставятся до отбора или внутрь промпта, обрезка ответа должна быть видна.

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