14 KiB
PRD — VESTI: новостной агрегатор с веб-интерфейсом
Правило проекта (пользователь, 2026-09-09): все задачи выполняются через OpenSpec — каждый элемент/изменение оформляется как change (proposal, design, specs, tasks), проверяется
openspec validate. Изменения фиксировать в STATUS.md / TODO.md / WALKTHROUGH.md / PRD.md.
- Статус: v0.2 (2026-09-10) — прототип работает, реальная публикация включена
- Расположение: /opt/vesti
- Эволюция: /opt/news (наследие — код, схема, sources.yaml, Telethon-сессия)
- Репозитории: vesti-* (источник истины gitverse.ru, зеркало gitea bigbox:3000)
1. Цель
Построить собственный новостной конвейер «СМИ»: собирать новости из многих источников (RSS, почтовые рассылки, сайты, Telegram, WeChat, X), дедуплицировать и объединять в одну статью со ссылками на все источники (юридическая защита), проверять факты, дополнять мультимедиа, категоризировать по направлениям, и формировать:
- Дайджесты по направлениям — для еженедельных видео (учитывают популярность тем, реакции, мои оценки интересности).
- Ленты новостных ботов — Telegram, VK, fediverse по направлениям.
Всё управление — через веб-сайт с авторизацией: списки внешних новостей с метриками популярности, списки опубликованных со своими метриками, редактирование прямо в интерфейсе.
Собственный контент (канал @dedinit «Дед в АйТи» ~1092 поста) — отдельный класс: is_own=1/is_own_canonical=1, приоритетная классификация (critical без LLM при словарном попадании), fan-out по направлениям.
2. Направления (категории)
Технологии, Политика, Игры, Электроника, БЯМ (большие языковые модели), Линукс.
3. Языки ботов
Отдельные боты под комбинации направление×язык: ru, en, zh, ko. Именование: @dedinit_vesti_<направление>_<язык>_bot. Прототип (текущий): бот-контроллер @dedinit_controller_bot публикует в канал @dedinit_vesti (один бот → много каналов через добавление админом; каналы из VESTI_BOT_CHANNELS). Отдельные боты направлений — фаза 2.
4. Принципы
- Каноническая БД — PostgreSQL (решение D1; для прототипа допустим SQLite, миграция на Postgres на фазе 2).
- Банк статей — markdown-бандлы с frontmatter + мультимедиа рядом:
bundles/<направление>/<YYYY-MM>/<slug>.md+media/<slug>/. - Всё локально на bigbox; массовая классификация — Ollama qwen3:8b-nothink; облачный LLM (deepseek, отдельный API/ключ — учёт затрат проекта) — факт-чек, слияние, дайджесты (решение E2).
- n8n — оркестратор для RSS/email/сайтов (отдельный контейнер, общий для инфраструктуры), НЕ для Telegram (Bot API не читает чужие каналы — только MTProto/Telethon).
- Публикация ботов — режим «черновик на подтверждение» (решение C2-b): сначала подтверждение человеком, потом смягчение.
- Переиспользуем существующий стек: Qdrant (docker-qdrant-1, :6333), Redis (:6379), Ollama (:11434), telegram-tunnel (SOCKS5 :1080 → VPS01), Garage S3 (медиа, фаза 2), GoToSocial (fediverse-бот, фаза 2), gitea (:3000, зеркало) / gitverse.ru (истина).
- Тихие watchdogs: молчат, пока всё живо (политика пользователя).
5. Архитектура (текущая, 2026-09-10)
sources.yaml (реестр, git)
│
▼
telegram_crawler.py (cron каждые 30 мин, Telethon MTProto, SOCKS5 :1080)
│ инкрементально после last_post_id; метрики views/reactions; --all / --source
▼
backfill_dedinit.py (разовый бэкфилл своего канала ~1092 постов, is_own=1, медиа)
│
▼
SQLite vesti.db (raw-посты, tg_state, runs, классификации) ──► Postgres (фаза 2)
│
▼
classifier.py (Ollama qwen3:8b-nothink + словарный фильтр)
│ направление, relevance, interest 1-5, summary; мультинаправления
▼
vesti-web (FastAPI + Bootstrap 5.3 + Jinja2 + HTMX, 127.0.0.1:8400)
│ авторизация; кандидаты → подтвердить/отклонить; «Свои»; метрики
▼ (подтверждённые)
publisher-service (FastAPI-микросервис, Docker vesti-publisher, :8410)
│ POST /api/v1/publish → Bot API через SOCKS5 :1080 (host.docker.internal)
│ медиа: маунт ../../media/media:/srv/publisher/media:ro (веб шлёт media/<file>)
▼
Telegram @dedinit_vesti (бот-контроллер @dedinit_controller_bot; каналы из VESTI_BOT_CHANNELS)
│
▼
bundles/<направление>/<YYYY-MM>/<slug>.md (markdown + frontmatter) + media/
Сервисы/порты (актуально):
| Сервис | Порт | Способ запуска |
|---|---|---|
| vesti-web | 127.0.0.1:8400 | systemd-юнит vesti-web.service (установлен; daemon-reload на bigbox зависает — внешняя проблема, подхватится при перезагрузке). Пароль: VESTI_WEB_PASSWORD or ADMIN_PASSWORD из .env |
| publisher-service | 127.0.0.1:8410 | Docker (docker-compose, restart policy), healthz |
| telegram-tunnel | SOCKS5 127.0.0.1:1080 | systemd telegram-tunnel.service |
| Ollama | 127.0.0.1:11434 | существующий (qwen3:8b-nothink) |
Cron (Hermes cron, no_agent, тихие):
- vesti-crawler-all-sources (
*/30 * * * *) — краулер по всем включённым источникам, лог /opt/vesti/logs/crawler-cron.log; молчит при успехе. - vesti-watchdog (
*/15 * * * *) — publisher :8410 healthz, веб :8400, SOCKS5 :1080, контейнер, БД; шумит в Telegram только при проблеме.
Фаза 2+: n8n (RSS/email/сайты), Postgres, S3 (Garage), семантический dedup (Qdrant bge-m3), факт-чек облаком, дайджесты для видео, боты VK/fediverse, WeChat/X.
6. Метрики популярности
- Внешние TG-посты: views + reactions (через Telethon при краулинге).
- Свои посты: views через Bot API (GET /api/v1/views/{channel}/{mid}).
- Сайты/RSS (фаза 2): только свои переходы (UTM-метки в ссылках).
- Интерес темы: кол-во реакций/просмотров → показатель хайповости; залайканные комментарии — сигнал (фаза 2).
7. Дайджесты (G1, G3)
Отдельная сущность по направлению: по каждой теме — суть, источники, оценка популярности. Для еженедельного видео: 5–10 новостных поводов в выпуске, выходные. Оценки интересности — в вебе (1–5 + флаг «в дайджест»).
8. Технологический стек
| Слой | Технология | Статус |
|---|---|---|
| Язык | Python 3.12, venv /opt/vesti/.venv | новый |
| Telegram-краулер | Telethon (MTProto), SOCKS5 :1080, сессия /opt/vesti/telegram/ | новый (переисп. код /opt/news) |
| Бот-публикатор | publisher-service: httpx[socks] → Bot API (не python-telegram-bot) | новый |
| Каноническая БД | SQLite (прототип) → PostgreSQL (фаза 2) | новый |
| LLM-классификация | Ollama qwen3:8b-nothink (:11434) | существующий |
| Облачный LLM | deepseek (отдельный API/ключ) | фаза 2 |
| Векторный поиск | Qdrant :6333 (коллекция news), bge-m3 | существующий |
| Веб | FastAPI + Bootstrap 5.3 + Jinja2 + HTMX | новый, :8400 |
| Оркестратор | n8n (отдельный контейнер, :5678), общий | фаза 2 |
| Медиа | локально /opt/vesti/media/media/ → Garage S3 | фаза 2 |
| Git | gitverse.ru (истина), gitea bigbox :3000 (зеркало) | существующий |
| Доставка | Telegram-боты, потом VK/fediverse | новый |
9. OpenSpec
Проект ведётся по OpenSpec: /opt/vesti/openspec/. Active changes:
tg-crawler-publisher-prototype(прототип конвейера; реализован),own-content-hub(свой канал @dedinit, fan-out; код готов, бэкфилл идёт, интеграционная проверка после него),publisher-service(микросервис публикации; создан 2026-09-09, validate чист, реальная публикация работает). Файлы: proposal.md, design.md, specs/*/spec.md, tasks.md.
10. Прототип — текущее состояние
Вертикаль Линукс (ru) + свой контент:
- TG-краулер 8 публичных каналов (Telethon, SOCKS5, инкрементальный, метрики; форварды из своих каналов отфильтровываются, чужие — с fwd-полями без медиа).
- Классификатор (словари + локальный qwen3:8b-nothink; мультинаправления; свои посты → critical без LLM при словарном попадании).
- Реальная публикация: бот-контроллер @dedinit_controller_bot → @dedinit_vesti (проверено: message_id 2–4, approve через веб работает).
- Веб (127.0.0.1:8400): кандидаты → подтвердить/отклонить, «Свои», метрики, опубликованные.
- Банк статей markdown с frontmatter (bundles/, origin:own для своих).
- Бэкфилл канала @dedinit (~1092 постов is_own=1, медиа) — скрипт crawler/backfill_dedinit.py, запущен 2026-09-10.
Источники: linuxklub, linuxos_tg, dotfiles_linux, linux_education, LinuxMastery, linuxcamp_tg, gitgate, krxnotes + канал dedinit (own).
11. Критерии готовности прототипа (DoD)
- Краулер собирает посты из линукс-каналов с метриками, без дублей (проверка: count по sha256 = count id). — ДОСТИГНУТО
- Классификатор корректно относит ≥90% контрольных постов к направлению. — В ПРОВЕРКЕ (после бэкфилла — прогон по 543+ неклассифицированным)
- Бот публикует карточку-пост только после подтверждения в вебе. — ДОСТИГНУТО
- Веб доступен локально, с авторизацией; кандидаты/опубликованные/метрики видны. — ДОСТИГНУТО
- Бандл создан в bundles/linux/YYYY-MM/.md с frontmatter и источниками. — ДОСТИГНУТО
- Watchdog молчит 3 дня подряд при здоровой системе. — В ПРОВЕРКЕ (cron создан 2026-09-10)
- Смягчение публикации (C2: b → a/в) — отдельное решение после прототипа.
12. Открытые вопросы / следующие фазы
- Миграция SQLite → Postgres (когда поток статей вырастет).
- Облачный LLM API для факт-чека/слияния/дайджестов — отдельный ключ/учёт затрат.
- n8n-воркфлоу для RSS/email/сайтов (общий контейнер).
- Боты VK, fediverse (GoToSocial) — фаза 2; отдельные боты направлений (@dedinit_vesti___bot) — фаза 2.
- WeChat (закрытый, сложный) и X (платный API) — исследование отдельно.
- Автопубликация/смягчение условий (после прототипа).
- Дайджесты: сущность, связь с видео-сценарием.
- git-репозиторий /opt/vesti (gitverse истина, gitea зеркало) — инициализировать.
- openspec archive publisher-service / tg-crawler-publisher-prototype после подтверждения и интеграционных проверок.