Add architecture package and application skeleton
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# ADR 001: Runtime Stack
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
AI Orchestrator должен быть:
|
||||
|
||||
- независимым от Local LLM Platform;
|
||||
- пригодным для долгоживущих task runtime;
|
||||
- удобным для строгой типизации контрактов;
|
||||
- удобным для async I/O: models, MCP, worker connections, event streams;
|
||||
- запускаемым без Docker на локальной машине;
|
||||
- переносимым на удаленное окружение, включая `docker-test`.
|
||||
|
||||
## Decision
|
||||
|
||||
Основной стек:
|
||||
|
||||
- язык: Python 3.13+;
|
||||
- HTTP API: FastAPI;
|
||||
- конфиги и схемы запроса/ответа: Pydantic v2;
|
||||
- app settings: `pydantic-settings`;
|
||||
- конфиги проекта: YAML;
|
||||
- event streaming для UI: SSE как основной transport;
|
||||
- worker transport: WebSocket;
|
||||
- structured logging: JSON lines через стандартный logging layer и event envelopes;
|
||||
- тесты: `pytest` + `pytest-asyncio`;
|
||||
- runtime packaging: стандартный `pyproject.toml`.
|
||||
|
||||
## Why This Stack
|
||||
|
||||
Python хорошо подходит для orchestration-heavy систем, где важнее:
|
||||
|
||||
- строгое моделирование состояния;
|
||||
- быстрая интеграция с внешними AI/MCP endpoint;
|
||||
- асинхронный I/O;
|
||||
- прозрачные схемы данных;
|
||||
- простая локальная разработка без контейнеров.
|
||||
|
||||
FastAPI выбран как delivery adapter, а не как центр архитектуры.
|
||||
Доменные модели и application services не должны зависеть от FastAPI.
|
||||
|
||||
## Consequences
|
||||
|
||||
Плюсы:
|
||||
|
||||
- быстрый путь к строгим контрактам;
|
||||
- хорошая ergonomics для async adapters;
|
||||
- удобный локальный запуск без Docker;
|
||||
- понятный переход к PostgreSQL и production deployment.
|
||||
|
||||
Минусы:
|
||||
|
||||
- нужен дисциплинированный layering, чтобы не “утонуть” в framework-driven code;
|
||||
- CPU-heavy задачи должны оставаться вне основного request loop.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- локальный Docker как обязательный dev path;
|
||||
- тяжелая зависимость от конкретной ORM на уровне domain;
|
||||
- framework-first архитектура.
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# ADR 002: Storage And Persistence Model
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
Проект не должен начинаться с чисто временного `in-memory` мышления, иначе потом придется переделывать:
|
||||
|
||||
- task lifecycle;
|
||||
- event log;
|
||||
- confirmations;
|
||||
- worker session tracking;
|
||||
- replay и audit.
|
||||
|
||||
## Decision
|
||||
|
||||
Хранилище проектируется как persistent-first:
|
||||
|
||||
- production target: PostgreSQL;
|
||||
- local development fallback: SQLite без Docker;
|
||||
- repository interfaces определяются в application/domain boundary;
|
||||
- materialized current state хранится отдельно от append-only event log;
|
||||
- критические переходы состояний должны логироваться как события.
|
||||
|
||||
## Data Model Principles
|
||||
|
||||
1. `tasks`, `task_nodes`, `confirmations`, `worker_sessions` хранят текущее состояние.
|
||||
2. `task_events` хранит audit trail и feed для replay/debugging.
|
||||
3. Вызовы моделей, MCP и worker сохраняются как отдельные invocation records.
|
||||
4. Артефакты хранят metadata в БД, а payload может лежать во внешнем файловом хранилище.
|
||||
|
||||
## Consequences
|
||||
|
||||
Плюсы:
|
||||
|
||||
- можно строить UI progress и audit без догадок;
|
||||
- проще реализовать replay/resume;
|
||||
- нет боли миграции с “простых dict” на реальную БД.
|
||||
|
||||
Минусы:
|
||||
|
||||
- немного более сложный старт;
|
||||
- нужно заранее аккуратно продумать схему.
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# ADR 003: User Event Stream Transport
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
UI требует поток событий по task execution:
|
||||
|
||||
- `task_started`
|
||||
- `node_started`
|
||||
- `node_completed`
|
||||
- `confirmation_required`
|
||||
- `task_completed`
|
||||
- `task_failed`
|
||||
|
||||
В исходных документах упомянуты `SSE/WebSocket`, но как основной transport нужно выбрать один.
|
||||
|
||||
## Decision
|
||||
|
||||
Основной transport для пользовательского event stream: Server-Sent Events.
|
||||
|
||||
WebSocket не исключается, но считается вторичным adapter для будущих realtime-потребностей.
|
||||
|
||||
## Rationale
|
||||
|
||||
SSE проще для:
|
||||
|
||||
- однонаправленного server-to-client прогресса;
|
||||
- проксирования;
|
||||
- reconnect semantics;
|
||||
- дебага и совместимости с обычными HTTP-инструментами.
|
||||
|
||||
WebSocket нужен не UI в первую очередь, а local worker transport.
|
||||
|
||||
## Consequences
|
||||
|
||||
`GET /tasks/{id}/events` фиксируется как SSE endpoint.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# ADR 004: Local Worker Transport
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
Local Worker работает на машине пользователя и должен:
|
||||
|
||||
- сам инициировать соединение;
|
||||
- не требовать входящего порта на пользовательском ПК;
|
||||
- поддерживать прогресс, heartbeat и dispatch commands.
|
||||
|
||||
## Decision
|
||||
|
||||
Основной transport для worker gateway: outbound WebSocket session от worker к серверу.
|
||||
|
||||
## Required Semantics
|
||||
|
||||
- registration handshake;
|
||||
- capability advertisement;
|
||||
- heartbeat;
|
||||
- command dispatch;
|
||||
- progress events;
|
||||
- result envelope;
|
||||
- cancellation;
|
||||
- reconnect with new session id.
|
||||
|
||||
## Consequences
|
||||
|
||||
Worker transport остается отдельным delivery/infrastructure adapter и не врастает в orchestration core.
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# ADR 005: Policy Evaluation Model
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
Ключевой принцип проекта: без hardcoded bans.
|
||||
Система должна принимать решения через policy engine, а не через встроенные запреты по имени tool или по содержимому команды.
|
||||
|
||||
## Decision
|
||||
|
||||
Policy engine работает в 4 шага:
|
||||
|
||||
1. request/action нормализуется в `ActionDescriptor`;
|
||||
2. descriptor классифицируется по `resource` и `risk_level`;
|
||||
3. policy evaluator находит эффективное правило;
|
||||
4. возвращается `PolicyDecision`.
|
||||
|
||||
## Core Types
|
||||
|
||||
`resource`:
|
||||
|
||||
- `filesystem`
|
||||
- `shell`
|
||||
- `sql`
|
||||
- `mcp`
|
||||
- `external_models`
|
||||
- `desktop`
|
||||
- `browser`
|
||||
- `network`
|
||||
- `cost`
|
||||
- `system`
|
||||
|
||||
`risk_level`:
|
||||
|
||||
- `safe`
|
||||
- `write`
|
||||
- `destructive`
|
||||
- `system`
|
||||
- `cost`
|
||||
|
||||
`decision`:
|
||||
|
||||
- `allow`
|
||||
- `confirm`
|
||||
- `manual`
|
||||
- `disabled_by_config`
|
||||
|
||||
## Important Rule
|
||||
|
||||
Policy engine не получает “tool name only” как источник истины.
|
||||
Он должен опираться на action classification и metadata от tool/model/worker adapters.
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# ADR 006: Event Taxonomy And Correlation
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
Оркестратор должен быть отлаживаемым и воспроизводимым.
|
||||
Для этого все значимые переходы должны иметь единый event model.
|
||||
|
||||
## Decision
|
||||
|
||||
Все события включают:
|
||||
|
||||
- `event_id`
|
||||
- `event_type`
|
||||
- `occurred_at`
|
||||
- `task_id`
|
||||
- `conversation_id` when available
|
||||
- `node_id` when available
|
||||
- `correlation_id`
|
||||
- `causation_id`
|
||||
- `payload`
|
||||
|
||||
## Event Families
|
||||
|
||||
1. Task lifecycle
|
||||
2. Node lifecycle
|
||||
3. Policy decisions
|
||||
4. Confirmation lifecycle
|
||||
5. Model invocation lifecycle
|
||||
6. Tool invocation lifecycle
|
||||
7. Worker session lifecycle
|
||||
8. Worker command lifecycle
|
||||
9. Finalizer/result lifecycle
|
||||
10. System warnings/errors
|
||||
|
||||
## Consequences
|
||||
|
||||
UI, audit log, replay tooling и debugging должны читать одну и ту же taxonomy, а не разрозненные лог-сообщения.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# ADR 007: Model Router Fallback Semantics
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
В проекте уже зафиксирована идея:
|
||||
|
||||
- сначала weak;
|
||||
- затем retry weak;
|
||||
- затем strong fallback;
|
||||
- external models не запрещаются кодом, а регулируются конфигом и policy.
|
||||
|
||||
## Decision
|
||||
|
||||
Fallback pipeline:
|
||||
|
||||
1. slot resolution выбирает provider и model для requested slot;
|
||||
2. weak model вызывается первой;
|
||||
3. если ответ невалиден по output contract, выполняется ограниченный retry weak;
|
||||
4. если weak исчерпан, router проверяет доступность strong;
|
||||
5. если strong разрешен config+policy, выполняется strong fallback;
|
||||
6. если strong недоступен, возвращается structured degraded result.
|
||||
|
||||
## Notes
|
||||
|
||||
- “невалиден” означает нарушение response schema, parser failure или явный quality rejection;
|
||||
- fallback trail сохраняется в invocation log;
|
||||
- внешняя strong model не вызывается, если `allow_external_models=false` или policy запрещает.
|
||||
|
||||
Reference in New Issue
Block a user