Что было до нас

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

Клуб просил сервис, который сам пишет и сам выкладывает. С тремя жёсткими условиями.

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

Как это работает

Клуб задаёт рубрики и дни выхода: «каждый вторник в 11:00 история участника». Сервис раскладывает эти правила на сетку недель вперёд и подсвечивает пустые места. Календарь показывает, где плана нет.

За три дня до даты нейросеть пишет черновик. Отдельно под Telegram, отдельно под ВКонтакте, своей длиной и своим форматом. Опирается на четыре слоя правил: профиль клуба, правила русского текста, формат площадки и саму тему.

Факты, даты, имена и ссылки берутся из реестра фактов клуба. Чего в реестре нет, помечается как «уточнить». Выдумывать цифры нейросети негде.

Готовый текст проверяет код. Вторую нейросеть для проверки не звали, это срезало клиенту примерно треть счёта. Дальше человек читает, правит кнопками «короче», «переписать иначе», «адаптировать под VK» и жмёт одну кнопку «Подтвердить». После этого пост выходит сам.

!
Граница слова в JavaScript не работает с кириллицей

Проверка стоп-слов стояла на регулярке с \b. На русском тексте эта граница просто не срабатывает: ключевая фраза из списка клуба молча проходила бы линтер насквозь. То есть проверка, ради которой строился весь контур, пропускала бы именно то, ради чего её и ставили. Границы переписали через [^\p{L}] с флагом u, на реальных фразах написали отдельный тест. Поймали это на прогоне, до передачи проекта.

Деньги остаются у клиента и видны в рублях

Ключ клуба хранится зашифрованным (AES-256-GCM). Отправлять его сервис может только на адреса из белого списка. Каждый вызов нейросети записывается в рублях, у месяца есть лимит: по умолчанию 2000 ₽, на 80% предупреждение, на 100% отдельное подтверждение «это будет сверх лимита».

Цена видна до генерации. Пост в 1650 знаков стоит 3 копейки на дешёвой модели, около 14 копеек на модели по умолчанию и 60 копеек на самой дорогой. Наполнение плана восемью темами обошлось в 8 копеек. При 40 постах в месяц прогноз расхода от 600 до 1800 ₽.

Предохранители против репутационной ошибки

Живая лента клуба читается людьми, поэтому робота обвесили ограничителями. Часть из них появилась после разбора рисков ещё до кода.

  • Идемпотентность: двойной клик и двойной тик расписания дубля не дают.
  • Холостой ход канала: сервис делает всё, кроме самой отправки. Отладка идёт мимо живой ленты.
  • Стоп-кран «Остановить всё», окно тишины с 08:00 до 21:00, стоп-дни, суточные лимиты на канал.
  • Канал отключается сам после двух ошибок подряд. Опоздавший пост возвращается человеку.
  • Линтер проверяет текст на запретные для клуба формулировки. Красный уровень блокирует постановку в расписание.
  • Доступы без паролей в переписке: одноразовая ссылка-приглашение на трое суток, пароль человек придумывает сам, выход на всех устройствах одной кнопкой.

Грабли, на которых споткнулись

Перед кодом проект прогнали через трёх адверсарных критиков: по безопасности, по репутации живых каналов и по срокам приёмки. Критик по приёмке нашёл, что план работ строит примерно половину обещанного объёма, а холостого хода в порядке работ не было вовсе: приёмка и дни правок шли бы по живой ленте клуба. Пять необратимых вещей переставили в начало. Каждая стоила от 5 до 40 минут тогда и от полудня до «никогда» потом.

Защита от дубля была построена неправильно. Ключ считался от версии поста и даты выхода: правишь текст, версия меняется, ключ другой, в канал уходит второй пост. В VK опубликованное групповым токеном нельзя ни удалить, ни отредактировать, ошибка необратима. Переделали на факт «в этот канал уже уходило», строка вставляется в журнал до обращения к площадке.

