Initial commit: VoIdeaAI - voice-first AI idea assistant
This commit is contained in:
@@ -0,0 +1,357 @@
|
||||
# Rules & Conventions
|
||||
|
||||
Конституция проекта. Применяется ко всем компонентам.
|
||||
Если в специфичном компоненте нет явного описания ситуации — решение принимается по этим правилам.
|
||||
Если сомневаешься — спроси.
|
||||
|
||||
---
|
||||
|
||||
## 1. Code Style Standards
|
||||
|
||||
**Python (PEP8 + ruff):**
|
||||
- Кодировка UTF-8, отступы 4 пробела
|
||||
- Максимальная длина строки: 88 символов (ruff format)
|
||||
- Именование: переменные/функции — snake_case, классы — PascalCase, константы — UPPER_SNAKE_CASE
|
||||
- Аннотации типов — обязательны для аргументов и возвращаемых значений всех функций
|
||||
- Строки: двойные кавычки " для данных, одинарные ' для docstrings
|
||||
- Импорты: stdlib → third-party → local (алфавитный порядок внутри групп). Абсолютные импорты, относительные запрещены
|
||||
- Типизация: `from __future__ import annotations` во всех файлах, `X | None` вместо `Optional[X]`
|
||||
- Пробелы: вокруг операторов, не внутри скобок
|
||||
|
||||
**SQL:**
|
||||
- Ключевые слова — UPPERCASE (SELECT, FROM, WHERE)
|
||||
- Имена таблиц и полей — snake_case
|
||||
- Сложные запросы разбивать на строки, выравнивать JOIN и WHERE
|
||||
|
||||
**Оптимальный размер файла:**
|
||||
- Маршруты/контроллеры: не более 500 строк → разбить на модули
|
||||
- Модели: не более 200 строк
|
||||
- Сервисы: не более 300 строк
|
||||
|
||||
**TypeScript/React:**
|
||||
- Formatter: Prettier (100 символов)
|
||||
- Типы: strict TypeScript, any запрещён
|
||||
- Импорты: абсолютные через @/ alias
|
||||
- Стили: Tailwind CSS
|
||||
- Состояние: Zustand (новые сториджи) или Context API (legacy)
|
||||
- Формы: react-hook-form + zod (сложные), нативный form (простые)
|
||||
- Асинхронность: async/await
|
||||
- Доступность: WCAG AA (eslint-plugin-jsx-a11y enforcement)
|
||||
- i18n-ready: строки через constants/strings.ts, рендер через <T>
|
||||
|
||||
**ESLint (React/TypeScript):**
|
||||
- Плагины: @eslint/js + typescript-eslint + eslint-plugin-jsx-a11y
|
||||
- Парсер: @typescript-eslint/parser (flat config)
|
||||
- Ключевые правила:
|
||||
- `@typescript-eslint/no-explicit-any`: error
|
||||
- `@typescript-eslint/strict-boolean-expressions`: error
|
||||
- `@typescript-eslint/no-unused-vars`: error (кроме `_`)
|
||||
- `jsx-a11y/alt-text`: error
|
||||
- `jsx-a11y/aria-props`: error
|
||||
- `jsx-a11y/aria-role`: error
|
||||
- `jsx-a11y/label-has-associated-control`: error
|
||||
- `jsx-a11y/click-events-have-key-events`: error
|
||||
- `no-console`: error (кроме warn, error)
|
||||
- `prefer-const`: error
|
||||
- `no-var`: error
|
||||
- Установка: `npm install -D eslint @eslint/js typescript-eslint eslint-plugin-jsx-a11y`
|
||||
- Запуск: `npx eslint src/` (в CI после npm install)
|
||||
|
||||
**Testing (Vitest):**
|
||||
- Фреймворк: Vitest + @testing-library/react
|
||||
- Имена файлов: `*.test.ts` / `*.test.tsx` рядом с модулем
|
||||
- Smoke: каждая страница рендерится без падения
|
||||
- Store: каждый action тестируется
|
||||
- JSON-репортёр: `vitest run --reporter=json` (для QATesterAgent)
|
||||
- Установка: `npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom`
|
||||
- Запуск: `npx vitest run` (в CI после npm ci)
|
||||
|
||||
---
|
||||
|
||||
## 2. Documentation
|
||||
|
||||
- Docstrings: Google-style для всех публичных классов, функций, методов
|
||||
- TODO/FIXME: с указанием причины. `# TODO(#TASK): причина`
|
||||
- Предупреждения о рисках: если код затрагивает безопасность, производительность или совместимость
|
||||
- README.md: в каждой папке с кодом — краткое описание
|
||||
|
||||
---
|
||||
|
||||
## 3. Naming Conventions
|
||||
|
||||
**Переменные окружения:**
|
||||
```
|
||||
PROJECT_NAME=
|
||||
PROJECT_VERSION=X.Y.Z
|
||||
PROJECT_ENV=local|development|staging|production
|
||||
SERVER_HOST=X.X.X.X
|
||||
SERVER_PORT=8020
|
||||
DATABASE_URL=...
|
||||
JWT_SECRET_KEY=
|
||||
```
|
||||
|
||||
**Индексы БД:**
|
||||
```
|
||||
ix_tablename_column
|
||||
uq_tablename_column
|
||||
fk_tablename_column
|
||||
```
|
||||
|
||||
**Ветки Git:**
|
||||
```
|
||||
main → стабильная, продакшен
|
||||
develop → интеграция фич
|
||||
feature/* → новая функция
|
||||
hotfix/* → срочное исправление
|
||||
release/* → подготовка релиза
|
||||
```
|
||||
|
||||
**Миграции БД:**
|
||||
```
|
||||
{action}_{table}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Git & Versioning
|
||||
|
||||
### 4.1 Формат
|
||||
SemVer: MAJOR.MINOR.PATCH
|
||||
|
||||
### 4.2 CHANGELOG
|
||||
```
|
||||
CHANGELOG/
|
||||
├── v1.0.md # 1.0.0 → 1.0.n (патчи дописываются)
|
||||
├── v1.1.md # 1.1.0 → 1.1.n
|
||||
└── v2.0.md # 2.0.0 → ...
|
||||
```
|
||||
|
||||
### 4.3 Conventional Commits
|
||||
```
|
||||
<тип>[optional scope]: <описание>
|
||||
|
||||
feat: → новая функция → MINOR
|
||||
fix: → исправление → PATCH
|
||||
BREAKING: → несовместимость → MAJOR
|
||||
docs: → документация
|
||||
refactor: → рефакторинг
|
||||
test: → тесты
|
||||
chore: → обслуживание
|
||||
```
|
||||
|
||||
### 4.4 Agent Versioning (если используются агенты)
|
||||
|
||||
Агенты версионируются независимо по A.B.C.
|
||||
- **A (major)**: breaking change в публичном интерфейсе
|
||||
- **B (minor)**: новая capability
|
||||
- **C (patch)**: внутренние правки, авто-бамп по checksum
|
||||
|
||||
Changelog агентов: `CHANGELOG/agents/<name>.md`
|
||||
|
||||
---
|
||||
|
||||
## 5. Code Review
|
||||
|
||||
- Обязателен для всех PR в main и develop
|
||||
- Минимум 1 апрув от admin/owner
|
||||
- [ASK]: кто апрувит в текущем проекте?
|
||||
|
||||
Чеклист ревью:
|
||||
- [ ] Нет секретов в коде
|
||||
- [ ] Нет сырых Exception в API ответах
|
||||
- [ ] Есть тесты (или TODO с причиной)
|
||||
- [ ] Документация обновлена
|
||||
- [ ] ADR создан при архитектурных изменениях
|
||||
- [ ] CHANGELOG обновлён
|
||||
|
||||
---
|
||||
|
||||
## 6. Definition of Done (DoD)
|
||||
|
||||
- [ ] Код написан (соответствует стилю §1)
|
||||
- [ ] Линт проходит (ruff — 0 errors)
|
||||
- [ ] Тесты написаны (минимум 1 smoke)
|
||||
- [ ] Тесты проходят (pytest — green)
|
||||
- [ ] Документация обновлена
|
||||
- [ ] .env.example обновлён (если новая переменная)
|
||||
- [ ] Миграция написана (если менялась БД)
|
||||
- [ ] CHANGELOG обновлён
|
||||
|
||||
---
|
||||
|
||||
## 7. Architecture (SOLID + слоистая)
|
||||
|
||||
**Слои (зависимости только внутрь):**
|
||||
```
|
||||
API → Services → Integrations → Data → Core
|
||||
```
|
||||
|
||||
**SOLID:**
|
||||
- S: каждый модуль — одна доменная область
|
||||
- O: новые интеграции — новые классы
|
||||
- L: сервисы подчиняются общему интерфейсу
|
||||
- I: сервис принимает только нужные зависимости
|
||||
- D: API зависит от абстракции Service
|
||||
|
||||
---
|
||||
|
||||
## 8. Error Handling
|
||||
|
||||
| Слой | Действие |
|
||||
|------|---------|
|
||||
| API | HTTPException с detail и status_code |
|
||||
| Services | Бизнес-исключения без HTTP-статусов |
|
||||
| Integrations | try/except с fallback |
|
||||
| DB | Ошибки БД не всплывают выше |
|
||||
|
||||
---
|
||||
|
||||
## 9. Security Base
|
||||
|
||||
- .env — всегда в .gitignore
|
||||
- JWT: алгоритм HS256, expire = 60 минут, refresh = 30 дней
|
||||
- Пароли: bcrypt через passlib
|
||||
- Pydantic валидация на всех входах
|
||||
- RBAC: роли user, admin
|
||||
|
||||
---
|
||||
|
||||
## 10. Logging Standards
|
||||
|
||||
**Формат строки лога:**
|
||||
```
|
||||
[ISO8601] [LEVEL] [component] message key=val
|
||||
2026-05-10T14:30:00.000Z INFO [auth] User logged in user_id=abc
|
||||
```
|
||||
|
||||
**Уровни по слоям:**
|
||||
|
||||
| Слой | DEBUG | INFO | WARNING | ERROR |
|
||||
|------|-------|------|---------|-------|
|
||||
| API | Параметры | Request | — | 5xx |
|
||||
| Service | Входные | Операция | Превышен лимит | Ошибка БД |
|
||||
| Integration | Raw ответ | Успех | Timeout | Внешний API |
|
||||
|
||||
**Запрещено:** f-строки в logger. Только %s (lazy evaluation).
|
||||
|
||||
---
|
||||
|
||||
## 11. Sensitive Data Policy
|
||||
|
||||
**Никогда не логировать:**
|
||||
- Пароли (даже хэш)
|
||||
- JWT токены
|
||||
- API keys и секреты
|
||||
- Email в открытом виде
|
||||
|
||||
**Маскировать в логах:**
|
||||
- Email: u***@mail.ru
|
||||
- IP: 195.208.*.*
|
||||
|
||||
---
|
||||
|
||||
## 12. Third-party Call Fallback Pattern
|
||||
|
||||
```
|
||||
1. Попытка (timeout: 10s)
|
||||
2. Успех → return data
|
||||
3. Таймаут → retry 1 (через 2s)
|
||||
4. Таймаут → retry 2 (через 5s)
|
||||
5. 4xx → WARNING, return None/fallback
|
||||
6. 5xx → ERROR, retry → если снова 5xx → return None/fallback
|
||||
7. Все retry исчерпаны → return fallback результат
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Performance Budgets (примерные)
|
||||
|
||||
| Метрика | Лимит (p95) |
|
||||
|---------|-------------|
|
||||
| API response | < 500ms |
|
||||
| DB query (одиночный) | < 100ms |
|
||||
| DB query (агрегатный) | < 300ms |
|
||||
| AI call | < 5s (иначе fallback) |
|
||||
| WebUI page load | < 2s |
|
||||
|
||||
---
|
||||
|
||||
## 14. Data Retention Policy (примерная)
|
||||
|
||||
| Данные | Срок хранения |
|
||||
|--------|---------------|
|
||||
| SystemLog | 90 дней |
|
||||
| SecurityEvent | 1 год |
|
||||
| User data | До удаления + 30 дней |
|
||||
| Session (JWT) | 24 часа |
|
||||
|
||||
---
|
||||
|
||||
## 15. Dependency Management
|
||||
|
||||
- **patch**: в любой момент (bugfix, security)
|
||||
- **minor**: не чаще 1 раза в спринт
|
||||
- **major**: только с полным регрессом
|
||||
|
||||
---
|
||||
|
||||
## 16. Async/Sync Decision Matrix
|
||||
|
||||
| Сценарий | Механизм |
|
||||
|----------|----------|
|
||||
| GET-запросы, CRUD | sync (await) |
|
||||
| Отправка email | async (Celery или прямой) |
|
||||
| AI вызовы | async (Celery или прямой) |
|
||||
| Бэкапы | async (Celery) |
|
||||
|
||||
---
|
||||
|
||||
## 17. ADR (Architecture Decision Records)
|
||||
|
||||
Любое значимое архитектурное решение фиксируется в `docs/adr/NNN-title.md`.
|
||||
|
||||
Формат:
|
||||
```markdown
|
||||
# ADR-NNN: Название решения
|
||||
|
||||
Статус: принято
|
||||
Контекст: описание проблемы
|
||||
Решение: что выбрано
|
||||
Последствия: плюсы и минусы
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 18. Tooling
|
||||
|
||||
| Инструмент | Назначение |
|
||||
|------------|------------|
|
||||
| ruff | Линтер (E, F, W, I, N, UP) |
|
||||
| ruff format | Форматтер (line-length=88) |
|
||||
| mypy | Type checker |
|
||||
| pytest | Тесты (asyncio_mode=auto) |
|
||||
| pre-commit | Хуки (ruff, ruff-format, trailing-whitespace) |
|
||||
|
||||
---
|
||||
|
||||
## 19. API Version Lifecycle
|
||||
|
||||
```
|
||||
Текущая: /api/v1/* — стабильная
|
||||
Deprecation: 3 месяца после выхода новой версии
|
||||
Отключение: 410 Gone
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 20. Module Public API Convention
|
||||
|
||||
`__init__.py` содержит ТОЛЬКО публичный API модуля:
|
||||
```python
|
||||
from app.models.user import User
|
||||
__all__ = ["User"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Документ создан на основе шаблона. Адаптируйте под конкретный проект.*
|
||||
@@ -0,0 +1,112 @@
|
||||
# Архитектура проекта
|
||||
|
||||
## Слоистая архитектура
|
||||
|
||||
Проект построен по принципу строгой слоистости. Зависимости могут идти **только внутрь** — от API к Core.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ API │
|
||||
│ HTTP роуты, Pydantic валидация, OpenAPI │
|
||||
│ Зависимости: Services │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Services │
|
||||
│ Бизнес-логика, оркестрация │
|
||||
│ Зависимости: Integrations, Data │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Integrations │
|
||||
│ Внешние API, AI провайдеры, fallback chain │
|
||||
│ Зависимости: Data │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Tasks │
|
||||
│ Фоновые задачи (Celery или прямой вызов) │
|
||||
│ Зависимости: Services, Integrations │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Agents │
|
||||
│ Системные агенты (саморазвитие проекта) │
|
||||
│ Зависимости: Services, Integrations │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Data │
|
||||
│ Модели БД, репозитории, миграции │
|
||||
│ Зависимости: Core │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Core │
|
||||
│ Config, base classes, security, dependencies │
|
||||
│ Зависимости: нет (фундамент) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Правила слоёв
|
||||
|
||||
1. **API** не знает про БД. Он получает `db: AsyncSession` через `Depends(get_db)`, но не создаёт сессии сам. Он не импортирует модели.
|
||||
|
||||
2. **Services** не знают про HTTP. Они не импортируют FastAPI, Request, Response, HTTPException. Работают с бизнес-данными через сессию БД.
|
||||
|
||||
3. **Integrations** не знают про бизнес-логику. Они оборачивают внешние API, управляют таймаутами и ретраями.
|
||||
|
||||
4. **Data** (models) — SQLAlchemy модели. Не содержат бизнес-логики. Только структура данных.
|
||||
|
||||
5. **Core** — фундамент. Config читает .env, base содержит абстракции, security управляет JWT, dependencies содержит FastAPI-зависимости.
|
||||
|
||||
---
|
||||
|
||||
## SOLID в проекте
|
||||
|
||||
### S — Single Responsibility
|
||||
Каждый модуль делает одну вещь:
|
||||
- `idea_service.py` — только операции с идеями
|
||||
- `yandex_gpt.py` — только вызов Yandex GPT
|
||||
- `auth.py` — только аутентификация
|
||||
|
||||
### O — Open/Closed
|
||||
Новые интеграции — новые классы, а не модификация старых:
|
||||
- `AIProvider` (ABC) → `YandexGPTProvider`, `GigaChatProvider`
|
||||
- `BaseAgent` (ABC) → `DocAgent`, `AuditAgent`, ...
|
||||
|
||||
### L — Liskov Substitution
|
||||
Сервисы принимают `AsyncSession` — любую реализацию (SQLite, PostgreSQL):
|
||||
- Код работает одинаково на обеих БД
|
||||
|
||||
### I — Interface Segregation
|
||||
Сервис принимает только то, что нужно:
|
||||
- `IdeaService(db)` — не принимает config, security, и т.д.
|
||||
- `AuthService(db, settings)` — принимает то, что реально нужно
|
||||
|
||||
### D — Dependency Inversion
|
||||
API зависит от `IdeaService`, а не от `IdeaServicePostgres`:
|
||||
- Сервисы — это абстракция над слоем данных
|
||||
- Можно подменить реализацию не меняя API
|
||||
|
||||
---
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
Сессия БД создаётся FastAPI и передаётся через Depends:
|
||||
```python
|
||||
async def get_db() -> AsyncSession:
|
||||
async with async_session_maker() as session:
|
||||
yield session
|
||||
```
|
||||
|
||||
Сервисы получают сессию в конструкторе:
|
||||
```python
|
||||
class IdeaService:
|
||||
def __init__(self, db: AsyncSession):
|
||||
self.db = db
|
||||
```
|
||||
|
||||
API создаёт сервис на каждый запрос:
|
||||
```python
|
||||
@router.get("/")
|
||||
async def list_ideas(db: AsyncSession = Depends(get_db)):
|
||||
service = IdeaService(db)
|
||||
return await service.list_all()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по архитектуре
|
||||
|
||||
- Нужен ли Repository Pattern (отдельный слой между сервисами и моделями)?
|
||||
- Использовать ли CQRS (разделение чтения и записи)?
|
||||
- Нужен ли Event Bus для межсервисного взаимодействия?
|
||||
@@ -0,0 +1,44 @@
|
||||
# Технологический стек
|
||||
|
||||
## Стек по умолчанию
|
||||
|
||||
| Компонент | Технология | Версия | Примечание |
|
||||
|-----------|-----------|--------|------------|
|
||||
| Язык | Python | 3.12+ | |
|
||||
| Фреймворк | FastAPI | 0.115+ | async, OpenAPI |
|
||||
| ORM | SQLAlchemy | 2.0+ | async |
|
||||
| Валидация | Pydantic | 2.x | v2 синтаксис |
|
||||
| База данных (dev) | SQLite | — | через aiosqlite |
|
||||
| База данных (prod) | PostgreSQL | 14+ | через asyncpg |
|
||||
| Миграции | Alembic | 1.14+ | |
|
||||
| Аутентификация | JWT + bcrypt | — | passlib |
|
||||
| Фронтенд | React + Vite + TS | 18/5/5 | |
|
||||
| Стили | Tailwind CSS | 3.4+ | |
|
||||
| PWA | vite-plugin-pwa | 0.20+ | |
|
||||
| Тесты | pytest | 8+ | asyncio_mode=auto |
|
||||
| Линтер | ruff | | |
|
||||
| Форматтер | ruff format | | line-length=88 |
|
||||
|
||||
## Опциональные компоненты
|
||||
|
||||
| Компонент | Когда добавлять | Альтернативы |
|
||||
|-----------|----------------|--------------|
|
||||
| **Celery** + Redis | Для фоновых задач (AI, email, backup) | Прямой вызов в dev |
|
||||
| **PostgreSQL** | Для production | SQLite в dev |
|
||||
| **AI providers** | Если нужен AI-анализ | Yandex GPT, GigaChat, OpenAI |
|
||||
| **OAuth2** | Если нужен вход через соцсети | Яндекс, Google, GitHub |
|
||||
| **SMTP** | Если нужны email-уведомления | |
|
||||
| **Docker** | Для воспроизводимого деплоя | |
|
||||
| **Prometheus + Grafana** | Для мониторинга в prod | |
|
||||
| **System Agents** | Для саморазвития проекта | 4 ядерных, остальные по необходимости |
|
||||
|
||||
## [ASK] Выбор стека
|
||||
|
||||
Перед началом проекта ответьте на вопросы:
|
||||
|
||||
1. **Будет ли проект в production?** Если да → PostgreSQL + мониторинг
|
||||
2. **Нужны ли фоновые задачи?** Если да → Celery (или прямой вызов на старте)
|
||||
3. **Нужен ли AI?** Если да → FallbackChain с 2+ провайдерами
|
||||
4. **Нужен ли фронтенд?** Если да → React/Vite/Tailwind
|
||||
5. **Нужна ли PWA?** Если да → vite-plugin-pwa + Service Worker
|
||||
6. **Нужны ли агенты?** Если да → 4 ядерных с первого коммита
|
||||
@@ -0,0 +1,151 @@
|
||||
# Структура проекта
|
||||
|
||||
```
|
||||
project/
|
||||
│
|
||||
├── app/ # Backend
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py # FastAPI app: lifespan, middleware, routers, CORS
|
||||
│ │
|
||||
│ ├── api/ # HTTP слой
|
||||
│ │ ├── __init__.py # api_v1_router
|
||||
│ │ └── v1/ # Версионированные роуты
|
||||
│ │ ├── __init__.py # Сборка всех роутеров
|
||||
│ │ ├── auth.py # POST /login, /register, /refresh, /oauth
|
||||
│ │ ├── users.py # GET/PATCH /me
|
||||
│ │ ├── ideas.py # CRUD /ideas + POST /analyze
|
||||
│ │ ├── agents.py # GET /agents + POST /run
|
||||
│ │ ├── sync.py # POST /pull, /push
|
||||
│ │ └── admin.py # GET /users, /health, /logs
|
||||
│ │
|
||||
│ ├── core/ # Фундамент
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── config.py # Pydantic Settings из .env
|
||||
│ │ ├── base.py # SQLBase, CoreModel, UUIDMixin, TimestampMixin
|
||||
│ │ ├── database.py # create_async_engine, async_session_maker, get_db
|
||||
│ │ ├── security.py # create_token, decode_token, hash/verify password
|
||||
│ │ ├── exceptions.py # HTTPException подклассы
|
||||
│ │ ├── dependencies.py # get_db, get_current_user, require_admin
|
||||
│ │ └── metrics.py # Middleware: request timer, counters
|
||||
│ │
|
||||
│ ├── models/ # SQLAlchemy модели
|
||||
│ │ ├── __init__.py # Все модели в __all__
|
||||
│ │ ├── user.py # User: id, email, password, roles
|
||||
│ │ ├── idea.py # Idea: title, content, tags, status
|
||||
│ │ ├── agent.py # AgentConfig: version, checksum
|
||||
│ │ ├── backlog.py # BacklogTask: title, status, priority
|
||||
│ │ └── log.py # LogEntry: level, source, message
|
||||
│ │
|
||||
│ ├── schemas/ # Pydantic схемы (Request/Response)
|
||||
│ │ ├── __init__.py # Все схемы в __all__
|
||||
│ │ ├── auth.py # LoginRequest, TokenResponse, etc.
|
||||
│ │ ├── user.py # UserCreate, UserResponse, etc.
|
||||
│ │ ├── idea.py # IdeaCreate, IdeaResponse, AnalyzeResponse
|
||||
│ │ ├── agent.py # AgentRunRequest, AgentStatusResponse
|
||||
│ │ ├── sync.py # SyncPullRequest, SyncResponse
|
||||
│ │ └── admin.py # SystemHealth, LogEntryResponse
|
||||
│ │
|
||||
│ ├── services/ # Бизнес-логика
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── auth_service.py # Регистрация, логин, OAuth
|
||||
│ │ ├── user_service.py # CRUD пользователей
|
||||
│ │ ├── idea_service.py # CRUD идей
|
||||
│ │ ├── agent_service.py # Управление агентами
|
||||
│ │ ├── analysis_service.py # Запуск AI-анализа
|
||||
│ │ └── sync_service.py # Синхронизация
|
||||
│ │
|
||||
│ ├── integrations/ # Внешние сервисы
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── ai/ # AI провайдеры
|
||||
│ │ ├── __init__.py # AIProvider, AIResult, FallbackChain
|
||||
│ │ ├── base.py # AIProvider ABC, AIResult dataclass
|
||||
│ │ ├── prompt_loader.py # Загрузка промптов из YAML/MD
|
||||
│ │ ├── yandex_gpt.py # YandexGPTProvider
|
||||
│ │ ├── gigachat.py # GigaChatProvider
|
||||
│ │ └── fallback.py # FallbackChain
|
||||
│ │
|
||||
│ ├── tasks/ # Фоновые задачи
|
||||
│ │ ├── __init__.py # Celery app (ленивый импорт)
|
||||
│ │ └── analysis.py # analyze_idea (Celery или прямой вызов)
|
||||
│ │
|
||||
│ └── agents/ # Системные агенты
|
||||
│ ├── __init__.py
|
||||
│ ├── base.py # BaseAgent ABC, AgentResult, AgentStatus
|
||||
│ ├── registry.py # AgentRegistry
|
||||
│ ├── models.py # AgentState, AgentReport, AgentMetric
|
||||
│ ├── triggers.py # Триггеры запуска
|
||||
│ ├── doc_agent.py # Пишет документацию
|
||||
│ ├── audit_agent.py # Проверяет правила
|
||||
│ ├── evolution_agent.py # Версионирует агентов
|
||||
│ ├── supervisor_agent.py # Следит за всеми агентами
|
||||
│ └── ... # Остальные агенты по необходимости
|
||||
│
|
||||
├── webui/ # Frontend
|
||||
│ ├── index.html
|
||||
│ ├── package.json
|
||||
│ ├── vite.config.ts # Vite + React + PWA + API proxy
|
||||
│ ├── tsconfig.json
|
||||
│ ├── tailwind.config.js
|
||||
│ ├── postcss.config.js
|
||||
│ ├── public/
|
||||
│ │ ├── favicon.svg
|
||||
│ │ ├── manifest.json
|
||||
│ │ └── icons/
|
||||
│ └── src/
|
||||
│ ├── main.tsx
|
||||
│ ├── App.tsx # BrowserRouter + Routes
|
||||
│ ├── index.css # Tailwind directives
|
||||
│ ├── vite-env.d.ts
|
||||
│ ├── api/
|
||||
│ │ ├── client.ts # apiFetch, setTokens, refreshAccessToken
|
||||
│ │ └── ideas.ts # Типы + функции для /ideas
|
||||
│ ├── auth/
|
||||
│ │ └── AuthContext.tsx # useAuth() hook
|
||||
│ ├── components/
|
||||
│ │ ├── Layout.tsx # Header + main
|
||||
│ │ └── ProtectedRoute.tsx # Auth guard
|
||||
│ └── pages/
|
||||
│ ├── LoginPage.tsx
|
||||
│ ├── RegisterPage.tsx
|
||||
│ ├── Dashboard.tsx # Список идей
|
||||
│ ├── IdeaView.tsx # Просмотр + анализ
|
||||
│ ├── IdeaCreate.tsx # Создание идеи
|
||||
│ ├── IdeaEdit.tsx # Редактирование
|
||||
│ └── AdminPage.tsx # Админ-панель
|
||||
│
|
||||
├── tests/ # Тесты
|
||||
│ ├── conftest.py # Глобальные фикстуры
|
||||
│ ├── unit/ # Unit-тесты
|
||||
│ │ ├── conftest.py
|
||||
│ │ └── test_*.py
|
||||
│ ├── integration/ # Интеграционные тесты
|
||||
│ │ ├── conftest.py
|
||||
│ │ ├── test_api.py
|
||||
│ │ └── test_db.py
|
||||
│ └── smoke/ # Smoke-тесты
|
||||
│ └── test_health.py
|
||||
│
|
||||
├── docs/ # Документация
|
||||
│ ├── 00-rules.md
|
||||
│ ├── ... (остальные файлы правил)
|
||||
│ ├── adr/ # Architecture Decision Records
|
||||
│ ├── agents/ # Системные агенты
|
||||
│ ├── decisions/ # Руководства по выбору
|
||||
│ ├── checklists/ # Чеклисты
|
||||
│ └── runbook/ # Эксплуатация
|
||||
│
|
||||
├── CHANGELOG/ # Версионирование
|
||||
│ ├── v1.0.md # CHANGELOG версии 1.0
|
||||
│ └── agents/ # Changelog агентов
|
||||
│ ├── doc_agent.md
|
||||
│ └── ...
|
||||
│
|
||||
├── migrations/ # Alembic (если PostgreSQL)
|
||||
│ └── versions/
|
||||
│
|
||||
├── .env.example
|
||||
├── .gitignore
|
||||
├── project.yaml
|
||||
├── requirements.txt
|
||||
└── README.md
|
||||
```
|
||||
@@ -0,0 +1,97 @@
|
||||
# Версионирование
|
||||
|
||||
## Формат: SemVer
|
||||
|
||||
```
|
||||
MAJOR.MINOR.PATCH
|
||||
```
|
||||
|
||||
- **MAJOR**: несовместимые изменения API
|
||||
- **MINOR**: новая функциональность (обратно совместимо)
|
||||
- **PATCH**: исправления багов
|
||||
|
||||
## CHANGELOG
|
||||
|
||||
**Где хранить:** `CHANGELOG/`
|
||||
|
||||
**Правила:**
|
||||
- Каждая MAJOR версия → новый файл: `CHANGELOG/v1.0.md`
|
||||
- Каждая MINOR версия → новый файл: `CHANGELOG/v1.1.md`
|
||||
- PATCH дописывается в существующий файл
|
||||
|
||||
```
|
||||
CHANGELOG/
|
||||
├── v1.0.md # 1.0.0 → 1.0.5
|
||||
├── v1.1.md # 1.1.0 → 1.1.3
|
||||
└── v2.0.md # 2.0.0 → ...
|
||||
```
|
||||
|
||||
## Conventional Commits
|
||||
|
||||
Каждый коммит должен соответствовать формату:
|
||||
|
||||
```
|
||||
<тип>[optional scope]: <описание>
|
||||
|
||||
[optional body]
|
||||
[optional footer]
|
||||
```
|
||||
|
||||
| Тип | Действие | Влияние на версию |
|
||||
|-----|----------|-------------------|
|
||||
| `feat` | Новая функция | MINOR |
|
||||
| `fix` | Исправление | PATCH |
|
||||
| `BREAKING` | В теле или `!` после типа | MAJOR |
|
||||
| `docs` | Документация | — |
|
||||
| `refactor` | Рефакторинг | — |
|
||||
| `test` | Тесты | — |
|
||||
| `chore` | Обслуживание | — |
|
||||
|
||||
## Agent Versioning (если используются агенты)
|
||||
|
||||
Каждый агент версионируется **независимо** от проекта по A.B.C.
|
||||
|
||||
### Правила бампа
|
||||
|
||||
| Компонент | Когда меняется | Кто меняет |
|
||||
|-----------|---------------|------------|
|
||||
| **A (major)** | Breaking change в публичном интерфейсе | EvolutionAgent |
|
||||
| **B (minor)** | Новая capability (метод, роль, prompt) | EvolutionAgent |
|
||||
| **C (patch)** | Внутренние правки, без изменения поведения | Сам агент (авто) |
|
||||
|
||||
### Механика
|
||||
|
||||
1. Агент запускается → вычисляет SHA256 checksum своего `__file__`
|
||||
2. Сравнивает с хранимым checksum (в БД или в changelog файле)
|
||||
3. Не совпал → авто-бамп patch → запись в changelog → обновление checksum
|
||||
4. EvolutionAgent управляет minor/major бампами
|
||||
|
||||
### Хранение
|
||||
|
||||
```
|
||||
CHANGELOG/agents/<agent_name>.md
|
||||
```
|
||||
|
||||
Формат:
|
||||
```markdown
|
||||
# audit_agent Changelog
|
||||
<!-- checksum: sha256hash -->
|
||||
|
||||
## 1.0.2 (2026-05-10)
|
||||
- Fixed: описание исправления
|
||||
|
||||
## 1.0.1 (2026-05-09)
|
||||
- Fixed: ещё одно исправление
|
||||
|
||||
## 1.0.0 (2026-05-08)
|
||||
- Initial version
|
||||
```
|
||||
|
||||
### Разделение ответственности
|
||||
|
||||
| Аспект | Владелец | Где хранится |
|
||||
|--------|----------|--------------|
|
||||
| Версия проекта | SpecAgent (или человек) | `project.yaml`, `CHANGELOG/v*.md` |
|
||||
| Версия агента | EvolutionAgent | `AgentConfig.version` (БД) |
|
||||
| Changelog проекта | SpecAgent (или человек) | `CHANGELOG/v*.md` |
|
||||
| Changelog агента | EvolutionAgent | `CHANGELOG/agents/<name>.md` |
|
||||
@@ -0,0 +1,117 @@
|
||||
# Стандарты тестирования
|
||||
|
||||
## Философия
|
||||
|
||||
**Код без тестов — это не код, а предложение.** Если функцию нельзя проверить — она либо не нужна, либо её нужно переписать.
|
||||
|
||||
---
|
||||
|
||||
## Пирамида тестов
|
||||
|
||||
```
|
||||
/\ E2E (10%): сквозные сценарии
|
||||
/ \
|
||||
/ \
|
||||
/──────\ Integration (20%): API, БД, внешние сервисы
|
||||
/ \
|
||||
/──────────\ Unit (70%): изолированные модули
|
||||
/ \
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Типы тестов
|
||||
|
||||
### Unit-тесты (`tests/unit/`)
|
||||
- Тестируют один класс/функцию в изоляции
|
||||
- Внешние зависимости мокаются
|
||||
- Быстрые (миллисекунды)
|
||||
- Пример: тест сервиса с mocked репозиторием
|
||||
|
||||
```python
|
||||
async def test_idea_service_create():
|
||||
service = IdeaService(mock_db)
|
||||
idea = await service.create(user_id="1", title="Test", content="Content")
|
||||
assert idea.title == "Test"
|
||||
assert idea.status == "draft"
|
||||
```
|
||||
|
||||
### Integration-тесты (`tests/integration/`)
|
||||
- Тестируют взаимодействие компонентов
|
||||
- Используют реальную БД (SQLite в памяти)
|
||||
- Проверяют API endpoints, БД запросы
|
||||
- Пример: тест регистрации пользователя
|
||||
|
||||
```python
|
||||
async def test_register_user(async_client):
|
||||
response = await async_client.post("/api/v1/auth/register", json={
|
||||
"username": "test",
|
||||
"email": "test@test.com",
|
||||
"password": "secret123",
|
||||
})
|
||||
assert response.status_code == 201
|
||||
data = response.json()
|
||||
assert "access_token" in data
|
||||
```
|
||||
|
||||
### Smoke-тесты (`tests/smoke/`)
|
||||
- Минимум 1 тест на каждый endpoint
|
||||
- Проверяют что endpoint отвечает и возвращает корректный статус
|
||||
- Быстрая проверка здоровья системы
|
||||
|
||||
```python
|
||||
async def test_health_endpoint(async_client):
|
||||
response = await async_client.get("/health")
|
||||
assert response.status_code == 200
|
||||
assert response.json()["status"] == "healthy"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Покрытие
|
||||
|
||||
- **Общее покрытие:** > 80%
|
||||
- **Критический код (auth, security, payments):** 100%
|
||||
- **Новый код:** без тестов не принимается в PR
|
||||
|
||||
---
|
||||
|
||||
## Что тестировать
|
||||
|
||||
### Обязательно (9 сценариев для каждого endpoint)
|
||||
|
||||
1. **Missing field** → 422
|
||||
2. **Wrong type** → 422
|
||||
3. **Expired/invalid token** → 401
|
||||
4. **Wrong permissions** → 403
|
||||
5. **Not found** → 404
|
||||
6. **Conflict** → 409
|
||||
7. **Success** → 200/201
|
||||
8. **Rate limit** → 429 (если реализован)
|
||||
9. **Idempotency** → тот же результат при повторе
|
||||
|
||||
### Для каждого сервиса
|
||||
- Успешное выполнение
|
||||
- Ошибка валидации
|
||||
- Ошибка БД
|
||||
- Граничные случаи (пустой список, null, максимальная длина)
|
||||
|
||||
---
|
||||
|
||||
## Конфигурация pytest
|
||||
|
||||
```ini
|
||||
# pyproject.toml или pytest.ini
|
||||
[tool.pytest.ini_options]
|
||||
asyncio_mode = "auto"
|
||||
testpaths = ["tests"]
|
||||
python_files = ["test_*.py"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по тестированию
|
||||
|
||||
- Нужен ли coverage порог в CI? (рекомендуется 80%)
|
||||
- Использовать ли vcrpy для записи ответов внешних API? (да, для AI провайдеров)
|
||||
- Нужны ли performance-тесты? (да, для критических endpoint'ов)
|
||||
@@ -0,0 +1,86 @@
|
||||
# Безопасность
|
||||
|
||||
## Базовые требования
|
||||
|
||||
- `.env` — всегда в `.gitignore`. Никогда не коммитить.
|
||||
- JWT: алгоритм HS256, access_token = 60 минут, refresh_token = 30 дней
|
||||
- Пароли: bcrypt через passlib
|
||||
- Pydantic валидация на всех входах
|
||||
- RBAC: роли user, admin
|
||||
|
||||
---
|
||||
|
||||
## Аутентификация
|
||||
|
||||
### JWT
|
||||
|
||||
```python
|
||||
# app/core/security.py
|
||||
|
||||
def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str:
|
||||
to_encode = data.copy()
|
||||
expire = datetime.now(timezone.utc) + (expires_delta or timedelta(minutes=60))
|
||||
to_encode.update({"exp": expire, "type": "access"})
|
||||
return jwt.encode(to_encode, settings.jwt_secret_key, algorithm=settings.jwt_algorithm)
|
||||
|
||||
def decode_token(token: str) -> dict[str, Any] | None:
|
||||
try:
|
||||
return jwt.decode(token, settings.jwt_secret_key, algorithms=[settings.jwt_algorithm])
|
||||
except JWTError:
|
||||
return None
|
||||
```
|
||||
|
||||
### Password hashing
|
||||
|
||||
```python
|
||||
from passlib.context import CryptContext
|
||||
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
return pwd_context.hash(password)
|
||||
|
||||
def verify_password(plain: str, hashed: str) -> bool:
|
||||
return pwd_context.verify(plain, hashed)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RBAC
|
||||
|
||||
| Роль | Права |
|
||||
|------|-------|
|
||||
| user | Базовые: CRUD своих данных, запуск анализа |
|
||||
| admin (is_superuser) | Управление пользователями, просмотр логов, системные настройки |
|
||||
|
||||
Проверка прав:
|
||||
```python
|
||||
async def require_admin(user: Annotated[User, Depends(get_current_user)]) -> User:
|
||||
if not user.is_superuser:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required")
|
||||
return user
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sensitive Data
|
||||
|
||||
**Никогда не логировать:**
|
||||
- Пароли (даже хэш)
|
||||
- JWT токены
|
||||
- API keys и секреты
|
||||
- Email в открытом виде (только user_id)
|
||||
|
||||
**Маскировать в логах:**
|
||||
- Email: u***@mail.ru
|
||||
- IP: 195.208.*.*
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по безопасности
|
||||
|
||||
- Нужен ли audit log? (рекомендуется для production)
|
||||
- Нужно ли шифрование данных в покое? (да, если хранятся персональные данные)
|
||||
- Нужен ли rate limiting? (да, для production)
|
||||
- Нужен ли CORS? (да, если фронтенд на другом домене)
|
||||
- Нужен ли CSRF? (нет, если используем JWT в Bearer header)
|
||||
@@ -0,0 +1,77 @@
|
||||
# Производительность
|
||||
|
||||
## Performance Budgets
|
||||
|
||||
| Метрика | Лимит (p95) | Примечание |
|
||||
|---------|-------------|------------|
|
||||
| API response (без AI) | < 500ms | |
|
||||
| API response (с AI) | < 5s | Fallback после 5s |
|
||||
| DB query (одиночный) | < 100ms | С индексом |
|
||||
| DB query (агрегатный) | < 300ms | |
|
||||
| WebUI page load | < 2s | |
|
||||
| AI call | < 5s | Иначе fallback |
|
||||
|
||||
---
|
||||
|
||||
## Индексы БД
|
||||
|
||||
**Что индексировать:**
|
||||
- Поля в WHERE и JOIN: `user_id`, `status`, `email`
|
||||
- Поля сортировки: `created_at`
|
||||
- Внешние ключи: `user_id`, `parent_id`
|
||||
|
||||
**Формат имени индекса:** `ix_tablename_column`
|
||||
|
||||
```sql
|
||||
CREATE INDEX ix_ideas_user_id ON ideas(user_id);
|
||||
CREATE INDEX ix_ideas_status ON ideas(status);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Connection Pool
|
||||
|
||||
```python
|
||||
engine = create_async_engine(
|
||||
settings.database_url,
|
||||
pool_size=10, # Постоянные соединения
|
||||
max_overflow=20, # Дополнительные при пике
|
||||
pool_pre_ping=True, # Проверка перед использованием
|
||||
)
|
||||
```
|
||||
|
||||
Для SQLite pool настраивать не нужно — он файловый.
|
||||
|
||||
---
|
||||
|
||||
## Метрики (если реализованы)
|
||||
|
||||
Собираемые метрики:
|
||||
|
||||
| Метрика | Тип | Описание |
|
||||
|---------|-----|----------|
|
||||
| `http_requests_total` | Counter | Всего запросов |
|
||||
| `http_request_duration_ms` | Histogram | Время ответа (p50/p95/p99) |
|
||||
| `http_requests_by_endpoint` | Counter | По endpoint'ам |
|
||||
| `http_errors_total` | Counter | 4xx и 5xx |
|
||||
| `db_query_duration_ms` | Histogram | Время запросов к БД |
|
||||
| `ai_provider_calls` | Counter | Вызовы AI провайдеров |
|
||||
| `agent_execution_duration` | Histogram | Время выполнения агентов |
|
||||
|
||||
**Где хранить:** в БД (таблица `agent_metrics`), в перспективе — Prometheus.
|
||||
|
||||
---
|
||||
|
||||
## Когда оптимизировать
|
||||
|
||||
1. **Профилировать до оптимизации.** Не гадать — измерять.
|
||||
2. **Оптимизировать только горячие пути.** 90% времени уходит на 10% кода.
|
||||
3. **Кэшировать только то, что реально часто читается.** Преждевременное кэширование — корень всех зол.
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по производительности
|
||||
|
||||
- Нужен ли Redis кэш? (да, если часто читаются одни и те же данные)
|
||||
- Нужен ли CDN для статики? (да, для production)
|
||||
- Нужен ли database sharding? (нет, до 10M записей)
|
||||
@@ -0,0 +1,106 @@
|
||||
# Обработка ошибок
|
||||
|
||||
## Матрица ошибок по слоям
|
||||
|
||||
| Слой | Что делаем | Пример |
|
||||
|------|-----------|--------|
|
||||
| **API** | HTTPException с detail и status_code | `raise HTTPException(404, detail="Not found")` |
|
||||
| **Services** | Бизнес-исключения без HTTP-статусов | `raise IdeaNotFoundError(idea_id)` |
|
||||
| **Integrations** | try/except с fallback | `return AIResult(success=False, error=...)` |
|
||||
| **Data/DB** | Ошибки не всплывают выше | Ловим в сервисе |
|
||||
|
||||
---
|
||||
|
||||
## Иерархия исключений
|
||||
|
||||
```python
|
||||
# app/core/exceptions.py
|
||||
|
||||
class AppError(Exception):
|
||||
"""Базовое исключение приложения."""
|
||||
def __init__(self, message: str, details: dict | None = None):
|
||||
self.message = message
|
||||
self.details = details or {}
|
||||
|
||||
class NotFoundError(AppError):
|
||||
"""Ресурс не найден."""
|
||||
def __init__(self, resource: str, resource_id: str):
|
||||
super().__init__(f"{resource} not found: {resource_id}", {"resource": resource, "id": resource_id})
|
||||
|
||||
class ValidationError(AppError):
|
||||
"""Ошибка валидации."""
|
||||
def __init__(self, field: str, message: str):
|
||||
super().__init__(message, {"field": field})
|
||||
|
||||
class AuthError(AppError):
|
||||
"""Ошибка аутентификации."""
|
||||
def __init__(self, message: str = "Authentication failed"):
|
||||
super().__init__(message)
|
||||
|
||||
class ForbiddenError(AppError):
|
||||
"""Нет прав."""
|
||||
def __init__(self, message: str = "Access denied"):
|
||||
super().__init__(message)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fallback Pattern (для внешних вызовов)
|
||||
|
||||
```python
|
||||
# Паттерн для всех вызовов внешних API
|
||||
|
||||
async def call_with_fallback(provider: AIProvider, prompt: str) -> AIResult:
|
||||
max_retries = 2
|
||||
last_error = None
|
||||
|
||||
for attempt in range(max_retries + 1):
|
||||
try:
|
||||
result = await asyncio.wait_for(
|
||||
provider.analyze(prompt),
|
||||
timeout=10.0
|
||||
)
|
||||
if result.success:
|
||||
return result
|
||||
last_error = result
|
||||
except asyncio.TimeoutError:
|
||||
last_error = AIResult(success=False, error="Timeout")
|
||||
except Exception as e:
|
||||
last_error = AIResult(success=False, error=str(e))
|
||||
|
||||
if attempt < max_retries:
|
||||
await asyncio.sleep(2 if attempt == 0 else 5)
|
||||
|
||||
return last_error
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Логирование ошибок
|
||||
|
||||
| Уровень | Когда | Пример |
|
||||
|---------|-------|--------|
|
||||
| DEBUG | Входящие параметры | `Request params: id=123` |
|
||||
| INFO | Успешная операция | `User created: id=456` |
|
||||
| WARNING | Timeout, retry | `Yandex GPT timeout, retry 1/2` |
|
||||
| ERROR | Ошибка внешнего API | `GigaChat 500: Internal error` |
|
||||
| CRITICAL | Исчерпаны все retry | `All AI providers failed for idea 789` |
|
||||
|
||||
---
|
||||
|
||||
## Graceful Degradation
|
||||
|
||||
Когда внешний сервис недоступен:
|
||||
|
||||
1. **DB недоступна** → 503 Service Unavailable
|
||||
2. **Redis недоступен** → работаем без кэша (log WARNING)
|
||||
3. **AI провайдер недоступен** → возвращаем fallback результат
|
||||
4. **Celery недоступен** → выполняем задачу синхронно
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по обработке ошибок
|
||||
|
||||
- Нужны ли пользовательские исключения для всех бизнес-сценариев?
|
||||
- Нужен ли sentry или аналогичный мониторинг ошибок?
|
||||
- Как обрабатывать ошибки валидации на фронтенде?
|
||||
@@ -0,0 +1,78 @@
|
||||
# Логирование
|
||||
|
||||
---
|
||||
|
||||
## Формат строки лога
|
||||
|
||||
```
|
||||
[ISO8601] [LEVEL] [component] message key=val
|
||||
```
|
||||
|
||||
Пример:
|
||||
```
|
||||
2026-05-10T14:30:00.000Z INFO [auth] User logged in user_id=abc123
|
||||
2026-05-10T14:30:01.000Z WARNING [ai] Yandex GPT timeout retry=1 max_retries=2
|
||||
2026-05-10T14:30:02.000Z ERROR [sync] Sync failed for user_id=abc123 error="Connection refused"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Уровни по слоям
|
||||
|
||||
| Слой | DEBUG | INFO | WARNING | ERROR |
|
||||
|------|-------|------|---------|-------|
|
||||
| **API** | Параметры запроса | Request обработан | — | 5xx ошибки |
|
||||
| **Service** | Входные данные | Операция выполнена | Превышен лимит | Ошибка БД |
|
||||
| **Integration** | Raw ответ провайдера | Успешный вызов | Timeout, retry | Внешний API ошибка |
|
||||
| **Agent** | Checksum вычислен | Агент выполнен | Версия не совпала | Ошибка выполнения |
|
||||
|
||||
---
|
||||
|
||||
## Правила
|
||||
|
||||
- **Запрещены f-строки в logger.** Только %s (lazy evaluation):
|
||||
```python
|
||||
# ПЛОХО:
|
||||
logger.info(f"User {user_id} logged in")
|
||||
|
||||
# ХОРОШО:
|
||||
logger.info("User %s logged in", user_id)
|
||||
```
|
||||
|
||||
- **Структурированные данные** передавайте как extra:
|
||||
```python
|
||||
logger.info("Idea analyzed", extra={"idea_id": idea_id, "duration_ms": duration})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sensitive Data
|
||||
|
||||
**Никогда не логировать:**
|
||||
- Пароли (даже хэш)
|
||||
- JWT токены
|
||||
- API keys и секреты
|
||||
- Email в открытом виде (логировать user_id)
|
||||
- IP адреса полностью (маскировать: 195.208.*.*)
|
||||
|
||||
---
|
||||
|
||||
## Конфигурация
|
||||
|
||||
```python
|
||||
import logging
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s.%(msecs)03dZ %(levelname)s [%(name)s] %(message)s",
|
||||
datefmt="%Y-%m-%dT%H:%M:%S",
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по логированию
|
||||
|
||||
- Структурированное логирование (JSON) или текстовое? (JSON — для production)
|
||||
- Отправлять логи в централизованную систему? (рекомендуется для production)
|
||||
- Нужен ли audit log для операций с данными? (да, если регуляторные требования)
|
||||
@@ -0,0 +1,91 @@
|
||||
# Стандарты документации
|
||||
|
||||
---
|
||||
|
||||
## Docstrings
|
||||
|
||||
**Формат:** Google-style для всех публичных классов, функций, методов.
|
||||
|
||||
```python
|
||||
def calculate_roi(investment: float, return_value: float, years: int = 1) -> float:
|
||||
"""Calculate Return on Investment.
|
||||
|
||||
Args:
|
||||
investment: Initial investment amount
|
||||
return_value: Total return after period
|
||||
years: Investment period in years (default: 1)
|
||||
|
||||
Returns:
|
||||
ROI as a percentage (e.g., 150.0 for 150%)
|
||||
|
||||
Raises:
|
||||
ValueError: If investment is zero or negative
|
||||
"""
|
||||
if investment <= 0:
|
||||
raise ValueError("Investment must be positive")
|
||||
return ((return_value - investment) / investment) * 100
|
||||
```
|
||||
|
||||
### Когда писать docstrings
|
||||
- Всегда для публичных классов и методов
|
||||
- Для сложных приватных методов (более 10 строк)
|
||||
- Для модулей: краткое описание в начале файла
|
||||
|
||||
---
|
||||
|
||||
## TODO и FIXME
|
||||
|
||||
```python
|
||||
# TODO(#TASK-42): Реализовать rate limiting
|
||||
# FIXME(#BUG-7): Некорректный подсчёт при пустом списке
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## README.md
|
||||
|
||||
Каждая папка `app/*` должна содержать README.md с кратким описанием:
|
||||
- Назначение модуля
|
||||
- Ключевые классы/функции
|
||||
- Пример использования (если неочевидно)
|
||||
|
||||
---
|
||||
|
||||
## ADR (Architecture Decision Records)
|
||||
|
||||
Каждое архитектурное решение фиксируется в `docs/adr/NNN-title.md`.
|
||||
|
||||
ADR нужен когда:
|
||||
- Выбирается технология (БД, фреймворк, провайдер)
|
||||
- Меняется архитектура (новый слой, новый паттерн)
|
||||
- Принимается решение с долгосрочными последствиями
|
||||
|
||||
ADR не нужен когда:
|
||||
- Обычный багфикс
|
||||
- Косметические изменения
|
||||
- Выбор имени переменной
|
||||
|
||||
---
|
||||
|
||||
## CHANGELOG
|
||||
|
||||
CHANGELOG — это контракт с пользователем. Каждое изменение, влияющее на работу:
|
||||
|
||||
### Для пользователей:
|
||||
- Новые функции
|
||||
- Изменения API
|
||||
- Исправления багов
|
||||
- Изменения зависимостей
|
||||
|
||||
### Для разработчиков:
|
||||
- Рефакторинг (если влияет на API модуля)
|
||||
- Изменения конфигурации
|
||||
- Обновления БД
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по документации
|
||||
|
||||
- Генерировать документацию автоматически? (Sphinx, MkDocs — рекомендуется)
|
||||
- Нужна ли API документация для фронтенд-разработчиков? (да, OpenAPI доступен в /docs)
|
||||
- Какой формат для диаграмм? (Mermaid — рекомендуется, читается и человеком и ИИ)
|
||||
@@ -0,0 +1,66 @@
|
||||
# Управление зависимостями
|
||||
|
||||
---
|
||||
|
||||
## Формат
|
||||
|
||||
**Рекомендуемый:** `requirements.txt`
|
||||
|
||||
```
|
||||
# === Core ===
|
||||
fastapi==0.115.6
|
||||
uvicorn[standard]==0.34.0
|
||||
pydantic==2.10.3
|
||||
pydantic-settings==2.7.0
|
||||
|
||||
# === Database ===
|
||||
sqlalchemy[asyncio]==2.0.36
|
||||
aiosqlite==0.20.0 # dev (SQLite)
|
||||
asyncpg==0.30.0 # prod (PostgreSQL)
|
||||
alembic==1.14.1
|
||||
|
||||
# === Auth ===
|
||||
python-jose[cryptography]==3.3.0
|
||||
passlib[bcrypt]==1.7.4
|
||||
|
||||
# === AI ===
|
||||
httpx==0.28.1
|
||||
pyyaml==6.0.2
|
||||
|
||||
# === Tasks (опционально) ===
|
||||
celery==5.4.0
|
||||
redis==5.2.1
|
||||
|
||||
# === Dev ===
|
||||
pytest==8.3.4
|
||||
pytest-asyncio==0.24.0
|
||||
ruff==0.8.4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Правила обновления
|
||||
|
||||
| Тип | Когда | Проверка |
|
||||
|-----|-------|----------|
|
||||
| **patch** | В любой момент (bugfix, security) | CI passes |
|
||||
| **minor** | Не чаще 1 раза в спринт | Full regression |
|
||||
| **major** | Только с полным регрессом | + migration guide |
|
||||
|
||||
---
|
||||
|
||||
## Аудит зависимостей
|
||||
|
||||
Периодически проверять уязвимости:
|
||||
```bash
|
||||
pip-audit
|
||||
safety check
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по зависимостям
|
||||
|
||||
- `requirements.txt` или `pyproject.toml`? (pyproject.toml — современный стандарт)
|
||||
- `pip` или `poetry`/`uv`? (uv — быстрее, poetry — управление зависимостями)
|
||||
- Нужна ли заморозка версий (`pip freeze > requirements-lock.txt`)? (да, для production)
|
||||
@@ -0,0 +1,59 @@
|
||||
# Code Review
|
||||
|
||||
---
|
||||
|
||||
## Обязательность
|
||||
|
||||
- Все PR в `main` и `develop` проходят code review
|
||||
- Минимум 1 апрув от admin/owner
|
||||
|
||||
---
|
||||
|
||||
## Чеклист ревью
|
||||
|
||||
### Безопасность
|
||||
- [ ] Нет секретов, ключей, паролей в коде
|
||||
- [ ] Нет чувствительных данных в логах
|
||||
- [ ] Входные данные проходят Pydantic валидацию
|
||||
- [ ] Проверены права доступа (RBAC)
|
||||
|
||||
### Качество кода
|
||||
- [ ] Нет сырых Exception в API ответах (заменены на HTTPException)
|
||||
- [ ] Есть обработка ошибок для внешних вызовов (try/except)
|
||||
- [ ] Docstrings написаны (Google-style)
|
||||
- [ ] Аннотации типов проставлены
|
||||
- [ ] Ruff проходит (0 errors)
|
||||
- [ ] mypy проходит (0 errors)
|
||||
|
||||
### Тесты
|
||||
- [ ] Есть тесты на новую функциональность
|
||||
- [ ] Есть smoke-тест на новые endpoint'ы
|
||||
- [ ] Тесты проходят
|
||||
|
||||
### Документация
|
||||
- [ ] .env.example обновлён (если новая переменная)
|
||||
- [ ] CHANGELOG обновлён
|
||||
- [ ] ADR создан (если архитектурное изменение)
|
||||
|
||||
---
|
||||
|
||||
## Как писать комментарии
|
||||
|
||||
```markdown
|
||||
**Вопрос:** Зачем здесь этот блок? Кажется неиспользуемым.
|
||||
— Я бы предложил вынести в отдельный метод.
|
||||
|
||||
**Предложение:** Этот фрагмент дублируется в 3 местах.
|
||||
— Давай вынесем в общий хелпер в core/utils.py.
|
||||
|
||||
**Замечание (блокирующее):** Здесь пароль попадает в лог.
|
||||
— Нужно убрать логирование password. См. §11 Sensitive Data Policy.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по ревью
|
||||
|
||||
- Использовать GitHub Code Owners? (рекомендуется для больших команд)
|
||||
- Добавить авто-ревью (агент)? (рекомендуется: AuditAgent проверяет базовые правила)
|
||||
- Сколько максимум строк на PR? (рекомендуется < 500 строк)
|
||||
@@ -0,0 +1,84 @@
|
||||
# Git Flow
|
||||
|
||||
---
|
||||
|
||||
## Ветки
|
||||
|
||||
```
|
||||
main # Стабильная, production-ready
|
||||
develop # Интеграция фич
|
||||
feature/* # Новая функция (ветвится от develop)
|
||||
hotfix/* # Срочное исправление (ветвится от main)
|
||||
release/* # Подготовка релиза (ветвится от develop)
|
||||
```
|
||||
|
||||
### Когда какую ветку использовать
|
||||
|
||||
| Ситуация | Ветка | Цель |
|
||||
|----------|-------|------|
|
||||
| Начало работы над фичой | `feature/idea-analysis` | develop |
|
||||
| Исправление бага в production | `hotfix/crash-on-empty` | main |
|
||||
| Подготовка релиза | `release/1.2.0` | main |
|
||||
| Эксперимент | `experiment/new-auth` | — |
|
||||
|
||||
---
|
||||
|
||||
## Conventional Commits
|
||||
|
||||
```
|
||||
<тип>[optional scope]: <описание>
|
||||
|
||||
[optional body]
|
||||
[optional footer]
|
||||
```
|
||||
|
||||
### Типы
|
||||
|
||||
| Тип | Пример | Влияние на версию |
|
||||
|-----|--------|-------------------|
|
||||
| `feat` | `feat: add AI analysis endpoint` | MINOR |
|
||||
| `fix` | `fix: handle empty idea list` | PATCH |
|
||||
| `BREAKING` | `feat!: change API response format` | MAJOR |
|
||||
| `docs` | `docs: update README` | — |
|
||||
| `refactor` | `refactor: extract IdeaService` | — |
|
||||
| `test` | `test: add smoke tests for auth` | — |
|
||||
| `chore` | `chore: update dependencies` | — |
|
||||
|
||||
### Примеры
|
||||
|
||||
```
|
||||
feat(api): add POST /ideas/{id}/analyze endpoint
|
||||
|
||||
- Celery task for async analysis
|
||||
- Fallback to direct call if Celery unavailable
|
||||
- Store results in AgentReport table
|
||||
|
||||
Closes #42
|
||||
```
|
||||
|
||||
```
|
||||
fix: validate email format on registration
|
||||
|
||||
BREAKING: removed support for dotless emails
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commit Message
|
||||
|
||||
```
|
||||
50 символов: краткое описание (императив, без точки)
|
||||
|
||||
72 символа: тело коммита при необходимости.
|
||||
Можно писать несколько строк.
|
||||
- Каждый пункт с дефиса
|
||||
- Описываем ЧТО и ЗАЧЕМ, а не КАК
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по Git
|
||||
|
||||
- Git Flow или GitHub Flow? (GitHub Flow проще: main + feature/* + PR)
|
||||
- Нужны ли релизные ветки? (да, если несколько версий в поддержке)
|
||||
- Squash при merge? (рекомендуется: 1 PR = 1 коммит в develop)
|
||||
@@ -0,0 +1,31 @@
|
||||
# Политика хранения данных
|
||||
|
||||
---
|
||||
|
||||
## Сроки хранения
|
||||
|
||||
| Тип данных | Срок | Причина |
|
||||
|-----------|------|---------|
|
||||
| System Logs | 90 дней | Отладка, аудит |
|
||||
| Security Events | 1 год | Регуляторные требования |
|
||||
| User Data | До удаления + 30 дней | Возможность восстановления |
|
||||
| Session (JWT) | 24 часа | Безопасность |
|
||||
| AI Analysis Results | 90 дней | История анализа |
|
||||
| Agent Reports | 180 дней | Саморазвитие агентов |
|
||||
| Backlog Tasks | 1 год | Планирование |
|
||||
| Notifications | 30 дней | Актуальность |
|
||||
|
||||
---
|
||||
|
||||
## Удаление данных
|
||||
|
||||
**Hard delete:** для временных данных (логи, сессии)
|
||||
**Soft delete:** для пользовательских данных (is_active = False)
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по хранению
|
||||
|
||||
- Какие регуляторные требования применимы? (152-ФЗ, GDPR, CCPA)
|
||||
- Нужна ли архивация вместо удаления? (рекомендуется для audit trail)
|
||||
- Как часто чистить старые данные? (cron раз в день)
|
||||
@@ -0,0 +1,48 @@
|
||||
# Политика миграций БД
|
||||
|
||||
---
|
||||
|
||||
## Инструмент: Alembic
|
||||
|
||||
Миграции управляются через Alembic.
|
||||
|
||||
```bash
|
||||
# Создать миграцию
|
||||
alembic revision --autogenerate -m "add_users_table"
|
||||
|
||||
# Применить
|
||||
alembic upgrade head
|
||||
|
||||
# Откатить
|
||||
alembic downgrade -1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Правила
|
||||
|
||||
1. **Одна миграция на одно изменение.** Не смешивать разные изменения в одной миграции.
|
||||
2. **Обратная совместимость.** Миграция должна иметь downgrade.
|
||||
3. **Тестирование.** Каждая миграция тестируется (upgrade + downgrade).
|
||||
4. **Именование:** `{revision}_{action}_{table}.py`
|
||||
- `a1b2c3d4e5f6_add_content_to_ideas.py`
|
||||
|
||||
---
|
||||
|
||||
## Названия миграций
|
||||
|
||||
```
|
||||
create_{table}
|
||||
add_{column}_to_{table}
|
||||
remove_{column}_from_{table}
|
||||
add_index_on_{table}_{column}
|
||||
add_fk_{table}_{column}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по миграциям
|
||||
|
||||
- Автоматические миграции на production? (не рекомендуется — только через CI после проверки)
|
||||
- Data migration (перенос данных) vs schema migration? (data migration = отдельный скрипт)
|
||||
- Как бекапить БД перед миграцией? (pg_dump / sqlite3 .backup)
|
||||
@@ -0,0 +1,42 @@
|
||||
# Жизненный цикл API
|
||||
|
||||
---
|
||||
|
||||
## Версионирование
|
||||
|
||||
API версионируется через URL:
|
||||
|
||||
```
|
||||
/api/v1/ideas # Текущая стабильная
|
||||
/api/v2/ideas # Будущая версия
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Жизненный цикл
|
||||
|
||||
```
|
||||
Стабильная (v1) → Deprecation → 410 Gone
|
||||
```
|
||||
|
||||
| Фаза | Длительность | Действие |
|
||||
|------|-------------|----------|
|
||||
| **Стабильная** | Неопределённо | Полная поддержка |
|
||||
| **Deprecation** | 3 месяца после выхода v2 | WARNING в заголовке `Sunset: ...` |
|
||||
| **Gone** | — | HTTP 410 Gone |
|
||||
|
||||
---
|
||||
|
||||
## Поддержка
|
||||
|
||||
- Одновременно поддерживаются **не более 2 версий**
|
||||
- Новая версия = новый префикс (`/api/v2/`)
|
||||
- Старая версия продолжает работать 3 месяца
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по API
|
||||
|
||||
- Сколько версий поддерживать одновременно? (рекомендуется 2: текущая + предыдущая)
|
||||
- Нужна ли HATEOAS? (нет, если фронтенд отдельно)
|
||||
- Как документировать breaking changes? (CHANGELOG + ADR)
|
||||
@@ -0,0 +1,143 @@
|
||||
# Саморазвитие и эволюция проекта
|
||||
|
||||
---
|
||||
|
||||
## Зачем проекту саморазвитие
|
||||
|
||||
Проект, который не развивается, умирает. Но развитие требует ресурсов, которых у команды может не быть. Решение: **агенты автоматизируют развитие.**
|
||||
|
||||
1. **Проект живёт дольше команды** — агенты продолжают работу независимо
|
||||
2. **Автоматизация рутины** — тесты, документация, ревью
|
||||
3. **Адаптация** — проект сам подстраивается под новые требования
|
||||
|
||||
---
|
||||
|
||||
## Три уровня саморазвития
|
||||
|
||||
### Level 1: Reactive (базовый)
|
||||
Агенты реагируют на события:
|
||||
- Pre-commit: AuditAgent проверяет правила
|
||||
- Push: SecurityAgent проверяет зависимости
|
||||
- Cron: DocAgent обновляет документацию
|
||||
|
||||
**Начинаем с этого уровня.**
|
||||
|
||||
### Level 2: Proactive (целевой)
|
||||
Агенты предлагают улучшения:
|
||||
- EvolutionAgent анализирует код и предлагает рефакторинг
|
||||
- ObserverAgent собирает метрики и предлагает оптимизацию
|
||||
- FixAgent анализирует ошибки и предлагает исправления
|
||||
|
||||
**Достигаем к Stage 3 (см. migration-path.md).**
|
||||
|
||||
### Level 3: Autonomous (будущее)
|
||||
Агенты принимают решения:
|
||||
- Self-healing: авто-откат при росте ошибок
|
||||
- Auto-versioning: автоматический бамп версий
|
||||
- Auto-scaling: масштабирование под нагрузку
|
||||
|
||||
---
|
||||
|
||||
## Ядро агентов (создаются с первого коммита)
|
||||
|
||||
4 агента, которые должны жить в проекте всегда:
|
||||
|
||||
| Агент | Роль | Триггеры | Без него |
|
||||
|-------|------|----------|----------|
|
||||
| **DocAgent** | Пишет документацию | pre-commit, manual | Документация пишется "потом" → никогда |
|
||||
| **AuditAgent** | Проверяет правила | pre-commit, push, cron | Правила не применяются |
|
||||
| **EvolutionAgent** | Версионирует агентов | cron, event, manual | Агенты не эволюционируют |
|
||||
| **SupervisorAgent** | Следит за всеми агентами | cron, event, manual | Экосистема не контролируется |
|
||||
|
||||
### Подробнее о каждом
|
||||
|
||||
**DocAgent:**
|
||||
- При каждом коммите проверяет, что документация соответствует коду
|
||||
- Если находит недокументированный публичный метод — добавляет docstring
|
||||
- Обновляет ADR при архитектурных изменениях
|
||||
|
||||
**AuditAgent:**
|
||||
- Проверяет каждый коммит на соответствие `docs/00-rules.md`
|
||||
- Проверяет: стиль кода, наличие тестов, docstrings, .env.example
|
||||
- Пишет отчёт о нарушениях
|
||||
|
||||
**EvolutionAgent:**
|
||||
- Отслеживает версии всех агентов
|
||||
- При изменении checksum агента — бампит версию
|
||||
- При добавлении новой capability — бампит minor
|
||||
- При breaking change — бампит major
|
||||
|
||||
**SupervisorAgent:**
|
||||
- Регулярно проверяет health всех агентов
|
||||
- Собирает метрики выполнения (длительность, успешность)
|
||||
- При падении агента — перезапускает или шлёт алерт
|
||||
- Формирует сводный отчёт о состоянии экосистемы
|
||||
|
||||
---
|
||||
|
||||
## Расширение агентов
|
||||
|
||||
По мере роста проекта добавляются:
|
||||
|
||||
| Агент | Когда | Зачем |
|
||||
|-------|-------|-------|
|
||||
| QATesterAgent | Появились тесты | Поддерживать качество тестов |
|
||||
| FixAgent | Пойман первый баг | Анализировать и исправлять |
|
||||
| BacklogAgent | Появился техдолг | Управлять задачами |
|
||||
| SecurityAgent | Перед production | Проверять безопасность |
|
||||
| SpecAgent | Перед релизом | Управлять версией |
|
||||
| RolloutAgent | Перед деплоем | Постепенный rollout |
|
||||
| ObserverAgent | После запуска | Собирать метрики |
|
||||
| UITestAgent | Есть UI | Визуальное тестирование |
|
||||
|
||||
---
|
||||
|
||||
## Agent Versioning
|
||||
|
||||
Каждый агент версионируется независимо по A.B.C.
|
||||
|
||||
**Почему независимо:** агенты изменяются с разной скоростью. DocAgent может меняться каждый день, а SecurityAgent — раз в месяц.
|
||||
|
||||
**Как работает:**
|
||||
1. Агент запускается → вычисляет SHA256 своего файла (`compute_checksum()`)
|
||||
2. Сравнивает с хранимым checksum
|
||||
3. Если не совпал → авто-бамп patch + запись в changelog
|
||||
4. EvolutionAgent анализирует изменения и решает: это minor (новая capability) или major (breaking change)?
|
||||
|
||||
**Хранение:** `CHANGELOG/agents/<name>.md`
|
||||
```markdown
|
||||
# doc_agent Changelog
|
||||
<!-- checksum: a1b2c3d4e5f6... -->
|
||||
|
||||
## 1.2.0 (2026-05-10)
|
||||
- Added: поддержка YAML-формата для промптов
|
||||
|
||||
## 1.1.3 (2026-05-09)
|
||||
- Fixed: обработка пустых docstrings
|
||||
|
||||
## 1.0.0 (2026-05-01)
|
||||
- Initial version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Триггеры запуска агентов
|
||||
|
||||
| Триггер | Когда | Какие агенты |
|
||||
|---------|-------|-------------|
|
||||
| `pre_commit` | Перед каждым коммитом | AuditAgent, DocAgent |
|
||||
| `push` | При пуше в remote | SecurityAgent, BacklogAgent, SpecAgent |
|
||||
| `tag_creation` | При создании git-тега | RolloutAgent, SpecAgent |
|
||||
| `cron` | По расписанию (daily) | EvolutionAgent, SupervisorAgent, ObserverAgent |
|
||||
| `manual` | Вручную из админки | Любой |
|
||||
| `api` | Через API | Любой |
|
||||
| `event` | При событии (ошибка, деплой) | FixAgent, RolloutAgent |
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по саморазвитию
|
||||
|
||||
- Сколько агентов нужно на старте? (рекомендация: 4 ядерных, остальные по необходимости)
|
||||
- Как часто запускать EvolutionAgent? (рекомендация: ежедневно по cron)
|
||||
- Кто пишет агентов? (рекомендация: команда, начиная с самого простого — DocAgent)
|
||||
- Нужен ли SupervisorAgent на старте? (да — замкнутый круг: агенты без контроля = хаос)
|
||||
@@ -0,0 +1,50 @@
|
||||
# ADR-{NNN}: {Название решения}
|
||||
|
||||
**Статус:** {черновик | принято | отклонено | заменено}
|
||||
**Дата:** {YYYY-MM-DD}
|
||||
|
||||
---
|
||||
|
||||
## Контекст
|
||||
|
||||
{Опишите проблему: что заставило принять решение, какие требования, какая мотивация}
|
||||
|
||||
## Рассматривались
|
||||
|
||||
- **Вариант А**: {описание}
|
||||
- Плюсы: {список}
|
||||
- Минусы: {список}
|
||||
- **Вариант Б**: {описание}
|
||||
- Плюсы: {список}
|
||||
- Минусы: {список}
|
||||
- **Вариант В** (если есть): {описание}
|
||||
- Плюсы: {список}
|
||||
- Минусы: {список}
|
||||
|
||||
## Решение
|
||||
|
||||
{Выбранный вариант} — {краткое обоснование почему}
|
||||
|
||||
## Последствия
|
||||
|
||||
### Положительные
|
||||
- {пункт}
|
||||
- {пункт}
|
||||
|
||||
### Отрицательные
|
||||
- {пункт}
|
||||
- {пункт}
|
||||
|
||||
### Миграция
|
||||
{Что нужно сделать чтобы перейти на это решение}
|
||||
|
||||
## Ответственный
|
||||
|
||||
**Decision maker:** {роль: Owner / Architect / Team}
|
||||
**Review date:** {когда пересмотреть: дата или условие}
|
||||
|
||||
---
|
||||
|
||||
## Связанные ADR
|
||||
|
||||
- ADR-{NNN}: {Название}
|
||||
@@ -0,0 +1,64 @@
|
||||
# Управление промптами агентов
|
||||
|
||||
---
|
||||
|
||||
## Принцип
|
||||
|
||||
Промпты — это код. Они версионируются, хранятся в репозитории и проходят code review.
|
||||
Никаких hardcoded промптов в Python-коде.
|
||||
|
||||
---
|
||||
|
||||
## Где хранить
|
||||
|
||||
### Вариант A: YAML (рекомендован)
|
||||
`docs/agent_prompts.yaml`
|
||||
|
||||
```yaml
|
||||
coordinator:
|
||||
system_prompt: "Ты — координатор. Твоя задача..."
|
||||
provider: yandex_gpt
|
||||
temperature: 0.7
|
||||
max_tokens: 2000
|
||||
```
|
||||
|
||||
**Плюсы:** Простота редактирования, структурированность, легко парсить.
|
||||
**Минусы:** Сложные промпты с примерами неудобно читать в YAML.
|
||||
|
||||
### Вариант B: Markdown
|
||||
`docs/specs/agents/coordinator.md`
|
||||
|
||||
```markdown
|
||||
## Prompt Template
|
||||
```
|
||||
Ты — координатор. Твоя задача...
|
||||
```
|
||||
```
|
||||
|
||||
**Плюсы:** Читаемость, поддержка форматирования, примеры.
|
||||
**Минусы:** Сложнее парсить, нет структуры.
|
||||
|
||||
### Рекомендация
|
||||
**YAML для настроек + MD для детальных спецификаций.**
|
||||
`PromptLoader` пробует YAML, если не нашёл — падает на MD.
|
||||
|
||||
---
|
||||
|
||||
## Структура YAML
|
||||
|
||||
```yaml
|
||||
coordinator:
|
||||
system_prompt: "текст промпта"
|
||||
provider: "yandex_gpt" # какой провайдер
|
||||
temperature: 0.7 # креативность (0.0-1.0)
|
||||
max_tokens: 2000 # макс. длина ответа
|
||||
model: "yandexgpt/latest" # конкретная модель (опционально)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK]
|
||||
|
||||
- Какой формат выбрать? (рекомендация: YAML для быстрых промптов, MD для сложных)
|
||||
- Нужна ли валидация промптов? (да, проверять что все placeholder'ы заполнены)
|
||||
- Кто редактирует промпты? (разработчики + AI-агенты через EvolutionAgent)
|
||||
@@ -0,0 +1,61 @@
|
||||
# Паттерны промптов
|
||||
|
||||
---
|
||||
|
||||
## 1. System + User разделение
|
||||
|
||||
```python
|
||||
system_prompt = "Ты — бизнес-аналитик. Анализируй идеи."
|
||||
user_prompt = f"Название: {idea.title}\nОписание: {idea.content}"
|
||||
|
||||
# Формирование:
|
||||
full_prompt = f"{system_prompt}\n\n{user_prompt}"
|
||||
```
|
||||
|
||||
**Используется:** AIProvider.format_prompt()
|
||||
|
||||
---
|
||||
|
||||
## 2. Structured output
|
||||
|
||||
```python
|
||||
system_prompt = """
|
||||
Ты — финансовый консультант.
|
||||
Ответ верни ТОЛЬКО в формате JSON:
|
||||
{
|
||||
"roi": число,
|
||||
"risk_level": "low|medium|high",
|
||||
"recommendations": [строка, ...]
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Few-shot (примеры)
|
||||
|
||||
```python
|
||||
system_prompt = """
|
||||
Ты — UI-дизайнер. Анализируй интерфейс.
|
||||
|
||||
Пример хорошего анализа:
|
||||
Интерфейс: Экран входа
|
||||
Проблема: Кнопка "Забыли пароль" не видна
|
||||
Решение: Переместить под форму входа
|
||||
Рекомендация: Высокий приоритет
|
||||
|
||||
Теперь проанализируй:
|
||||
"""
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Параметры
|
||||
|
||||
| Параметр | Значение | Когда менять |
|
||||
|----------|----------|-------------|
|
||||
| `temperature: 0.1-0.3` | Низкая креативность | Юридические, финансовые промпты |
|
||||
| `temperature: 0.5-0.7` | Средняя | Стандартный анализ |
|
||||
| `temperature: 0.8-1.0` | Высокая | Мозговой штурм, креатив |
|
||||
| `max_tokens: 500` | Короткий ответ | Классификация |
|
||||
| `max_tokens: 4000` | Длинный ответ | Детальный анализ |
|
||||
@@ -0,0 +1,103 @@
|
||||
# Хранение и загрузка промптов
|
||||
|
||||
---
|
||||
|
||||
## Загрузчик (PromptLoader)
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
import yaml, re
|
||||
|
||||
AGENT_SPECS_DIR = Path("docs/specs/agents")
|
||||
AGENT_PROMPTS_YAML = Path("docs/agent_prompts.yaml")
|
||||
|
||||
def get_prompt_config(role: str) -> dict | None:
|
||||
"""Get prompt config for a role."""
|
||||
# 1. Пробуем YAML
|
||||
config = _load_from_yaml(role)
|
||||
if config:
|
||||
return config
|
||||
# 2. Пробуем MD
|
||||
return _load_from_spec(role)
|
||||
|
||||
def _load_from_yaml(role: str) -> dict | None:
|
||||
"""Load from docs/agent_prompts.yaml."""
|
||||
if not AGENT_PROMPTS_YAML.exists():
|
||||
return None
|
||||
data = yaml.safe_load(AGENT_PROMPTS_YAML.read_text(encoding="utf-8"))
|
||||
return data.get(role) if data else None
|
||||
|
||||
def _load_from_spec(role: str) -> dict | None:
|
||||
"""Load from docs/specs/agents/<role>.md."""
|
||||
spec_path = AGENT_SPECS_DIR / f"{role}.md"
|
||||
if not spec_path.exists():
|
||||
return None
|
||||
content = spec_path.read_text(encoding="utf-8")
|
||||
match = re.search(r"## Prompt Template\n+```\n(.+?)\n```", content, re.DOTALL)
|
||||
if not match:
|
||||
return None
|
||||
return {
|
||||
"system_prompt": match.group(1).strip(),
|
||||
"provider": "yandex_gpt",
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 2000,
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Пример YAML-файла
|
||||
|
||||
`docs/agent_prompts.yaml`
|
||||
|
||||
```yaml
|
||||
coordinator:
|
||||
system_prompt: "Ты — координатор..."
|
||||
provider: yandex_gpt
|
||||
temperature: 0.7
|
||||
max_tokens: 2000
|
||||
|
||||
business_analyst:
|
||||
system_prompt: "Ты — бизнес-аналитик..."
|
||||
provider: yandex_gpt
|
||||
temperature: 0.5
|
||||
max_tokens: 3000
|
||||
|
||||
legal_expert:
|
||||
system_prompt: "Ты — юрист..."
|
||||
provider: gigachat # Для юридических вопросов
|
||||
temperature: 0.3
|
||||
max_tokens: 3000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Пример MD-файла
|
||||
|
||||
`docs/specs/agents/business_analyst.md`
|
||||
|
||||
```markdown
|
||||
# Бизнес-аналитик
|
||||
|
||||
**Провайдер:** Yandex GPT
|
||||
**Температура:** 0.5
|
||||
**Макс. токенов:** 3000
|
||||
|
||||
## Prompt Template
|
||||
```
|
||||
Ты — бизнес-аналитик.
|
||||
Проанализируй идею и оцени:
|
||||
1. Целевую аудиторию
|
||||
2. ROI
|
||||
3. Сроки реализации
|
||||
...
|
||||
```
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK]
|
||||
|
||||
- Какой формат использовать по умолчанию? (рекомендация: YAML + MD fallback)
|
||||
- Нужна ли валидация placeholder'ов в промптах? (да, {...} должны быть заменены)
|
||||
- Нужна ли версионирование промптов? (да, через git — каждый промпт MD/YAML файл)
|
||||
@@ -0,0 +1,19 @@
|
||||
# {Role Name}
|
||||
|
||||
**Провайдер:** {yandex_gpt | gigachat}
|
||||
**Температура:** {0.1-1.0}
|
||||
**Макс. токенов:** {500-4000}
|
||||
|
||||
## Описание
|
||||
|
||||
{Краткое описание роли AI-агента. Что делает, какие вопросы решает.}
|
||||
|
||||
## Prompt Template
|
||||
|
||||
```text
|
||||
Ты — {role_name}. {описание}.
|
||||
|
||||
{инструкции}
|
||||
|
||||
{формат ответа}
|
||||
```
|
||||
@@ -0,0 +1,14 @@
|
||||
# Шаблон промпта в YAML
|
||||
# Используйте как основу для нового AI-агента
|
||||
|
||||
role_name:
|
||||
system_prompt: |
|
||||
Ты — {role_name}. {описание роли}.
|
||||
|
||||
{инструкции}
|
||||
|
||||
{формат ответа}
|
||||
provider: yandex_gpt # или gigachat
|
||||
temperature: 0.7 # 0.1-1.0
|
||||
max_tokens: 2000 # макс. длина
|
||||
model: "" # опционально: конкретная модель
|
||||
@@ -0,0 +1,61 @@
|
||||
# Обзор системных агентов
|
||||
|
||||
---
|
||||
|
||||
## Что такое системный агент
|
||||
|
||||
Системный агент — это программа, которая автоматизирует поддержку и развитие проекта.
|
||||
В отличие от AI-агента (который анализирует пользовательские данные), системный агент работает **над проектом**: пишет документацию, проверяет правила, версионирует код.
|
||||
|
||||
---
|
||||
|
||||
## Когда внедрять агентов
|
||||
|
||||
**С первого коммита.** 4 ядерных агента создаются сразу.
|
||||
Остальные — по мере возникновения потребности.
|
||||
|
||||
---
|
||||
|
||||
## Отличие системного агента от AI-агента
|
||||
|
||||
| Характеристика | Системный агент | AI-агент |
|
||||
|---------------|-----------------|-----------|
|
||||
| Что делает | Поддерживает проект | Анализирует данные пользователя |
|
||||
| Кто запускает | Триггеры (pre-commit, cron) | Пользователь (через UI) |
|
||||
| Результат | Чистый код, docs, версии | Анализ идеи, рекомендации |
|
||||
| Пример | DocAgent пишет docstrings | Координатор анализирует идею |
|
||||
| Версионируется | Да (A.B.C независимо) | Нет |
|
||||
|
||||
---
|
||||
|
||||
## Ядро (4 агента, обязательны)
|
||||
|
||||
| # | Агент | Роль | Триггеры |
|
||||
|---|-------|------|----------|
|
||||
| 1 | **DocAgent** | Пишет документацию | pre-commit, manual |
|
||||
| 2 | **AuditAgent** | Проверяет правила | pre-commit, push, cron |
|
||||
| 3 | **EvolutionAgent** | Версионирует агентов | cron, event, manual |
|
||||
| 4 | **SupervisorAgent** | Следит за всеми агентами | cron, event, manual |
|
||||
|
||||
---
|
||||
|
||||
## Расширение (по необходимости)
|
||||
|
||||
| # | Агент | Когда добавлять |
|
||||
|---|-------|----------------|
|
||||
| 5 | **QATesterAgent** | Появились тесты |
|
||||
| 6 | **FixAgent** | Пойман первый баг |
|
||||
| 7 | **BacklogAgent** | Появился техдолг |
|
||||
| 8 | **SecurityAgent** | Перед production |
|
||||
| 9 | **SpecAgent** | Перед релизом |
|
||||
| 10 | **RolloutAgent** | Перед деплоем |
|
||||
| 11 | **ObserverAgent** | После запуска |
|
||||
| 12 | **UITestAgent** | Есть UI |
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по агентам
|
||||
|
||||
- Сколько агентов нужно сейчас? (рекомендация: 4 ядерных, потом по необходимости)
|
||||
- Есть ли ресурс на разработку агентов? (DocAgent ≈ 2 часа, AuditAgent ≈ 4 часа)
|
||||
- Кто будет поддерживать агентов? (те же разработчики)
|
||||
@@ -0,0 +1,107 @@
|
||||
# Архитектура агентов
|
||||
|
||||
---
|
||||
|
||||
## BaseAgent
|
||||
|
||||
Все агенты наследуются от `BaseAgent`:
|
||||
|
||||
```python
|
||||
class BaseAgent(ABC):
|
||||
name: str # Уникальное имя агента
|
||||
version: str = "1.0.0" # Текущая версия
|
||||
description: str = "" # Описание для registry
|
||||
triggers: list[AgentTrigger] # Когда запускается
|
||||
|
||||
async def run(self, context: dict | None = None) -> AgentResult:
|
||||
"""Выполнить задачу агента."""
|
||||
|
||||
async def health_check(self) -> bool:
|
||||
"""Проверить что агент работоспособен."""
|
||||
|
||||
def compute_checksum(self) -> str:
|
||||
"""SHA256 от __file__ агента."""
|
||||
|
||||
def bump_version(self, version_type: str = "patch") -> str:
|
||||
"""Увеличить версию (major/minor/patch)."""
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Жизненный цикл
|
||||
|
||||
```
|
||||
IDLE → RUNNING → [DONE | ERROR] → IDLE
|
||||
↘ OFFLINE
|
||||
```
|
||||
|
||||
1. Агент запускается (триггер или вручную)
|
||||
2. Статус → RUNNING
|
||||
3. Выполняется `run(context)`
|
||||
4. Статус → IDLE (успех) или ERROR (ошибка)
|
||||
5. Результат сохраняется в AgentReport
|
||||
|
||||
---
|
||||
|
||||
## AgentResult
|
||||
|
||||
```python
|
||||
class AgentResult:
|
||||
success: bool # Успешно ли выполнен
|
||||
message: str # Сообщение для лога
|
||||
data: dict[str, Any] # Произвольные данные результата
|
||||
errors: list[str] # Список ошибок
|
||||
duration_ms: int # Время выполнения
|
||||
timestamp: datetime # Когда выполнен
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AgentRegistry
|
||||
|
||||
Регистрация всех агентов в едином реестре:
|
||||
|
||||
```python
|
||||
class AgentRegistry:
|
||||
def register(self, agent: BaseAgent): ...
|
||||
def get(self, name: str) -> BaseAgent | None: ...
|
||||
def list_agents(self) -> list[dict]: ...
|
||||
async def run_agent(self, name: str, context=None) -> AgentResult: ...
|
||||
async def run_all(self, context=None) -> dict[str, AgentResult]: ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Триггеры
|
||||
|
||||
| Триггер | Когда | Пример |
|
||||
|---------|-------|--------|
|
||||
| `MANUAL` | Вручную из админки | Запуск DocAgent |
|
||||
| `PRE_COMMIT` | Перед git commit | AuditAgent проверяет правила |
|
||||
| `PUSH` | git push | SecurityAgent проверяет зависимости |
|
||||
| `TAG_CREATION` | git tag | SpecAgent обновляет CHANGELOG |
|
||||
| `CRON` | По расписанию | EvolutionAgent ежедневный анализ |
|
||||
| `API` | Через API-endpoint | Запуск из админ-панели |
|
||||
| `EVENT` | Событие в системе | FixAgent при ошибке |
|
||||
|
||||
---
|
||||
|
||||
## Хранение промптов
|
||||
|
||||
Промпты агентов хранятся в `docs/agent_prompts.yaml` или в отдельных MD-файлах в `docs/specs/agents/`.
|
||||
|
||||
Загрузка через `PromptLoader`:
|
||||
```python
|
||||
def get_prompt_config(role: str) -> dict | None:
|
||||
# 1. Попробовать YAML (docs/agent_prompts.yaml)
|
||||
# 2. Не найдено → загрузить из MD (docs/specs/agents/<role>.md)
|
||||
# 3. Не найдено → None
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по архитектуре
|
||||
|
||||
- Нужен ли AgentRegistry? (да, обязателен для SupervisorAgent)
|
||||
- Хранить состояние агентов в БД или в памяти? (в БД для отказоустойчивости)
|
||||
- Как передавать контекст агенту? (через `context: dict` — гибко, но без типизации)
|
||||
@@ -0,0 +1,81 @@
|
||||
# Версионирование агентов
|
||||
|
||||
---
|
||||
|
||||
## Принцип
|
||||
|
||||
Каждый агент версионируется **независимо** от проекта и от других агентов по A.B.C (SemVer).
|
||||
|
||||
---
|
||||
|
||||
## Правила бампа
|
||||
|
||||
| Компонент | Когда | Кто |
|
||||
|-----------|-------|-----|
|
||||
| **A (major)** | Breaking change в публичном интерфейсе (сигнатура `run()`, публичные методы) | EvolutionAgent |
|
||||
| **B (minor)** | Новая capability (новый метод, новый prompt, новая роль) | EvolutionAgent |
|
||||
| **C (patch)** | Внутренние правки без изменения поведения | Сам агент (авто) |
|
||||
|
||||
---
|
||||
|
||||
## Механика
|
||||
|
||||
```
|
||||
Каждый Agent.run()
|
||||
→ compute_checksum() — SHA256 от __file__ агента
|
||||
→ сравнивает с AgentConfig.checksum в БД
|
||||
→ не совпал → bump_version("patch") → запись в changelog → обновление БД
|
||||
→ совпал → ничего
|
||||
|
||||
EvolutionAgent
|
||||
→ анализирует код агента
|
||||
→ нашёл новую capability → bump_version("minor")
|
||||
→ нашёл breaking change → bump_version("major")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Хранение checksum
|
||||
|
||||
Checksum хранится в двух местах:
|
||||
1. **В БД** (`AgentConfig.checksum`) — для быстрого сравнения
|
||||
2. **В changelog файле** (`<!-- checksum: ... -->`) — для git history
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
Файл: `CHANGELOG/agents/<agent_name>.md`
|
||||
|
||||
```markdown
|
||||
# audit_agent Changelog
|
||||
<!-- checksum: a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890 -->
|
||||
|
||||
## 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.yaml`, `CHANGELOG/v*.md` |
|
||||
| Версия агента | EvolutionAgent | `AgentConfig.version` (БД) |
|
||||
| Changelog проекта | SpecAgent / человек | `CHANGELOG/v*.md` |
|
||||
| Changelog агента | EvolutionAgent | `CHANGELOG/agents/<name>.md` |
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по версионированию
|
||||
|
||||
- Версионировать агентов с первого коммита? (да — привычка, потом не внедрить)
|
||||
- Допустим ли ручной бамп версии? (да, EvolutionAgent — автоматизация, но человек может и вручную)
|
||||
- Нужна ли блокировка бампа (если checksum не совпал — не запускать)? (нет, только предупреждение)
|
||||
@@ -0,0 +1,34 @@
|
||||
# Шаблон changelog агента
|
||||
|
||||
Используйте для инициализации changelog нового агента.
|
||||
|
||||
Формат файла: `CHANGELOG/agents/{agent_name}.md`
|
||||
|
||||
```markdown
|
||||
# {agent_name} Changelog
|
||||
<!-- checksum: {sha256_hash} -->
|
||||
|
||||
## 1.0.0 ({date})
|
||||
- Initial version
|
||||
```
|
||||
|
||||
## Пример
|
||||
|
||||
```markdown
|
||||
# doc_agent Changelog
|
||||
<!-- checksum: a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890 -->
|
||||
|
||||
## 1.2.1 (2026-05-11)
|
||||
- Fixed: update README on file rename
|
||||
- Fixed: handle empty docstrings gracefully
|
||||
|
||||
## 1.2.0 (2026-05-10)
|
||||
- Added: YAML prompt loading support
|
||||
- Added: cross-reference validation
|
||||
|
||||
## 1.1.0 (2026-05-09)
|
||||
- Added: auto-generate README for new modules
|
||||
|
||||
## 1.0.0 (2026-05-01)
|
||||
- Initial version
|
||||
```
|
||||
@@ -0,0 +1,69 @@
|
||||
# Шаблон кода агента
|
||||
|
||||
Используйте этот шаблон для создания нового системного агента.
|
||||
|
||||
```python
|
||||
"""Agent: {name} — {description}."""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
|
||||
from app.agents.base import BaseAgent, AgentResult, AgentTrigger
|
||||
|
||||
|
||||
class {Name}Agent(BaseAgent):
|
||||
"""{Description} agent.
|
||||
|
||||
Triggers: {triggers}
|
||||
"""
|
||||
|
||||
name = "{name}"
|
||||
version = "1.0.0"
|
||||
description = "{description}"
|
||||
triggers = [AgentTrigger.MANUAL]
|
||||
|
||||
async def run(self, context: dict[str, Any] | None = None) -> AgentResult:
|
||||
"""Execute agent task.
|
||||
|
||||
Args:
|
||||
context: Optional context with execution parameters
|
||||
|
||||
Returns:
|
||||
AgentResult with execution outcome
|
||||
"""
|
||||
start = datetime.now(timezone.utc)
|
||||
errors: list[str] = []
|
||||
data: dict[str, Any] = {}
|
||||
|
||||
try:
|
||||
# === AGENT LOGIC HERE ===
|
||||
# 1. Do the work
|
||||
# 2. Collect results
|
||||
# 3. Handle errors
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
errors.append(str(e))
|
||||
|
||||
duration = int((datetime.now(timezone.utc) - start).total_seconds() * 1000)
|
||||
|
||||
result = AgentResult(
|
||||
success=len(errors) == 0,
|
||||
message=f"{self.name} completed with {len(errors)} errors",
|
||||
data=data,
|
||||
errors=errors,
|
||||
duration_ms=duration,
|
||||
)
|
||||
|
||||
# Auto-version check
|
||||
new_version = await self._check_version()
|
||||
if new_version:
|
||||
result.data["version_bumped"] = True
|
||||
result.data["new_version"] = new_version
|
||||
|
||||
return result
|
||||
|
||||
async def health_check(self) -> bool:
|
||||
"""Check if agent can execute."""
|
||||
return True
|
||||
```
|
||||
@@ -0,0 +1,117 @@
|
||||
# Стратегия тестирования API
|
||||
|
||||
---
|
||||
|
||||
## 9 обязательных сценариев для каждого endpoint
|
||||
|
||||
### 1. Missing field → 422
|
||||
```python
|
||||
async def test_create_missing_field(async_client):
|
||||
response = await async_client.post("/api/v1/ideas", json={})
|
||||
assert response.status_code == 422
|
||||
```
|
||||
|
||||
### 2. Wrong type → 422
|
||||
```python
|
||||
async def test_create_wrong_type(async_client):
|
||||
response = await async_client.post("/api/v1/ideas", json={
|
||||
"title": 123, # Должна быть строка
|
||||
"content": "test",
|
||||
})
|
||||
assert response.status_code == 422
|
||||
```
|
||||
|
||||
### 3. Expired/invalid token → 401
|
||||
```python
|
||||
async def test_unauthorized(async_client):
|
||||
response = await async_client.get("/api/v1/ideas", headers={
|
||||
"Authorization": "Bearer invalid_token"
|
||||
})
|
||||
assert response.status_code == 401
|
||||
```
|
||||
|
||||
### 4. Wrong permissions → 403
|
||||
```python
|
||||
async def test_forbidden(async_client, user_token):
|
||||
response = await async_client.get(
|
||||
"/api/v1/admin/users",
|
||||
headers={"Authorization": f"Bearer {user_token}"},
|
||||
)
|
||||
assert response.status_code == 403
|
||||
```
|
||||
|
||||
### 5. Not found → 404
|
||||
```python
|
||||
async def test_not_found(async_client, user_token):
|
||||
response = await async_client.get(
|
||||
"/api/v1/ideas/nonexistent",
|
||||
headers={"Authorization": f"Bearer {user_token}"},
|
||||
)
|
||||
assert response.status_code == 404
|
||||
```
|
||||
|
||||
### 6. Conflict → 409
|
||||
```python
|
||||
async def test_duplicate_email(async_client):
|
||||
# Создать первого пользователя
|
||||
await async_client.post("/api/v1/auth/register", json={...})
|
||||
# Попробовать создать с тем же email
|
||||
response = await async_client.post("/api/v1/auth/register", json={...})
|
||||
assert response.status_code == 409
|
||||
```
|
||||
|
||||
### 7. Success → 200/201
|
||||
```python
|
||||
async def test_create_success(async_client, user_token):
|
||||
response = await async_client.post(
|
||||
"/api/v1/ideas",
|
||||
json={"title": "Test", "content": "Content"},
|
||||
headers={"Authorization": f"Bearer {user_token}"},
|
||||
)
|
||||
assert response.status_code == 201
|
||||
data = response.json()
|
||||
assert data["title"] == "Test"
|
||||
```
|
||||
|
||||
### 8. Rate limit → 429 (если реализован)
|
||||
```python
|
||||
async def test_rate_limit(async_client, user_token):
|
||||
for _ in range(100):
|
||||
await async_client.get("/api/v1/ideas", headers={...})
|
||||
response = await async_client.get("/api/v1/ideas", headers={...})
|
||||
assert response.status_code == 429
|
||||
```
|
||||
|
||||
### 9. Idempotency → тот же результат при повторе
|
||||
```python
|
||||
async def test_idempotent_delete(async_client, user_token, idea_id):
|
||||
response1 = await async_client.delete(f"/api/v1/ideas/{idea_id}", headers={...})
|
||||
response2 = await async_client.delete(f"/api/v1/ideas/{idea_id}", headers={...})
|
||||
assert response1.status_code == 204
|
||||
assert response2.status_code == 404 # Уже удалено
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Структура тестов
|
||||
|
||||
```
|
||||
tests/
|
||||
├── conftest.py # Глобальные фикстуры
|
||||
├── unit/ # изолированные тесты
|
||||
├── integration/
|
||||
│ ├── conftest.py # Фикстуры для API тестов
|
||||
│ ├── test_auth.py # 9 сценариев для auth
|
||||
│ ├── test_ideas.py # 9 сценариев для ideas
|
||||
│ └── test_admin.py # 9 сценариев для admin
|
||||
└── smoke/
|
||||
└── test_health.py # smoke-тесты
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK]
|
||||
|
||||
- Все ли 9 сценариев нужны для каждого endpoint? (рекомендация: да, но можно начать с успех + not found + unauthorized)
|
||||
- Нужны ли тесты на idempotency? (да, для DELETE и PATCH)
|
||||
- Как часто прогонять? (при каждом PR — обязательно, при каждом push — желательно)
|
||||
@@ -0,0 +1,19 @@
|
||||
# Pre-commit чеклист
|
||||
|
||||
Перед каждым коммитом:
|
||||
|
||||
- [ ] `ruff check .` — 0 errors
|
||||
- [ ] `ruff format --check .` — форматирование в порядке
|
||||
- [ ] `mypy app/` — 0 errors (если настроен)
|
||||
- [ ] `pytest` — все тесты зелёные
|
||||
- [ ] CHANGELOG обновлён (если изменение влияет на пользователя)
|
||||
- [ ] .env.example обновлён (если новая переменная)
|
||||
- [ ] Нет секретов и токенов в коде (grep на api_key, secret, password)
|
||||
- [ ] Нет TODO/FIXME без тикета
|
||||
- [ ] Миграция написана (если менялась БД)
|
||||
- [ ] Docstrings написаны (для новых публичных методов)
|
||||
|
||||
**Автоматически (pre-commit hooks):**
|
||||
- `ruff` — линтинг и форматирование
|
||||
- `trailing-whitespace` — удаление лишних пробелов
|
||||
- `check-added-large-files` — проверка больших файлов
|
||||
@@ -0,0 +1,25 @@
|
||||
# Code Review чеклист
|
||||
|
||||
## Безопасность
|
||||
- [ ] Нет секретов, ключей, паролей в коде
|
||||
- [ ] Нет чувствительных данных в логах
|
||||
- [ ] Входные данные проходят Pydantic валидацию
|
||||
- [ ] Проверены права доступа (RBAC)
|
||||
|
||||
## Качество
|
||||
- [ ] Нет сырых Exception в API ответах
|
||||
- [ ] Есть обработка ошибок для внешних вызовов
|
||||
- [ ] Docstrings написаны (Google-style)
|
||||
- [ ] Аннотации типов проставлены
|
||||
- [ ] Ruff проходит (0 errors)
|
||||
- [ ] mypy проходит (0 errors)
|
||||
|
||||
## Тесты
|
||||
- [ ] Есть тесты на новую функциональность
|
||||
- [ ] Есть smoke-тест на новые endpoint'ы
|
||||
- [ ] Тесты проходят
|
||||
|
||||
## Документация
|
||||
- [ ] .env.example обновлён
|
||||
- [ ] CHANGELOG обновлён
|
||||
- [ ] ADR создан (если архитектурное изменение)
|
||||
@@ -0,0 +1,28 @@
|
||||
# Pre-deploy чеклист
|
||||
|
||||
## База данных
|
||||
- [ ] Миграции написаны и протестированы (upgrade + downgrade)
|
||||
- [ ] Резервная копия БД создана
|
||||
- [ ] Проверено что данные не потеряются
|
||||
|
||||
## Конфигурация
|
||||
- [ ] .env настроен для production
|
||||
- [ ] Все секреты установлены (не дефолтные)
|
||||
- [ ] CORS настроен на реальный домен
|
||||
- [ ] LOG_LEVEL = WARNING (не DEBUG)
|
||||
- [ ] DEBUG = False
|
||||
|
||||
## Инфраструктура
|
||||
- [ ] SSL сертификаты (Let's Encrypt)
|
||||
- [ ] Nginx настроен (или аналог)
|
||||
- [ ] systemd unit создан (если без Docker)
|
||||
|
||||
## CI/CD
|
||||
- [ ] CI проходит (lint + test)
|
||||
- [ ] CD скопировал артефакты на сервер
|
||||
- [ ] Health check проходит после деплоя
|
||||
|
||||
## Мониторинг
|
||||
- [ ] Health endpoint работает
|
||||
- [ ] Логи пишутся в файл
|
||||
- [ ] Алерты настроены (если нужны)
|
||||
@@ -0,0 +1,28 @@
|
||||
# Incident Response чеклист
|
||||
|
||||
## Immediate (первые 5 минут)
|
||||
1. [ ] Определить severity
|
||||
- **Critical**: сервис недоступен, данные потеряны
|
||||
- **Major**: функциональность severely impacted
|
||||
- **Minor**: не влияет на пользователей
|
||||
2. [ ] Остановить кровотечение
|
||||
- Rollback до последней стабильной версии
|
||||
- Отключить проблемную функциональность
|
||||
- Переключить на fallback
|
||||
3. [ ] Уведомить команду
|
||||
|
||||
## Investigation (15-30 минут)
|
||||
4. [ ] Проверить логи (app, nginx, system)
|
||||
5. [ ] Проверить метрики (когда началось, что изменилось)
|
||||
6. [ ] Проверить последний деплой / изменения
|
||||
7. [ ] Воспроизвести проблему (если возможно)
|
||||
|
||||
## Resolution
|
||||
8. [ ] Применить исправление
|
||||
9. [ ] Проверить что сервис восстановлен
|
||||
10. [ ] Уведомить о восстановлении
|
||||
|
||||
## Postmortem (в течение 24 часов)
|
||||
11. [ ] Написать postmortem
|
||||
12. [ ] Создать задачу на предотвращение
|
||||
13. [ ] Добавить мониторинг / тест на этот сценарий
|
||||
@@ -0,0 +1,28 @@
|
||||
# Definition of Done
|
||||
|
||||
Задача считается выполненной только когда ВСЕ пункты отмечены:
|
||||
|
||||
## Код
|
||||
- [ ] Код написан (соответствует стилю из 00-rules.md §1)
|
||||
- [ ] Линт проходит (ruff — 0 errors)
|
||||
- [ ] Форматирование соблюдено (ruff format)
|
||||
|
||||
## Тесты
|
||||
- [ ] Тесты написаны (минимум 1 smoke-тест)
|
||||
- [ ] Тесты проходят (pytest — green)
|
||||
- [ ] Покрытие новых строк > 80%
|
||||
|
||||
## Документация
|
||||
- [ ] Docstrings написаны (Google-style)
|
||||
- [ ] .env.example обновлён (если новая переменная)
|
||||
- [ ] CHANGELOG обновлён (если изменение влияет на API/пользователя)
|
||||
- [ ] ADR создан (если архитектурное изменение)
|
||||
|
||||
## Инфраструктура
|
||||
- [ ] Миграция написана (если менялась БД)
|
||||
- [ ] Миграция протестирована (upgrade + downgrade)
|
||||
|
||||
## Процесс
|
||||
- [ ] PR создан
|
||||
- [ ] Code review пройден (минимум 1 апрув)
|
||||
- [ ] Ветка смержена в develop/main
|
||||
@@ -0,0 +1,57 @@
|
||||
# Decision Log
|
||||
|
||||
Лёгкий трекер каждодневных решений.
|
||||
В отличие от ADR (фиксируют архитектуру), Decision Log фиксирует **контекст** — почему мы сделали тот или иной выбор.
|
||||
Через 3 месяца никто не вспомнит "почему мы взяли SQLite", а Decision Log напомнит.
|
||||
|
||||
---
|
||||
|
||||
## Формат записи
|
||||
|
||||
```markdown
|
||||
## {YYYY-MM-DD}: {Решение}
|
||||
|
||||
**Контекст:** {Почему встал вопрос, какие были ограничения}
|
||||
**Решение:** {Что выбрали}
|
||||
**Альтернативы:** {Что рассматривали, почему не взяли}
|
||||
**Кто:** {Кто принял решение}
|
||||
**Статус:** {действует | пересмотреть через N | заменено}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Пример
|
||||
|
||||
```markdown
|
||||
## 2026-05-10: Выбрали SQLite для разработки
|
||||
|
||||
**Контекст:** У команды Windows, PostgreSQL требует установки и настройки.
|
||||
На старте важна скорость — поднять проект за 5 минут, а не за час.
|
||||
|
||||
**Решение:** SQLite + aiosqlite для локальной разработки.
|
||||
PostgreSQL — только на production.
|
||||
|
||||
**Альтернативы:**
|
||||
- PostgreSQL + Docker — работает, но Docker не у всех
|
||||
- PostgreSQL native — адская установка на Windows
|
||||
|
||||
**Кто:** @owner
|
||||
**Статус:** действует. Пересмотреть перед production.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Когда создавать запись
|
||||
|
||||
- Выбрали технологию (БД, провайдер, фреймворк)
|
||||
- Отложили функциональность (не делаем OAuth сейчас)
|
||||
- Изменили подход (было sessions, стало JWT)
|
||||
- Архитектурный компромисс (знаем что не идеально, но время поджимает)
|
||||
|
||||
---
|
||||
|
||||
## [ASK]
|
||||
|
||||
- Вести Decision Log в Markdown или в YAML? (Markdown — читаемость)
|
||||
- Хранить в репозитории или в Notion/wiki? (в репозитории — git history + доступность)
|
||||
- Кто заполняет? (тот, кто принял решение, сразу)
|
||||
@@ -0,0 +1,60 @@
|
||||
# Выбор базы данных
|
||||
|
||||
---
|
||||
|
||||
## Decision Tree
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Какую БД?] --> B{Многопользовательская?}
|
||||
B -->|Нет / прототип| C[SQLite]
|
||||
B -->|Да| D{Нужен JSONB?}
|
||||
D -->|Да| E[PostgreSQL]
|
||||
D -->|Нет| F{Нужен full-text search?}
|
||||
F -->|Да| E
|
||||
F -->|Нет| G[SQLite / PostgreSQL]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Варианты
|
||||
|
||||
### SQLite
|
||||
| | |
|
||||
|---|---|
|
||||
| **Когда** | Прототип, dev, однопользовательское |
|
||||
| **Плюсы** | Не требует установки, встроенная, ноль конфигурации |
|
||||
| **Минусы** | Нет конкурентной записи, нет JSONB, нет ARRAY, нет полнотекстового поиска |
|
||||
| **Драйвер** | aiosqlite |
|
||||
|
||||
### PostgreSQL
|
||||
| | |
|
||||
|---|---|
|
||||
| **Когда** | Production, многопользовательское, аналитика |
|
||||
| **Плюсы** | ACID, JSONB, ARRAY, full-text search, масштабирование |
|
||||
| **Минусы** | Требует установки, настройки, памяти |
|
||||
| **Драйвер** | asyncpg |
|
||||
|
||||
---
|
||||
|
||||
## Рекомендация
|
||||
|
||||
**SQLite для разработки, PostgreSQL для production.**
|
||||
Обе БД поддерживаются через SQLAlchemy с минимальными отличиями в моделях.
|
||||
|
||||
### Что нужно для портабельности
|
||||
|
||||
```python
|
||||
# Вместо PostgreSQL-specific типов используем универсальные:
|
||||
UUID → String(36)
|
||||
JSONB → JSON
|
||||
ARRAY → JSON
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK]
|
||||
|
||||
- Какая БД нужна на старте? (рекомендация: SQLite)
|
||||
- Когда переходить на PostgreSQL? (перед production)
|
||||
- Нужна ли поддержка обеих БД одновременно? (желательно — unit-тесты на SQLite быстрее)
|
||||
@@ -0,0 +1,59 @@
|
||||
# Выбор схемы аутентификации
|
||||
|
||||
---
|
||||
|
||||
## Decision Tree
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Схема auth?] --> B{Нужен вход через соцсети?}
|
||||
B -->|Нет| C[Email + пароль]
|
||||
B -->|Да| D{OAuth2}
|
||||
D --> E[Выбрать провайдеров]
|
||||
C --> F[Выбрать JWT или Session]
|
||||
F -->|SPA/PWA| G[JWT + refresh token]
|
||||
F -->|SSR| H[Session + cookie]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Варианты
|
||||
|
||||
### Email + пароль
|
||||
| | |
|
||||
|---|---|
|
||||
| **Плюсы** | Простота, не зависит от third-party, полный контроль |
|
||||
| **Минусы** | Пользователь должен помнить пароль, риск утечки |
|
||||
| **Хэширование** | bcrypt через passlib |
|
||||
|
||||
### OAuth2 (Яндекс, Google, GitHub, Apple)
|
||||
| | |
|
||||
|---|---|
|
||||
| **Плюсы** | Удобство для пользователя, нет паролей на нашей стороне |
|
||||
| **Минусы** | Зависимость от провайдера, нужны client_id/secret, нужен публичный URL для callback |
|
||||
| **Схема** | Один пользователь = один провайдер (нельзя привязать два) |
|
||||
|
||||
### JWT vs Session
|
||||
|
||||
| | JWT | Session |
|
||||
|---|---|---|
|
||||
| **Хранение** | На клиенте (localStorage) | На сервере (Redis/БД) |
|
||||
| **Масштабирование** | Не нужна общая session storage | Нужен Redis |
|
||||
| **Отзыв токена** | Сложно (до expire) | Мгновенно |
|
||||
| **SPA/PWA** | Идеально | Сложнее |
|
||||
|
||||
---
|
||||
|
||||
## Рекомендация
|
||||
|
||||
**Email + пароль + JWT** для старта.
|
||||
OAuth2 добавить перед production (если нужен).
|
||||
JWT с refresh token для SPA/PWA, session для SSR.
|
||||
|
||||
---
|
||||
|
||||
## [ASK]
|
||||
|
||||
- Нужен ли вход через соцсети? (рекомендация: Яндекс для РФ, Google для международных)
|
||||
- JWT или Session? (рекомендация: JWT + refresh token)
|
||||
- Сколько провайдеров OAuth? (рекомендация: 1-2, не больше)
|
||||
@@ -0,0 +1,91 @@
|
||||
# Интеграция AI
|
||||
|
||||
---
|
||||
|
||||
## Паттерн: FallbackChain
|
||||
|
||||
```
|
||||
Запрос → Provider 1 → Успех → результат
|
||||
Ошибка → Provider 2 → Успех → результат
|
||||
Ошибка → Fallback результат
|
||||
```
|
||||
|
||||
### Реализация
|
||||
|
||||
```python
|
||||
class FallbackChain:
|
||||
def __init__(self, providers: list[AIProvider], max_retries: int = 2):
|
||||
self.providers = providers
|
||||
self.max_retries = max_retries
|
||||
|
||||
async def analyze(self, prompt: str, **kwargs) -> AIResult:
|
||||
last_error = None
|
||||
for provider in self.providers:
|
||||
for attempt in range(self.max_retries + 1):
|
||||
try:
|
||||
result = await provider.analyze(prompt, **kwargs)
|
||||
if result.success:
|
||||
return result
|
||||
last_error = result
|
||||
except Exception as e:
|
||||
last_error = AIResult(success=False, error=str(e))
|
||||
if attempt < self.max_retries:
|
||||
await asyncio.sleep(2 if attempt == 0 else 5)
|
||||
return last_error or AIResult(success=False, error="All providers failed")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Таймауты и ретраи (следуя §12 правил)
|
||||
|
||||
```
|
||||
1. Попытка (timeout: 10s)
|
||||
2. Успех → return
|
||||
3. Таймаут → retry 1 (через 2s)
|
||||
4. Таймаут → retry 2 (через 5s)
|
||||
5. 4xx → WARNING, return fallback
|
||||
6. 5xx → ERROR, retry → fallback
|
||||
7. Все retry исчерпаны → return fallback
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Хранение промптов
|
||||
|
||||
**Рекомендуемый формат:** YAML (`docs/agent_prompts.yaml`)
|
||||
|
||||
```yaml
|
||||
coordinator:
|
||||
system_prompt: "Ты — координатор проекта..."
|
||||
provider: yandex_gpt
|
||||
temperature: 0.7
|
||||
max_tokens: 2000
|
||||
|
||||
business_analyst:
|
||||
system_prompt: "Ты — бизнес-аналитик..."
|
||||
provider: yandex_gpt
|
||||
temperature: 0.5
|
||||
max_tokens: 3000
|
||||
```
|
||||
|
||||
**Альтернатива:** MD-файлы в `docs/specs/agents/` (для детальных спецификаций)
|
||||
|
||||
---
|
||||
|
||||
## Провайдеры
|
||||
|
||||
| Провайдер | Когда | Аутентификация |
|
||||
|-----------|-------|---------------|
|
||||
| Yandex GPT | РФ, хорошая русская речь | IAM token или API key |
|
||||
| GigaChat (Sber) | РФ, юридические/финансовые темы | OAuth client credentials |
|
||||
| OpenAI | Международные проекты | API key |
|
||||
| Локальная модель | Оффлайн, конфиденциальность | Не требуется |
|
||||
|
||||
---
|
||||
|
||||
## [ASK]
|
||||
|
||||
- Какие AI провайдеры нужны? (рекомендация: минимум 2 для fallback)
|
||||
- Нужен ли fallback chain? (да — обязателен для отказоустойчивости)
|
||||
- Где хранить промпты? (рекомендация: YAML — простота редактирования)
|
||||
- Нужен ли локальный AI? (да, если конфиденциальность критична)
|
||||
@@ -0,0 +1,76 @@
|
||||
# Выбор фронтенда
|
||||
|
||||
---
|
||||
|
||||
## Decision Tree
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Фронтенд?] --> B{SPA или SSR?}
|
||||
B -->|SPA| C[React + Vite + TS]
|
||||
B -->|SSR| D[Next.js]
|
||||
C --> E{Нужен оффлайн?}
|
||||
E -->|Да| F[PWA + vite-plugin-pwa]
|
||||
E -->|Нет| G[Без PWA]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Варианты
|
||||
|
||||
### React + Vite + TypeScript
|
||||
| | |
|
||||
|---|---|
|
||||
| **Когда** | SPA, PWA, мобильное приложение |
|
||||
| **Плюсы** | Популярный, большая экосистема, Vite быстрый, PWA-ready |
|
||||
| **Минусы** | SPA — медленный первый заход (но PWA решает) |
|
||||
|
||||
### Next.js
|
||||
| | |
|
||||
|---|---|
|
||||
| **Когда** | SSR, SEO, контентный сайт |
|
||||
| **Плюсы** | SSR, SEO, App Router |
|
||||
| **Минусы** | Сложнее деплой, не подходит для PWA |
|
||||
|
||||
---
|
||||
|
||||
## Стили
|
||||
|
||||
| Решение | Когда |
|
||||
|---------|-------|
|
||||
| **Tailwind CSS** | Всегда (рекомендовано) |
|
||||
| CSS Modules | Если Tailwind не подходит |
|
||||
| CSS-in-JS | Не рекомендуется (производительность) |
|
||||
|
||||
---
|
||||
|
||||
## Состояние
|
||||
|
||||
| Решение | Когда |
|
||||
|---------|-------|
|
||||
| **React Context + hooks** | Маленькое приложение (< 5 страниц) |
|
||||
| **zustand** | Среднее приложение (рекомендовано) |
|
||||
| **RTK** | Большое приложение с множеством запросов |
|
||||
|
||||
---
|
||||
|
||||
## PWA
|
||||
|
||||
**Когда нужен:**
|
||||
- Приложение должно работать оффлайн
|
||||
- Пользователи на мобильных устройствах
|
||||
- Нужно push-уведомления
|
||||
|
||||
**Технологии:**
|
||||
- `vite-plugin-pwa` — генерация service worker
|
||||
- `manifest.json` — установка на домашний экран
|
||||
- IndexedDB — оффлайн-хранение
|
||||
|
||||
---
|
||||
|
||||
## [ASK]
|
||||
|
||||
- Нужен ли фронтенд вообще? (API-first или full-stack?)
|
||||
- SPA или SSR? (SPA+PWA для приложений, SSR для контента)
|
||||
- Нужна ли PWA? (да, если мобильные пользователи и оффлайн)
|
||||
- Какой Router? (react-router-dom — стандарт)
|
||||
@@ -0,0 +1,82 @@
|
||||
# Стратегия деплоя
|
||||
|
||||
---
|
||||
|
||||
## Decision Tree
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Как деплоить?] --> B{Один сервер?}
|
||||
B -->|Да| C[Docker-compose]
|
||||
B -->|Нет| D{Нужна оркестрация?}
|
||||
D -->|Да| E[Kubernetes]
|
||||
D -->|Нет| F[Docker-compose + несколько серверов]
|
||||
C --> G{VPS или облако?}
|
||||
G -->|VPS| H[Ubuntu + systemd]
|
||||
G -->|Облако| I[Docker + cloud provider]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Варианты
|
||||
|
||||
### Docker-compose (рекомендован для старта)
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
ports: ["8020:8020"]
|
||||
env_file: .env
|
||||
db:
|
||||
image: postgres:14
|
||||
volumes: ["pgdata:/var/lib/postgresql/data"]
|
||||
redis:
|
||||
image: redis:7
|
||||
worker:
|
||||
build: .
|
||||
command: celery -A app.tasks worker -l info
|
||||
```
|
||||
|
||||
### Systemd (без Docker, VPS)
|
||||
```ini
|
||||
[Unit]
|
||||
Description=VoIdea API
|
||||
|
||||
[Service]
|
||||
ExecStart=/home/voidea/venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8020
|
||||
WorkingDirectory=/home/voidea
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI/CD
|
||||
|
||||
**Рекомендуется:** GitHub Actions
|
||||
|
||||
```yaml
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with: { python-version: "3.12" }
|
||||
- run: pip install -r requirements.txt
|
||||
- run: ruff check
|
||||
- run: pytest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [ASK]
|
||||
|
||||
- Docker или без Docker? (Docker для воспроизводимости)
|
||||
- VPS или облако? (VPS дешевле, облако масштабируемее)
|
||||
- CI/CD какой? (GitHub Actions — бесплатно для публичных репозиториев)
|
||||
- Нужен ли staging? (да, перед production)
|
||||
@@ -0,0 +1,49 @@
|
||||
# Мониторинг и алертинг
|
||||
|
||||
---
|
||||
|
||||
## Базовый мониторинг (нужен всегда)
|
||||
|
||||
### Health endpoints
|
||||
```python
|
||||
GET /health → {"status": "healthy", "version": "1.0.0", "db": "connected"}
|
||||
GET /api/v1/health → {"status": "healthy", "api_version": "v1"}
|
||||
```
|
||||
|
||||
### Метрики
|
||||
Собираются через middleware и хранятся в БД:
|
||||
- Время ответа (p50/p95/p99)
|
||||
- Количество запросов (всего, по endpoint'ам)
|
||||
- Количество ошибок (4xx, 5xx)
|
||||
- Статус внешних сервисов (БД, Redis, AI провайдеры)
|
||||
|
||||
---
|
||||
|
||||
## Production мониторинг
|
||||
|
||||
### Prometheus + Grafana (рекомендовано)
|
||||
|
||||
| Компонент | Метрики |
|
||||
|-----------|---------|
|
||||
| Application | Время ответа, ошибки, request rate |
|
||||
| Database | Connection pool, query time |
|
||||
| Redis | Memory, hits/misses |
|
||||
| Celery | Task queue length, execution time |
|
||||
| System | CPU, RAM, disk, network |
|
||||
|
||||
### Алерты
|
||||
|
||||
| Условие | Действие |
|
||||
|---------|----------|
|
||||
| error rate > 1% | Уведомление в Telegram/Slack |
|
||||
| API response p95 > 1s | Уведомление |
|
||||
| DB connection pool > 80% | Предупреждение |
|
||||
| Service down | PagerDuty / звонок |
|
||||
|
||||
---
|
||||
|
||||
## [ASK]
|
||||
|
||||
- Нужен ли мониторинг на старте? (базовый — да, Prometheus — перед production)
|
||||
- Отправлять ли алерты? (да, если есть кто-то кто на них реагирует)
|
||||
- Какой канал для алертов? (Telegram — простой, PagerDuty — профессиональный)
|
||||
@@ -0,0 +1,88 @@
|
||||
# Управление переменными окружения
|
||||
|
||||
---
|
||||
|
||||
## Принцип
|
||||
|
||||
Все настройки, которые меняются между окружениями (local, staging, production) — в переменных окружения. Никаких hardcoded значений в коде.
|
||||
|
||||
---
|
||||
|
||||
## Формат: .env
|
||||
|
||||
```bash
|
||||
# === Core ===
|
||||
PROJECT_NAME=MyProject
|
||||
PROJECT_VERSION=1.0.0
|
||||
PROJECT_ENV=local
|
||||
|
||||
# === Server ===
|
||||
SERVER_HOST=0.0.0.0
|
||||
SERVER_PORT=8020
|
||||
|
||||
# === Database ===
|
||||
DATABASE_URL=sqlite+aiosqlite:///./app.db
|
||||
# DATABASE_URL=postgresql+asyncpg://user:pass@localhost/dbname
|
||||
|
||||
# === JWT ===
|
||||
JWT_SECRET_KEY=your-secret-key-here
|
||||
JWT_ALGORITHM=HS256
|
||||
|
||||
# === Logging ===
|
||||
LOG_LEVEL=INFO
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Валидация при старте
|
||||
|
||||
```python
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
class Settings(BaseSettings):
|
||||
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
||||
|
||||
project_name: str = "MyProject"
|
||||
database_url: str = "sqlite+aiosqlite:///./app.db"
|
||||
jwt_secret_key: str = ""
|
||||
|
||||
@property
|
||||
def is_sqlite(self) -> bool:
|
||||
return "sqlite" in self.database_url
|
||||
|
||||
def validate_for_production(self) -> None:
|
||||
"""Проверить что критические переменные установлены."""
|
||||
if self.project_env == "production":
|
||||
assert self.jwt_secret_key, "JWT_SECRET_KEY not set"
|
||||
assert "postgresql" in self.database_url, "Use PostgreSQL in production"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Синхронизация .env.example
|
||||
|
||||
`.env.example` должен быть в репозитории и обновляться при каждом добавлении переменной.
|
||||
|
||||
Правила:
|
||||
- Все переменные с комментариями
|
||||
- Чувствительные значения пустые (пароли, ключи)
|
||||
- Секции разделены комментариями (`# === Database ===`)
|
||||
- Примеры значений в комментариях
|
||||
|
||||
---
|
||||
|
||||
## Secrets management
|
||||
|
||||
| Окружение | Где хранить секреты |
|
||||
|-----------|-------------------|
|
||||
| Local | `.env` (в .gitignore) |
|
||||
| Staging | GitHub Secrets / 1Password |
|
||||
| Production | GitHub Secrets / Vault |
|
||||
|
||||
---
|
||||
|
||||
## [ASK]
|
||||
|
||||
- Какой метод управления секретами? (рекомендация: .env + GitHub Secrets)
|
||||
- Нужен ли Vault? (нет, < 10 разработчиков)
|
||||
- Как часто менять JWT_SECRET_KEY? (при утечке или раз в год)
|
||||
@@ -0,0 +1,141 @@
|
||||
# Поэтапный план взросления проекта
|
||||
|
||||
Проект не строится сразу целиком. Он проходит этапы — от прототипа до саморазвивающейся системы.
|
||||
Агенты живут с первого коммита. Новые агенты добавляются когда возникает потребность.
|
||||
|
||||
---
|
||||
|
||||
## Stage 0: Foundation — Ядро
|
||||
|
||||
**Начинаем здесь.** Проект только родился.
|
||||
|
||||
### Код
|
||||
- FastAPI + SQLite + базовая auth
|
||||
- Минимальный набор правил (00-rules.md)
|
||||
- Базовые CRUD endpoints
|
||||
- Pydantic схемы на все входы
|
||||
|
||||
### Агенты (создаются в первую очередь)
|
||||
- **DocAgent** — пишет документацию параллельно с кодом
|
||||
- **AuditAgent** — проверяет каждый коммит на правила
|
||||
- **EvolutionAgent** — версионирует агентов
|
||||
- **SupervisorAgent** — следит за всеми агентами
|
||||
|
||||
### Инфраструктура
|
||||
- SQLite (aiosqlite)
|
||||
- Прямой вызов фоновых задач (без Celery)
|
||||
|
||||
### До Stage 1
|
||||
Сразу после того, как есть первый endpoint и auth.
|
||||
|
||||
---
|
||||
|
||||
## Stage 1: Growth — Рост
|
||||
|
||||
Проект обрастает функциональностью.
|
||||
|
||||
### Добавляемый код
|
||||
- Полноценные сервисы
|
||||
- Интеграции (AI провайдеры)
|
||||
- Фронтенд (если нужен)
|
||||
- Тесты
|
||||
|
||||
### Добавляемые агенты
|
||||
- **QATesterAgent** — когда появились тесты (авто-проверка покрытия)
|
||||
- **FixAgent** — когда пойман первый баг (анализ ошибок)
|
||||
- **BacklogAgent** — когда появился техдолг (управление TODO/FIXME)
|
||||
|
||||
### Инфраструктура
|
||||
- Те же SQLite + прямой вызов
|
||||
- Тестовое покрытие > 50%
|
||||
|
||||
### До Stage 2
|
||||
Перед первым production-релизом.
|
||||
|
||||
---
|
||||
|
||||
## Stage 2: Production-ready
|
||||
|
||||
Проект готов к реальным пользователям.
|
||||
|
||||
### Добавляемый код
|
||||
- PostgreSQL + asyncpg
|
||||
- Redis + Celery для фоновых задач
|
||||
- Мониторинг (health + метрики)
|
||||
- Полная документация
|
||||
|
||||
### Добавляемые агенты
|
||||
- **SecurityAgent** — проверка конфигов, зависимостей
|
||||
- **SpecAgent** — управление версией проекта, CHANGELOG
|
||||
- **RolloutAgent** — постепенное развёртывание
|
||||
|
||||
### Инфраструктура
|
||||
- PostgreSQL
|
||||
- Redis + Celery worker
|
||||
- CI/CD (lint → test → build → deploy)
|
||||
- SSL (Let's Encrypt)
|
||||
- .env для production + staging
|
||||
|
||||
### До Stage 3
|
||||
После запуска, когда появились первые пользователи и метрики.
|
||||
|
||||
---
|
||||
|
||||
## Stage 3: Autonomous — Саморазвитие
|
||||
|
||||
Проект начинает развиваться самостоятельно.
|
||||
|
||||
### Добавляемый код
|
||||
- Metrics middleware
|
||||
- Prometheus/Grafana (или встроенные метрики)
|
||||
- Agent report dashboard
|
||||
- Self-healing механизмы
|
||||
|
||||
### Добавляемые агенты
|
||||
- **ObserverAgent** — сбор метрик использования
|
||||
- **UITestAgent** — визуальное тестирование (если есть UI)
|
||||
|
||||
### Инфраструктура
|
||||
- A/B тестирование
|
||||
- Auto-scaling (при необходимости)
|
||||
- Автоматический откат при росте ошибок
|
||||
|
||||
### До Stage 4
|
||||
Когда > 1000 пользователей или > 3 разработчиков.
|
||||
|
||||
---
|
||||
|
||||
## Stage 4: Evolution — Эволюция
|
||||
|
||||
Проект развивается автономно.
|
||||
|
||||
### Уровень саморазвития
|
||||
- Агенты не только предлагают, но и применяют изменения
|
||||
- EvolutionAgent принимает решения о рефакторинге
|
||||
- FixAgent применяет исправления (с PR на ревью)
|
||||
- ObserverAgent на основе метрик предлагает roadmap
|
||||
|
||||
### Инфраструктура
|
||||
- Полный мониторинг с алертами
|
||||
- Автоматическое масштабирование
|
||||
- Disaster recovery plan
|
||||
- Postmortem культура
|
||||
|
||||
---
|
||||
|
||||
## Сводная таблица
|
||||
|
||||
| Stage | БД | Задачи | Агентов | Тесты | Мониторинг | Саморазвитие |
|
||||
|-------|----|--------|---------|-------|-----------|-------------|
|
||||
| 0 | SQLite | Прямой | 4 | > 20% | Нет | Reactive |
|
||||
| 1 | SQLite | Прямой | 7 | > 50% | Нет | Reactive |
|
||||
| 2 | PostgreSQL | Celery | 9 | > 80% | Базовый | Reactive |
|
||||
| 3 | PostgreSQL | Celery | 11 | > 80% | Prometheus | Proactive |
|
||||
| 4 | PostgreSQL | Celery | 11+ | > 90% | Full | Autonomous |
|
||||
|
||||
---
|
||||
|
||||
## [ASK] На каком вы этапе?
|
||||
|
||||
Оцените текущее состояние проекта и выберите целевой этап.
|
||||
Рекомендация: начинайте со Stage 0, не прыгайте через этапы.
|
||||
@@ -0,0 +1,35 @@
|
||||
# Runbook: Запуск проекта
|
||||
|
||||
---
|
||||
|
||||
## Первый запуск
|
||||
|
||||
```bash
|
||||
# 1. Клонировать репозиторий
|
||||
git clone <repo> && cd <repo>
|
||||
|
||||
# 2. Настроить окружение
|
||||
cp .env.example .env
|
||||
# Редактировать .env: JWT_SECRET_KEY, DATABASE_URL
|
||||
|
||||
# 3. Установить зависимости
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 4. Запустить
|
||||
uvicorn app.main:app --reload --host 0.0.0.0 --port 8020
|
||||
```
|
||||
|
||||
## Проверка
|
||||
|
||||
```bash
|
||||
curl http://localhost:8020/health
|
||||
# → {"status": "healthy"}
|
||||
curl http://localhost:8020/docs
|
||||
# → Swagger UI
|
||||
```
|
||||
|
||||
## Остановка
|
||||
|
||||
```bash
|
||||
Ctrl+C # или kill $(pgrep -f uvicorn)
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
# Runbook: Резервное копирование
|
||||
|
||||
---
|
||||
|
||||
## SQLite
|
||||
|
||||
```bash
|
||||
# Ручной бэкап
|
||||
cp app.db app.db.backup.$(date +%Y%m%d)
|
||||
|
||||
# Автоматический (cron)
|
||||
0 3 * * * cp /path/to/app.db /path/to/backups/app.db.$(date +\%Y\%m\%d)
|
||||
```
|
||||
|
||||
## PostgreSQL
|
||||
|
||||
```bash
|
||||
# Ручной бэкап
|
||||
pg_dump -U voidea -d voidea > backup.$(date +%Y%m%d).sql
|
||||
|
||||
# Восстановление
|
||||
psql -U voidea -d voidea < backup.sql
|
||||
|
||||
# Автоматический (cron)
|
||||
0 3 * * * pg_dump -U voidea -d voidea | gzip > /backups/db.$(date +\%Y\%m\%d).sql.gz
|
||||
```
|
||||
|
||||
## Что бэкапить
|
||||
- Базу данных (ежедневно)
|
||||
- .env (секреты, отдельно, в Vault/1Password)
|
||||
- User uploaded files (если есть)
|
||||
|
||||
## Хранение
|
||||
- Последние 7 дней: локально
|
||||
- Последние 30 дней: S3/облако
|
||||
- Старше 30 дней: удалять
|
||||
@@ -0,0 +1,53 @@
|
||||
# Runbook: Инциденты
|
||||
|
||||
---
|
||||
|
||||
## Сервис недоступен
|
||||
|
||||
```bash
|
||||
# 1. Проверить что процесс жив
|
||||
ps aux | grep uvicorn
|
||||
|
||||
# 2. Проверить логи
|
||||
journalctl -u voidea -n 50 --no-pager
|
||||
|
||||
# 3. Перезапустить
|
||||
systemctl restart voidea
|
||||
|
||||
# 4. Проверить
|
||||
curl http://localhost:8020/health
|
||||
|
||||
# 5. Если не помогло → rollback
|
||||
git checkout <previous-stable-tag>
|
||||
systemctl restart voidea
|
||||
```
|
||||
|
||||
## База данных недоступна
|
||||
|
||||
```bash
|
||||
# 1. Проверить PostgreSQL
|
||||
systemctl status postgresql
|
||||
|
||||
# 2. Проверить логи
|
||||
journalctl -u postgresql -n 50
|
||||
|
||||
# 3. Перезапустить
|
||||
systemctl restart postgresql
|
||||
|
||||
# 4. Если повреждена → восстановить из backup
|
||||
# psql -U voidea -d voidea < backup.sql
|
||||
```
|
||||
|
||||
## Высокая загрузка CPU
|
||||
|
||||
```bash
|
||||
# 1. Найти процесс
|
||||
top -o %CPU
|
||||
|
||||
# 2. Найти endpoint
|
||||
tail -n 100 /var/log/voidea/access.log
|
||||
|
||||
# 3. Временно отключить (если endpoint не критичен)
|
||||
|
||||
# 4. Разбираться после восстановления
|
||||
```
|
||||
@@ -0,0 +1,28 @@
|
||||
# Runbook: Масштабирование
|
||||
|
||||
---
|
||||
|
||||
## Когда масштабироваться
|
||||
|
||||
| Метрика | Действие |
|
||||
|---------|----------|
|
||||
| CPU > 80% постоянно | Добавить ядер/воркеров |
|
||||
| RAM > 80% | Увеличить RAM |
|
||||
| DB > 10M записей | Индексы → шардинг |
|
||||
| Response time p95 > 1s | Кэширование → реплики БД |
|
||||
|
||||
## Как масштабировать
|
||||
|
||||
### Vertical (проще)
|
||||
```bash
|
||||
# Увеличить ресурсы VPS
|
||||
# Затем перезапустить
|
||||
systemctl restart voidea
|
||||
```
|
||||
|
||||
### Horizontal (сложнее)
|
||||
```bash
|
||||
# 1. Поставить load balancer (Nginx)
|
||||
# 2. Запустить несколько инстансов
|
||||
# 3. Настроить shared session/кэш (Redis)
|
||||
```
|
||||
@@ -0,0 +1,41 @@
|
||||
# Runbook: Обновление
|
||||
|
||||
---
|
||||
|
||||
## Обновление с нулевым даунтаймом
|
||||
|
||||
```bash
|
||||
# 1. Задеплоить новую версию на второй порт (8021)
|
||||
# 2. Проверить health нового инстанса
|
||||
curl http://localhost:8021/health
|
||||
|
||||
# 3. Переключить Nginx на новый порт
|
||||
# 4. Остановить старый инстанс
|
||||
```
|
||||
|
||||
## Обновление зависимостей
|
||||
|
||||
```bash
|
||||
# 1. Обновить requirements.txt
|
||||
pip install --upgrade -r requirements.txt
|
||||
|
||||
# 2. Проверить
|
||||
ruff check .
|
||||
pytest
|
||||
|
||||
# 3. Закоммитить
|
||||
git add requirements.txt && git commit -m "chore: update dependencies"
|
||||
```
|
||||
|
||||
## Откат
|
||||
|
||||
```bash
|
||||
# 1. Откатить код
|
||||
git revert HEAD
|
||||
|
||||
# 2. Откатить БД (если была миграция)
|
||||
alembic downgrade -1
|
||||
|
||||
# 3. Перезапустить
|
||||
systemctl restart voidea
|
||||
```
|
||||
Reference in New Issue
Block a user