Files

196 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/<slug>.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_<dir>_<lang>_bot) — фаза 2.
- WeChat (закрытый, сложный) и X (платный API) — исследование отдельно.
- Автопубликация/смягчение условий (после прототипа).
- Дайджесты: сущность, связь с видео-сценарием.
- git-репозиторий /opt/vesti (gitverse истина, gitea зеркало) — инициализировать.
- openspec archive publisher-service / tg-crawler-publisher-prototype после
подтверждения и интеграционных проверок.