Холостой ход съел боевую публикацию. Холостой прогон занимал тот же ключ защиты от дубля, и после включения боевого режима пост уже не отправлялся: система считала, что он уходил. Развели два разных намерения, комментарий об этом стоит прямо в коде публикации.

Свободное поле «адрес нейросети» открывало кражу ключа клиента. Сервер сам отправляет по указанному адресу расшифрованный ключ. Достаточно уговорить владельца поменять адрес «для рублёвого шлюза», и первая же проверка ключа уедет мошеннику. Шифрование тут бесполезно. Сделали выбор из белого списка, только https, отказ на приватные адреса, запрет переадресаций.

Чистка секретов стояла не на той границе. Текст ошибки чистили перед записью в лог, а провайдеры возвращают в ошибке эхо запроса вместе с ключом. Ключ лёг бы открытым в базу, оттуда на экран журнала и в ночную резервную копию. Перенесли чистку на границу провайдера, до любой записи.

Что получилось и как передавали

Сервис собран за 6 дней активной работы, разнесённых с 13 по 26 августа: 19 коммитов, 26 таблиц базы, 32 серверных роута, 11 экранов, 13 333 строки кода и 64 автотеста, все зелёные. До первой строки кода написано 15 аналитических документов на 4 481 строку.

На живом прогоне пост вышел в канал через 40 секунд после назначенного времени, в журнале осталась запись от имени робота. Правка доезжает до рабочего сервера примерно за две минуты: push, сборка образа, сервер забирает по таймеру. Миграции идут с контрольными суммами и копией базы до наката.

Часть модулей взяли из своей внутренней разработки, примерно 35% кода. Ядро написали заново: разбор показал, что вместе с готовым кодом в клиентский продукт переехали бы его старые проблемы и наш собственный бренд внутри. Полдня выигрыша того не стоили.

База и медиа лежат одним томом рядом с сервисом, наружу база не смотрит. Один бэкап забирает всё сразу, восстановление объясняется по телефону за минуту. Обучение и справка встроены внутрь интерфейса, поэтому сервис можно передать без сопровождающего рядом.

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

Сколько стоит один пост, если его пишет нейросеть?

Пост в 1650 знаков стоит 3 копейки на дешёвой модели, около 14 копеек на модели по умолчанию и 60 копеек на самой дорогой. Наполнение плана восемью темами обошлось в 8 копеек. При 40 постах в месяц расход выходит от 600 до 1800 ₽. Цена видна до генерации.

Можно ли платить нейросети напрямую, без посредника?

Да, клуб подключает свой ключ и платит провайдеру сам. Ключ хранится зашифрованным по AES-256-GCM и уходит только на адреса из белого списка. Каждый вызов записывается в рублях, у месяца есть лимит: по умолчанию 2000 ₽, на 80% предупреждение, на 100% отдельное подтверждение.

Как сделать автопостинг в Telegram и ВКонтакте по расписанию?

Клуб задаёт рубрики и дни выхода, сервис раскладывает их на сетку недель вперёд и подсвечивает пустые места. За три дня до даты нейросеть пишет черновик отдельно под Telegram и отдельно под ВКонтакте. Человек читает, правит кнопками и жмёт «Подтвердить». Дальше пост выходит сам: на живом прогоне он ушёл в канал через 40 секунд после назначенного времени.

Как не дать нейросети опубликовать лишнее?

Готовый текст проверяет код по списку запретных для клуба формулировок, красный уровень блокирует постановку в расписание. Есть стоп-кран «Остановить всё», ограничение по часам, стоп-дни и суточные лимиты на канал. После двух ошибок подряд канал отключается сам. Дубли закрыты записью факта отправки до обращения к площадке.

Сколько времени занимает разработка такого сервиса?

Шесть дней активной работы, разнесённых с 13 по 26 августа. Получилось 26 таблиц базы, 32 серверных роута, 11 экранов и 64 автотеста, все зелёные. До первой строки кода написали 15 аналитических документов.