253 lines
16 KiB
Markdown
253 lines
16 KiB
Markdown
# DuckLM — Текущее состояние проекта
|
||
|
||
## 1. Что это
|
||
|
||
DuckLM — локальный event-driven multi-model AI agent runtime. Система принимает пользовательскую задачу, извлекает релевантную память, собирает контекст, принимает orchestration-решение, при необходимости строит план, исполняет шаги через tools и coder, оценивает результаты через critic, сохраняет полезное в долговременную память, публикует события и поддерживает streaming клиенту.
|
||
|
||
**Ключевой принцип:** центр системы — `RuntimeLoop`. Все execution transitions проходят через него. Router, Orchestrator, ExecutionEngine — decision-producing компоненты, которые только возвращают структурированные объекты (ExecutionDirective), но не исполняют действия напрямую.
|
||
|
||
## 2. Архитектура
|
||
|
||
```
|
||
Client / CLI / API
|
||
│
|
||
▼
|
||
RuntimeLoop (runtime_loop.py)
|
||
│
|
||
├── State Store / Checkpoints (SQLite)
|
||
├── ContextBuilder
|
||
├── AsyncRouter (Thinker → JSON Compiler)
|
||
├── ExecutionEngine / ExecutionScheduler
|
||
│ ├── ToolRegistry / ToolSandbox
|
||
│ ├── CoderAdapter
|
||
│ └── CriticAdapter
|
||
├── PermissionService
|
||
├── MemoryRecallService
|
||
├── MemoryWritePolicy
|
||
├── MemoryInterface (SQLite + hnswlib)
|
||
└── EventBus → SQLiteEventStore
|
||
│
|
||
▼
|
||
StreamingManager → WebSocket
|
||
```
|
||
|
||
## 3. Структура проекта
|
||
|
||
```
|
||
ducklm/
|
||
main.py # Точка входа (импорт app.api.server.app)
|
||
app/
|
||
api/
|
||
server.py # FastAPI: POST /chat, WS /stream, GET /health, etc.
|
||
static/index.html # Веб-чат (dark theme, Enter=отправить, Shift+Enter=новая строка)
|
||
cli/__init__.py # Пока пустой
|
||
core/
|
||
contracts.py # Pydantic модели: UserTask, PlanStep, ToolResult, CriticScore, ...
|
||
config.py # AppConfig, load_app_config()
|
||
async_router.py # AsyncRouter: Thinker + JSON Compiler pipeline
|
||
context_builder.py # ContextBuilder: сборка контекста с бюджетами
|
||
execution_engine.py # ExecutionEngine: исполнение plan/tool/respond/coder
|
||
execution_scheduler.py # ExecutionScheduler: парсинг плана, граф задач, цикл выполнения
|
||
intent_parser.py # IntentParser: извлечение tool intents из текста
|
||
permission_service.py # PermissionService: проверка и разрешений команд
|
||
permission_resolution.py # Pydantic модели для API разрешений
|
||
events/
|
||
event_bus.py # EventBus: per-task ordered publishing
|
||
event_store.py # SQLiteEventStore: append-only log
|
||
event_types.py # Константы типов событий
|
||
memory/
|
||
interface.py # MemoryInterface: insert/search/get/delete/reindex/cleanup
|
||
store.py # MemoryStore: SQLite хранение MemoryEntry + embeddings
|
||
vector_index.py # VectorIndex: hnswlib L2 index
|
||
recall.py # MemoryRecallService: LLM-based решение о необходимости recall
|
||
write_policy.py # MemoryWritePolicy: детерминированное решение о записи
|
||
models/
|
||
adapters.py # create_adapter/create_llama_adapter (llama-cpp-python)
|
||
async_adapters.py # AsyncOrchestratorAdapter, AsyncCoderAdapter, AsyncCriticAdapter
|
||
orchestrator.py # OrchestratorAdapter: обёртка над Llama
|
||
coder.py # CoderAdapter
|
||
critic.py # CriticAdapter
|
||
embeddings.py # EmbeddingsAdapter (sentence-transformers)
|
||
permissions/
|
||
approval_store.py # SQLiteApprovalStore
|
||
runtime/
|
||
runtime_loop.py # RuntimeLoop: центральный цикл (sync)
|
||
async_runtime_loop.py # AsyncRuntimeLoop: альтернативная async версия
|
||
runtime_controller.py # RuntimeController: composition root, инициализация всего
|
||
services/__init__.py # Пустой
|
||
state/
|
||
task_state_store.py # SQLiteTaskStateStore
|
||
checkpoint_store.py # SQLiteCheckpointStore
|
||
streaming/
|
||
manager.py # StreamingManager: подписка на события → WebSocket
|
||
tools/
|
||
base.py, registry.py, sandbox.py, discover.py
|
||
shell_exec.py, file_read.py, file_write.py, memory_tools.py
|
||
plugins/ # Plugin discovery: shell_exec, file_read, file_write, memory_tools
|
||
config/
|
||
models.json # Конфигурация моделей
|
||
runtime.json # Таймауты, retry limits, context budgets
|
||
permissions.json # Категории команд, пути
|
||
prompts/ # Markdown промпты для каждой роли
|
||
thinker.md, json_compiler.md, coder.md, critic.md, sys_util.md, orchestrator.md, planning.md, system.md
|
||
data/
|
||
events/events.sqlite3 # Event store
|
||
state/task_state.sqlite3 # Task state
|
||
state/checkpoints.sqlite3 # Checkpoints
|
||
permissions/approvals.sqlite3 # Permission cache
|
||
memory/memory.sqlite3 # Memory store
|
||
memory/index.bin # Vector index
|
||
models/ # GGUF модели и sentence-transformers
|
||
tests/
|
||
test_contracts.py # 6 тестов: контракты, router
|
||
test_runtime_loop.py # 2 теста: runtime loop events, permission flow
|
||
test_tools_flow.py # 7 тестов: file read/write, shell, recovery, permissions
|
||
test_api_handlers.py # 6 тестов: health, events, chat, permissions, feedback
|
||
```
|
||
|
||
## 4. Модели и их роли
|
||
|
||
| Роль | Модель | Backend | Конфиг |
|
||
|------|--------|---------|--------|
|
||
| Thinker (orchestrator) | Qwen3.5-9B-GLM5.1-Distill-v1-Q4_K_M.gguf | vulkan (GPU) | max_tokens=2048, temp=0.3 |
|
||
| JSON Compiler | gemma-4-E4B-it-Q4_K_M.gguf | cpu | max_tokens=1024, temp=0.1 |
|
||
| Critic | gemma-4-E4B-it-Q4_K_M.gguf (shared с compiler) | cpu | max_tokens=1024, temp=0.1 |
|
||
| Coder | X-Coder-SFT-Qwen3-8B.Q6_K.gguf | cpu | max_tokens=2048, temp=0.2 |
|
||
| Sys Utility | Menlo_Lucy-Q4_K_M.gguf | cpu | max_tokens=1024, temp=0.1 |
|
||
| Embeddings | all-MiniLM-L6-v2 (sentence-transformers) | — | dim=384 |
|
||
|
||
**Важно:** Critic и JSON Compiler используют одну и ту же модель (gemma-4B), но разные экземпляры адаптеров. Модели не дублируются в памяти — используется кэширование через `_get_or_create_llm()` с ключом (path, backend, n_gpu_layers, n_ctx).
|
||
|
||
## 5. Конфигурация
|
||
|
||
Все настройки в `config/`:
|
||
- **models.json** — пути к GGUF файлам, backend, GPU layers, max_tokens, temperature
|
||
- **runtime.json** — таймауты (step=30s, task=5min), retry limits, context budgets, retrieval_top_k
|
||
- **permissions.json** — hard_stop команды (rm -rf /, dd, mkfs), no_always команды (shutdown, killall), normal команды
|
||
- **prompts/*.md** — системные промпты для каждой роли модели
|
||
|
||
## 6. API
|
||
|
||
FastAPI сервер на порту 8000 (`scripts/server.sh`):
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| GET | `/` | Веб-чат (index.html) |
|
||
| GET | `/health` | Health check |
|
||
| GET | `/events` | Список последних событий |
|
||
| POST | `/chat` | Отправить задачу (UserTask) → получить результат |
|
||
| POST | `/permissions/resolve` | Разрешить/запретить команду |
|
||
| POST | `/secrets/resolve` | Передать sudo-пароль |
|
||
| POST | `/password/resolve` | Передать пароль (альтернативный путь) |
|
||
| POST | `/critic/feedback` | Обратная связь от пользователя |
|
||
| WS | `/stream/{task_id}` | Streaming событий по задаче |
|
||
|
||
## 7. Поток выполнения задачи
|
||
|
||
1. Клиент → POST /chat → `RuntimeController.handle_task()`
|
||
2. `RuntimeLoop.run_task()`:
|
||
- Проверка hard-stop команд через PermissionService
|
||
- Создание task state в SQLiteTaskStateStore
|
||
- Публикация TASK_RECEIVED
|
||
- Checkpoint: received
|
||
- ContextBuilder.build() — сборка контекста (memory, tools, budgets)
|
||
- MemoryRecallService.recall() — LLM решает, нужно ли искать в памяти
|
||
- AsyncRouter.decide() — Thinker → JSON Compiler → ExecutionDirective
|
||
- ExecutionEngine.execute() — исполнение directive:
|
||
- plan → парсинг шагов → граф → последовательное выполнение
|
||
- tool → проверка разрешений → ToolSandbox → ToolResult
|
||
- respond → прямой ответ
|
||
- coder → CoderAdapter
|
||
- Critic оценка каждого шага (correctness, usefulness, safety)
|
||
- Recovery при неудачных шагах (retry/continue/respond/fail)
|
||
- MemoryWritePolicy — решение о записи в долговременную память
|
||
- Checkpoint: final state
|
||
- Публикация TASK_COMPLETED / TASK_FAILED / TASK_AWAITING_PERMISSION
|
||
3. Результат возвращается клиенту + события доступны через WebSocket
|
||
|
||
## 8. Что реализовано и работает
|
||
|
||
### Core (полностью)
|
||
- [x] Модульная структура проекта (app/, config/, data/, tests/)
|
||
- [x] Typed contracts (Pydantic модели для всех сущностей)
|
||
- [x] RuntimeLoop — центральный цикл
|
||
- [x] RuntimeController — composition root
|
||
- [x] EventBus + SQLiteEventStore (append-only, per-task ordering)
|
||
- [x] TaskStateStore + CheckpointStore (SQLite)
|
||
- [x] ContextBuilder с token budgets
|
||
- [x] AsyncRouter: Thinker → JSON Compiler pipeline с retry и JSON fix
|
||
- [x] IntentParser: извлечение tool intents из естественного языка
|
||
- [x] ExecutionEngine: plan/tool/respond/coder/fail
|
||
- [x] ExecutionScheduler: парсинг плана, DAG граф, cycle detection
|
||
- [x] PermissionService: hard_stop/no_always/normal категории, кэш разрешений
|
||
- [x] ToolSandbox: timeout, cwd restrictions
|
||
- [x] ToolRegistry + Plugin Discovery
|
||
- [x] Tools: shell_exec, file_read, file_write, memory_insert/search/list
|
||
- [x] CriticAdapter с retry и recovery (continue/retry/respond/fail)
|
||
- [x] MemoryInterface: SQLite + hnswlib vector index
|
||
- [x] MemoryRecallService: LLM-based решение о необходимости recall
|
||
- [x] MemoryWritePolicy: детерминированное решение о записи
|
||
- [x] EmbeddingsAdapter (sentence-transformers)
|
||
- [x] FastAPI API: /chat, /health, /events, /permissions/resolve, /secrets/resolve, /critic/feedback
|
||
- [x] WebSocket streaming (/stream/{task_id})
|
||
- [x] Веб-чат (dark theme, Enter=отправить, Shift+Enter=новая строка, панель событий, permission controls, feedback dialog)
|
||
- [x] 21 тест (все проходят)
|
||
|
||
### Известные баги (исправлены)
|
||
- RECALL_PROMPT_TEMPLATE format string escaping — фигурные скобки в JSON примерах нужно двоить
|
||
- VectorIndex._get_memory_id возвращал неправильный ID (hash вместо хранения mapping)
|
||
- recall_model по умолчанию был sys_util, изменён на json_compiler
|
||
|
||
## 9. Что ещё нужно сделать
|
||
|
||
### Приоритет 1 — Доработка до полного MVP
|
||
- [ ] **Resume из checkpoint** — после падения/перезапуска восстанавливать задачу из последнего checkpoint
|
||
- [ ] **CLI интерфейс** — отправка задач, просмотр событий, поиск в памяти из терминала (app/cli/ пока пустой)
|
||
- [ ] **Structured logging** — вместо print() использовать logging с форматированием
|
||
- [ ] **WS /stream** — доработать (сейчас базово работает, но нет подписки на новые события в реальном времени при длительных задачах)
|
||
|
||
### Приоритет 2 — Улучшения
|
||
- [ ] **Retry/recovery policy** — более надёжная обработка ошибок tool execution
|
||
- [ ] **Replay из event store** — воспроизведение истории задачи для отладки
|
||
- [ ] **Параллельное выполнение шагов** — сейчас только sequential DAG, можно добавить parallel для независимых шагов
|
||
- [ ] **Веб-чат: отображение streaming ответа** — сейчас ответ приходит целиком, можно добавить потоковую передачу
|
||
- [ ] **Веб-чат: отображение tool output** — более красивый рендер результатов shell/file операций
|
||
- [ ] **Memory cleanup** — автоматическая очистка старых/низко-весовых записей (базовая логика есть в MemoryInterface.cleanup, но не вызывается автоматически)
|
||
|
||
### Приоритет 3 — Расширения
|
||
- [ ] **web_search / web_fetch tools** — второй приоритет по TASK_3.md
|
||
- [ ] **Telegram bot stub** — thin клиент для удалённого управления
|
||
- [ ] **Coder integration в план** — пока coder adapter есть, но не интегрирован в планирование как отдельный step kind
|
||
- [ ] **Модели: загрузка при старте** — load_models_at_startup() вызывается из lifespan, но если модели не загружены, runtime работает в fallback mode (respond only)
|
||
- [ ] **Документация API** — OpenAPI схема генерируется FastAPI, но можно добавить примеры
|
||
|
||
## 10. Запуск
|
||
|
||
```bash
|
||
cd ~/git/ducklm
|
||
./scripts/server.sh
|
||
# или
|
||
uvicorn main:app --host 0.0.0.0 --port 8000
|
||
```
|
||
|
||
Веб-чат: http://localhost:8000/
|
||
|
||
## 11. Тестирование
|
||
|
||
```bash
|
||
cd ~/git/ducklm
|
||
python -m pytest tests/ -v
|
||
```
|
||
|
||
21 тест, все проходят. Покрытие: контракты, runtime loop, tool flow, API handlers.
|
||
|
||
## 12. Технологии
|
||
|
||
- **Python 3.13**, FastAPI, uvicorn, websockets
|
||
- **llama-cpp-python** — локальный инференс GGUF моделей (Vulkan/CPU)
|
||
- **sentence-transformers** — эмбеддинги (all-MiniLM-L6-v2)
|
||
- **hnswlib** — векторный поиск (L2 метрика)
|
||
- **SQLite** — event store, task state, checkpoints, memory, permissions
|
||
- **Pydantic** — все контракты
|
||
- **pytest** — тестирование
|