Привет, Хабр! В эпоху информационного шума умение оставаться в контексте и фильтровать важное — особенно ценно. Несколько месяцев назад, устав от бесконечного думскроллинга, я создал для себя персональный агрегатор RSS-лент с ИИ-обработкой и недавно выложил его в открытый доступ. Несмотря на кажущееся устаревание, RSS и в 2025 году остаётся актуальным, если подойти к нему с умом.

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

От идеи к вайбкодингу

Сперва я подумал - в каком виде мне самому было бы удобно получать свежие новости? Выбрал вариант с Telegram-каналом. Вооружился вайбкодингом и через Telegram Bot API за пару часов настроил постинг в канал из нужного мне источника - стало чуточку спокойнее :)

Пример так всё и начиналось
Пример так всё и начиналось

Шли дни и я задумался над тем, как здорово было бы пропускать неинтересные новости в телеграм-канале, даже если они постятся в одном вместе с важными, а еще было бы классно переводить новости со всяких Reuters, WallStreet Journal и прочего. Все источники новостей я разделил по категориям, в каждой категории у источника есть ссылки RSS-ленты с новостями в этой категории. Новости хотелось получать максимально быстро, отсюда появилось название сервиса - FireFeed.

Отсюда началась веселая история с агрегатором - не спеша по вечерам я добавлял функционал - сначала реализовал переводы через API Google-переводчика, смотрел в сторону DeepL и ругался на их качество + потенциальные ограничения в виде лимитов API - по итогу принял волевое решение использовать машинный AI-перевод, доводя функционал до рабочего состояния.

В результате на сегодняшний день проект разросся, потребляет стабильно на VPS сервере 4-5Гб ОЗУ и утилизирует CPU в 2-3 потока. Из функций имеется:

  • Агрегация: Сбор информации из любого количества источников.

  • Мультиязычность: Работа с контентом на 4 языках - русском, английском, немецком и французком.

  • Фильтрация и персонализация: Понимание интересов пользователя и доставка релевантного контента (это пока реализовано на уровне Telegram Bot где можно выбрать персональные категории).

  • Обработка: Перевод при помощи M2M100_418B и Helsinki-OPUS с обнаружением дубликатов через сравнение эмбеддингов (векторов, хранимых в Postgres при помощи расширения pgvector).

  • Универсальное API, в который стучатся все остальные сервисы (например, в Telegram) в реальном времени.

Реализация этих функций потребовала не только грамотной архитектуры, но и интеграции c LLM, что делает проект особенно интересным с технической точки зрения.

Архитектура: Микросервисный? подход и асинхронность

FireFeed построен как набор взаимосвязанных компонентов, что позволяет масштабировать отдельные части системы независимо. Основные элементы сейчас представлены в виде отдельных сервисов на Python:

  1. Telegram Bot: Интерфейс для взаимодействия с пользователями. Отвечает за получение команд, управление подписками и отправку новостей.

  2. RSS Parser Service: Работает в фоне через systemctl, этот сервис опрашивает RSS-ленты, а затем обрабатывает полученные данные. Помимо непосредственно функционала парсера, сервис включает в себя дедупликатор данных для поддержания качества потока.

  3. REST API: Единая точка входа - позволяет внешним системам интегрироваться с FireFeed, на базе API построен в том числе и сайт агрегатора с аутентификацией пользователей по JWT, а также привязной Telegram-аккаунта к учетной записи на сайте агрегатора.

  4. Сервис переводчика - он отвечает за перевод новостей на язык пользователя, помимо перевода туда включены проверки на семантику через spacy, и всякие утилитарные функции по пост-обработке переводов.

  5. User Management - сервис хранит информацию о пользователях, их интересах и подписках.

Хотя... кого я обманываю? Все же мы понимаем, что микросервисный подход в чистом виде это про совсем другое - как минимум про наличие отдельных БД :)

Асинхронщина

