Initial commit: VoIdeaAI - voice-first AI idea assistant
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# ADR-001: Выбор PostgreSQL как основной СУБД
|
||||
|
||||
**Статус:** принято
|
||||
**Дата:** 2026-05-10
|
||||
|
||||
---
|
||||
|
||||
## Контекст
|
||||
|
||||
Для проекта VoIdea требуется база данных с поддержкой:
|
||||
- Сложных запросов (аналитика идей)
|
||||
- JSONB для гибкости (метаданные, настройки)
|
||||
- ACID транзакции (финансовые операции, подписки)
|
||||
- Масштабируемость (тысячи пользователей)
|
||||
- Хорошая работа с Python (asyncpg)
|
||||
|
||||
Рассматривались:
|
||||
- **PostgreSQL** — реляционная, ACID, JSONB, mature
|
||||
- **MongoDB** — документоориентированная, гибкость, шардинг
|
||||
- **SQLite** — простая, не подходит для production
|
||||
|
||||
---
|
||||
|
||||
## Решение
|
||||
|
||||
**PostgreSQL** выбран как основная СУБД.
|
||||
|
||||
### Обоснование
|
||||
|
||||
| Критерий | PostgreSQL | MongoDB | SQLite |
|
||||
|----------|------------|---------|--------|
|
||||
| ACID | ✅ Полный | ❌ Eventual | ✅ Полный |
|
||||
| JSONB | ✅ Отличный | ✅ Лучший | ❌ Limited |
|
||||
| Масштабируемость | ✅ Хорошая | ✅ Отличная | ❌ Плохая |
|
||||
| Python async | ✅ asyncpg | ✅ motor | ❌ |
|
||||
| Сложные запросы | ✅ Отличные | ❌ Limited | ⚠️ Basic |
|
||||
| Зрелость | ✅ 20+ лет | ⚠️ 15 лет | ✅ |
|
||||
|
||||
### Преимущества для VoIdea
|
||||
|
||||
1. **JSONB** — хранение зашифрованных данных, настроек агентов
|
||||
2. **ACID** — безопасность транзакций (подписки, платежи)
|
||||
3. **Индексы** — поиск по метаданным, пользователям
|
||||
4. **PostGIS** — геолокация (future: автоопределение региона)
|
||||
5. **Full-text search** — поиск в идеях
|
||||
|
||||
---
|
||||
|
||||
## Последствия
|
||||
|
||||
### Положительные
|
||||
|
||||
- Надёжная, проверенная база данных
|
||||
- Отличная производительность
|
||||
- Большое сообщество
|
||||
- Хорошая документация
|
||||
- Mature ORM (SQLAlchemy async)
|
||||
|
||||
### Отрицательные
|
||||
|
||||
- Требует установку и настройку (PostgreSQL server)
|
||||
- Миграции Alembic для схемы
|
||||
- Начальная настройка (создание пользователя, базы)
|
||||
|
||||
### Миграция
|
||||
|
||||
Для локальной разработки:
|
||||
```bash
|
||||
# Windows
|
||||
# Скачать PostgreSQL с postgresql.org/download/windows
|
||||
# или использовать Chocolatey:
|
||||
choco install postgresql
|
||||
|
||||
# Ubuntu (VPS)
|
||||
apt install postgresql postgresql-contrib
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Реализация
|
||||
|
||||
### Конфигурация в .env
|
||||
|
||||
```bash
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_NAME=voidea
|
||||
DB_USER=voidea
|
||||
DB_PASS=your_secure_password
|
||||
```
|
||||
|
||||
### SQLAlchemy async setup
|
||||
|
||||
```python
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
|
||||
engine = create_async_engine(
|
||||
f"postgresql+asyncpg://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}",
|
||||
echo=True
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ответственный
|
||||
|
||||
**Decision maker:** Owner
|
||||
**Review date:** При масштабировании (> 10,000 пользователей)
|
||||
|
||||
---
|
||||
|
||||
*Создано: 2026-05-10*
|
||||
*Обновлено при изменениях SpecAgent*
|
||||
@@ -0,0 +1,397 @@
|
||||
# ADR-002: Архитектура системных агентов
|
||||
|
||||
**Статус:** принято
|
||||
**Дата:** 2026-05-10
|
||||
|
||||
---
|
||||
|
||||
## Контекст
|
||||
|
||||
Проект VoIdea требует автоматизации через AI-агентов. Необходимо определить:
|
||||
- Количество агентов
|
||||
- Их обязанности
|
||||
- Взаимодействие между агентами
|
||||
- Стек реализации
|
||||
|
||||
Рассматривались:
|
||||
- **Один суперагент** — всё в одном, сложно масштабировать
|
||||
- **Ручное управление** — человек выполняет всё
|
||||
- **11 отдельных агентов** — модульность, специализация
|
||||
|
||||
---
|
||||
|
||||
## Решение
|
||||
|
||||
**11 системных агентов**, каждый со своей ответственностью.
|
||||
|
||||
### Список агентов
|
||||
|
||||
| Агент | Ответственность | Триггеры |
|
||||
|-------|-----------------|----------|
|
||||
| DocAgent | Документация, комментарии | pre-commit, push, manual |
|
||||
| AuditAgent | Соблюдение правил, прогресс | pre-commit, daily, manual |
|
||||
| SecurityAgent | Безопасность, уязвимости | pre-commit, weekly, manual |
|
||||
| SpecAgent | Спецификации, версионирование | tag creation, push |
|
||||
| ObserverAgent | Наблюдение за пользователями | continuous, daily report |
|
||||
| QATesterAgent | Функциональное тестирование | pre-commit, daily, manual |
|
||||
| FixAgent | Исправление багов | QATesterAgent results |
|
||||
| UITestAgent | Визуальное тестирование | weekly, manual |
|
||||
| RolloutAgent | Постепенное развёртывание | after tests, manual |
|
||||
| EvolutionAgent | Саморазвитие агентов | daily, learning |
|
||||
| BacklogAgent | Управление задачами | continuous |
|
||||
|
||||
---
|
||||
|
||||
## Архитектура
|
||||
|
||||
### Структура файлов
|
||||
|
||||
```
|
||||
app/agents/
|
||||
├── __init__.py # Публичный API
|
||||
├── base.py # Базовый класс Agent
|
||||
├── doc_agent.py # DocAgent
|
||||
├── audit_agent.py # AuditAgent
|
||||
├── security_agent.py # SecurityAgent
|
||||
├── spec_agent.py # SpecAgent
|
||||
├── observer_agent.py # ObserverAgent
|
||||
├── qa_tester_agent.py # QATesterAgent
|
||||
├── fix_agent.py # FixAgent
|
||||
├── ui_test_agent.py # UITestAgent
|
||||
├── rollout_agent.py # RolloutAgent
|
||||
├── evolution_agent.py # EvolutionAgent
|
||||
└── backlog_agent.py # BacklogAgent
|
||||
```
|
||||
|
||||
### Базовый класс
|
||||
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Optional
|
||||
|
||||
class BaseAgent(ABC):
|
||||
name: str
|
||||
version: str
|
||||
|
||||
@abstractmethod
|
||||
async def run(self, context: dict) -> AgentResult:
|
||||
"""Основной метод выполнения"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def health_check(self) -> bool:
|
||||
"""Проверка работоспособности"""
|
||||
pass
|
||||
|
||||
async def get_status(self) -> AgentStatus:
|
||||
"""Текущий статус агента"""
|
||||
pass
|
||||
|
||||
async def get_metrics(self) -> AgentMetrics:
|
||||
"""Метрики работы агента"""
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Меж-агентское взаимодействие
|
||||
|
||||
### Делегирование
|
||||
|
||||
```python
|
||||
class AgentA:
|
||||
async def process(self, task):
|
||||
if task.requires_agent_b:
|
||||
result = await delegate_to(
|
||||
target=AgentB,
|
||||
task=task,
|
||||
timeout=30
|
||||
)
|
||||
# continue processing
|
||||
```
|
||||
|
||||
### Event-driven
|
||||
|
||||
```python
|
||||
class EventBus:
|
||||
async def publish(self, event: AgentEvent):
|
||||
await self._handlers[event.type].handle(event)
|
||||
|
||||
class AgentB:
|
||||
@event_handler(AgentEventTypes.TASK_DELEGATED)
|
||||
async def handle_delegated_task(self, event):
|
||||
# process task
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Описание поведения каждого агента
|
||||
|
||||
### DocAgent
|
||||
|
||||
**Цель:** Поддержание документации в актуальном состоянии.
|
||||
|
||||
**Обязанности:**
|
||||
- Создание README.md для новых модулей
|
||||
- Обновление docs при изменении кода
|
||||
- Генерация docstrings
|
||||
- Ведение Runbook
|
||||
|
||||
**Триггеры:**
|
||||
- Создание нового файла
|
||||
- Изменение существующего > 50 строк
|
||||
- Создание новой папки
|
||||
- Push в main/develop
|
||||
|
||||
**Взаимодействие:**
|
||||
- SpecAgent → обновление спецификаций
|
||||
- AuditAgent → проверка актуальности docs
|
||||
|
||||
---
|
||||
|
||||
### AuditAgent
|
||||
|
||||
**Цель:** Контроль соблюдения правил проекта.
|
||||
|
||||
**Обязанности:**
|
||||
- Проверка code style (ruff)
|
||||
- Проверка типизации (mypy)
|
||||
- Контроль прогресса по плану
|
||||
- Фиксация отклонений
|
||||
|
||||
**Триггеры:**
|
||||
- pre-commit hook
|
||||
- Ежедневно 09:00
|
||||
- По запросу администратора
|
||||
|
||||
**Взаимодействие:**
|
||||
- SecurityAgent → проверка безопасности
|
||||
- DocAgent → обновление отчётов
|
||||
|
||||
---
|
||||
|
||||
### SecurityAgent
|
||||
|
||||
**Цель:** Обеспечение безопасности проекта.
|
||||
|
||||
**Обязанности:**
|
||||
- Сканирование уязвимостей
|
||||
- Проверка input валидации
|
||||
- Контроль зависимостей (safety)
|
||||
- Соответствие 152-ФЗ
|
||||
|
||||
**Триггеры:**
|
||||
- pre-commit hook
|
||||
- Еженедельно (полное сканирование)
|
||||
- При добавлении зависимости
|
||||
|
||||
**Взаимодействие:**
|
||||
- FixAgent → исправление уязвимостей
|
||||
- RolloutAgent → блокировка при критических уязвимостях
|
||||
|
||||
---
|
||||
|
||||
### SpecAgent
|
||||
|
||||
**Цель:** Управление спецификациями и версионированием.
|
||||
|
||||
**Обязанности:**
|
||||
- Генерация CHANGELOG
|
||||
- Обновление project.json
|
||||
- Управление ADR
|
||||
- Версионирование кода
|
||||
|
||||
**Триггеры:**
|
||||
- Создание git tag
|
||||
- Push в main
|
||||
- Изменение спецификаций
|
||||
|
||||
**Взаимодействие:**
|
||||
- DocAgent → обновление docs
|
||||
- EvolutionAgent → фиксация изменений
|
||||
|
||||
---
|
||||
|
||||
### ObserverAgent
|
||||
|
||||
**Цель:** Сбор и анализ данных о пользователях.
|
||||
|
||||
**Обязанности:**
|
||||
- Сбор метрик использования
|
||||
- Генерация идей для развития
|
||||
- Выявление паттернов поведения
|
||||
- Отчёты для EvolutionAgent
|
||||
|
||||
**Триггеры:**
|
||||
- Непрерывный сбор данных
|
||||
- Ежедневный отчёт
|
||||
- По запросу EvolutionAgent
|
||||
|
||||
**Метрики (MVP):**
|
||||
- page_views
|
||||
- session_duration
|
||||
- feature_usage_frequency
|
||||
- conversion_rate
|
||||
|
||||
**Взаимодействие:**
|
||||
- EvolutionAgent → данные для анализа
|
||||
- RolloutAgent → метрики для решения
|
||||
|
||||
---
|
||||
|
||||
### QATesterAgent
|
||||
|
||||
**Цель:** Функциональное тестирование.
|
||||
|
||||
**Обязанности:**
|
||||
- Создание временных аккаунтов
|
||||
- Выполнение тестов
|
||||
- Очистка временных данных
|
||||
- Генерация отчётов
|
||||
|
||||
**Триггеры:**
|
||||
- pre-commit hook
|
||||
- Ежедневно в 06:00
|
||||
- Вручную через админ-панель
|
||||
- После FixAgent исправления
|
||||
|
||||
**Взаимодействие:**
|
||||
- FixAgent → исправление найденных багов
|
||||
- UITestAgent → визуальное тестирование
|
||||
- RolloutAgent → результаты для решения
|
||||
|
||||
---
|
||||
|
||||
### FixAgent
|
||||
|
||||
**Цель:** Автоматическое исправление багов.
|
||||
|
||||
**Обязанности:**
|
||||
- Анализ багов из QATesterAgent
|
||||
- Генерация исправлений
|
||||
- Создание PR
|
||||
- Валидация исправлений
|
||||
|
||||
**Триггеры:**
|
||||
- Результаты QATesterAgent
|
||||
- Критические ошибки в логах
|
||||
- По запросу человека
|
||||
|
||||
**Взаимодействие:**
|
||||
- QATesterAgent → повторное тестирование
|
||||
- DocAgent → обновление документации
|
||||
- Git → создание PR
|
||||
|
||||
---
|
||||
|
||||
### UITestAgent
|
||||
|
||||
**Цель:** Визуальное тестирование интерфейса.
|
||||
|
||||
**Обязанности:**
|
||||
- Скриншот-тестирование
|
||||
- Проверка layout
|
||||
- Accessibility testing
|
||||
- Кросс-браузерное тестирование
|
||||
|
||||
**Триггеры:**
|
||||
- Еженедельно
|
||||
- После изменений в UI
|
||||
- Вручную через админ-панель
|
||||
|
||||
**Взаимодействие:**
|
||||
- QATesterAgent → результаты
|
||||
- FixAgent → исправление визуальных багов
|
||||
|
||||
---
|
||||
|
||||
### RolloutAgent
|
||||
|
||||
**Цель:** Управление развёртыванием.
|
||||
|
||||
**Обязанности:**
|
||||
- Контроль этапов rollout (3→1%→5%→15%→100%)
|
||||
- Мониторинг метрик
|
||||
- Принятие решения о переходе
|
||||
- Откат при проблемах
|
||||
|
||||
**Триггеры:**
|
||||
- После успешных тестов
|
||||
- Ежедневный мониторинг
|
||||
- По решению человека
|
||||
|
||||
**Взаимодействие:**
|
||||
- ObserverAgent → метрики
|
||||
- FixAgent → исправление проблем
|
||||
- BacklogAgent → создание задач
|
||||
|
||||
---
|
||||
|
||||
### EvolutionAgent
|
||||
|
||||
**Цель:** Саморазвитие агентов.
|
||||
|
||||
**Обязанности:**
|
||||
- Анализ эффективности агентов
|
||||
- Генерация предложений по улучшению
|
||||
- Обновление capabilities
|
||||
- Обучение на данных
|
||||
|
||||
**Триггеры:**
|
||||
- Ежедневно
|
||||
- При обнаружении новых паттернов
|
||||
- По запросу BacklogAgent
|
||||
|
||||
**Взаимодействие:**
|
||||
- ObserverAgent → данные о пользователях
|
||||
- Все агенты → улучшение работы
|
||||
|
||||
---
|
||||
|
||||
### BacklogAgent
|
||||
|
||||
**Цель:** Управление отложенными задачами.
|
||||
|
||||
**Обязанности:**
|
||||
- Создание задач из предложений
|
||||
- Приоритизация
|
||||
- Отслеживание статуса
|
||||
- Напоминания
|
||||
|
||||
**Триггеры:**
|
||||
- Непрерывный мониторинг
|
||||
- По предложению других агентов
|
||||
- Вручную через админ-панель
|
||||
|
||||
**Взаимодействие:**
|
||||
- Все агенты → создание задач
|
||||
- RolloutAgent → задачи для реализации
|
||||
|
||||
---
|
||||
|
||||
## Последствия
|
||||
|
||||
### Положительные
|
||||
|
||||
- Модульность — легко добавлять новых агентов
|
||||
- Специализация — каждый делает своё
|
||||
- Тестируемость — можно тестировать отдельно
|
||||
- Масштабируемость — агенты работают параллельно
|
||||
|
||||
### Отрицательные
|
||||
|
||||
- Сложность координации
|
||||
- Возможные конфликты агентов
|
||||
- Overhead на коммуникацию
|
||||
|
||||
---
|
||||
|
||||
## Ответственный
|
||||
|
||||
**Decision maker:** Owner
|
||||
**Review date:** При добавлении новых агентов
|
||||
|
||||
---
|
||||
|
||||
*Создано: 2026-05-10*
|
||||
*Обновлено при изменениях EvolutionAgent*
|
||||
@@ -0,0 +1,243 @@
|
||||
# ADR-003: OAuth схема авторизации
|
||||
|
||||
**Статус:** принято
|
||||
**Дата:** 2026-05-10
|
||||
|
||||
---
|
||||
|
||||
## Контекст
|
||||
|
||||
Проект VoIdea поддерживает несколько способов авторизации:
|
||||
- Email + пароль
|
||||
- OAuth провайдеры (Яндекс, Google, Apple)
|
||||
|
||||
Необходимо определить правила привязки провайдеров к аккаунтам.
|
||||
|
||||
---
|
||||
|
||||
## Решение
|
||||
|
||||
**Один пользователь = один провайдер**
|
||||
|
||||
> Нельзя привязать Google к аккаунту, зарегистрированному через Яндекс.
|
||||
|
||||
---
|
||||
|
||||
## Правила
|
||||
|
||||
### Основные
|
||||
|
||||
1. **При регистрации через OAuth** — аккаунт привязан к этому провайдеру навсегда
|
||||
2. **При регистрации через email** — можно использовать только email + пароль
|
||||
3. **Нельзя добавить второй провайдер** — даже если email совпадает
|
||||
|
||||
### Примеры
|
||||
|
||||
| Действие | Результат |
|
||||
|----------|-----------|
|
||||
| Регистрация через Яндекс → Вход через Google | ❌ Ошибка: создай новый аккаунт |
|
||||
| Регистрация через Google → Вход через Яндекс | ❌ Ошибка: создай новый аккаунт |
|
||||
| Регистрация через email → Вход через Яндекс | ❌ Ошибка: это разные аккаунты |
|
||||
| Регистрация через Яндекс → Повторный вход через Яндекс | ✅ Работает |
|
||||
|
||||
---
|
||||
|
||||
## Обоснование
|
||||
|
||||
### Почему один провайдер
|
||||
|
||||
1. **Безопасность**
|
||||
- Меньше точек входа для атак
|
||||
- Сложнее украсть аккаунт
|
||||
- Чёткая атрибуция действий
|
||||
|
||||
2. **Простота реализации**
|
||||
- Не нужно merge аккаунтов
|
||||
- Не нужно решать конфликты данных
|
||||
- Понятная модель данных
|
||||
|
||||
3. **Privacy**
|
||||
- Данные не смешиваются между провайдерами
|
||||
- Пользователь понимает что использует
|
||||
|
||||
4. **Яндекс vs Google**
|
||||
- Разные экосистемы
|
||||
- Разные данные пользователя
|
||||
- Разная политика безопасности
|
||||
|
||||
---
|
||||
|
||||
## Структура данных
|
||||
|
||||
### Users table
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY,
|
||||
|
||||
-- Идентификация
|
||||
email VARCHAR(255) UNIQUE, -- NULL если OAuth без email
|
||||
password_hash VARCHAR(255), -- NULL если OAuth-only
|
||||
|
||||
-- OAuth (только один провайдер)
|
||||
oauth_provider VARCHAR(20), -- yandex|google|apple|null
|
||||
oauth_id VARCHAR(255), -- ID в системе провайдера
|
||||
|
||||
-- Метаданные
|
||||
email_verified BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
|
||||
-- Constraints
|
||||
CONSTRAINT users_oauth_xor_email CHECK (
|
||||
-- OAuth с email
|
||||
(oauth_provider IS NOT NULL AND email IS NOT NULL) OR
|
||||
-- Email-only
|
||||
(oauth_provider IS NULL AND email IS NOT NULL AND password_hash IS NOT NULL)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX uq_users_oauth
|
||||
ON users(oauth_provider, oauth_id)
|
||||
WHERE oauth_provider IS NOT NULL;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OAuth Flow
|
||||
|
||||
### Пример: Яндекс OAuth
|
||||
|
||||
```python
|
||||
async def yandex_oauth_callback(code: str, db: AsyncSession):
|
||||
# 1. Получаем access_token
|
||||
token_data = await yandex_api.get_token(code)
|
||||
|
||||
# 2. Получаем данные пользователя
|
||||
user_data = await yandex_api.get_user_info(token_data.access_token)
|
||||
|
||||
# 3. Проверяем/создаём аккаунт
|
||||
user = await db.execute(
|
||||
select(User).where(
|
||||
User.oauth_provider == 'yandex',
|
||||
User.oauth_id == user_data.id
|
||||
)
|
||||
)
|
||||
|
||||
if not user:
|
||||
# Новый пользователь
|
||||
user = User(
|
||||
email=user_data.email,
|
||||
oauth_provider='yandex',
|
||||
oauth_id=user_data.id
|
||||
)
|
||||
db.add(user)
|
||||
await db.commit()
|
||||
|
||||
# 4. Создаём JWT session
|
||||
return create_jwt_session(user.id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Google OAuth
|
||||
|
||||
Аналогично Яндексу, с заменой endpoint-ов.
|
||||
|
||||
### Различия
|
||||
|
||||
| Параметр | Яндекс | Google |
|
||||
|----------|--------|--------|
|
||||
| OAuth endpoint | oauth.yandex.ru | oauth2.googleapis.com |
|
||||
| User info | login.yandex.ru | www.googleapis.com/oauth2/v2/userinfo |
|
||||
| Scope | login:email, profile | email, profile |
|
||||
|
||||
---
|
||||
|
||||
## Apple OAuth (отложено)
|
||||
|
||||
Apple будет реализован ближе к коммерческой версии.
|
||||
|
||||
### Требования
|
||||
|
||||
- App Store Developer Account ($99/год)
|
||||
- Private Key для подписи (в Keychain)
|
||||
- Тот же принцип: один пользователь = один провайдер
|
||||
|
||||
---
|
||||
|
||||
## Защита от привязки чужого аккаунта
|
||||
|
||||
### Проблема
|
||||
|
||||
Злоумышленник может попытаться привязать Google к чужому email.
|
||||
|
||||
### Решение
|
||||
|
||||
1. **Email verification**
|
||||
- OAuth возвращает verified email
|
||||
- Привязка только verified email
|
||||
|
||||
2. **Separate tables**
|
||||
- OAuth и email разделены логически
|
||||
- Разные flows для входа
|
||||
|
||||
3. **Audit logging**
|
||||
- Все попытки OAuth логируются
|
||||
- Подозрительная активность → SecurityAgent
|
||||
|
||||
---
|
||||
|
||||
## Последствия
|
||||
|
||||
### Положительные
|
||||
|
||||
- Простая модель данных
|
||||
- Безопасность выше
|
||||
- Понятно для пользователя
|
||||
- Легко реализовать
|
||||
|
||||
### Отрицательные
|
||||
|
||||
- Пользователь не может "добавить" Google к существующему аккаунту
|
||||
- При потере доступа к провайдеру — сложнее восстановить
|
||||
- Нельзя merge аккаунты
|
||||
|
||||
### Workaround для пользователя
|
||||
|
||||
При потере доступа к OAuth провайдеру:
|
||||
1. Обращение в поддержку
|
||||
2. Подтверждение личности
|
||||
3. Смена email (если нужно)
|
||||
4. Сброс пароля на email
|
||||
|
||||
---
|
||||
|
||||
## Конфигурация
|
||||
|
||||
```bash
|
||||
# .env
|
||||
OAUTH_YANDEX_ID=your_yandex_client_id
|
||||
OAUTH_YANDEX_SECRET=your_yandex_secret
|
||||
OAUTH_YANDEX_REDIRECT_URI=http://localhost:8020/auth/yandex/callback
|
||||
|
||||
OAUTH_GOOGLE_ID=your_google_client_id
|
||||
OAUTH_GOOGLE_SECRET=your_google_secret
|
||||
OAUTH_GOOGLE_REDIRECT_URI=http://localhost:8020/auth/google/callback
|
||||
|
||||
# Apple - зарезервировано для будущего
|
||||
# OAUTH_APPLE_ID=
|
||||
# OAUTH_APPLE_TEAM_ID=
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ответственный
|
||||
|
||||
**Decision maker:** Owner
|
||||
**Review date:** При добавлении Apple OAuth
|
||||
|
||||
---
|
||||
|
||||
*Создано: 2026-05-10*
|
||||
*См. также: docs/backlog/oauth-schema-note.md*
|
||||
@@ -0,0 +1,294 @@
|
||||
# ADR-004: Постепенное развёртывание (Rollout)
|
||||
|
||||
**Статус:** принято
|
||||
**Дата:** 2026-05-10
|
||||
|
||||
---
|
||||
|
||||
## Контекст
|
||||
|
||||
При выпуске нового функционала требуется минимизировать риски и быстро реагировать на проблемы.
|
||||
|
||||
Рассматривались:
|
||||
- **Big bang** — сразу на всех пользователей
|
||||
- **Feature flags** — включение для избранных
|
||||
- **Постепенное развёртывание** — процент от пользователей
|
||||
|
||||
---
|
||||
|
||||
## Решение
|
||||
|
||||
**Поэтапное развёртывание с мониторингом**
|
||||
|
||||
```
|
||||
Stage 0: Development → Тестирование агентами
|
||||
Stage 1: 3 users → Первые пользователи
|
||||
Stage 2: 1% → Расширение выборки
|
||||
Stage 3: 5% → Продолжение
|
||||
Stage 4: 15% → Почти все
|
||||
Stage 5: 100% → Production
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Этапы
|
||||
|
||||
### Stage 0: Development
|
||||
|
||||
**Кто:** Агенты (QATesterAgent, FixAgent, UITestAgent)
|
||||
|
||||
**Критерии перехода:**
|
||||
- Все тесты пройдены
|
||||
- Нет критических ошибок
|
||||
- Документация обновлена
|
||||
|
||||
**Действия:**
|
||||
```python
|
||||
async def promote_to_stage_1():
|
||||
# 1. Финальная проверка тестов
|
||||
test_results = await QATesterAgent.run_full_suite()
|
||||
|
||||
# 2. Проверка документации
|
||||
await AuditAgent.verify_docs_complete()
|
||||
|
||||
# 3. Решение о переходе
|
||||
if test_results.success:
|
||||
RolloutAgent.set_stage(1)
|
||||
notify_admins("Готов к rollout: 3 пользователя")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Stage 1: 3 users
|
||||
|
||||
**Кто:** 3 добровольца (beta testers)
|
||||
|
||||
**Критерии перехода в Stage 2:**
|
||||
- 2 дня без критических ошибок
|
||||
- Error rate < 1%
|
||||
- User feedback положительный
|
||||
- ObserverAgent не фиксирует аномалий
|
||||
|
||||
**Действия:**
|
||||
```python
|
||||
async def check_stage_1_health():
|
||||
metrics = await ObserverAgent.get_metrics(days=2)
|
||||
|
||||
# Проверки
|
||||
if metrics.error_rate < 0.01:
|
||||
if metrics.user_satisfaction > 0.8:
|
||||
if not metrics.anomalies_detected:
|
||||
await promote_to_stage_2()
|
||||
else:
|
||||
await analyze_anomalies()
|
||||
else:
|
||||
await log_issue("Low satisfaction")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Stage 2: 1% пользователей
|
||||
|
||||
**Кто:** ~10 пользователей (при 1000 MAU)
|
||||
|
||||
**Критерии перехода в Stage 3:**
|
||||
- 2 дня стабильности
|
||||
- Нет regresion
|
||||
- Performance acceptable (< 500ms)
|
||||
|
||||
---
|
||||
|
||||
### Stage 3: 5% пользователей
|
||||
|
||||
**Кто:** ~50 пользователей
|
||||
|
||||
**При проблемах:** Откат до Stage 2
|
||||
|
||||
---
|
||||
|
||||
### Stage 4: 15% пользователей
|
||||
|
||||
**Кто:** ~150 пользователей
|
||||
|
||||
**При проблемах:** Откат до Stage 3
|
||||
|
||||
---
|
||||
|
||||
### Stage 5: 100% (Production)
|
||||
|
||||
**Кто:** Все пользователи
|
||||
|
||||
**Критерии:**
|
||||
- Все предыдущие stages стабильны
|
||||
- Мониторинг непрерывный
|
||||
- Подготовлен changelog для магазинов
|
||||
|
||||
---
|
||||
|
||||
## Мониторинг
|
||||
|
||||
### ObserverAgent отслеживает
|
||||
|
||||
```python
|
||||
class RolloutMetrics:
|
||||
error_rate: float # Цель: < 1%
|
||||
response_time_p95: float # Цель: < 500ms
|
||||
user_satisfaction: float # Цель: > 0.8
|
||||
feature_usage: float # Цель: рост
|
||||
complaints_count: int # Цель: 0
|
||||
rollback_requests: int # Цель: 0
|
||||
```
|
||||
|
||||
### При аномалиях
|
||||
|
||||
1. **Alert** → RolloutAgent получает уведомление
|
||||
2. **Analysis** → FixAgent анализирует логи
|
||||
3. **Decision** → Человек + агент решают
|
||||
4. **Action** → Откат или продолжение
|
||||
|
||||
---
|
||||
|
||||
## Откат (Rollback)
|
||||
|
||||
### Когда
|
||||
|
||||
- Error rate > 5%
|
||||
- Критические баги в production
|
||||
- Решение человека
|
||||
|
||||
### Процедура
|
||||
|
||||
```python
|
||||
async def rollback_to(stage: int):
|
||||
# 1. Приостановить rollout
|
||||
RolloutAgent.pause()
|
||||
|
||||
# 2. Зафиксировать состояние
|
||||
await AuditAgent.log_rollback(stage)
|
||||
|
||||
# 3. Откатить код
|
||||
await git.revert_to_stable_version()
|
||||
|
||||
# 4. Уведомить
|
||||
notify_admins(f"Откат до Stage {stage}")
|
||||
|
||||
# 5. Создать задачу
|
||||
await BacklogAgent.create_task(
|
||||
title=f"Rollback reason: ...",
|
||||
priority="high"
|
||||
)
|
||||
|
||||
# 6. После исправления → вернуться к Stage 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## UI в админ-панели
|
||||
|
||||
### Dashboard
|
||||
|
||||
```
|
||||
Rollout Status: Stage 2 (1%)
|
||||
|
||||
Progress:
|
||||
[████████░░░░░░░░░░░░░░░░░░░] 1%
|
||||
|
||||
Metrics:
|
||||
- Error rate: 0.5% ✓
|
||||
- Response time: 234ms ✓
|
||||
- Users: 10/1000
|
||||
|
||||
Actions:
|
||||
[Приостановить] [Откат] [Форсировать]
|
||||
```
|
||||
|
||||
### История
|
||||
|
||||
```
|
||||
Stage 0 → Stage 1: 2026-05-10 (PASSED)
|
||||
Stage 1 → Stage 2: 2026-05-12 (PASSED)
|
||||
Stage 2 → Stage 3: 2026-05-14 (IN PROGRESS)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Feature Flags
|
||||
|
||||
Для гибкости используем Feature Flags:
|
||||
|
||||
```sql
|
||||
CREATE TABLE feature_flags (
|
||||
id UUID PRIMARY KEY,
|
||||
name VARCHAR(100) UNIQUE NOT NULL,
|
||||
enabled BOOLEAN DEFAULT FALSE,
|
||||
rollout_percentage INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
```python
|
||||
async def is_feature_enabled(user_id: UUID, feature: str) -> bool:
|
||||
flag = await db.get_feature_flag(feature)
|
||||
if not flag.enabled:
|
||||
return False
|
||||
|
||||
# Проверка процента
|
||||
user_hash = hash(user_id)
|
||||
return (user_hash % 100) < flag.rollout_percentage
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Последствия
|
||||
|
||||
### Положительные
|
||||
|
||||
- Минимизация рисков
|
||||
- Быстрая реакция на проблемы
|
||||
- Эволюция с обратной связью
|
||||
- Прозрачность для команды
|
||||
|
||||
### Отрицательные
|
||||
|
||||
- Медленнее релиз
|
||||
- Сложнее управление
|
||||
- Требуется мониторинг
|
||||
|
||||
---
|
||||
|
||||
## Конфигурация
|
||||
|
||||
```yaml
|
||||
rollout:
|
||||
stages:
|
||||
- name: development
|
||||
users: 0
|
||||
duration: unlimited
|
||||
- name: "3 users"
|
||||
users: 3
|
||||
duration: 2 days
|
||||
- name: "1%"
|
||||
users_percentage: 1
|
||||
duration: 2 days
|
||||
- name: "5%"
|
||||
users_percentage: 5
|
||||
duration: 2 days
|
||||
- name: "15%"
|
||||
users_percentage: 15
|
||||
duration: 3 days
|
||||
- name: "100%"
|
||||
users_percentage: 100
|
||||
duration: unlimited
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ответственный
|
||||
|
||||
**Decision maker:** Owner + RolloutAgent
|
||||
**Review date:** После каждого успешного rollout
|
||||
|
||||
---
|
||||
|
||||
*Создано: 2026-05-10*
|
||||
*Управляется RolloutAgent*
|
||||
@@ -0,0 +1,330 @@
|
||||
# ADR-005: Design Tokens как единый источник стилей
|
||||
|
||||
**Статус:** принято
|
||||
**Дата:** 2026-05-10
|
||||
|
||||
---
|
||||
|
||||
## Контекст
|
||||
|
||||
Проект VoIdea работает на нескольких платформах:
|
||||
- Web (PWA)
|
||||
- iOS (Swift/SwiftUI)
|
||||
- Android (Kotlin)
|
||||
|
||||
Требуется унифицировать стили (цвета, шрифты, отступы) между всеми платформами.
|
||||
|
||||
Рассматривались:
|
||||
- **Ручное копирование** — непоследовательно, сложно поддерживать
|
||||
- **Shared library** — требует синхронизации
|
||||
- **Design Tokens (JSON)** — единый источник, генераторы
|
||||
|
||||
---
|
||||
|
||||
## Решение
|
||||
|
||||
**Design Tokens в JSON + генераторы для каждой платформы**
|
||||
|
||||
```
|
||||
docs/design-system/tokens.json (источник истины)
|
||||
│
|
||||
├── generators/css_generator.py → app/design-tokens/css/theme.css
|
||||
├── generators/swift_generator.py → app/design-tokens/swift/Colors.swift
|
||||
└── generators/kotlin_generator.py → app/design-tokens/kotlin/colors.xml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Структура tokens.json
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"project": "VoIdea",
|
||||
"themes": ["system", "dark", "light"],
|
||||
|
||||
"colors": {
|
||||
"primary": {
|
||||
"500": "#6366F1",
|
||||
"600": "#4F46E5",
|
||||
"default": "#6366F1",
|
||||
"hover": "#4F46E5"
|
||||
},
|
||||
"background": {
|
||||
"system": "auto",
|
||||
"dark": "#0F172A",
|
||||
"light": "#FFFFFF"
|
||||
},
|
||||
"semantic": {
|
||||
"error": "#EF4444",
|
||||
"warning": "#F59E0B",
|
||||
"success": "#22C55E",
|
||||
"info": "#3B82F6"
|
||||
}
|
||||
},
|
||||
|
||||
"typography": {
|
||||
"font_family": {
|
||||
"primary": "Inter, system-ui, sans-serif"
|
||||
},
|
||||
"size": {
|
||||
"xs": "0.75rem",
|
||||
"sm": "0.875rem",
|
||||
"base": "1rem"
|
||||
}
|
||||
},
|
||||
|
||||
"spacing": {
|
||||
"xs": "0.25rem",
|
||||
"sm": "0.5rem",
|
||||
"md": "1rem",
|
||||
"lg": "1.5rem"
|
||||
},
|
||||
|
||||
"border_radius": {
|
||||
"sm": "0.25rem",
|
||||
"md": "0.5rem",
|
||||
"lg": "0.75rem"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Генераторы
|
||||
|
||||
### CSS Generator
|
||||
|
||||
```python
|
||||
# docs/design-system/generators/css_generator.py
|
||||
|
||||
def generate(tokens: dict) -> str:
|
||||
css = ":root {\n"
|
||||
|
||||
for category, values in tokens.items():
|
||||
if category == "colors":
|
||||
for name, value in flatten(values):
|
||||
css += f" --color-{name}: {value};\n"
|
||||
|
||||
elif category == "typography":
|
||||
for name, value in flatten(values):
|
||||
css += f" --font-{name}: {value};\n"
|
||||
|
||||
elif category == "spacing":
|
||||
for name, value in flatten(values):
|
||||
css += f" --spacing-{name}: {value};\n"
|
||||
|
||||
css += "}\n"
|
||||
return css
|
||||
```
|
||||
|
||||
**Выход:** `app/design-tokens/css/theme.css`
|
||||
|
||||
```css
|
||||
:root {
|
||||
--color-primary: #6366F1;
|
||||
--color-background-dark: #0F172A;
|
||||
--font-family-primary: Inter, system-ui, sans-serif;
|
||||
--spacing-md: 1rem;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Swift Generator
|
||||
|
||||
```python
|
||||
# docs/design-system/generators/swift_generator.py
|
||||
|
||||
def generate(tokens: dict) -> str:
|
||||
swift = "import SwiftUI\n\n"
|
||||
swift += "enum Colors {\n"
|
||||
|
||||
for name, value in flatten(tokens["colors"]):
|
||||
snake_to_camel = to_camel_case(name)
|
||||
swift += f' static let {snake_to_camel} = Color(hex: "{value}")\n'
|
||||
|
||||
swift += "}\n"
|
||||
swift += "enum Typography {\n"
|
||||
|
||||
# ... font generation
|
||||
|
||||
return swift
|
||||
```
|
||||
|
||||
**Выход:** `app/design-tokens/swift/Colors.swift`
|
||||
|
||||
```swift
|
||||
import SwiftUI
|
||||
|
||||
enum Colors {
|
||||
static let primary = Color(hex: "#6366F1")
|
||||
static let backgroundDark = Color(hex: "#0F172A")
|
||||
}
|
||||
|
||||
enum Typography {
|
||||
static let fontFamilyPrimary = "Inter"
|
||||
static let fontSizeBase: CGFloat = 16
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Kotlin Generator
|
||||
|
||||
```python
|
||||
# docs/design-system/generators/kotlin_generator.py
|
||||
|
||||
def generate(tokens: dict) -> str:
|
||||
xml = '<?xml version="1.0" encoding="utf-8"?>\n'
|
||||
xml += '<resources>\n'
|
||||
|
||||
for name, value in flatten(tokens["colors"]):
|
||||
safe_name = name.replace("_", "_")
|
||||
xml += f' <color name="{safe_name}">{value}</color>\n'
|
||||
|
||||
xml += '</resources>\n'
|
||||
return xml
|
||||
```
|
||||
|
||||
**Выход:** `app/design-tokens/kotlin/colors.xml`
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="primary">#6366F1</color>
|
||||
<color name="background_dark">#0F172A</color>
|
||||
</resources>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Темы
|
||||
|
||||
### System (Auto)
|
||||
|
||||
```css
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root[data-theme="system"] {
|
||||
--color-background: #0F172A;
|
||||
--color-text: #F8FAFC;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root[data-theme="system"] {
|
||||
--color-background: #FFFFFF;
|
||||
--color-text: #0F172A;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Dark / Light
|
||||
|
||||
```css
|
||||
[data-theme="dark"] {
|
||||
--color-background: #0F172A;
|
||||
--color-text: #F8FAFC;
|
||||
}
|
||||
|
||||
[data-theme="light"] {
|
||||
--color-background: #FFFFFF;
|
||||
--color-text: #0F172A;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
```yaml
|
||||
# .github/workflows/design-system.yml
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'docs/design-system/tokens.json'
|
||||
|
||||
jobs:
|
||||
generate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Generate CSS
|
||||
run: python -m generators css
|
||||
|
||||
- name: Generate Swift
|
||||
run: python -m generators swift
|
||||
|
||||
- name: Generate Kotlin
|
||||
run: python -m generators kotlin
|
||||
|
||||
- name: Commit
|
||||
run: |
|
||||
git add app/design-tokens/
|
||||
git commit -m "style: regenerate design tokens"
|
||||
git push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Правила использования
|
||||
|
||||
1. **Никогда не редактировать сгенерированные файлы вручную**
|
||||
2. **Все изменения только в tokens.json**
|
||||
3. **После изменения tokens.json → запустить генераторы**
|
||||
4. **DocAgent обновляет документацию**
|
||||
|
||||
---
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Обновление tokens.json
|
||||
|
||||
1. Открыть `docs/design-system/tokens.json`
|
||||
2. Внести изменения
|
||||
3. Запустить `python -m generators all`
|
||||
4. Проверить сгенерированные файлы
|
||||
5. Зафиксировать в git
|
||||
|
||||
### Добавление новых token
|
||||
|
||||
```json
|
||||
{
|
||||
"new_token": {
|
||||
"system": "auto",
|
||||
"dark": "#value",
|
||||
"light": "#value"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Последствия
|
||||
|
||||
### Положительные
|
||||
|
||||
- Единый источник истины
|
||||
- Согласованность между платформами
|
||||
- Легко добавлять темы
|
||||
- Автоматическая генерация
|
||||
|
||||
### Отрицательные
|
||||
|
||||
- Дополнительный слой абстракции
|
||||
- Требует генерацию при изменениях
|
||||
- Разные форматы вывода
|
||||
|
||||
---
|
||||
|
||||
## Ответственный
|
||||
|
||||
**Decision maker:** Frontend team
|
||||
**Review date:** При добавлении новой платформы
|
||||
|
||||
---
|
||||
|
||||
*Создано: 2026-05-10*
|
||||
*Обновляется DocAgent при изменениях*
|
||||
@@ -0,0 +1,156 @@
|
||||
# ADR-006: Версионирование агентов
|
||||
|
||||
**Статус:** принято
|
||||
**Дата:** 2026-05-10
|
||||
|
||||
---
|
||||
|
||||
## Контекст
|
||||
|
||||
11 системных агентов VoIdea самообучаются — их код, промпты и capabilities изменяются
|
||||
автоматически через EvolutionAgent или вручную. Без контроля версий невозможно:
|
||||
|
||||
- Отследить, когда и какой агент изменился
|
||||
- Понять, какие изменения были внесены
|
||||
- Откатить агента до предыдущей версии при проблемах
|
||||
- Синхронизировать версии агентов между окружениями (local → VPS)
|
||||
|
||||
Рассматривались:
|
||||
- **Единая версия для всех** — не отражает индивидуальных изменений
|
||||
- **Только git** — не покрывает runtime-эволюцию (изменение промптов без коммита)
|
||||
- **A.B.C для каждого агента** — точный контроль, авто-детект изменений
|
||||
|
||||
---
|
||||
|
||||
## Решение
|
||||
|
||||
**A.B.C (SemVer) для каждого агента**, changelog в `CHANGELOG/agents/<name>.md`.
|
||||
|
||||
### Правила бампа
|
||||
|
||||
| Компонент | Когда меняется | Кто меняет |
|
||||
|-----------|---------------|------------|
|
||||
| **A (major)** | Breaking change: сигнатура `run()` или публичные методы | EvolutionAgent (анализ кода) |
|
||||
| **B (minor)** | Новая capability: новый метод, новый prompt, новая роль | EvolutionAgent (добавление capability) |
|
||||
| **C (patch)** | Внутренние правки: багфикс, оптимизация, уточнение промпта | Сам агент (авто-сравнение checksum) |
|
||||
|
||||
### Механика
|
||||
|
||||
```
|
||||
Каждый Agent.run()
|
||||
→ compute_checksum() — SHA256 от __file__ агента
|
||||
→ сравнивает с AgentConfig.checksum в БД
|
||||
→ не совпал → bump_version("patch") → запись в changelog → обновление БД
|
||||
→ совпал → ничего
|
||||
|
||||
EvolutionAgent
|
||||
→ добавляет capability → bump_version("minor") → запись в changelog
|
||||
→ обнаружил breaking change → bump_version("major") → запись в changelog
|
||||
```
|
||||
|
||||
### Хранение
|
||||
|
||||
```
|
||||
CHANGELOG/agents/
|
||||
├── doc_agent.md
|
||||
├── audit_agent.md
|
||||
├── security_agent.md
|
||||
├── spec_agent.md
|
||||
├── observer_agent.md
|
||||
├── qa_tester_agent.md
|
||||
├── fix_agent.md
|
||||
├── ui_test_agent.md
|
||||
├── rollout_agent.md
|
||||
├── evolution_agent.md
|
||||
└── backlog_agent.md
|
||||
```
|
||||
|
||||
Формат changelog агента:
|
||||
```markdown
|
||||
# audit_agent Changelog
|
||||
|
||||
## 1.0.2 (2026-05-10)
|
||||
- Fixed: ruff output parsing for Windows paths
|
||||
|
||||
## 1.0.1 (2026-05-09)
|
||||
- Fixed: missing error handling in health_check
|
||||
|
||||
## 1.0.0 (2026-05-08)
|
||||
- Initial version
|
||||
```
|
||||
|
||||
### Разделение ответственности
|
||||
|
||||
| Аспект | Владелец | Где хранится |
|
||||
|--------|----------|--------------|
|
||||
| Версия проекта | SpecAgent | `project.json`, `CHANGELOG/v*.md` |
|
||||
| Версия агента | EvolutionAgent | `AgentConfig.version` (БД) |
|
||||
| Changelog проекта | SpecAgent | `CHANGELOG/v*.md` |
|
||||
| Changelog агента | EvolutionAgent | `CHANGELOG/agents/<name>.md` |
|
||||
|
||||
---
|
||||
|
||||
## Архитектура
|
||||
|
||||
### Изменения в моделях БД
|
||||
|
||||
```python
|
||||
class AgentConfig(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "agent_configs"
|
||||
|
||||
agent_name: str # unique
|
||||
is_enabled: bool
|
||||
version: str # "1.0.0" — новое поле
|
||||
checksum: str # SHA256 — новое поле
|
||||
config: str | None # JSON
|
||||
last_run_at: datetime # DateTime вместо String
|
||||
```
|
||||
|
||||
### Изменения в BaseAgent
|
||||
|
||||
```python
|
||||
class BaseAgent(ABC):
|
||||
name: str
|
||||
version: str = "1.0.0"
|
||||
CHANGELOG_DIR = "CHANGELOG/agents/"
|
||||
|
||||
def compute_checksum(self) -> str:
|
||||
"""SHA256 от __file__ агента"""
|
||||
...
|
||||
|
||||
def bump_version(self, version_type: str = "patch") -> str:
|
||||
"""Увеличить A/B/C, обновить self.version"""
|
||||
...
|
||||
|
||||
def write_changelog(self, version: str, entries: list[str]) -> None:
|
||||
"""Дописать запись в CHANGELOG/agents/<name>.md"""
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Последствия
|
||||
|
||||
### Положительные
|
||||
|
||||
- Полная traceability изменений каждого агента
|
||||
- Автоматическое версионирование без участия человека
|
||||
- Совместимо с git (checksum детектит и runtime-изменения)
|
||||
- Единый формат changelog для всех агентов
|
||||
|
||||
### Отрицательные
|
||||
|
||||
- Дополнительная нагрузка на БД (чтение/запись checksum при каждом run)
|
||||
- SHA256 файла не детектит изменения в импортируемых зависимостях
|
||||
- Patch-версия может расти быстро при частых правках
|
||||
|
||||
---
|
||||
|
||||
## Ответственный
|
||||
|
||||
**Decision maker:** Owner
|
||||
**Review date:** При изменении архитектуры агентов
|
||||
|
||||
---
|
||||
|
||||
*Создано: 2026-05-10*
|
||||
Reference in New Issue
Block a user