- Выпуск целиком по расписанию: сбор, отбор фактов, дедуп, текст, обложка, публикация
- Первый прогон: 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