commit 959f4acace9f7bd8dad35d0d630fd1819de08d20 Author: neo Date: Mon Sep 14 09:37:44 2026 +0500 docs: content-atom roadmap (MVP -> V5) diff --git a/README.md b/README.md new file mode 100644 index 0000000..e316d12 --- /dev/null +++ b/README.md @@ -0,0 +1,312 @@ +# Content-atom: Implementation Plan + +> **For Hermes:** roadmap-план по этапам (MVP → V2 → V3). Реализация — по этапам, не все сразу. + +**Goal:** Атомарный контент-конвейер — атом смысла как источник правды, каналы переупаковывают атомы вместо переписывания статей. + +**Architecture:** Все стадии работают с `atom_id` вместо `articleIds`. `extract` разбирает статью на типизированные атомы, все 6 слоёв editor-pro привязываются к атому, `atom_versions` переупаковывают атом под каналы, `compositions` собирают выходы, `panel` показывает дашборд смысла. + +**Tech Stack:** Python (editor-pro, neurogeo), TypeScript (content-pipeline, panel), Qdrant (эмбеддинги), Memgraph/AGE (граф, опционально), FastAPI (панель), Qwen 35B (локальный). + +--- + +## Контекст и предположения + +### Что уже есть +- **editor-pro** — 6-слойный конвейер (typo → LLM → enrich → GEO → interlink → astro-seo), работает со статьёй. Стек: Python, spaCy, httpx, neo4j (Memgraph), PennyLane (QAOA). Модель: Qwen 35B. +- **NeuroGEO** — GEO-скоринг целой статьи, FastAPI на :8009. +- **content-pipeline** — TS-оркестратор (`generate → optimize → publish`) + CMS-слой для управления публикациями. Работает с объектом `Article`. +- **Qdrant** — эмбеддинги уже используются (qdrant_save.py). +- **Memgraph** — для интрелинки. + +### Что меняем +- Article → atom. Новая стадия `extract` в начале конвейера. +- Все стадии привязываются к `atom_id` вместо `articleIds`. +- `atom_versions` — переупаковка одного атома под каналы. +- `compositions` — сборка выходов (статья/TG/видео). +- `panel` — дашборд смысла вместо списка статей. + +### Что НЕ тащим сразу +- Postgres + pgvector — Qdrant для эмбеддингов, атомы в том, что уже стоит. +- Graph (Memgraph/AGE) — только V4, когда атомы отработают. +- Адаптеры TG/видео — V3, когда web-канал стабилен. + +--- + +## Дорожная карта + +### Этап 1 — MVP: атомы + переупаковка (web-канал) +**Цель:** `extract` разбирает статью на атомы, `atom_versions` переупаковывают под web. + +- **atoms** — типизированные атомы (claim/fact/stat/qa/comparison/hook/cta и др.), body по типам (JSONB). +- **atom_versions** — версии под каналы (web_article | telegram | youtube_script | podcast | newsletter | service_card). +- **extract** — стадия: статья → типизированные атомы. +- **jobs/runs** — задачи пайплайна + стоимость/качество на каждый запуск. +- **Qdrant** — эмбеддинги атомов для дедупликации. + +**KPI:** 4 домена (orabota, ipoteka-ufa, ufa-kvartira, rabota-ufa), один канал (web). + +### Этап 2 — связи + композиции (второй канал: Telegram) +**Цель:** дедупликация между доменами, сборка выходов. + +- **atom_relations** — связи (supports/contradicts/expands/example_of/part_of/derived_from). +- **compositions** — рецепты сборки (типы, темы, порядок), atom_ids. +- **prompts** — промпты как данные (версионирование, A/B). +- **Дедупликация** — embedding + atom_relations вместо дублирования статей. + +**KPI:** 2 канала (web + Telegram), дедup между доменами. + +### Этап 3 — медиа + метрики (видео, подкаст, рассылка) +**Цель:** сквозная аналитика от смысла до метрики. + +- **assets** — медиа-активы (image | audio | video | subtitle), транскрипты. +- **atom_assets** — привязка атомов к медиа. +- **publications** — публикации (URL/channel_id/external_id) + метрики (views/clicks/conversions/revenue). +- **Адаптеры** — видео-сценарий, подкаст, рассылка. + +**KPI:** 4+ канала, сквозная аналитика. + +### Этап 4 — граф (Memgraph / Apache AGE) +**Цель:** обходы и анализ связей. + +- Проекция: Atom → Topic → Entity → Project → Asset. +- graph_outbox — Postgres → воркер → Memgraph (только id + минимальные атрибуты, тело атома НЕ хранится). +- Идемпотентные операции (MERGE по id), ребилд из Postgres за час. +- Сборка через обходы графа (топологическая сортировка). + +**KPI:** граф-проекция, сборка через обходы. + +### Этап 5 — панель (дашборд смысла) +**Цель:** управление атомами вместо статей. + +- Дашборд: атомы по доменам, статусы (draft → enriched → edited → approved → published), версии под каналы. +- Стоимость на атом + runs — юнит-экономика по домену. +- Дедупликация по embedding. +- Composition builder — сборка выходов. + +**KPI:** панель управляет атомами, юнит-экономика по домену. + +--- + +## Схемы данных + +### Атомы +```sql +CREATE TABLE atoms ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + project_id UUID REFERENCES projects(id) ON DELETE CASCADE, + type TEXT NOT NULL, -- claim | fact | stat | quote | definition | instruction | case | comparison | qa | opinion | hook | transition | cta | media_ref + status TEXT NOT NULL DEFAULT 'draft', -- draft | enriched | edited | approved | published | archived + title TEXT, + summary TEXT, + body JSONB NOT NULL, -- типизированное тело по типу атома + plain_text TEXT, + language TEXT DEFAULT 'ru', + entities JSONB DEFAULT '[]', + topics TEXT[] DEFAULT '{}', + intent TEXT, + audience TEXT, + confidence NUMERIC(3,2), + quality_score NUMERIC(4,2), + readability NUMERIC(5,2), + word_count INT, + source_refs JSONB DEFAULT '[]', + embedding VECTOR(1024), + generator JSONB, + cost JSONB, + pipeline_stage TEXT, + parent_atom_id UUID, + lineage_root_id UUID, + created_at TIMESTAMPTZ DEFAULT now(), + updated_at TIMESTAMPTZ DEFAULT now() +); +``` + +### atom_versions +```sql +CREATE TABLE atom_versions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + atom_id UUID REFERENCES atoms(id) ON DELETE CASCADE, + channel TEXT NOT NULL, -- web_article | telegram | youtube_script | podcast | newsletter | service_card + format TEXT NOT NULL, -- markdown | html | plain | srt | ssml | json + content TEXT NOT NULL, + meta JSONB DEFAULT '{}', + quality_score NUMERIC(4,2), + created_at TIMESTAMPTZ DEFAULT now(), + UNIQUE (atom_id, channel, format) +); +``` + +### atom_relations +```sql +CREATE TABLE atom_relations ( + from_id UUID REFERENCES atoms(id) ON DELETE CASCADE, + to_id UUID REFERENCES atoms(id) ON DELETE CASCADE, + kind TEXT NOT NULL, -- supports | contradicts | expands | example_of | part_of | refutes | precedes | derived_from + weight NUMERIC(3,2) DEFAULT 1.0, + meta JSONB DEFAULT '{}', + PRIMARY KEY (from_id, to_id, kind) +); +``` + +### compositions +```sql +CREATE TABLE compositions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + project_id UUID REFERENCES projects(id) ON DELETE CASCADE, + channel TEXT NOT NULL, + slug TEXT, + title TEXT, + status TEXT DEFAULT 'draft', -- draft | building | ready | scheduled | published + recipe JSONB NOT NULL, -- правила сборки: типы, темы, порядок + atom_ids UUID[] NOT NULL, -- порядок атомов в композиции + content TEXT, + meta JSONB DEFAULT '{}', + scheduled_at TIMESTAMPTZ, + published_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT now(), + UNIQUE (project_id, channel, slug) +); +``` + +### publications +```sql +CREATE TABLE publications ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + composition_id UUID REFERENCES compositions(id) ON DELETE CASCADE, + target TEXT NOT NULL, -- URL, channel_id, video_id + external_id TEXT, + published_at TIMESTAMPTZ DEFAULT now(), + metrics JSONB DEFAULT '{}' -- views, clicks, conversions, revenue +); +``` + +### jobs + runs +```sql +CREATE TABLE jobs ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + kind TEXT NOT NULL, -- generate | edit | adapt | publish | index + atom_id UUID REFERENCES atoms(id), + composition_id UUID REFERENCES compositions(id), + status TEXT DEFAULT 'queued',-- queued | running | done | failed | dead + attempts INT DEFAULT 0, + max_attempts INT DEFAULT 3, + payload JSONB DEFAULT '{}', + error TEXT, + scheduled_at TIMESTAMPTZ DEFAULT now(), + started_at TIMESTAMPTZ, + finished_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT now() +); + +CREATE TABLE runs ( + id BIGSERIAL PRIMARY KEY, + job_id UUID REFERENCES jobs(id) ON DELETE CASCADE, + stage TEXT NOT NULL, + model TEXT, + prompt_id UUID REFERENCES prompts(id), + tokens_in INT, + tokens_out INT, + seconds NUMERIC, + cost_usd NUMERIC DEFAULT 0, + quality NUMERIC(4,2), + created_at TIMESTAMPTZ DEFAULT now() +); +``` + +--- + +## Интеграция с текущими сервисами + +### editor-pro (все 6 слоёв) +- Привязать каждый слой к `atom_id` вместо обработки статьи целиком. +- `extract` — новая стадия: `python -m editor_pro.main --extract` → атомы. +- Все слои работают с кластером атомов, не с одной статьёй. + +### neurogeo +- GEO-скоринг на кластере атомов, не на статье. +- Цитируемость на атоме (fact/stat/claim). + +### content-pipeline (TS) +- Новый шаг `extract` в `DEFAULT_PIPELINE`. +- `atomId` вместо `articleIds`. +- `atom_versions` — переупаковка под каналы. + +### Qdrant +- Эмбеддинги атомов (уже используется). +- Дедупликация по embedding + atom_relations. + +### Memgraph (V4) +- Проекция: Atom → Topic → Entity → Project. +- graph_outbox: Postgres → воркер → Memgraph. + +--- + +## Панель (дашборд смысла) + +**Стек:** FastAPI + SQLite/Postgres + React/Vue (или Astro для единого со стэком). + +**Разделы:** +- Атомы по доменам (orabota, ipoteka-ufa, ufa-kvartira, rabota-ufa). +- Статусы атомов (draft → enriched → edited → approved → published). +- Версии под каналы (web/TG/видео). +- Стоимость на атом + runs — юнит-экономика по домену. +- Дедупликация по embedding. +- Composition builder — сборка выходов. + +**KPI:** панель управляет атомами, юнит-экономика по домену. + +--- + +## Правила + +- Каждая стадия не зависит от предыдущих напрямую — только через атом. +- Промпты тянутся из `prompts` по `slug + version`. +- Все запуски пишутся в `runs` — воспроизводимость и аналитика. +- Идемпотентность через `(atom_id, stage, prompt_id, input_hash)`. +- Ошибки → `jobs.error`, ретраи с экспоненциальной задержкой. +- После `max_attempts` — статус `dead`, требует ручного вмешательства. + +--- + +## Антипаттерны (из концепции) + +- Хранить готовый HTML в атоме. +- Перезаписывать атом под канал. +- Промпты в коде, а не в БД. +- Один промпт на все проекты. +- Масштабировать на 50 доменов до юнит-экономики. +- Граф без ребилда из Postgres. +- Публикация без `compositions` (нельзя пересобрать). +- Метрики только на уровне сайта, не атома/композиции. + +--- + +## Что даёт такая архитектура + +| Возможность | Как достигается | +|---|---| +| Мультиканальность без переписывания | `atom_versions` + `compositions` | +| Дедупликация | `embedding` + `atom_relations` | +| Полное покрытие темы | граф связей + рецепты сборки | +| Сквозная аналитика | `publications.metrics` + `runs` | +| Воспроизводимость | `prompts` как данные + `runs` | +| Масштабирование на N доменов | `projects` + общий движок | +| Избежание footprint | разные шаблоны, ритмы, промпты | +| Стоимость и юнит-экономика | `cost` на атом + `runs` | +| Защита от обновлений | обновляешь атом → все производные пересобираются | +| Продажа частями | композиции и проекты изолированы | + +--- + +## Открытые вопросы + +1. **Хранение атомов:** Postgres (как в концепции) или уже есть Postgres? Qdrant только для эмбеддингов. +2. **Панель:** FastAPI + React (отдельно) или Astro (единый стэк)? +3. **V4 граф:** Memgraph или Apache AGE (если Postgres)? +4. **Эмбеддинги:** какая модель (embed-ru, multilingual, локальная)? +5. **Приоритет доменов:** 4 домена (orabota, ipoteka-ufa, ufa-kvartira, rabota-ufa) или начать с одного? + +--- + +*Версия 1.0 — roadmap по этапам. Детальные задачи (TDD) — на этапе реализации каждого уровня.*