· обновлено · 1 мин

Апрув контента кнопками в Telegram: когда конвейеру статей не нужна админка

Ручной апрув встраивают как одну точку остановки в конце конвейера: после автопроверки бот шлет в Telegram inline-кнопки 'Апрув' и 'Не апрув'. В callback_data кладут id и версию черновика (Telegram дает до 64 байт), апрув переводит статью в публикацию. Кнопки хватает, пока решение одно и всегда "да/нет"; админка нужна, когда действий на черновик много и они разные.

Апрув контента кнопками в Telegram вместо админки: callback_data, версии, защита от гонок и критерий, когда агентскому конвейеру хватает бота, а когда нужен свой веб-интерфейс.

Апрув контента кнопками в Telegram: когда конвейеру статей не нужна админка
В статье · 10 разделовразвернуть

Я собрал конвейер, который сам придумывает темы статей, пишет черновики, гоняет их через фактчек и скоркарту и публикует. Красиво. Одна проблема: между "нейронка написала" и "оно ушло в паблик" мне все-таки хочется сказать "да" своими руками. Не открывать ноут, не логиниться в админку - а просто ткнуть кнопку в Telegram с телефона. Вот про эту кнопку и рассказ.

Что такое human-in-the-loop простыми словами

Human-in-the-loop - это когда автоматическая система в ключевой точке останавливается и ждет решения человека, вместо того чтобы гнать до конца сама. AI-агент делает черновую работу, а человек ставит финальную галочку: годится или нет. В моем случае агент пишет и проверяет статью, а я решаю, публиковать ли ее.

Звучит как здравый смысл, но по цифрам это еще и мейнстрим. Gartner прогнозирует, что к 2026 году 90% корпоративных генеративных AI-приложений будут требовать формального human-in-the-loop процесса. То есть полностью автономная генерация контента - это пока красивая мечта, а на земле почти везде остается человек, который жмет "апрув". Я не корпорация, но грабли ровно те же.

Зачем ручной апрув в автоматическом конвейере статей

Затем, что нейронка уверенно врет. У меня перед публикацией стоит своя проверялка - отдельная стадия гоняет каждый источник из факт-листа по URL и выставляет черновику балл по скоркарте (контент, SEO, E-E-A-T, техничка, цитируемость). Если балл ниже порога, статья даже до меня не доедет, ее развернет авторевизия.

Но пройденная скоркарта - это не "статья хорошая". Это "статья не завалила формальные проверки". Тон, неудачная шутка, факт, который технически с источником, но по сути кривой, - все это ловится только глазами. Поэтому последний шаг я себе оставил. Автоматика доводит черновик до состояния "можно смотреть", а публикует его только после моего "да". Один человек, одна галочка, один барьер перед паблишем.

Можно ли этот барьер убрать и публиковать все подряд? Технически да. Но я пробовал доверять генерации вслепую на другом проекте и получил пару статей, за которые потом было стыдно. Больше не хочу.

Почему кнопки в Telegram удобнее админки

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

С кнопкой в боте это выглядит так: падает уведомление "К публикации: заголовок такой-то, черновик #42, скор 88", под ним две кнопки - "Апрув" и "Не апрув". Я жму прямо из ленты, лежа на диване с телефона ) Ноль логинов, ноль переключений контекста.

Ключевая мысль: админка и кнопки не конкурируют, они про разные режимы. Админка - это когда я сел разбираться, читать текст целиком, оставлять правки. Кнопка - это когда мне нужно только финальное решение по уже проверенному черновику. 90% апрувов - это второй сценарий, и глупо ради него каждый раз поднимать всю панель.

Как устроен апрув кнопками: callback_data, версии, идемпотентность

Кнопка в Telegram - это inline-клавиатура, где у каждой кнопки есть поле callback_data: строка, которая прилетит боту при нажатии. Я зашил в нее все, что нужно, чтобы понять, что человек нажал:

apprv:<id_черновика>:<версия>:<ok|no>

