Бот @futur_e_news_bot появился как ответ на три типичные проблемы новостных лент: одно событие прилетает из пяти каналов с разными заголовками, 90% повестки нерелевантны конкретному читателю, а поток негатива утром стал нормой. Разработчик потратил несколько выходных и получил работающий продукт, который сейчас хранит около 1500 новостей в базе и обслуживает десятки активных пользователей.
Центральная идея — фильтр тональности. Каждая входящая новость оценивается языковой моделью по шкале от 0 до 3: ноль означает нейтральный или позитивный материал, тройка — тяжёлая трагедия. По умолчанию пользователь видит только уровни 0 и условно нейтральное, остальное подключается вручную через четыре ступени в настройках. Персонализация работает через реакции: нажатие 🔥, ❤️ или 😢 сдвигает «вектор вкуса» пользователя, и следующая выдача ранжируется с учётом этого сигнала. Начальные интересы вытягиваются из профиля Telegram, дальнейшая настройка — через реакции или текстовую команду вроде «больше про космос, меньше про политику».
| Компонент | Память | Стоимость, $/мес |
|---|---|---|
| Бот (app + swap) | 512 МБ + 512 МБ | ~3.2 |
| RSSHub (приватный) | 256 МБ | ~1.9 |
| Том SQLite | 1 ГБ | ~0.15 |
| OpenRouter (LLM) | — | 0–1 |
| Итого | — | ~5–6 |
Дедупликация реализована через векторные эмбеддинги. Для каждой новости при инжесте вычисляется эмбеддинг (384-мерный вектор через fastembed с ONNX-моделью локально, без внешнего API), затем выполняется KNN-запрос к базе. Если ближайший сосед оказывается ближе заданного порога косинусного расстояния — новость считается дублём и прикрепляется к существующей карточке как дополнительный источник. В итоге пять материалов об одном событии превращаются в одну карточку с пометкой «📰 5 источников». Количество источников само по себе учитывается в ранжировании: мультиисточные события поднимаются выше.
Пять изданий об одном событии превращаются в одну карточку с перечнем источников — дедупликация через векторные эмбеддинги.
Ключевое инфраструктурное решение — отказ от Postgres в пользу sqlite-vec. Расширение добавляет в SQLite виртуальные таблицы vec0 с поддержкой косинусной, L2 и Hamming-метрик и KNN-поиском прямо через SQL. Это функциональный аналог pgvector, но встраиваемый: никакой отдельной машины, никаких миграций и бэкапов базы данных. На Fly.io минимальный инстанс под Postgres стоит от $5 в месяц — sqlite-vec убирает эту статью расходов полностью. Единственное ограничение: SQLite не поддерживает параллельную запись от нескольких процессов, но для архитектуры с одним воркером это несущественно — режим WAL и параметр busy_timeout решают редкие конфликты.
Для задач, требующих языковой модели, используется OpenRouter — маршрутизатор с единым API-ключом и доступом к десяткам моделей, включая полностью бесплатные (с ограничением по частоте запросов). Один промпт на новость закрывает сразу несколько задач: определение категории и тегов, оценку важности и срочности, тональность, краткое резюме на 1–2 предложения и перевод RU↔EN. Платные модели подключаются как запасной вариант при исчерпании бесплатных лимитов.
Итоговая инфраструктура состоит из двух машин Fly.io: основная на 512 МБ RAM запускает aiogram с long-polling, APScheduler с четырьмя джобами (пайплайн каждые 15 минут, доставка каждые 20 минут, проверка срочных новостей каждую минуту, дневной дайджест) и SQLite на томе 1 ГБ. Вторая машина на 256 МБ — приватный инстанс RSSHub, доступный только по внутренней сети Fly и превращающий Telegram-каналы в RSS-ленты. Никакого публичного HTTP, балансировщиков и Redis. При текущей нагрузке основная машина использует около 167 МБ из 459 МБ доступной RAM, CPU практически простаивает — запас до сотен пользователей без апгрейда железа есть.
