# ARCHITECTURE Этот документ фиксирует целевую архитектуру `ducklm` как локального event-driven multi-model execution runtime. `TASK_3.md` — это директива для ИИ-кодера. `ARCHITECTURE.md` — это короткая инженерная карта системы: что является ядром, какие есть слои, как течёт управление, где принимаются решения, а где только исполняются переходы. ## 1. Core Principle Система строится вокруг `Runtime Loop Controller`. Центр системы: - не `router` - не `orchestrator` - не `execution engine` Центр системы: - `runtime loop` Именно он замыкает жизненный цикл задачи: ```text task -> state load/create -> context build -> orchestration decision -> plan/directive -> execution -> critic -> memory policy -> checkpoint -> next step / complete / fail ``` ## 2. Layer Model Целевая форма системы: ```text Client / CLI / API | v Runtime Loop Controller | +--> State Store / Checkpoints +--> Context Builder +--> Router +--> Orchestrator / Planner +--> Execution Engine / Scheduler | | | +--> Tool Layer | +--> Coder | +--> Critic +--> Memory Write Policy +--> Memory Store + Vector Index +--> Event Bus + Event Store +--> Streaming Projection ``` Принцип: - `runtime loop` координирует - `router` рекомендует - `orchestrator` думает - `execution engine` исполняет - `tools/coder` делают работу - `critic` оценивает - `memory policy` решает запись - `event bus` фиксирует историю - `state store` даёт resume ## 3. Responsibility Boundaries ### Runtime Loop Controller Отвечает за: - task lifecycle - state transitions - вызов компонентов в правильном порядке - применение decision objects - checkpointing - completion / failure path Не отвечает за: - policy reasoning - raw tool execution - prompt assembly inline ### Router Это `policy evaluator + decision suggester`. Контракт: ```text (input state + assembled context) -> ExecutionDirective ``` Свойства: - pure function - no side effects - no tool execution - no state mutation ### Orchestrator / Planner Отвечает за: - orchestration reasoning - deciding whether planning is needed - generating plan JSON - returning structured directives Не отвечает за: - execution - direct state mutation - tool invocation ### Execution Engine / Scheduler Отвечает за: - step scheduling - task graph traversal - step execution coordination - calling tool/coder adapters - reporting structured results Не отвечает за: - ownership of global lifecycle - high-level policy ### Critic Отвечает за: - evaluation of tool/coder outputs - returning structured scores and explanation Не отвечает за: - final memory write decision - execution retry policy ### Memory Write Policy Отвечает за: - deterministic decision about storing memory - dedup / merge / skip behavior Не отвечает за: - semantic retrieval - critic scoring ## 4. Decision Model Все decision-producing components должны возвращать структурированные объекты. Базовый контракт: ```json { "type": "plan|tool|coder|respond|replan|store_memory|request_permission|complete|fail|noop", "payload": {}, "requires_permission": false, "confidence": 0.0, "reason": "string" } ``` Это главный антихаосный инвариант системы. Следствие: - компоненты не исполняют решения напрямую - компоненты не мутируют state напрямую - runtime loop применяет решения и переводит систему дальше ## 5. Execution Flow Нормальный путь выполнения: 1. Клиент отправляет task. 2. Runtime loop создаёт или загружает task state. 3. Публикуется `task_received`. 4. Context builder собирает execution context. 5. Router возвращает decision object. 6. Orchestrator возвращает direct action или plan. 7. План валидируется и преобразуется в task graph. 8. Execution engine выбирает следующий шаг. 9. Tool или coder исполняет шаг через adapter. 10. Result возвращается в runtime loop. 11. Critic возвращает evaluation suggestion. 12. Memory policy возвращает decision по записи. 13. State checkpoint сохраняется. 14. Event bus фиксирует события. 15. Runtime loop выбирает `continue / replan / complete / fail`. ## 6. Task Graph Model Внешний planner может вернуть список шагов. Внутри runtime план должен жить как task graph: ```json { "nodes": [ { "id": "step-1", "kind": "tool", "tool": "shell_exec", "args": {"command": "hostnamectl"}, "depends_on": [] } ] } ``` Сейчас допускается sequential DAG execution. В будущем это даёт путь к parallel scheduling без переписывания модели. ## 7. Event Backbone Система event-driven. `EventBus` нужен не только для стриминга, а как внутренняя хребтовая шина. Минимальные свойства: - ordering per task - monotonic sequence per task - durable append to event store - replay capability - consumer idempotency Минимальная модель доставки: - `at least once` Правило идемпотентности: - событие дедуплицируется по `task_id + sequence` Streaming layer — это projection от event bus, а не источник правды. ## 8. State Persistence Так как runtime задуман как long-running autonomous system, in-memory lifecycle недостаточен. Нужны: - task state store - checkpoint store - resume from crash/restart Минимальная стратегия: - checkpoint after critical transitions - latest valid checkpoint is resumable Primary choice для MVP: - `SQLite` ## 9. Async and Isolation LLM loop не должен блокироваться долгими tool operations. Поэтому нужны: - async execution adapters - timeout wrappers - cancellation handling - bounded concurrency Для опасных или тяжёлых операций нужен отдельный sandbox layer. Особенно для: - `shell_exec` - browser/web fallback - generated helper scripts ## 10. Memory Architecture Memory — отдельная подсистема хранения, а не JSON dump. Рекомендуемая форма: - metadata store: `SQLite` - vector index: `FAISS` или `hnswlib` Два разных процесса: - retrieval - write decision Это специально разделено. `critic` только оценивает. `memory write policy` принимает финальное решение. Минимальная логика записи должна быть детерминированной: ```text (critic_score + memory_type + runtime_weight + dedup_state + safety_state) -> decision ``` ## 11. Failure Model Система должна быть устойчивой к частичным сбоям. Ожидаемые controlled failure paths: - invalid planner output -> replan or fail - tool timeout -> retry or fail - critic failure -> fallback policy - memory failure -> skip write and continue where safe - streaming failure -> sync fallback Главный принцип: - subsystem failure не должен автоматически означать runtime collapse ## 12. Why This Shape Эта архитектура нужна, чтобы система не деградировала в один из плохих вариантов: - `router-god-object` - `runtime loop with hidden policy logic` - `LLM that directly executes tools` - `streaming instead of event model` - `critic as memory authority` - `in-memory only autonomous runtime` Если держать эти границы жёстко, проект остаётся расширяемым. Если границы размыть, система быстро превратится в трудноотлаживаемый procedural agent.