Например, apprv:42:3:ok - апрув третьей версии черновика №42. Тут важное ограничение платформы: Telegram разрешает в callback_data максимум 64 байта (по официальной документации Bot API). Моя строка - это несколько цифр и пара слов, влезает с огромным запасом. Правило простое: не пихать в callback_data сам контент, только идентификаторы, а тело брать из базы по id.

Дальше три вещи, из-за которых это не разваливается на проде.

Первое - кому вообще слать кнопку. Рассылку запускает конец cron-проверки: после того как черновик получил проходной балл, отдельный скрипт делает approvals.py sweep. Он выбирает только черновики в статусе review, с актуальной пройденной скоркаркой именно этой версии, и которым кнопку этой версии еще не слали. Последнее условие - через отдельную табличку отправленных запросов, так что даже если sweep дернется десять раз, одна кнопка уйдет ровно один раз. Идемпотентность руками, но зато предсказуемо.

Второе - версии. В callback_data лежит номер версии не просто так. Пока кнопка висела у меня в чате, я мог зайти в админку, оставить правку, и черновик пересобрался - стал версией 4. Если я потом тыкну старую кнопку от версии 3, апрув отклонится как устаревший: "черновик изменился, дождись новой кнопки или апрувь в админке". Без этой проверки я бы одним нажатием опубликовал позавчерашний текст поверх свежих правок. Классические гонки, только в интерфейсе.

Третье - кто ловит нажатие. Само нажатие обрабатывает мой Telegram-бот на сервере: разбирает callback_data и дергает draft-approve или draft-decline в базе. Апрув переводит черновик в статус approved (дальше его подхватит паблишер), не-апрув оставляет запись о решении, но статью не трогает. Бот тонкий: вся логика "можно ли апрувить" живет в базе, а не в обработчике кнопки. Так проще - клавиатуру и бота можно переписать, а правила остаются в одном месте.

Где кнопки не заменяют админку

Не заменяют там, где нужна не галочка, а правка. Кнопка умеет ровно два ответа: да и нет. Если я жму "Не апрув", черновик просто остается в review - это не команда "перепиши", а "я пока не готов это публиковать".

Реальные правки идут не с кнопки, а из веб-админки: я оставляю коммент к черновику, отдельная стадия эти комменты читает и вносит изменения, бампает версию, снова гоняет через скоркарту. И вот когда новая версия получает проходной балл, конвейер сам присылает мне новую кнопку. Круг замкнулся: кнопка - для финального "да", админка - для содержательного диалога с текстом.

Поэтому у меня не "бот вместо админки", а "бот перед админкой". Кнопка снимает 90% рутинных апрувов, где смотреть особо нечего - проверки прошли, заголовок норм, жми и лети. А в панель я захожу только когда текст реально надо править руками.

Нужен ли агентскому конвейеру свой веб-интерфейс или хватит бота

Считать надо не цену верстки, а сколько решений принимает живой человек и насколько они разные. Одно решение на черновик, всегда "да" или "нет" - хватает бота с кнопками. Несколько разных действий на каждый объект (одобрить, поправить мету, перепроверить, посмотреть историю) - это уже админка. А если человека в контуре нет и агент читает вывод другого агента, не нужно ни того, ни другого: хватает CLI и cron.

У меня в одном репозитории блог-конвейера живут все три варианта.

Машинная часть - scripts/bp_db.py, 482 строки и ни одного экрана. Через него cron собирает беклог тем, генерирует черновики и скорит их: агент дергает topics-list, берет тему, пишет черновик, кладет в базу, следующий джоб подхватывает без всякого "нажми кнопку". Показывать форму тут просто некому.

Финальное "да" - кнопки, про которые вся статья выше.

