docs: initial spec and implementation plan

This commit is contained in:
2026-04-03 21:48:24 +05:00
commit ebd86cd6e8
12 changed files with 5937 additions and 0 deletions
@@ -0,0 +1,87 @@
<h2>Архитектурные подходы</h2>
<p class="subtitle">Выбери подход к построению сервиса транскрибации и протоколирования</p>
<div class="options">
<div class="option" data-choice="a" onclick="toggleSelect(this)">
<div class="letter">A</div>
<div class="content">
<h3>Монолит на Python (FastAPI)</h3>
<p><strong>Стек:</strong> FastAPI + Celery + PostgreSQL + Redis + React</p>
<p>Единое приложение: API, обработка задач, боты, веб-интерфейс — всё в одном репозитории и процессе (+ воркеры Celery для тяжёлых задач).</p>
<div class="pros-cons">
<div class="pros">
<h4>✓ Плюсы</h4>
<ul>
<li>Простота разработки и деплоя</li>
<li>Один язык, одна кодовая база</li>
<li>Быстрый старт — MVP за недели</li>
<li>Легко отлаживать</li>
</ul>
</div>
<div class="cons">
<h4>✗ Минусы</h4>
<ul>
<li>Масштабирование — только вертикальное</li>
<li>Боты, API, обработка связаны — падение одного влияет на всё</li>
<li>При росте код станет сложным</li>
</ul>
</div>
</div>
</div>
</div>
<div class="option" data-choice="b" onclick="toggleSelect(this)">
<div class="letter">B</div>
<div class="content">
<h3>Модульный монолит с очередями (рекомендация)</h3>
<p><strong>Стек:</strong> FastAPI + Celery + PostgreSQL + Redis + React/Vue</p>
<p>Единый деплой, но внутри — изолированные модули: API, транскрибация, генерация протоколов, боты мессенджеров. Общение между модулями — через очереди задач (Celery/Redis). Каждый модуль можно выделить в отдельный сервис позже.</p>
<div class="pros-cons">
<div class="pros">
<h4>✓ Плюсы</h4>
<ul>
<li>Баланс простоты и масштабируемости</li>
<li>Модули изолированы — падение воркера не роняет API</li>
<li>Легко выделить модуль в микросервис при росте</li>
<li>Один деплой, но чистая архитектура</li>
</ul>
</div>
<div class="cons">
<h4>✗ Минусы</h4>
<ul>
<li>Чуть больше начальной настройки чем чистый монолит</li>
<li>Нужна дисциплина разделения модулей</li>
</ul>
</div>
</div>
</div>
</div>
<div class="option" data-choice="c" onclick="toggleSelect(this)">
<div class="letter">C</div>
<div class="content">
<h3>Микросервисы (Docker Compose)</h3>
<p><strong>Стек:</strong> FastAPI сервисы + RabbitMQ/Redis + PostgreSQL + React + Docker</p>
<p>Каждый компонент — отдельный контейнер: API Gateway, Transcription Service, Protocol Generator, Telegram Bot, Lensa Bot, Web Frontend.</p>
<div class="pros-cons">
<div class="pros">
<h4>✓ Плюсы</h4>
<ul>
<li>Максимальная гибкость и масштабируемость</li>
<li>Независимый деплой каждого сервиса</li>
<li>Полная изоляция компонентов</li>
</ul>
</div>
<div class="cons">
<h4>✗ Минусы</h4>
<ul>
<li>Сложность: сеть, мониторинг, логи, оркестрация</li>
<li>Оверкилл для одного сервера</li>
<li>Долгий старт разработки</li>
<li>Сложная отладка</li>
</ul>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,88 @@
<h2>Секция 1: Общая архитектура</h2>
<p class="subtitle">Модульный монолит — компоненты и потоки данных</p>
<div class="mockup">
<div class="mockup-header">Архитектура системы</div>
<div class="mockup-body" style="padding: 24px; font-family: monospace; font-size: 13px; line-height: 1.6;">
<div style="display: flex; gap: 16px; justify-content: center; margin-bottom: 20px;">
<div style="background: #3b82f6; color: white; padding: 10px 16px; border-radius: 8px; text-align: center;">
🌐 Web UI<br><small>React/Vue SPA</small>
</div>
<div style="background: #8b5cf6; color: white; padding: 10px 16px; border-radius: 8px; text-align: center;">
💬 Telegram Bot<br><small>aiogram</small>
</div>
<div style="background: #8b5cf6; color: white; padding: 10px 16px; border-radius: 8px; text-align: center;">
💬 Lensa Bot<br><small>SDK/webhook</small>
</div>
</div>
<div style="text-align: center; color: #666; margin: 8px 0;">▼ HTTP / WebSocket ▼</div>
<div style="background: #1e293b; color: #e2e8f0; padding: 16px; border-radius: 8px; margin-bottom: 16px;">
<div style="text-align: center; font-weight: bold; margin-bottom: 12px; color: #60a5fa;">⚙️ FastAPI Backend (модульный монолит)</div>
<div style="display: flex; gap: 8px; flex-wrap: wrap; justify-content: center;">
<div style="background: #334155; padding: 8px 12px; border-radius: 6px; border-left: 3px solid #3b82f6;">
<strong>API модуль</strong><br>
<small>REST endpoints<br>Auth (JWT)<br>Upload файлов</small>
</div>
<div style="background: #334155; padding: 8px 12px; border-radius: 6px; border-left: 3px solid #f59e0b;">
<strong>Транскрибация</strong><br>
<small>Whisper large-v3<br>FFmpeg конвертация<br>GPU обработка</small>
</div>
<div style="background: #334155; padding: 8px 12px; border-radius: 6px; border-left: 3px solid #10b981;">
<strong>Протоколирование</strong><br>
<small>LLM обработка<br>Шаблоны промптов<br>Экспорт DOCX/PDF/MD</small>
</div>
<div style="background: #334155; padding: 8px 12px; border-radius: 6px; border-left: 3px solid #8b5cf6;">
<strong>Мессенджеры</strong><br>
<small>Telegram бот<br>Lensa бот<br>Unified interface</small>
</div>
</div>
</div>
<div style="display: flex; gap: 16px; justify-content: center; margin-top: 16px;">
<div style="text-align: center; color: #666; margin: 8px 0;">▼ Celery задачи через Redis ▼</div>
</div>
<div style="display: flex; gap: 12px; justify-content: center; margin-top: 12px;">
<div style="background: #dc2626; color: white; padding: 10px 16px; border-radius: 8px; text-align: center;">
🔴 Redis<br><small>Очередь задач<br>+ кэш</small>
</div>
<div style="background: #2563eb; color: white; padding: 10px 16px; border-radius: 8px; text-align: center;">
🐘 PostgreSQL<br><small>Записи, протоколы<br>пользователи</small>
</div>
<div style="background: #6b7280; color: white; padding: 10px 16px; border-radius: 8px; text-align: center;">
📁 File Storage<br><small>Аудио/видео<br>экспорт файлы</small>
</div>
</div>
</div>
</div>
<div class="section" style="margin-top: 24px;">
<h3>Поток обработки записи</h3>
<div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px; font-size: 14px;">
<div style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px;">
<span style="background: #dbeafe; padding: 6px 12px; border-radius: 6px;">1. Загрузка файла</span>
<span style="color: #666;"></span>
<span style="background: #fef3c7; padding: 6px 12px; border-radius: 6px;">2. Извлечение аудио (FFmpeg)</span>
<span style="color: #666;"></span>
<span style="background: #fef3c7; padding: 6px 12px; border-radius: 6px;">3. Транскрибация (Whisper GPU)</span>
<span style="color: #666;"></span>
<span style="background: #d1fae5; padding: 6px 12px; border-radius: 6px;">4. Генерация протокола (LLM)</span>
<span style="color: #666;"></span>
<span style="background: #e0e7ff; padding: 6px 12px; border-radius: 6px;">5. Экспорт (DOCX/PDF/MD)</span>
</div>
</div>
</div>
<div class="section" style="margin-top: 24px;">
<h3>Ключевые решения</h3>
<ul style="font-size: 14px; line-height: 1.8;">
<li><strong>Whisper large-v3</strong> — локально на GPU (RTX 3070 Ti, 8GB VRAM достаточно)</li>
<li><strong>Celery + Redis</strong> — асинхронная обработка, транскрибация и LLM не блокируют API</li>
<li><strong>PostgreSQL</strong> — записи, протоколы, пользователи, шаблоны</li>
<li><strong>LLM через внешний API</strong> — OpenRouter/Groq/Together с возможностью подключить платные модели</li>
<li><strong>FFmpeg</strong> — извлечение аудио из видео, нормализация форматов</li>
<li><strong>Docker Compose</strong> — для деплоя всех компонентов на сервере</li>
</ul>
</div>
@@ -0,0 +1,122 @@
<h2>Секция 2: Модель данных</h2>
<p class="subtitle">Основные сущности и связи в PostgreSQL</p>
<div class="mockup">
<div class="mockup-header">ER-диаграмма</div>
<div class="mockup-body" style="padding: 24px; font-size: 13px;">
<div style="display: flex; gap: 16px; flex-wrap: wrap; justify-content: center;">
<!-- User -->
<div style="background: #1e293b; color: #e2e8f0; padding: 14px; border-radius: 8px; min-width: 200px; border-top: 3px solid #3b82f6;">
<div style="font-weight: bold; color: #60a5fa; margin-bottom: 8px;">👤 User</div>
<div style="font-size: 12px; line-height: 1.8;">
<code>id</code> UUID PK<br>
<code>username</code> str unique<br>
<code>email</code> str unique<br>
<code>password_hash</code> str<br>
<code>role</code> enum (admin, user)<br>
<code>is_active</code> bool<br>
<code>created_at</code> timestamp
</div>
</div>
<!-- Recording -->
<div style="background: #1e293b; color: #e2e8f0; padding: 14px; border-radius: 8px; min-width: 220px; border-top: 3px solid #f59e0b;">
<div style="font-weight: bold; color: #fbbf24; margin-bottom: 8px;">🎬 Recording</div>
<div style="font-size: 12px; line-height: 1.8;">
<code>id</code> UUID PK<br>
<code>user_id</code> FK → User<br>
<code>title</code> str<br>
<code>file_path</code> str<br>
<code>file_size</code> bigint<br>
<code>duration</code> interval<br>
<code>format</code> str (mp4, webm, mp3…)<br>
<code>source</code> enum (web, telegram, lensa)<br>
<code>status</code> enum (uploaded, processing, done, error)<br>
<code>created_at</code> timestamp
</div>
</div>
<!-- Transcription -->
<div style="background: #1e293b; color: #e2e8f0; padding: 14px; border-radius: 8px; min-width: 220px; border-top: 3px solid #10b981;">
<div style="font-weight: bold; color: #34d399; margin-bottom: 8px;">📝 Transcription</div>
<div style="font-size: 12px; line-height: 1.8;">
<code>id</code> UUID PK<br>
<code>recording_id</code> FK → Recording<br>
<code>text</code> text<br>
<code>segments</code> JSONB (таймкоды)<br>
<code>language</code> str<br>
<code>whisper_model</code> str<br>
<code>processing_time</code> interval<br>
<code>created_at</code> timestamp
</div>
</div>
<!-- Protocol -->
<div style="background: #1e293b; color: #e2e8f0; padding: 14px; border-radius: 8px; min-width: 220px; border-top: 3px solid #8b5cf6;">
<div style="font-weight: bold; color: #a78bfa; margin-bottom: 8px;">📋 Protocol</div>
<div style="font-size: 12px; line-height: 1.8;">
<code>id</code> UUID PK<br>
<code>transcription_id</code> FK → Transcription<br>
<code>template_id</code> FK → PromptTemplate<br>
<code>content</code> JSONB (структурированный)<br>
<code>raw_text</code> text<br>
<code>llm_model</code> str<br>
<code>status</code> enum (generating, done, error)<br>
<code>created_at</code> timestamp<br>
<code>updated_at</code> timestamp
</div>
</div>
<!-- PromptTemplate -->
<div style="background: #1e293b; color: #e2e8f0; padding: 14px; border-radius: 8px; min-width: 220px; border-top: 3px solid #ec4899;">
<div style="font-weight: bold; color: #f472b6; margin-bottom: 8px;">📐 PromptTemplate</div>
<div style="font-size: 12px; line-height: 1.8;">
<code>id</code> UUID PK<br>
<code>name</code> str<br>
<code>description</code> str<br>
<code>type</code> enum (client_survey, internal, standup, custom)<br>
<code>system_prompt</code> text<br>
<code>user_prompt</code> text<br>
<code>output_schema</code> JSONB<br>
<code>is_default</code> bool<br>
<code>created_at</code> timestamp
</div>
</div>
<!-- ExportFile -->
<div style="background: #1e293b; color: #e2e8f0; padding: 14px; border-radius: 8px; min-width: 200px; border-top: 3px solid #6b7280;">
<div style="font-weight: bold; color: #9ca3af; margin-bottom: 8px;">📄 ExportFile</div>
<div style="font-size: 12px; line-height: 1.8;">
<code>id</code> UUID PK<br>
<code>protocol_id</code> FK → Protocol<br>
<code>format</code> enum (docx, pdf, md)<br>
<code>file_path</code> str<br>
<code>file_size</code> bigint<br>
<code>created_at</code> timestamp
</div>
</div>
</div>
<!-- Relations -->
<div style="margin-top: 20px; padding: 12px; background: #f1f5f9; border-radius: 8px; font-size: 13px;">
<strong>Связи:</strong>
User 1→N Recording → 1→1 Transcription → 1→N Protocol → 1→N ExportFile<br>
PromptTemplate 1→N Protocol
</div>
</div>
</div>
<div class="section" style="margin-top: 24px;">
<h3>Управление хранилищем</h3>
<div style="background: #fefce8; border: 1px solid #fde047; border-radius: 8px; padding: 16px; font-size: 14px;">
<ul style="line-height: 1.8; margin: 0;">
<li><strong>Retention policies</strong> — настраиваемый срок хранения для записей, транскрипций и экспортов (по умолчанию: 90 дней)</li>
<li><strong>Ручная очистка</strong> — админ может удалить старые данные из UI (записи + связанные файлы)</li>
<li><strong>Автоочистка</strong> — Celery beat задача по расписанию удаляет данные старше retention period</li>
<li><strong>Дашборд хранилища</strong> — в админке видно занятое место (файлы + БД) с разбивкой по типам</li>
</ul>
</div>
</div>
@@ -0,0 +1,127 @@
<h2>Секция 5: Деплой и технологический стек</h2>
<p class="subtitle">Развёртывание на сервере 78.153.7.224</p>
<div class="mockup">
<div class="mockup-header">Docker Compose — контейнеры на сервере</div>
<div class="mockup-body" style="padding: 24px; font-size: 13px;">
<div style="background: #1e293b; color: #e2e8f0; padding: 16px; border-radius: 8px; margin-bottom: 16px;">
<div style="font-weight: bold; color: #60a5fa; margin-bottom: 12px;">🖥️ Сервер 78.153.7.224 — i9-10900 / 32GB RAM / RTX 3070 Ti</div>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<div style="background: #334155; padding: 10px; border-radius: 6px; border-top: 3px solid #3b82f6; flex: 1; min-width: 140px;">
<strong style="color: #60a5fa;">app</strong><br>
<small>FastAPI + Uvicorn<br>API + Web serving<br>Port 8000</small>
</div>
<div style="background: #334155; padding: 10px; border-radius: 6px; border-top: 3px solid #f59e0b; flex: 1; min-width: 140px;">
<strong style="color: #fbbf24;">worker</strong><br>
<small>Celery worker<br>Whisper (GPU)<br>LLM вызовы</small>
</div>
<div style="background: #334155; padding: 10px; border-radius: 6px; border-top: 3px solid #8b5cf6; flex: 1; min-width: 140px;">
<strong style="color: #a78bfa;">beat</strong><br>
<small>Celery beat<br>Автоочистка<br>Scheduled tasks</small>
</div>
<div style="background: #334155; padding: 10px; border-radius: 6px; border-top: 3px solid #dc2626; flex: 1; min-width: 140px;">
<strong style="color: #f87171;">redis</strong><br>
<small>Redis 7<br>Брокер задач<br>Кэш</small>
</div>
<div style="background: #334155; padding: 10px; border-radius: 6px; border-top: 3px solid #2563eb; flex: 1; min-width: 140px;">
<strong style="color: #60a5fa;">db</strong><br>
<small>PostgreSQL 16<br>Данные<br>Port 5432</small>
</div>
<div style="background: #334155; padding: 10px; border-radius: 6px; border-top: 3px solid #10b981; flex: 1; min-width: 140px;">
<strong style="color: #34d399;">nginx</strong><br>
<small>Reverse proxy<br>Static files<br>Port 80/443</small>
</div>
</div>
</div>
</div>
</div>
<div class="section" style="margin-top: 24px;">
<h3>Полный стек технологий</h3>
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
<div style="background: #f8fafc; border: 1px solid #e2e8f0; padding: 14px; border-radius: 8px; flex: 1; min-width: 220px; font-size: 13px; line-height: 1.8;">
<strong style="color: #3b82f6;">Backend</strong><br>
Python 3.11+<br>
FastAPI + Uvicorn<br>
Celery + Redis<br>
SQLAlchemy + Alembic<br>
Pydantic v2
</div>
<div style="background: #f8fafc; border: 1px solid #e2e8f0; padding: 14px; border-radius: 8px; flex: 1; min-width: 220px; font-size: 13px; line-height: 1.8;">
<strong style="color: #f59e0b;">AI / ML</strong><br>
Whisper large-v3 (faster-whisper)<br>
CUDA 12 + cuDNN<br>
FFmpeg (извлечение аудио)<br>
LiteLLM (unified LLM API)<br>
OpenRouter / Groq / платные
</div>
<div style="background: #f8fafc; border: 1px solid #e2e8f0; padding: 14px; border-radius: 8px; flex: 1; min-width: 220px; font-size: 13px; line-height: 1.8;">
<strong style="color: #10b981;">Frontend</strong><br>
React + TypeScript<br>
Vite (сборка)<br>
Tailwind CSS<br>
React Query (API)<br>
React Router
</div>
<div style="background: #f8fafc; border: 1px solid #e2e8f0; padding: 14px; border-radius: 8px; flex: 1; min-width: 220px; font-size: 13px; line-height: 1.8;">
<strong style="color: #8b5cf6;">Мессенджеры</strong><br>
aiogram 3.x (Telegram)<br>
Lensa SDK/webhook<br>
Общий MessengerService
</div>
<div style="background: #f8fafc; border: 1px solid #e2e8f0; padding: 14px; border-radius: 8px; flex: 1; min-width: 220px; font-size: 13px; line-height: 1.8;">
<strong style="color: #dc2626;">Экспорт</strong><br>
python-docx (DOCX)<br>
WeasyPrint (PDF)<br>
Jinja2 (шаблоны)
</div>
<div style="background: #f8fafc; border: 1px solid #e2e8f0; padding: 14px; border-radius: 8px; flex: 1; min-width: 220px; font-size: 13px; line-height: 1.8;">
<strong style="color: #6b7280;">Инфра</strong><br>
Docker + Docker Compose<br>
NVIDIA Container Toolkit<br>
Nginx (reverse proxy)<br>
Gitea (http://78.153.7.224:3000)<br>
Alembic (миграции БД)
</div>
</div>
</div>
<div class="section" style="margin-top: 24px;">
<h3>Распределение ресурсов сервера</h3>
<div style="background: #fefce8; border: 1px solid #fde047; border-radius: 8px; padding: 16px; font-size: 13px; line-height: 1.8;">
<ul style="margin: 0;">
<li><strong>GPU (8 GB VRAM)</strong> — целиком для Whisper (faster-whisper загружает модель ~3 GB, остальное — буфер обработки)</li>
<li><strong>RAM (32 GB)</strong> — ~4 GB PostgreSQL, ~2 GB Redis, ~4 GB FastAPI + workers, ~8 GB Whisper worker, остальное — ОС и запас</li>
<li><strong>CPU (i9-10900, 10 ядер)</strong> — хватит для параллельной обработки API + воркеры</li>
<li><strong>SSD 1 TB</strong> — ОС, Docker, БД, активные файлы</li>
<li><strong>HDD 1 TB</strong> — архивное хранение старых записей (перемещение по retention policy)</li>
</ul>
</div>
</div>
<div class="section" style="margin-top: 24px;">
<h3>CI/CD</h3>
<div style="background: #f1f5f9; padding: 16px; border-radius: 8px; font-size: 13px; line-height: 1.8;">
<ul style="margin: 0;">
<li>Код хранится в <strong>Gitea</strong> на http://78.153.7.224:3000</li>
<li>Деплой — <code>docker compose up -d --build</code> на сервере (ручной или через Gitea webhook)</li>
<li>Миграции БД — <code>alembic upgrade head</code> при старте контейнера app</li>
</ul>
</div>
</div>
@@ -0,0 +1,103 @@
<h2>Секция 4: Интеграция с мессенджерами</h2>
<p class="subtitle">Telegram и Lensa боты — полный цикл работы</p>
<div class="section">
<h3>Сценарий взаимодействия с ботом</h3>
<div class="mockup">
<div class="mockup-header">Telegram / Lensa — диалог с ботом</div>
<div class="mockup-body" style="padding: 20px; background: #0e1621; color: #e2e8f0; font-size: 13px; line-height: 1.8;">
<div style="margin-bottom: 16px;">
<div style="background: #2b5278; padding: 8px 14px; border-radius: 12px 12px 12px 4px; display: inline-block; max-width: 80%;">
👤 <em>Пользователь отправляет видео/аудио файл</em>
</div>
</div>
<div style="margin-bottom: 16px; text-align: right;">
<div style="background: #2b5278; padding: 8px 14px; border-radius: 12px 12px 4px 12px; display: inline-block; max-width: 80%; text-align: left; background: #1e3a5f;">
🤖 Файл получен! Выберите тип протокола:<br><br>
<span style="display: inline-flex; gap: 6px; flex-wrap: wrap;">
<span style="background: #3b82f6; padding: 4px 12px; border-radius: 6px; font-size: 12px;">📋 Обследование процессов</span>
<span style="background: #3b82f6; padding: 4px 12px; border-radius: 6px; font-size: 12px;">🤝 Вводная встреча</span>
<span style="background: #3b82f6; padding: 4px 12px; border-radius: 6px; font-size: 12px;">🏢 Внутренняя встреча</span>
<span style="background: #3b82f6; padding: 4px 12px; border-radius: 6px; font-size: 12px;">⚙️ Кастомный</span>
</span>
</div>
</div>
<div style="margin-bottom: 16px;">
<div style="background: #2b5278; padding: 8px 14px; border-radius: 12px 12px 12px 4px; display: inline-block;">
👤 <em>Нажимает «Обследование процессов»</em>
</div>
</div>
<div style="margin-bottom: 16px; text-align: right;">
<div style="background: #1e3a5f; padding: 8px 14px; border-radius: 12px 12px 4px 12px; display: inline-block; max-width: 80%; text-align: left;">
🤖 ⏳ Принято! Обработка займёт несколько минут.<br>
<span style="color: #94a3b8;">Транскрибация: ▓▓▓▓░░░░ 50%</span>
</div>
</div>
<div style="margin-bottom: 16px; text-align: right;">
<div style="background: #1e3a5f; padding: 8px 14px; border-radius: 12px 12px 4px 12px; display: inline-block; max-width: 80%; text-align: left;">
🤖 ✅ Протокол готов!<br><br>
<strong>Тема:</strong> Обследование процесса закупок ООО «Клиент»<br>
<strong>Дата:</strong> 01.04.2026<br>
<strong>Участники:</strong> Иванов И.И., Петров П.П., ...<br><br>
<em style="color: #94a3b8;">[показан полный текст протокола]</em><br><br>
<span style="display: inline-flex; gap: 6px; flex-wrap: wrap;">
<span style="background: #10b981; padding: 4px 12px; border-radius: 6px; font-size: 12px;">📄 DOCX</span>
<span style="background: #10b981; padding: 4px 12px; border-radius: 6px; font-size: 12px;">📕 PDF</span>
<span style="background: #10b981; padding: 4px 12px; border-radius: 6px; font-size: 12px;">📝 MD</span>
<span style="background: #f59e0b; padding: 4px 12px; border-radius: 6px; font-size: 12px;">🔄 Перегенерировать</span>
<span style="background: #6366f1; padding: 4px 12px; border-radius: 6px; font-size: 12px;">🌐 Открыть в вебе</span>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="section" style="margin-top: 24px;">
<h3>Команды бота</h3>
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
<div style="background: #f8fafc; border: 1px solid #e2e8f0; padding: 14px; border-radius: 8px; flex: 1; min-width: 220px; font-size: 13px; line-height: 1.8;">
<strong>Основные</strong><br>
<code>/start</code> — авторизация (логин/пароль)<br>
<code>/upload</code> — загрузить файл (или просто отправить)<br>
<code>/list</code> — список записей<br>
<code>/protocols</code> — список протоколов
</div>
<div style="background: #f8fafc; border: 1px solid #e2e8f0; padding: 14px; border-radius: 8px; flex: 1; min-width: 220px; font-size: 13px; line-height: 1.8;">
<strong>Работа с протоколами</strong><br>
<code>/view [id]</code> — просмотреть протокол<br>
<code>/export [id] [формат]</code> — экспорт<br>
<code>/regenerate [id]</code> — перегенерировать<br>
<code>/templates</code> — список шаблонов
</div>
<div style="background: #f8fafc; border: 1px solid #e2e8f0; padding: 14px; border-radius: 8px; flex: 1; min-width: 220px; font-size: 13px; line-height: 1.8;">
<strong>Настройки</strong><br>
<code>/settings</code> — шаблон по умолчанию<br>
<code>/help</code> — справка<br>
<code>/logout</code> — выход
</div>
</div>
</div>
<div class="section" style="margin-top: 24px;">
<h3>Технические детали</h3>
<div style="background: #f1f5f9; padding: 16px; border-radius: 8px; font-size: 13px; line-height: 1.8;">
<ul style="margin: 0;">
<li><strong>Telegram</strong> — aiogram 3.x, webhook mode (через FastAPI), inline-кнопки для навигации</li>
<li><strong>Lensa</strong> — SDK/webhook интеграция (аналогичный интерфейс)</li>
<li><strong>Общий слой</strong> — единый MessengerService абстрагирует логику от конкретного мессенджера</li>
<li><strong>Лимит файлов</strong> — Telegram ограничивает загрузку 50 МБ через бот API; для больших файлов — ссылка на веб-загрузку</li>
<li><strong>Авторизация</strong> — привязка аккаунта мессенджера к учётной записи через /start</li>
</ul>
</div>
</div>
@@ -0,0 +1,136 @@
<h2>Секция 3: Шаблоны протоколов и веб-интерфейс</h2>
<p class="subtitle">Предустановленные промпты и структура UI</p>
<div class="section">
<h3>Предустановленные шаблоны протоколов</h3>
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
<div style="background: #1e293b; color: #e2e8f0; padding: 16px; border-radius: 8px; flex: 1; min-width: 250px; border-left: 4px solid #8b5cf6;">
<div style="font-weight: bold; color: #a78bfa; margin-bottom: 8px;">📋 Обследование процессов клиента</div>
<div style="font-size: 12px; line-height: 1.8;">
<div style="color: #94a3b8; margin-bottom: 4px;">Разделы протокола:</div>
1. Дата, участники, тема встречи<br>
2. Описание обследуемого процесса<br>
<small style="color: #64748b;">&nbsp;&nbsp;&nbsp;(задачи бизнеса, подпроцесс)</small><br>
3. Текущее состояние (as-is)<br>
4. Выявленные проблемы / узкие места<br>
5. Договорённости и решения<br>
6. Открытые вопросы<br>
7. Задачи с ответственными и сроками<br>
8. Следующие шаги
</div>
</div>
<div style="background: #1e293b; color: #e2e8f0; padding: 16px; border-radius: 8px; flex: 1; min-width: 250px; border-left: 4px solid #3b82f6;">
<div style="font-weight: bold; color: #60a5fa; margin-bottom: 8px;">🏢 Внутренняя рабочая встреча</div>
<div style="font-size: 12px; line-height: 1.8;">
<div style="color: #94a3b8; margin-bottom: 4px;">Разделы протокола:</div>
1. Дата, участники, тема<br>
2. Обсуждённые вопросы<br>
3. Принятые решения<br>
4. Задачи с ответственными и сроками<br>
5. Следующие шаги
</div>
</div>
<div style="background: #1e293b; color: #e2e8f0; padding: 16px; border-radius: 8px; flex: 1; min-width: 250px; border-left: 4px solid #10b981;">
<div style="font-weight: bold; color: #34d399; margin-bottom: 8px;">⚡ Стендап / короткая встреча</div>
<div style="font-size: 12px; line-height: 1.8;">
<div style="color: #94a3b8; margin-bottom: 4px;">Разделы протокола:</div>
1. Дата, участники<br>
2. Статус по задачам (кто / что сделал)<br>
3. Блокеры и проблемы<br>
4. План на следующий период
</div>
</div>
</div>
<div style="margin-top: 12px; font-size: 13px; color: #64748b;">
+ Возможность создать кастомный шаблон с произвольными разделами и промптом
</div>
</div>
<div class="section" style="margin-top: 28px;">
<h3>Структура веб-интерфейса</h3>
</div>
<div class="mockup">
<div class="mockup-header">Основные экраны</div>
<div class="mockup-body" style="padding: 0;">
<!-- Nav -->
<div style="background: #1e293b; color: white; padding: 10px 20px; display: flex; justify-content: space-between; align-items: center; font-size: 13px;">
<div style="display: flex; gap: 20px; align-items: center;">
<strong style="color: #60a5fa;">MeetingProto</strong>
<span>Записи</span>
<span>Протоколы</span>
<span>Шаблоны</span>
<span style="color: #94a3b8;">Настройки</span>
</div>
<div style="display: flex; align-items: center; gap: 12px; font-size: 12px;">
<span style="color: #64748b;">Занято: 45.2 ГБ</span>
<span>👤 admin</span>
</div>
</div>
<!-- Recordings list -->
<div style="padding: 20px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
<h3 style="margin: 0;">Записи</h3>
<div style="display: flex; gap: 8px;">
<button style="background: #3b82f6; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer;">+ Загрузить</button>
</div>
</div>
<div style="font-size: 13px;">
<div style="display: grid; grid-template-columns: 2fr 1fr 1fr 1fr 1fr; gap: 8px; padding: 8px 0; border-bottom: 1px solid #e2e8f0; font-weight: bold; color: #64748b;">
<span>Название</span>
<span>Источник</span>
<span>Длительность</span>
<span>Статус</span>
<span>Действия</span>
</div>
<div style="display: grid; grid-template-columns: 2fr 1fr 1fr 1fr 1fr; gap: 8px; padding: 10px 0; border-bottom: 1px solid #f1f5f9;">
<span>Обследование_КлиентА_2026-04-01.mp4</span>
<span style="color: #8b5cf6;">🌐 Web</span>
<span>01:23:45</span>
<span><span style="background: #d1fae5; color: #065f46; padding: 2px 8px; border-radius: 10px; font-size: 11px;">Готово</span></span>
<span style="color: #3b82f6;">Протокол | Скачать</span>
</div>
<div style="display: grid; grid-template-columns: 2fr 1fr 1fr 1fr 1fr; gap: 8px; padding: 10px 0; border-bottom: 1px solid #f1f5f9;">
<span>standup_2026-04-02.ogg</span>
<span style="color: #8b5cf6;">💬 Telegram</span>
<span>00:15:32</span>
<span><span style="background: #fef3c7; color: #92400e; padding: 2px 8px; border-radius: 10px; font-size: 11px;">Транскрибация...</span></span>
<span style="color: #94a3b8;"></span>
</div>
<div style="display: grid; grid-template-columns: 2fr 1fr 1fr 1fr 1fr; gap: 8px; padding: 10px 0; border-bottom: 1px solid #f1f5f9;">
<span>retro_sprint42.webm</span>
<span style="color: #8b5cf6;">💬 Lensa</span>
<span>00:45:10</span>
<span><span style="background: #dbeafe; color: #1e40af; padding: 2px 8px; border-radius: 10px; font-size: 11px;">Протокол...</span></span>
<span style="color: #94a3b8;"></span>
</div>
</div>
</div>
</div>
</div>
<div class="section" style="margin-top: 20px;">
<h3>Страницы интерфейса</h3>
<div style="display: flex; gap: 12px; flex-wrap: wrap; font-size: 13px;">
<div style="background: #f1f5f9; padding: 12px; border-radius: 8px; flex: 1; min-width: 200px;">
<strong>Записи</strong> — список, загрузка, статус обработки, фильтры по источнику/дате
</div>
<div style="background: #f1f5f9; padding: 12px; border-radius: 8px; flex: 1; min-width: 200px;">
<strong>Протоколы</strong> — просмотр, редактирование, экспорт в DOCX/PDF/MD, перегенерация с другим шаблоном
</div>
<div style="background: #f1f5f9; padding: 12px; border-radius: 8px; flex: 1; min-width: 200px;">
<strong>Шаблоны</strong> — управление промптами, создание кастомных шаблонов, предпросмотр
</div>
<div style="background: #f1f5f9; padding: 12px; border-radius: 8px; flex: 1; min-width: 200px;">
<strong>Настройки</strong> — LLM провайдер/модель, retention period, очистка данных, API ключи, профиль
</div>
</div>
</div>
@@ -0,0 +1 @@
{"reason":"idle timeout","timestamp":1775226043406}
@@ -0,0 +1,25 @@
{"type":"server-started","port":56999,"host":"127.0.0.1","url_host":"localhost","url":"http://localhost:56999","screen_dir":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content","state_dir":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/state"}
{"type":"screen-added","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/approaches.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/approaches.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/approaches.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/approaches.html"}
{"source":"user-event","type":"click","text":"B\n \n Модульный монолит с очередями (рекомендация)\n Стек: FastAPI + Celery + PostgreSQL + Redis + React/Vue\n Единый деплой, но внутри — изолированные модули: API, транскрибация, генерация протоколов, боты мессенджеров. Общение между модулями — через очереди задач (Celery/Redis). Каждый модуль можно выделить в отдельный сервис позже.\n \n \n ✓ Плюсы\n \n Баланс простоты и масштабируемости\n Модули изолированы — падение воркера не роняет API\n Легко выделить модуль в микросервис при росте\n Один деплой, но чистая архитектура\n \n \n \n ✗ Минусы\n \n Чуть больше начальной настройки чем чистый монолит\n Нужна дисциплина разделения модулей","choice":"b","id":null,"timestamp":1775223177656}
{"type":"screen-added","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/architecture.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/architecture.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/architecture.html"}
{"type":"screen-added","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/data-model.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/data-model.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/data-model.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/data-model.html"}
{"type":"screen-added","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/templates-and-ui.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/templates-and-ui.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/templates-and-ui.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/templates-and-ui.html"}
{"type":"screen-added","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/messengers.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/messengers.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/messengers.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/messengers.html"}
{"type":"screen-added","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/deployment.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/deployment.html"}
{"type":"screen-updated","file":"/Users/romanchesnokov/Library/CloudStorage/OneDrive-Личная/Работа/Внутренние проекты/meeting_recording_analysis/.superpowers/brainstorm/47219-1775222083/content/deployment.html"}
{"type":"server-stopped","reason":"idle timeout"}
@@ -0,0 +1 @@
47231
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,267 @@
# Meeting Protocol Service — Design Spec
Сервис транскрибации аудио/видео записей встреч и автоматической генерации протоколов с использованием локальной модели Whisper и внешних LLM.
## 1. Цели и контекст
- Автоматизация создания протоколов по записям встреч (внутренних и с клиентами)
- Протоколы клиентских встреч — основа для результирующей документации по обследованию процессов
- Бесплатная локальная транскрибация (Whisper на GPU)
- Генерация протоколов через бесплатные/платные LLM API
- Веб-интерфейс + полнофункциональные боты в Telegram и Lensa
- Хранение кода и версионирование в Gitea на http://78.153.7.224:3000
## 2. Архитектура
### Подход: модульный монолит
Единый деплой с изолированными модулями, общение между которыми — через очереди задач (Celery/Redis). Каждый модуль можно выделить в отдельный сервис при необходимости.
### Модули
| Модуль | Ответственность |
|--------|----------------|
| **API** | REST endpoints, авторизация (JWT), загрузка файлов, раздача SPA |
| **Транскрибация** | Извлечение аудио (FFmpeg), транскрибация (faster-whisper на GPU) |
| **Протоколирование** | Вызов LLM, применение шаблонов промптов, экспорт DOCX/PDF/MD |
| **Мессенджеры** | Telegram бот (aiogram 3.x), Lensa бот (SDK/webhook), общий MessengerService |
### Поток обработки записи
1. Загрузка файла (web / telegram / lensa)
2. Извлечение аудио из видео (FFmpeg) — Celery task
3. Транскрибация (faster-whisper large-v3, GPU) — Celery task
4. Генерация протокола (LLM через LiteLLM) — Celery task
5. Сохранение результата, уведомление пользователя
6. Экспорт по запросу (DOCX/PDF/MD)
### Асинхронная обработка
Все тяжёлые операции (шаги 2-4) выполняются в Celery workers и не блокируют API. Пользователь получает обновления о прогрессе через polling (web) и push-уведомления (мессенджеры).
## 3. Модель данных
### User
- `id` UUID PK
- `username` str unique
- `email` str unique
- `password_hash` str
- `role` enum: admin, user (расширяемо)
- `is_active` bool
- `created_at` timestamp
### Recording
- `id` UUID PK
- `user_id` FK → User
- `title` str
- `file_path` str
- `file_size` bigint
- `duration` interval
- `format` str (mp4, webm, mp3, wav, ogg)
- `source` enum: web, telegram, lensa
- `status` enum: uploaded, processing, done, error
- `created_at` timestamp
### Transcription
- `id` UUID PK
- `recording_id` FK → Recording (1:1)
- `text` text
- `segments` JSONB (массив сегментов с таймкодами)
- `language` str
- `whisper_model` str
- `processing_time` interval
- `created_at` timestamp
### Protocol
- `id` UUID PK
- `transcription_id` FK → Transcription
- `template_id` FK → PromptTemplate
- `content` JSONB (структурированный по разделам)
- `raw_text` text
- `llm_model` str
- `status` enum: generating, done, error
- `created_at` timestamp
- `updated_at` timestamp
### PromptTemplate
- `id` UUID PK
- `name` str
- `description` str
- `type` enum: client_survey, client_intro, internal, custom
- `system_prompt` text
- `user_prompt` text
- `output_schema` JSONB (описание ожидаемых разделов)
- `is_default` bool
- `created_at` timestamp
### ExportFile
- `id` UUID PK
- `protocol_id` FK → Protocol
- `format` enum: docx, pdf, md
- `file_path` str
- `file_size` bigint
- `created_at` timestamp
### Связи
```
User 1→N Recording 1→1 Transcription 1→N Protocol 1→N ExportFile
PromptTemplate 1→N Protocol
```
## 4. Предустановленные шаблоны протоколов
### 4.1 Обследование процессов клиента
Разделы:
1. Дата, участники, тема встречи
2. Описание обследуемого процесса (задачи бизнеса, подпроцесс)
3. Текущее состояние (as-is)
4. Выявленные проблемы / узкие места
5. Договорённости и решения
6. Открытые вопросы (о которых не договорились)
7. Задачи с ответственными и сроками
8. Следующие шаги
### 4.2 Вводная встреча-знакомство с клиентом
Разделы:
1. Дата, участники, тема встречи
2. Верхнеуровневые цели клиента
3. Параметры будущего проекта
4. Бюджетные ограничения
5. Ограничения по срокам
6. Лица, принимающие решения (ЛПР)
7. Бенефициары
8. Лица, влияющие на принятие решения
9. Договорённости и решения
10. Открытые вопросы
11. Следующие шаги
### 4.3 Внутренняя рабочая встреча
Разделы:
1. Дата, участники, тема
2. Обсуждённые вопросы
3. Выявленные проблемы
4. Принятые решения
5. Открытые вопросы
6. Задачи с ответственными и сроками
7. Следующие шаги
### 4.4 Кастомный шаблон
Пользователь может создать свой шаблон с произвольными разделами и промптом через веб-интерфейс или бот.
## 5. Веб-интерфейс
### Страницы
| Страница | Функционал |
|----------|-----------|
| **Записи** | Список записей с фильтрами по источнику/дате, загрузка файлов, статус обработки |
| **Протоколы** | Просмотр, редактирование, экспорт (DOCX/PDF/MD), перегенерация с другим шаблоном |
| **Шаблоны** | Управление промптами, создание кастомных шаблонов, предпросмотр |
| **Настройки** | LLM провайдер/модель, API-ключи, retention period, очистка данных, профиль |
### Навигация
Шапка: логотип, разделы (Записи, Протоколы, Шаблоны, Настройки), индикатор занятого дискового пространства ("Занято: N ГБ"), профиль пользователя.
### Стек фронтенда
- React + TypeScript
- Vite (сборка)
- Tailwind CSS
- React Query (работа с API)
- React Router
## 6. Интеграция с мессенджерами
### Сценарий работы
1. Пользователь отправляет файл в бот
2. Бот предлагает выбрать тип протокола (inline-кнопки)
3. Бот показывает прогресс обработки
4. Бот отправляет готовый протокол + кнопки: экспорт (DOCX/PDF/MD), перегенерация, открыть в вебе
### Команды бота
**Основные:** `/start` (авторизация), `/upload` (или просто отправить файл), `/list` (записи), `/protocols` (протоколы)
**Работа с протоколами:** `/view [id]`, `/export [id] [формат]`, `/regenerate [id]`, `/templates`
**Настройки:** `/settings` (шаблон по умолчанию), `/help`, `/logout`
### Технические детали
- **Telegram** — aiogram 3.x, webhook mode через FastAPI, inline-кнопки
- **Lensa** — SDK/webhook интеграция с аналогичным интерфейсом
- **MessengerService** — общий слой абстракции, единая логика для обоих мессенджеров
- **Лимит файлов** — Telegram Bot API ограничивает 50 МБ; для больших файлов — ссылка на веб-загрузку
- **Авторизация** — привязка аккаунта мессенджера к учётной записи через `/start` (логин/пароль)
## 7. Авторизация
- Базовая: логин/пароль, JWT-токены
- Роли: admin, user (расширяемо в будущем)
- Все авторизованные пользователи видят все протоколы
- Привязка аккаунтов мессенджеров к учётной записи
- Подготовлена структура для расширения ролевой модели (поле role — enum, легко расширить)
## 8. Управление хранилищем
- **Retention policies** — настраиваемый срок хранения для записей, транскрипций и экспортов (по умолчанию: 90 дней)
- **Ручная очистка** — админ может удалить старые данные из настроек (записи + связанные файлы)
- **Автоочистка** — Celery beat задача по расписанию удаляет данные старше retention period
- **Индикатор** — в шапке навигации отображается занятое место ("Занято: N ГБ")
- **Стратегия хранения** — SSD для активных данных (ОС, Docker, БД), HDD для архива (старые записи)
## 9. Деплой
### Сервер
- **Адрес:** 78.153.7.224
- **ОС:** Windows (Docker Desktop установлен, PostgreSQL и другие сервисы уже развёрнуты)
- **CPU:** Intel i9-10900 (10 ядер, 2.8 GHz)
- **RAM:** 32 GB
- **GPU:** NVIDIA RTX 3070 Ti (8 GB VRAM)
- **Хранилище:** 1 TB SSD + 1 TB HDD
### Docker Compose — контейнеры
| Контейнер | Назначение |
|-----------|-----------|
| `app` | FastAPI + Uvicorn, API + раздача SPA, port 8000 |
| `worker` | Celery worker с GPU доступом (Whisper + LLM вызовы) |
| `beat` | Celery beat — планировщик (автоочистка, scheduled tasks) |
| `redis` | Redis 7 — брокер задач + кэш |
| `db` | PostgreSQL 16 |
| `nginx` | Reverse proxy, static files, port 80/443 |
### Распределение ресурсов
- **GPU (8 GB VRAM)** — faster-whisper large-v3 (~3 GB модель, остальное — буфер)
- **RAM** — ~4 GB PostgreSQL, ~2 GB Redis, ~4 GB FastAPI + workers, ~8 GB Whisper worker, остальное — ОС
- **CPU** — 10 ядер достаточно для параллельной работы API + воркеров
### Портативность
Архитектура на Docker Compose полностью переносима: `docker-compose.yml` + `.env``docker compose up -d` на любом сервере. При отсутствии GPU на целевом сервере — переключение faster-whisper на CPU-режим через переменную окружения.
### CI/CD
- Код — Gitea на http://78.153.7.224:3000
- Деплой — `docker compose up -d --build` (ручной или через Gitea webhook)
- Миграции — `alembic upgrade head` при старте контейнера `app`
## 10. Технологический стек — сводка
| Слой | Технологии |
|------|-----------|
| Backend | Python 3.11+, FastAPI, Uvicorn, Celery, Redis, SQLAlchemy, Alembic, Pydantic v2 |
| AI/ML | faster-whisper large-v3, CUDA 12 + cuDNN, FFmpeg, LiteLLM |
| Frontend | React, TypeScript, Vite, Tailwind CSS, React Query, React Router |
| Мессенджеры | aiogram 3.x (Telegram), Lensa SDK/webhook |
| Экспорт | python-docx (DOCX), WeasyPrint (PDF), Jinja2 (шаблоны) |
| Инфра | Docker, Docker Compose, NVIDIA Container Toolkit, Nginx, Gitea, Alembic |
Binary file not shown.