8.8 KiB
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
Именно он замыкает жизненный цикл задачи:
task
-> state load/create
-> context build
-> orchestration decision
-> plan/directive
-> execution
-> critic
-> memory policy
-> checkpoint
-> next step / complete / fail
2. Layer Model
Целевая форма системы:
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.
Контракт:
(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 должны возвращать структурированные объекты.
Базовый контракт:
{
"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
Нормальный путь выполнения:
- Клиент отправляет task.
- Runtime loop создаёт или загружает task state.
- Публикуется
task_received. - Context builder собирает execution context.
- Router возвращает decision object.
- Orchestrator возвращает direct action или plan.
- План валидируется и преобразуется в task graph.
- Execution engine выбирает следующий шаг.
- Tool или coder исполняет шаг через adapter.
- Result возвращается в runtime loop.
- Critic возвращает evaluation suggestion.
- Memory policy возвращает decision по записи.
- State checkpoint сохраняется.
- Event bus фиксирует события.
- Runtime loop выбирает
continue / replan / complete / fail.
6. Task Graph Model
Внешний planner может вернуть список шагов.
Внутри runtime план должен жить как task graph:
{
"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 принимает финальное решение.
Минимальная логика записи должна быть детерминированной:
(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-objectruntime loop with hidden policy logicLLM that directly executes toolsstreaming instead of event modelcritic as memory authorityin-memory only autonomous runtime
Если держать эти границы жёстко, проект остаётся расширяемым. Если границы размыть, система быстро превратится в трудноотлаживаемый procedural agent.