Правки и все остальное - FastAPI-админка. Первый коммит feat(admin): каркас FastAPI-админки - auth, login, сессии я сделал 13 июля 2026. К 4 августа в истории было 23 коммита с пометкой feat(admin) или fix(admin), а в admin/*.py - 2168 строк плюс 9 HTML-шаблонов. Фичи заезжали мелкими партиями почти месяц: логин, список черновиков с чипами-фильтрами и сортировкой по вниманию, карточка черновика со sticky-действиями, блок скоркарты с кнопками "Перепроверить" и override, SEO-страница со спарклайнами по запросам, включение и выключение каналов сбора тем, квота генерации и ритм публикаций, дашборд с блоками "ждут фактов" и "пустой день по ритму". Ни одну из них я не планировал заранее. Каждая появлялась, когда процесс подкидывал мне новое ручное действие, причем на каждой статье, а не разово. Проще было добавить экран, чем помнить очередной флаг для операции, которую делаешь по нескольку раз в неделю.

Если главная работа человека в конвейере - не писать, а принимать сделанное агентом, ему нужен инструмент именно под приемку. Подробнее про эту роль - в статье про мясной мешок в агентском пайплайне.

Агенты же удешевили GUI - почему бы не сделать админку сразу

Летом 2026 Томас Птачек написал пост в духе "хватит делать TUI": кодерский агент теперь верстает нормальный интерфейс почти бесплатно, так что одноразовые CLI-утилиты пора превращать в приложения. Саймон Уиллисон его разобрал и в целом согласился. Наполовину я с ними тоже согласен. Агент вроде Claude Code или Cursor правда накидывает форму, таблицу и обработчик кликов за один прогон, раньше на это уходил вечер.

Только стоимость GUI никогда не сидела в верстке. Она в решениях: что показать, кому что можно и что будет, если нажать не туда. Агент спокойно нарисует "список черновиков с фильтром по статусу". А вот что "Перепроверить" должна заново гонять blog-check, а не перекрашивать бейдж, или что override скора требует отдельного подтверждения, а не одного клика, он сам не решит. Случайный клик по override пускает в публикацию статью, которая гейт не прошла. Такое знаешь, только если уже обжегся на слишком легкой кнопке.

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

Как понять, что бота с кнопками уже мало

Мой тест такой: открыть свой скрипт и посчитать ветки вида "если человек хочет X, передай флаг --Y". В bp_db.py команды topics-list, draft-create, comment-add, topic-done вызывает либо cron, либо я сам раз в сессию - терпимо. Когда таких ручных флагов больше десятка и каждую неделю добавляются новые, экран обойдется дешевле, чем объяснять все это себе и агенту каждый раз заново.

С ботом то же самое. Пока под сообщением две кнопки, все хорошо. Захотелось третьей "поправь заголовок" и четвертой "перегенери лид" - значит, вы уже запихиваете админку в чат, и пора делать нормальную.

Выводы

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

А я пойду допиливать сам конвейер дальше. Следите за новостями.

Частые вопросы

Что положить в callback_data кнопки, чтобы не упереться в лимит Telegram?

Только идентификаторы, не сам контент. Telegram дает под callback_data 64 байта, поэтому в нее кладут id и версию записи (например apprv:42:3:ok), а полный текст берут из базы по id. Так строка влезает с запасом и не ломается на длинных данных.

Что будет, если нажать кнопку апрува после того, как черновик изменился?

Апрув устаревшей версии отклоняется. В callback_data зашита версия черновика: если после отправки кнопки текст пересобрали, старое нажатие вернет ошибку 'черновик изменился' и попросит дождаться новой кнопки или апрувнуть в админке. Это защита от гонки, когда публикуешь позавчерашний текст.

Когда агентскому конвейеру хватает бота с кнопками, а когда нужен свой веб-интерфейс?

Считать надо не цену верстки, а решения человека. Одно решение на черновик, всегда 'да' или 'нет' - хватает кнопок в боте. Несколько разных действий на каждый объект (поправить, перепроверить, посмотреть историю) - нужна админка. Человека в контуре нет - хватает CLI и cron. Сигнал к админке - больше десятка ручных флагов в скрипте, и их число растет каждую неделю.

Как готовился материал: черновик собран моим AI-конвейером по темам и заметкам из практики, факты сверены с первоисточниками, финальный текст я прочитал, поправил и утвердил перед публикацией. Обложку к статье тоже рисует нейросеть. Про сам конвейер — в разделе обо мне.

Влад Новиков
Пишу про AI-разработку без глянца. Новые разборы — сначала в канале.
Подписаться