Files
vesti/PRD.md
T

14 KiB
Raw Blame History

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), дедуплицировать и объединять в одну статью со ссылками на все источники (юридическая защита), проверять факты, дополнять мультимедиа, категоризировать по направлениям, и формировать:

  1. Дайджесты по направлениям — для еженедельных видео (учитывают популярность тем, реакции, мои оценки интересности).
  2. Ленты новостных ботов — 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) + свой контент:

  1. TG-краулер 8 публичных каналов (Telethon, SOCKS5, инкрементальный, метрики; форварды из своих каналов отфильтровываются, чужие — с fwd-полями без медиа).
  2. Классификатор (словари + локальный qwen3:8b-nothink; мультинаправления; свои посты → critical без LLM при словарном попадании).
  3. Реальная публикация: бот-контроллер @dedinit_controller_bot → @dedinit_vesti (проверено: message_id 2–4, approve через веб работает).
  4. Веб (127.0.0.1:8400): кандидаты → подтвердить/отклонить, «Свои», метрики, опубликованные.
  5. Банк статей markdown с frontmatter (bundles/, origin:own для своих).
  6. Бэкфилл канала @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)

  1. Краулер собирает посты из линукс-каналов с метриками, без дублей (проверка: count по sha256 = count id). — ДОСТИГНУТО
  2. Классификатор корректно относит ≥90% контрольных постов к направлению. — В ПРОВЕРКЕ (после бэкфилла — прогон по 543+ неклассифицированным)
  3. Бот публикует карточку-пост только после подтверждения в вебе. — ДОСТИГНУТО
  4. Веб доступен локально, с авторизацией; кандидаты/опубликованные/метрики видны. — ДОСТИГНУТО
  5. Бандл создан в bundles/linux/YYYY-MM/.md с frontmatter и источниками. — ДОСТИГНУТО
  6. Watchdog молчит 3 дня подряд при здоровой системе. — В ПРОВЕРКЕ (cron создан 2026-09-10)
  7. Смягчение публикации (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 после подтверждения и интеграционных проверок.