Пришлось намучиться с асинхронными вызовами: сначала логика была синхронной, и стек вызовов не позволял обрабатывать несколько процессов одновременно. Парсинг десятков RSS-лент с HTTP-запросами в синхронном режиме блокировал выполнение и снижал эффективность. В итоге переписал все сервисы с использованием asyncio в Python — это оказался ключевой архитектурный выбор. Асинхронность позволила эффективно управлять множеством I/O-операций (запросы к API, БД, взаимодействие с Telegram-ботом), не простаивая в ожидании. Это критично для сервиса, стремящегося к минимальной задержке и обработке новостей в реальном времени.

Технический стек

На текущий момент стек периодически претерпевает изменения, однако базовые его элементы выглядят следующим образом:

  • Backend: Python 3.8+, asyncio, FastAPI.

  • База данных: PostgreSQL с расширением pgvector и множеством индексов для ускорения работы.

  • AI/ML: Transformers, Sentence Transformers, SpaCy, Torch.

Этот стек позволил сочетать высокую производительность FastAPI с мощными возможностями обработки естественного языка (Hugging Face Transformers) и эффективной обработкой эмбеддингов. На старте думал между Django или FastAPI, однако выбрал второе и пока не жалею - все-таки API в том случае легче отделить от всего приложения.

Сбор и обработка данных: Как рождается новость

Для любителей почитать детали и требовательных Хабрачитателей, думаю важно затронуть весь процесс сбора и обработки данных в проекте - начнем-с:

1. RSS Manager: Контроль над потоком

Здесь у нас имеется центральный элемент в виде классаRSSManager. Он отвечает за:

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

  • Конкурентный парсинг: Использование asyncio.Semaphore для предотвращения перегрузки как собственной системы, так и серверов источников.

  • Обработку ошибок: Резервные механизмы (fallback), например, если feedparser не может распарсить ленту, используется aiohttp для прямого обращения к источнику.

2. Процесс обработки новости

RSS-лента это представление элементов в формате atom/xml, где каждый полученный элемент проходит через цепочку событий:

  1. Валидация: Проверка URL, структуры, уникальности.

  2. Извлечение медиа: Автоматическое извлечение изображений из xml или через анализ HTML-контента статьи. Это важно для визуального оформления, особенно в Telegram-боте. Все функции по работе с изображениями я выделил в отдельный ImageProcessor.

  3. Проверка дубликатов: Критический шаг, о котором речь пойдет подробнее ниже.

  4. Определение языка: Необходимо для последующего перевода и фильтрации.

  5. Перевод (если нужно): Асинхронная задача, отправляемая в очередь.

  6. Сохранение в БД: С полным контекстом (оригинал, перевод, эмбеддинг, изображение, язык, дата публикации, источник, категории).

Стоит заметить, что весь этот процесс должен быть надежным и устойчивым к сбоям. Ошибки в одном RSS-элементе ленты не должны останавливать обработку других и тем более всю очередь задач.

AI-компоненты в проекте: Сердце интеллекта

Если представить весь процесс обработки материалов парсером, схема будет такая:

Обработка новостей через FireFeed Parser в общем виде
Обработка новостей через FireFeed Parser в общем виде

Обнаружение дубликатов: Проблема и решение

Одна из самых сложных задач — избежать дубликатов. Один и тот же материал может появиться в разных лентах с разными заголовками, слегка переписанным текстом или просто с разными URL. Простое сравнение строк не работает. FireFeed решает это через семантический анализ. Анализатор реализован через модель и работу с эмбеддингами:

  • Sentence Transformers: Используется модель paraphrase-multilingual-MiniLM-L12-v2. Эта модель преобразует текст (заголовок + начало содержимого) в вектор (в данном случае 384-мерный), отражающий его смысл.

  • pgvector: PostgreSQL с этим расширением позволяет хранить эти векторы и эффективно находить похожие (через косинусное сходство) с помощью специализированных индексов (например, ivfflat).

  • Алгоритм: При поступлении новой новости, система генерирует её эмбеддинг и ищет в базе существующие эмбеддинги, косинусное сходство с которыми превышает определённый порог. Если дубликат найден, новость отбрасывается или помечается.

Так парсер умеет фильтровать не только точные копии, но и переформулированные версии одной и той же новости, порой значительно улучшая качество читаемого контента.

Система перевода: Преодоление языкового барьера

