У новостей об ИИ есть неприятное свойство: важная ссылка легко тонет среди вкладок, а заметка без первоисточника и цифр приносит больше работы, чем пользы. Для aixai.ru и телеграм-канала «Эй Ай думай» я не стал поручать агенту писать и размещать посты. Hermes получил более узкую задачу: собрать кандидатов, проверить, что за каждым стоит открываемый материал, и вернуть мне план, с которым можно принимать редакционное решение.
Такой выбор оказался важнее, чем ещё один бот с кнопкой «опубликовать». Я хочу понимать, откуда взялась тема, какие в ней есть числа и почему она не повторяет уже вышедший текст. Автоматизация убирает рутину вокруг этого решения, но не подменяет его.
Файлы задают агенту рамки
В статье оставляю оба исходных файла: бриф для Hermes и скрипт collect_candidates.py. Бриф объясняет, что считать готовым результатом, а скрипт делает повторяемую часть работы. Внутри брифа граница сформулирована без двусмысленности:
Главное правило: ты ничего не публикуешь. Ни постов, ни сообщений.
Результат: файл с планом и короткий отчёт.
Границы работы
- Ничего не публиковать, не коммитить в блог и не отправлять в каналы.
- Не обходить пейволлы и авторизацию.
- При 429 и 403 делать паузу и писать об этом в отчёте.
Это не запрет на работу с темой. Это договорённость о последнем действии: Hermes может собрать аргументы для публикации, но не сделает её сам.
Сначала агент собирает не новости, а доказательства
В основе задачи лежит небольшой Python-скрипт collect_candidates.py. Он обходит восемь типов источников: RSS-ленты, Hacker News, arXiv, Reddit, Dev.to, Lobsters, GitHub Trending и YouTube. В RSS настроено пятнадцать лент, среди них OpenAI, Hugging Face, Google AI, Microsoft Research, PyTorch и NVIDIA. Это не редакционная подписка на все их сообщения. Скрипт лишь создаёт широкую воронку, из которой потом можно отобрать несколько тем.
Нижние пороги, свежесть и вес источников вынесены в начало файла. Их можно менять под редакционный ритм, не переписывая сборщики:
MIN_WORDS = {
"youtube": 3000,
"rss": 1000,
"github": 1000,
"reddit": 2000,
"hn": 2000,
"arxiv": 150,
"devto": 800,
"lobsters": 800,
}
FRESH_DAYS = 7
FRESH_DAYS_EXEMPT = {"arxiv", "github", "youtube"}
SOURCE_WEIGHT = {
"arxiv": 3.0,
"rss": 2.5,
"github": 2.5,
"hn": 2.0,
"reddit": 1.5,
"lobsters": 1.5,
"devto": 1.0,
"youtube": 2.0,
}
У источников разные правила. Hacker News попадает в выборку, если история набрала больше восьмидесяти баллов за последние семь дней. Для arXiv берутся свежие работы из cs.AI, cs.LG и cs.CL. Reddit читается через RSS, потому что JSON-эндпоинты часто отвечают ошибкой 403. YouTube требует yt-dlp, а без него источник честно пропускается и попадает в отчёт как недоступный.
На выходе у кандидата есть заголовок, URL, дата, тип источника и технические данные для оценки. Это ещё не заготовка поста. Одна громкая ссылка ничего не говорит о том, можно ли по ней написать материал для блога или канала.
Объём и свежесть нельзя угадать по заголовку
Скрипт оценивает кандидатов по давности, весу источника и количеству доступного текста. Для новостных источников старше семи дней он прекращает отбор. arXiv, GitHub и YouTube остаются в списке дольше: у статьи, репозитория или видео другой срок жизни, чем у новости за сутки.
У каждого типа есть свой нижний порог. Для RSS и GitHub это тысяча слов, для материалов из Hacker News и Reddit, где важно перейти к исходной публикации, две тысячи. У arXiv достаточно короткого абстракта, поэтому порог ниже. Эти числа не измеряют качество текста. Они отвечают на более простой вопрос: есть ли в открытом материале хоть какая-то фактура, а не только анонс.
Параметр --deep берёт кандидатов с лучшей предварительной оценкой, скачивает страницу и считает слова в полном тексте. Если URL отдаёт ошибку, запрещает доступ или ведёт за пейволл, в результате остаётся deep_error. Hermes не должен заменить такую ошибку догадкой. Тема либо получает другой первоисточник, либо уходит в отклонённые.
Дедупликация проверяет не только ссылку
Повторить URL проще всего, но повторить уже сказанное можно и с новой ссылкой. Поэтому скрипт читает заголовки, slug и ссылки из опубликованных Markdown-статей, а также файл seen.json с URL прошлых планов. Если адрес уже использовался, он сразу исключается. Если пересекаются ключевые слова заголовка и существующего материала, кандидат тоже помечается как возможный дубль.
Проверка URL и пересечения слов остаётся короткой и читаемой. Её задача не решить за редактора, а остановить очевидные повторы:
def is_duplicate(entry, published, seen_urls: set, threshold=0.5) -> str | None:
url = entry["url"].rstrip("/")
if url in seen_urls:
return "url уже использовался"
for post in published:
if any(url == link.rstrip("/") for link in post["links"]):
return f"url в frontmatter поста {post['slug']}"
cand = tokens(entry["title"])
for post in published:
overlap = len(cand & post["tokens"]) / max(len(cand), 1)
if overlap >= threshold:
return f"тема пересекается с {post['slug']} ({overlap:.0%})"
Эта проверка намеренно грубая. Она не знает, что три новости о разных частях одного релиза могут оказаться одной темой, и не распознает пересказ без совпадающих слов. В задании для Hermes есть отдельная ручная проверка последних материалов блога. Перед публикацией я смотрю, не повторяет ли предложенный угол то, что уже вышло, даже если скрипт не увидел совпадение.
План важнее очереди на автопостинг
После сбора Hermes должен вернуть Markdown-план и JSON с теми же данными. Для каждой оставленной темы нужны рабочий заголовок, URL, конкретные цифры из открытого источника, угол подачи, приоритет и риск. В конце остаются отклонённые кандидаты с причиной: дубль, недостаточно материала, нет проверяемых цифр или недоступен источник. Отдельно видны ошибки по каждому источнику и число записей, отсеянных дедупликацией.
Так выглядит запуск, который оставляет после себя JSON для дальнейшей обработки и Markdown для чтения:
python3 collect_candidates.py \
--sources rss,hn,arxiv,reddit,devto,lobsters,github,youtube \
--blog-dir ~/blog/posts \
--seen-file seen.json \
--deep 10 \
--limit 15 \
--out plan.json --md plan.md
Именно здесь агент становится полезен как редакционный помощник. Я не трачу время на одинаковый обход лент, ручное сравнение URL и выяснение, почему очередной сервис ничего не вернул. Вместо набора вкладок появляется короткий документ с границами уверенности. Если в нём нет цифры или открываемой страницы, это причина не спешить с темой, а не повод написать её убедительнее.
Важное ограничение записано прямо в задаче Hermes: он ничего не публикует, не отправляет в мессенджеры и не коммитит в блог. При ответах 403 и 429 он не обходит блокировки и не ретраит источник в цикле. Агент должен сообщить, что ему не удалось получить, а не создать видимость полного мониторинга.
Последнее решение остаётся редакторским
После плана я открываю первоисточник, сверяю цифры и выбираю, подходит ли тема aixai.ru, каналу «Эй Ай думай» или обоим площадкам. Затем решаю, что именно в ней важно читателю: разбор релиза, практический эффект, сравнение подходов или повод ничего не писать. Только после этого появляется готовый текст и ручная публикация.
Hermes не сделал новости автоматическими. Он показал, что обычно прячется за словом «мониторинг»: источник материала, его свежесть, уже раскрытые темы и пробелы, которые не дают писать честный пост. Я всё ещё решаю, о чём говорить с читателем. Просто начинаю не с россыпи вкладок, а с проверяемого плана.
