База данных (schema v8)
Один SQLite-файл на все скиллы. 20+ таблиц, junction-механика для multi-project. Все скиллы пишут/читают сюда. Кто owner, кто reader, как scope'ить запросы — здесь.
Где лежит и как открыть
~/video-projects/director.db # ← одна shared БД для ВСЕХ скиллов и ВСЕХ проектов
video-projects/, НЕ внутри папки проекта. Все db.py скрипты резолвят её через os.path.dirname(project_dir) / "director.db". Если случайно есть ~/video-projects/<project>/director.db — это per-project (для отдельных stage6-импортов), и она НЕ читается основными скиллами.
Открыть руками:
sqlite3 ~/video-projects/director.db sqlite> .tables # список всех таблиц sqlite> .schema clips # схема таблицы clips sqlite> SELECT COUNT(*) FROM clips; # количество клипов sqlite> SELECT * FROM projects; # все проекты
Или GUI: DBeaver, TablePlus, DB Browser for SQLite.
Архитектура: ресурсы + junction-таблицы
Главная идея multi-project: один ресурс (например клип) может принадлежать нескольким проектам. Это реализовано через junction-таблицы (many-to-many between projects и ресурсами).
Когда video-analyzer импортирует клип, он:
1. INSERT в clips (если ещё нет)
2. INSERT в project_clips (clip_id, project_id) — линкует к текущему проекту
3. Тот же клип можно использовать в другом проекте — просто ещё одна строка в project_clips
Таблицы (ownership)
| Таблица | Owner (пишет) | Readers | Зачем |
|---|---|---|---|
projects | video-project-setup | Все | Реестр проектов (id, slug, name, niche, geo, created_at) |
brief | video-clipgen | copywriter, orchestrator | Структурированный бриф (target_audience, problem, solution, USP) |
reference_images | video-clipgen | video-clipgen | Reference photos для Veo (path, description, analyzed_data) |
clips | video-clipgen, video-analyzer | director, orchestrator | Clip library: path, description, duration, scene_type, mood, reference_image_ids |
generated_texts | video-copywriter | voiceover, director, orchestrator | Тексты озвучки/постов: type, text, target_audience, created_at |
transcriptions | video-copywriter | video-copywriter | Best-performing client creatives (whisper-расшифровки) для style-reference |
competitor_refs | dentistry-research stage6, copywriter | copywriter | Креативы конкурентов: text + transcript + ocr + scene + info_score для reference-адаптации |
voiceover_history | video-voiceover | director, orchestrator | Сгенерированные аудиофайлы (path, duration, voice_id, generated_text_id) |
voices | video-voiceover | voiceover, orchestrator | Voice pool с last_used_at для rotation |
voiceover_settings | video-voiceover | video-voiceover | TTS-настройки (speed, model, stability per project) |
videos | video-director | orchestrator, reviewer | Готовые ролики (path, variant_name, voiceover_id, generated_text_id) |
video_clips | video-director | director | Какие клипы пошли в какой ролик (для rotation) |
music | video-director | director, orchestrator | Background music tracks (path, mood, duration) |
models_3d | video-remotion | director, remotion | 3D-модели для motionfx (glb-файлы + meta) |
creative_posters | creative-poster | creative-orchestrator | Сгенерированные постеры: path, prompt, model, style, preset, cost, qa_score |
creative_performance | creative-orchestrator | creative-orchestrator | Meta-API метрики (ctr, cpl, conversions, spend) per poster_id |
carousel_decks | creative-carousel | creative-carousel | Карусели (project, topic, template, slide_count, plan_json) |
carousel_slides | creative-carousel | creative-carousel | Per-slide records линкованы к deck_id |
explainer_videos | creative-explainer-video | creative-explainer-video | Длинные видео (project, topic, template, aspect, scene_count, duration, voice_id) |
explainer_scenes | creative-explainer-video | creative-explainer-video | Per-scene records линкованы к explainer_videos.id |
performance | (зарезервировано на будущее) | Все | Метрики per-ad для scoring (пока не используется) |
schema_meta | schema.py | Все | Текущая версия схемы (v8) + migration history |
api_keys | Все | Все | API ключи (GOOGLE_API_KEY, ELEVENLABS_API_KEY) per project |
Junction-таблицы (multi-project linking)
Каждая junction = (project_id, resource_id, linked_at). Они дают many-to-many между проектами и ресурсами.
| Junction | Линкует | Где используется |
|---|---|---|
project_clips | projects ↔ clips | director, analyzer, clipgen фильтруют клипы по проекту |
project_videos | projects ↔ videos | orchestrator выдаёт «все ролики проекта X» |
project_voices | projects ↔ voices | voiceover rotation: «следующий голос из пула проекта» |
project_music | projects ↔ music | director выбирает background-музыку с правом доступа |
project_texts | projects ↔ generated_texts | copywriter список «всех текстов проекта» |
project_voiceovers | projects ↔ voiceover_history | «какая озвучка была сгенерирована для проекта» |
project_transcriptions | projects ↔ transcriptions | reference-транскрипции для style copywriter'а |
project_reference_images | projects ↔ reference_images | clipgen берёт reference photos только своего проекта |
project_models | projects ↔ models_3d | motionfx 3D-модели по проекту |
project_creative_posters | projects ↔ creative_posters | creative-orchestrator winners report per project |
Key relationships (schema v8)
Полная chain traceability: проект → текст → озвучка → видео → performance metrics.
projects.id ← project_clips.project_id → clips.id projects.id ← project_videos.project_id → videos.id projects.id ← project_voices.project_id → voices.id projects.id ← project_music.project_id → music.id projects.id ← project_texts.project_id → generated_texts.id projects.id ← project_voiceovers.project_id → voiceover_history.id projects.id ← project_transcriptions.project_id → transcriptions.id projects.id ← project_reference_images.project_id → reference_images.id projects.id ← project_models.project_id → models_3d.id projects.id ← project_creative_posters.project_id → creative_posters.id generated_texts.id ← voiceover_history.generated_text_id voiceover_history.id ← videos.voiceover_id generated_texts.id ← videos.generated_text_id clips.id ← video_clips.clip_id videos.id ← video_clips.video_id reference_images.id → clips.reference_image_ids (JSON-массив) competitor_refs.id ← generated_texts.competitor_ref_id voices.id ← voiceover_history.voice_db_id
Project scoping в SQL-запросах
Все db.py скрипты берут slug проекта из имени папки (os.path.basename(project_dir)), резолвят project.id и фильтруют через junction:
Insert: ресурс + junction-линк
# Python (упрощённо) clip_id = insert_clip(path, description, duration, ...) _link_to_project(conn, "project_clips", "clip_id", clip_id, project_dir)
Query: фильтр по проекту
SELECT c.* FROM clips c JOIN project_clips pc ON c.id = pc.clip_id WHERE pc.project_id = ?
Schema management
schema.py — single source of truth по схеме. Копия лежит в каждом скилле в scripts/schema.py (одинаковая). Версия меняется атомарно одной правкой во всех копиях.
python schema.py init <project-dir> # создать с нуля все таблицы python schema.py migrate <project-dir> # обновить существующую (идемпотентно) python schema.py info <project-dir> # показать tables, columns, row counts
CREATE TABLE IF NOT EXISTS и ALTER TABLE ADD COLUMN внутри try/except.
Migration history
| Версия | Что добавили |
|---|---|
| v6 | Junction-таблицы для multi-project (project_clips, project_voices и т.д.) |
| v7 | Расширение competitor_refs колонками из tools/dentistry-research/ (fb_ad_archive_id, clinic_name, transcript, ocr_text, scene_description) |
| v8 | Добавлены competitor_refs.info_score (0..100) и score_breakdown (JSON) для приоритизации референсов |
v8 — текущая. schema.py migrate работает на любую v6+ базу.
Competitor research pipeline (separate tool)
Standalone TypeScript-инструмент в agents-monorepo/tools/dentistry-research/ заполняет competitor_refs реальными креативами конкурентов (с FB Ad Library).
Source data: data/research.sqlite (отдельная БД, НЕ director.db).
| Stage | Скрипт | Что делает |
|---|---|---|
| 1 | stage1-parse-2gis.ts | Парсит клиники из 2GIS → clinics table |
| 2 / 2a / 2b | stage2*.ts | Матчинг клиник к FB pages (через websites, IG handles, keyword search) |
| 3 | stage3-fetch-creatives.ts | Тянет рекламы из FB Ad Library → creatives table |
| 4 | stage4-analyze.ts | OCR + transcription через OpenAI → analysis table (transcript, ocr_text, scene_description) |
| 4c | stage4c-score.ts | Считает info_score для каждой analysis-строки (идемпотентный backfill) |
| 5 | stage5-export.ts | CSV/JSON export |
| 6 | stage6-import-to-director.ts | Пушит выбранные креативы в director.db.competitor_refs |
Stage 6 import command
cd agents-monorepo/tools/dentistry-research npm run stage6 -- \ --director-db ~/video-projects/director.db \ [--min-creatives 5] \ [--niche dentistry] \ [--reclassify] \ [--dry-run]
- Auto-runs
schema.py migrateесли БД на pre-v8 - UPSERT по
fb_ad_archive_id(идемпотентно, повторные запуски не дублируют) - Фильтр по активности клиники (
--min-creatives, default 5) - Кеширует
dental_categoriesиз stage4 (Gemini классификатор только для непроклассифицированных) - Maps project slugs → global DENTAL_CATEGORIES (например
braces→orthodontics) - Шипит
info_scoreиscore_breakdownдля каждой строки
~/video-projects/director.db(shared, accessed черезdb.py get_db(..., shared=True))~/video-projects/<project>/director.db(per-project, default для db.py)
<project-dir>/director.db по умолчанию — поэтому stage6 только на shared не хватит.
Info_score 0..100 — для приоритизации референсов
Считается в tools/dentistry-research/stage4c-score.ts и приходит в director.db через stage6. Видеокопирайтер использует для выбора лучших конкурентских креативов.
| Компонент | Вес | Источник |
|---|---|---|
| transcript length | 25 | analysis.transcript |
| ad active duration | 18 | creatives.total_active_time |
| clinic creative count | 15 | derived from creatives |
| body_text length | 12 | creatives.body_text |
| variations (A/B count) | 9 | creatives.ads_count |
| currently active | 9 | creatives.is_active |
| OCR text length | 8 | analysis.ocr_text |
| scene description length | 4 | analysis.scene_description |
Default --min-score 50 отсекает короткие image-ads и неоттестированные one-offs. ≥70 — редко (~1% — топовые креативы крупных клиник).
Типичные запросы (для разработчика)
Все ресурсы проекта
-- сколько чего у проекта 'apparatus' SELECT (SELECT COUNT(*) FROM project_clips pc JOIN projects p ON pc.project_id = p.id WHERE p.slug = 'apparatus') AS clips, (SELECT COUNT(*) FROM project_voices pv JOIN projects p ON pv.project_id = p.id WHERE p.slug = 'apparatus') AS voices, (SELECT COUNT(*) FROM project_texts pt JOIN projects p ON pt.project_id = p.id WHERE p.slug = 'apparatus') AS texts, (SELECT COUNT(*) FROM project_videos pv JOIN projects p ON pv.project_id = p.id WHERE p.slug = 'apparatus') AS videos;
Какие клипы давно не использовались (для rotation)
SELECT c.id, c.path, MAX(v.created_at) AS last_used_at FROM clips c JOIN project_clips pc ON c.id = pc.clip_id JOIN projects p ON pc.project_id = p.id LEFT JOIN video_clips vc ON c.id = vc.clip_id LEFT JOIN videos v ON vc.video_id = v.id WHERE p.slug = 'apparatus' GROUP BY c.id ORDER BY last_used_at ASC NULLS FIRST LIMIT 10;
Winners report по статике (CPL)
SELECT cp.id, cp.path, cp.style, cp.preset, perf.cpl, perf.ctr, perf.conversions FROM creative_posters cp JOIN project_creative_posters pcp ON cp.id = pcp.poster_id JOIN projects p ON pcp.project_id = p.id JOIN creative_performance perf ON cp.id = perf.poster_id WHERE p.slug = 'apparatus' AND perf.cpl IS NOT NULL ORDER BY perf.cpl ASC LIMIT 10;
Бэкап и восстановление
# бэкап sqlite3 ~/video-projects/director.db ".backup '/tmp/director_backup_$(date +%Y%m%d).db'" # восстановить из бэкапа cp /tmp/director_backup_20260516.db ~/video-projects/director.db # экспорт всей БД в SQL-дамп sqlite3 ~/video-projects/director.db .dump > director_dump.sql
Куда дальше
- Multi-project архитектура — как организован filesystem уровень
- 7 пайплайнов — flow-диаграммы кто что вызывает
- video-project-setup — как новый проект попадает в БД