FireFeed на сегодняшний день умеет агрегировать информацию на 4 языках - русском, английском, немецком и французком. Для пользователей критично получать новости на понятном им языке, а сам по себе перевод — это ресурсоемкая задача.

  • Модели: Используются Helsinki-NLP OPUS-MT (хорошо показывает себя для конкретных пар языков) и M2M100 (универсальная модель). Однако давайте будем честны - когда ИИ переводит без разбора "Donald Trump" -> "Дональд Трумп" или "Niger River" -> "Черная река", это выглядит ужасно - поэтому в дополнение ко всему пришлось использовать терминологические словари для перевода. Это так же позволило не переводить массу брендов вроде Apple, "Самсунг Галактика" и тп.

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

  • Кэширование: Результаты переводов кэшируются, чтобы избежать повторных вычислений для уже обработанных новостей.

  • Учет контекста: Перевод адаптируется под терминологию конкретной области (например, финансовой).

Эта архитектура позволяет масштабировать компонент перевода независимо, добавляя мощности или используя GPU для ускорения. Пока же проект еще на самом старте своего развития, и сервер с GPU я не использую. Однако вы можете фыркнуть или развернуть его его на более мощном железе при необходимости.

Помимо переводов по мере возможности реализую суммаризатор на базе какого-нибудь легковесного RAG. Кстати, что посоветуете?

Где он, этот ваш Production-уровень?

Примерно раз в пару недель прошу Grok провести ревью текущего состояния репозитория и оценить его состояние по 10 бальной шкале. Получается забавно - то, что вчера ИИ оценивал в 10/10, завтра может оценить в 5 с кучей аргументов. Стоит при этом признать, дельные советы при этом так же бывают. В конце каждого ревью ИИ подмечает, "ваш проект готов на X/X к production-level".

Для дальнейшего развития и вывода проекта на тот самый production-level подумал будет не лишним обзавестись конфигуарацией для контейнерного окружения и сборки - сейчас она реализована в базовом исполнении и включает в себя:

  • Docker: Упрощает развертывание, тестирование и масштабирование отдельных сервисов.

  • nginx: Может использоваться как обратный прокси-сервер для FastAPI, а также для балансировки нагрузки при горизонтальном масштабировании.

  • systemd: Обеспечивает автоматический запуск и перезапуск сервисов в случае сбоев по всем традициям graceful shutdown.

И в заключение...

FireFeed — для меня это не просто код. Это код, который мне впервые не жалко дважды - во-первых потому, что я потратил на него не много времени, а во вторых - он уже нанес мне пользы и спасает от чтения кучи источников, надеюсь нанесёт и вам. В целом, считаю вышла неплохая попытка решить одну из проблем современности в виде избытка информации.

Бонусом помимо RSS-парсера на днях накидал веб-скраппер и буду рад вашим конструктивным issues и комментариям. На будущее есть идея добавить в него анализ контента с дальнейшими категориями.

Если кому-то интересно, весь код проектов, о которых идет речь, размещен у меня в свободном доступе на GitHub и GitVerse.

Стоит заметить, что это мой первый проект на Python - но куда деваться, даже если я не любитель этого ЯП, под него заточено много библиотек для упрощения с ИИ.

Комментарии (4)


  1. osj
    09.11.2025 09:55

    Сделал систему на основе FreshRSS: статьи из лент сохраняются в PostgreSQL, n8n выбирает материалы по категории и объёму, GPT (бесплатно) делает саммари и перевод на русский. После этого запускается поиск дубликатов через pgvector , так как источники на разных языках и могут пересекаться. Уникальные тексты автоматически публикуются в канал.


    1. yuryweiland Автор
      09.11.2025 09:55

      Супер! Как по нагрузке у вас вся эта связка работает? Если GPT именно OpenAPI - много ли по токенам съедает? И где разворачивали если не секрет


      1. osj
        09.11.2025 09:55

        FreshRSS, n8n и GPT4Free/OpenRouter(бесплатные лимиты, не отслеживаю токены) помещаются в 1 ГБ RAM — всё работает в Docker на обычном minipc или VPS, даже при параллельной нагрузке.


  1. xxasaw412
    09.11.2025 09:55

    Есть ещё syft ai