ducklm/CURRENT_STATE.md

463 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DuckLM — Текущее состояние проекта
**Дата анализа:** 2026-05-21
**Версия:** 0.2.0
**Расположение:** `~/git/ducklm_2`
---
## Последние изменения (Phase 1-7)
### Phase 1: MemoryPolicy — LLM-классификация памяти ✅
- `duck_core/memory/policy.py` — переписан с нуля: LLM-классификация через critic-роль
- Роль `memory_policy` добавлена в `config/models.yaml` + промпт
- Интегрирован в RuntimeLoop: `_run_memory_policy()` после каждой задачи
- События: `memory_policy_decision`, `memory_stored`, `memory_policy_failed`
- 6 тестов в `tests/smoke/test_memory_policy.py`
### Phase 2: Рефлексия (Critic) — автоматический вызов ✅
- `_run_reflection()` в RuntimeLoop — transcript из event store → critic → experience
- Параметр `reflect: bool = True` в `run_chat()`
- `ExperienceRecorder` передаётся через `create_app()`
- События: `reflection_completed`, `reflection_failed`
- 3 теста в `tests/smoke/test_reflection.py`
### Phase 3-4: ContextBuilder v2 + Summary-роль ✅
- Полностью переписан `duck_core/context_builder.py`
- Token budget awareness, приоритизация, суммаризация через summary-роль
- `estimate_tokens()`, `estimate_messages_tokens()` утилиты
- Подключён model_client для LLM-суммаризации
- 11 тестов в `tests/smoke/test_context_builder.py`
### Phase 5: VectorMemory — интеграция ✅
- `VectorMemory` добавлен в RuntimeLoop и `create_app()`
- **Локальная модель эмбеддингов**: `all-MiniLM-L6-v2` (384 размерности, sentence-transformers)
- При `memory_stored` также сохраняется в Qdrant (graceful fallback при ошибках)
- Поддержка двух режимов: локальная модель + remote `/v1/embeddings` endpoint
- `sentence-transformers` добавлен в зависимости `pyproject.toml`
- 4 теста в `tests/smoke/test_vector_memory_integration.py`
### Embeddings — архитектура
### Phase 6: Recall-роль ✅
- Роль `recall` добавлена в `config/models.yaml` + промпт `prompts/roles/recall.md`
- `ContextBuilder.recall_relevant_memory()` — LLM-фильтрация релевантных воспоминаний
- Интегрирован в `/v1/chat` endpoint
### Phase 7: Coder-роль — интеграция ✅
- `CoderTool` создан в `duck_core/tools/coder.py`
- Зарегистрирован в `ToolGateway.default()`
- Описан в `prompts/roles/action.md`
### Статус тестов
- **72 из 73** smoke-тестов проходят
- 1 тест (`test_llama_server_connection_live_skip_by_env`) требует живой llama-server
---
## 1. Что такое DuckLM
DuckLM — это **локальная агентная система (cognitive runtime)**, работающая поверх локальных языковых моделей через `llama-server`. Это не inference-сервер, а полноценный когнитивный цикл:
```
состояние → контекст → мышление → намерение → действие → наблюдение → рефлексия → память → опыт
```
Ключевая идея: DuckLM — это **оркестратор**, который управляет задачами, инструментами, памятью, навыками и рефлексией, используя локальные LLM через OpenAI-совместимый API (`llama-server`).
---
## 2. Архитектурные принципы
### 2.1. Использование готовых компонентов
| Компонент | Источник |
|-----------|----------|
| LLM inference | `llama-server` (llama.cpp, собранный с Vulkan) |
| Хранение состояния | SQLite (aiosqlite) |
| Векторная память | Qdrant (через docker-compose) |
| HTTP API | FastAPI |
| Web-интерфейс | Jinja2 + ванильный JS |
| Валидация данных | Pydantic |
| Конфигурация | PyYAML + python-dotenv |
**Не пишется с нуля:** inference server, model scheduler, vector DB, OpenAI API, MCP, песочница, workflow engine.
**Пишется с нуля:** Duck Core (runtime loop, context builder, model client, event store, tool gateway, approvals, skills, experience, memory policy, FastAPI API, WebChat).
### 2.2. Web/API first
- **WebChat** — интерфейс для человека (порт 8000)
- **HTTP API** — для кодера, тестов и внешних агентов
- CLI не входит в обязательную часть (если понадобится — тонкий клиент поверх HTTP API)
### 2.3. Роли моделей — логические, не физические
Роли: `thinker`, `critic`, `coder`, `action`, `summary`, `recall`, `sys_util`.
Все роли в текущей конфигурации указывают на одну физическую модель (`local-main` на порту 8081). Различие между ролями задаётся комбинацией:
- system prompt
- temperature
- max_output_tokens
- response_format / structured_output
- memory scope
- tool permissions
### 2.4. Token budget
```
DUCK_CTX_SIZE=4096 (в .env, хотя в коде дефолт 65536)
DUCK_MAX_INPUT_TOKENS=49152
DUCK_MAX_RECENT_EVENTS_TOKENS=12000
DUCK_MAX_MEMORY_TOKENS=8000
DUCK_MAX_SKILL_TOKENS=6000
```
Output limits по ролям:
- thinker: 8192
- critic: 4096
- coder: 16384
- action: 2048
- summary: 4096
---
## 3. Целевая архитектура (из ТЗ)
```
┌─────────────┐
│ WebChat │ ← интерфейс человека
└──────┬──────┘
┌─────────────┐
│ FastAPI │ ← интерфейс кодера, тестов и агентов
└──────┬──────┘
┌─────────────────────────────────────┐
│ Duck Core │
│ RuntimeLoop, TaskState, │
│ ContextBuilder, ModelClient, │
│ SkillRegistry, ToolGateway, │
│ ApprovalService, Reflection, │
│ MemoryPolicy, ExperienceRecorder │
└──────┬──────────────┬───────────────┘
│ │
▼ ▼
┌────────────┐ ┌──────────────────┐
│llama-server│ │ SQLite/PostgreSQL│
│OpenAI-comp.│ │ events/tasks/ │
└────────────┘ │ approvals │
│ └──────────────────┘
┌────────────┐
│ Qdrant │ ← semantic memory
└────────────┘
```
---
## 4. Что реализовано (текущее состояние)
### 4.1. Полностью реализованные компоненты
#### Конфигурация и настройки
- **`duck_core/config.py`** — `Settings` dataclass, загрузка из `.env`, кэширование через `lru_cache`
- **`config/models.yaml`** — 5 ролей (thinker, critic, coder, action, summary), все на `local-main` (порт 8081)
- **`.env`** / **`.env.example`** — полная конфигурация путей, портов, GPU, Qdrant
#### ModelClient
- **`duck_core/model_client.py`** — ролевая маршрутизация вызовов к llama-server
- `chat()` — синхронный вызов с измерением latency, usage
- `stream_chat()` — streaming через SSE (reasoning_delta + content_delta)
- `ping()` — проверка доступности всех ролей
- Автоматическая подстановка system prompt из файла
- Автоматический `response_format: json_schema` для action-роли
#### Хранение состояния (SQLite)
- **`duck_core/tasks/state.py`** — `TaskState` (Pydantic модель)
- **`duck_core/tasks/store.py`** — `TaskStore`: create, update_status, complete, fail, cancel, waiting_for_approval, get, list
- **`duck_core/events/store.py`** — `EventStore`: append (с авто-increment sequence), list_events, list_by_type
- **`duck_core/conversations/store.py`** — `ConversationStore`: create, ensure, get, list, add_message, list_messages, get_conversation_id_for_task
- **`duck_core/approvals/service.py`** — `ApprovalService`: create_pending, pending, get, allow_once, allow_forever, deny, is_allowed_forever
- **`duck_core/experience/recorder.py`** — `ExperienceRecorder`: record, list_records, get_record, write_skill_update_proposal
- **`duck_core/memory/store.py`** — `MemoryStore`: add, list, search (LIKE), relevant (scope-aware), infer_scope, _normalize_scope
#### Runtime Loop
- **`duck_core/runtime_loop.py`** — ядро когнитивного цикла:
- `run_chat()` — полный цикл: создание задачи → action loop → thinker → завершение
- `continue_after_approval()` — продолжение после одобрения действия
- `_run_action_loop()` — итеративный цикл вызова инструментов (max 4 итерации)
- `_run_action_tools()` — парсинг action directive от модели → вызов ToolGateway
- `_append_command_audit()` — аудит shell-команд через event store
- Обработка requires_approval → пауза с ожиданием решения пользователя
#### Context Builder
- **`duck_core/context_builder.py`** — `ContextBuilder.build_basic_messages()`: собирает сообщения из memory records, history и текущего user message
#### Tools
- **`duck_core/tools/base.py`** — `ToolResult` (Pydantic), `Tool` (Protocol)
- **`duck_core/tools/gateway.py`** — `ToolGateway`: маршрутизация action → конкретный инструмент
- **`duck_core/tools/file_read.py`** — `FileReadTool`: чтение файлов внутри workspace, запрет .env/.ssh/shadow
- **`duck_core/tools/file_write.py`** — `FileWriteTool`: запись внутри workspace, защита от перезаписи
- **`duck_core/tools/list_dir.py`** — `ListDirTool`: листинг директории внутри workspace
- **`duck_core/tools/search_files.py`** — `SearchFilesTool`: текстовый поиск по файлам (glob, case_sensitive)
- **`duck_core/tools/shell_exec_safe.py`** — `ShellExecSafeTool`: allowlist + blocklist + approval
- **`duck_core/tools/command_policy.py`** — `CommandPolicy`: классификация команд (readonly, system, destructive, dangerous fragments)
- **`duck_core/tools/paths.py`** — `resolve_workspace_path()`: защита от path traversal
#### Approvals
- **`duck_core/approvals/service.py`** — полный цикл согласований:
- Создание pending approval с SHA256-хешем действия
- Решения: allow_once, allow_forever, deny
- Проверка is_allowed_forever по хешу действия
- normalized_action хранится в JSON
#### Skills
- **`duck_core/skills/registry.py`** — `SkillRegistry`: загрузка из `*/skill.yaml`, парсинг procedure/examples/notes, поиск по ключевым словам
- **`skills/analyze_project/`** — единственный скилл: анализ структуры проекта
#### Experience & Reflection
- **`duck_core/experience/recorder.py`** — запись результатов задач, предложения по обновлению скиллов
- **`duck_core/reflection.py`** — `Reflection.reflect()`: вызов critic-роли для анализа транскрипта задачи
#### Memory
- **`duck_core/memory/store.py`** — `MemoryStore`: хранение в SQLite с поддержкой scope (global/workspace/conversation), importance, полнотекстовый поиск (LIKE)
- **`duck_core/memory/policy.py`** — `MemoryPolicy`: заглушка (всегда should_store=False)
- **`duck_core/memory/vector_memory.py`** — `VectorMemory`: интеграция с Qdrant для семантического поиска (требует embeddings endpoint)
#### FastAPI API
- **`duck_core/api.py`** — полный HTTP API (878 строк):
- `POST /v1/chat` — основной чат с сохранением в conversation
- `POST /v1/chat/stream` — streaming чат через SSE
- `POST /v1/tasks/{task_id}/continue/stream` — продолжение после одобрения
- `POST /v1/tasks/{task_id}/password/stream` — ввод sudo-пароля
- `GET/POST /v1/conversations` — управление диалогами
- `GET /v1/tasks`, `GET /v1/tasks/{task_id}/events` — инспекция задач
- `GET /v1/approvals/pending`, `POST /v1/approvals/{id}/allow_once|allow_forever|deny`
- `GET /v1/skills`, `GET /v1/skills/{skill_id}`
- `GET /v1/experience`
- `POST /v1/memory`, `GET /v1/memory`, `GET /v1/memory/search`
- `GET /v1/models/roles`, `GET /v1/models/ping`
- `GET /health`, `GET /v1/status`
- Веб-страницы: `/`, `/approvals`, `/skills`, `/memory`, `/experience`
#### WebChat UI
- **`duck_core/web/templates/index.html`** — полноценный WebChat с sidebar, conversation list, activity drawer
- **`duck_core/web/static/app.js`** (997 строк) — клиентская логика:
- SSE streaming с парсингом reasoning_delta, content_delta, tool_call_started/finished, tool_approval_requested, tool_password_requested
- Инлайн-терминалы для отображения вызовов инструментов
- Инлайн-кнопки одобрения/запрета действий
- Форма ввода sudo-пароля
- Activity drawer с вкладками Events/Commands/Memory
- Управление диалогами (create, select, load history)
- Enter для отправки, Shift+Enter для новой строки
- **`duck_core/web/static/style.css`** (1002 строки) — светлая тема, responsive layout
#### Скрипты
- **`scripts/llama/start_main.sh`** — управление llama-server (start/stop/restart/status/logs)
- **`scripts/llama/start_thinker_mtp_experimental.sh`** — экспериментальный MTP endpoint
- **`scripts/llama/build_vulkan.sh`** — сборка llama.cpp с Vulkan
- **`scripts/llama/healthcheck.sh`** — проверка здоровья llama-server
- **`scripts/verify/`** — 7 верификационных скриптов (basic_chat, file_write_read, tool_blocking, models_roles, skills, experience, memory)
- **`scripts/bench/bench_runtime.py`** — бенчмарк
#### Тесты
- 18 smoke-тестов в `tests/smoke/`:
- `test_models_config.py`, `test_model_client.py`, `test_api_health.py`
- `test_event_log.py`, `test_action_directive_schema.py`
- `test_tool_gateway.py`, `test_approvals.py`
- `test_skill_registry.py`, `test_experience_recorder.py`
- `test_vector_memory.py`, `test_memory_store.py`
- `test_chat_api.py`, `test_conversations.py`
- `test_runtime_reasoning.py`, `test_runtime_tools.py`
- `test_llama_server_connection.py`, `test_llama_service_script.py`
#### Документация
- 12 файлов в `docs/`:
- `architecture.md`, `how_to_run.md`, `how_to_test.md`
- `web_api.md`, `model_roles.md`, `tool_gateway.md`
- `memory_architecture.md`, `experience_learning.md`, `skills.md`
- `local_llama_server.md`, `performance_mtp.md`
- `superpowers/plans/2026-05-19-ducklm-runtime.md` — план реализации
#### Docker
- **`docker-compose.memory.yml`** — Qdrant (порты 6333/6334)
#### Сборка и запуск
- **`pyproject.toml`** — зависимости: fastapi, uvicorn, httpx, pydantic, pyyaml, jinja2, python-dotenv, jsonschema, aiosqlite, qdrant-client
- **`Makefile`** — цели: duck-up, duck-llama-main, duck-api, duck-dev, duck-smoke, duck-test, duck-verify
- **`data/duck.sqlite3`** — рабочая БД SQLite
- **`workspace/`** — рабочая директория для инструментов
---
## 5. План разработки (из `docs/superpowers/plans/2026-05-19-ducklm-runtime.md`)
План состоит из 4 задач:
### Task 1: Tests First
- Написать smoke tests для всех компонентов
-**Выполнено** — 18 тестов созданы
### Task 2: Runtime Core
- pyproject.toml, .env.example, config/models.yaml
- config.py, model_client.py, events/store.py, tasks/store.py, tasks/state.py
- context_builder.py, runtime_loop.py, api.py
-**Выполнено** — все компоненты реализованы
### Task 3: Stage Adapters
- tools/*, approvals/service.py, skills/registry.py
- experience/recorder.py, reflection.py, memory/*
- schemas/action_directive.schema.json
-**Выполнено** — все компоненты реализованы
### Task 4: Project Surface
- scripts/llama/*, scripts/verify/*, scripts/bench/*
- web/templates/*, web/static/*
- skills/analyze_project/*
- docker-compose.memory.yml, Makefile, README.md, docs/*
-**Выполнено** — все компоненты реализованы
---
## 6. Отступления от плана / дополнительные возможности
### 6.1. Что добавлено сверх плана
1. **ConversationStore** — полноценное управление диалогами (conversations + conversation_messages таблицы), чего не было явно в плане Task 2. План упоминал только tasks и events.
2. **Streaming API**`POST /v1/chat/stream` с SSE, `POST /v1/tasks/{id}/continue/stream`, `POST /v1/tasks/{id}/password/stream`. В плане не было явно указано streaming.
3. **Password flow** — полный цикл запроса sudo-пароля: `requires_password``tool_password_requested``/v1/tasks/{id}/password/stream`. В плане не было детализировано.
4. **Activity Drawer в WebChat** — боковая панель с вкладками Events/Commands/Memory, инлайн-терминалы для инструментов, инлайн-одобрения. Значительно больше, чем «пустая WebChat-страница» из этапа 1 ТЗ.
5. **Command Audit** — отдельный тип события `command_audit` для shell-команд с полной метаданной (action_type, risk_level, blocked, approved, returncode).
6. **Scope-aware Memory** — трёхуровневая система скоупов (global/workspace/conversation) с автоматическим infer_scope.
7. **Skill update proposals** — автоматическая запредложений по обновлению скиллов в `skills/_proposals/`.
8. **18 тестов вместо 11 запланированных** — добавлены: `test_chat_api`, `test_conversations`, `test_runtime_reasoning`, `test_runtime_tools`, `test_llama_server_connection`, `test_llama_service_script`, `test_memory_store`.
### 6.2. Что не реализовано (или реализовано частично)
1. **MemoryPolicy — заглушка.** `MemoryPolicy.classify()` всегда возвращает `should_store=False`. Нет LLM-классификации для автоматического сохранения памяти.
2. **ContextBuilder — минимальный.** Нет суммаризации старых events, нет обрезки по token budget, нет приоритизации контекста. Просто склеивает memory + history + user message.
3. **Critic не вызывается автоматически.** `Reflection.reflect()` есть, но не интегрирован в RuntimeLoop — нет автоматической рефлексии после завершения задачи.
4. **Summary роль не используется.** Нет автоматической суммаризации контекста при превышении budget.
5. **Coder роль не используется в основном потоке.** RuntimeLoop вызывает только action и thinker.
6. **Recall роль не определена в конфиге.** В ТЗ упоминается recall, но в `config/models.yaml` её нет.
7. **Sys_util роль не определена в конфиге.** Аналогично.
8. **VectorMemory не интегрирован в RuntimeLoop.** Qdrant-поиск не подключён к основному циклу (MemoryStore использует LIKE-поиск, а не векторный).
9. **WebChat — светлая тема.** В памяти пользователя указано предпочтение тёмной темы, но CSS реализован светлый (`color-scheme: light`, белый фон).
10. **Нет CLI.** Упомянуто в ТЗ как необязательное, но если понадобится — нужно делать.
11. **Нет автоматического применения skill patches.** Предложения пишутся в `skills/_proposals/`, но не применяются автоматически.
### 6.3. Технические заметки
- **Модель:** Qwen3.6 35B A3B, два варианта — nonMTP (основной, порт 8081) и MTP (экспериментальный, порт 8085)
- **GPU:** Radeon RX580, Vulkan backend, 20 GPU layers
- **llama-server бинарник:** `./vendor/llama.cpp/build/bin/llama-server`
- **ctx_size в .env:** 4096 (хотя в коде Settings дефолт 65536)
- **reasoning-budget:** 512 в .env, `--reasoning-budget 512 --cache-ram 0`
- **Python:** 3.13 (по путям `__pycache__`)
---
## 7. Структура базы данных (SQLite)
Таблицы:
- **tasks** — задачи (task_id, status, user_message, workspace, debug, final_response, created_at, updated_at)
- **events** — события (id, task_id, sequence, event_type, payload_json, created_at)
- **conversations** — диалоги (id, conversation_id, title, workspace, created_at, updated_at)
- **conversation_messages** — сообщения диалогов (id, conversation_id, role, content, reasoning_content, task_id, status, created_at)
- **approvals** — согласования (id, approval_id, task_id, action_hash, normalized_action_json, status, decision, created_at, updated_at)
- **experience_records** — записи опыта (id, task_id, skill_id, summary, result, what_worked_json, what_failed_json, reusable_lesson, suggested_skill_patch, confidence, created_at)
- **memories** — память (id, memory_id, text, scope, workspace, conversation_id, memory_type, importance, metadata_json, created_at, updated_at)
---
## 8. Когнитивный цикл (как работает RuntimeLoop)
1. Пользователь отправляет сообщение → `POST /v1/chat` или `/v1/chat/stream`
2. Создаётся Task + событие `task_created`
3. ContextBuilder собирает сообщения (memory + history + user message)
4. **Action loop** (до 4 итераций):
- Модель `action` генерирует JSON directive (schema: action_directive.schema.json)
- ToolGateway выполняет каждый action через соответствующий инструмент
- Если команда требует approval → пауза, создание Approval, ожидание решения
- Если sudo → запрос пароля
- Результаты собираются как tool_observations
5. Thinker получает все tool_observations и формирует финальный ответ
6. Задача завершена → `task_completed`
7. (Опционально) Reflection через critic — **не автоматизировано**
---
## 9. Статус готовности
| Компонент | Статус |
|-----------|--------|
| Конфигурация | ✅ Готово |
| ModelClient | ✅ Готово |
| TaskStore / EventStore | ✅ Готово |
| ConversationStore | ✅ Готово |
| RuntimeLoop | ✅ Готово |
| ContextBuilder | ⚠️ Минимальный |
| ToolGateway + Tools | ✅ Готово |
| ApprovalService | ✅ Готово |
| SkillRegistry | ✅ Готово |
| ExperienceRecorder | ✅ Готово |
| Reflection | ⚠️ Не интегрирован в loop |
| MemoryStore (SQLite) | ✅ Готово |
| MemoryPolicy | ✅ LLM-based (Phase 1) |
| VectorMemory (Qdrant) | ✅ Интегрирован (Phase 5) |
| FastAPI API | ✅ Готово |
| WebChat UI | ✅ Готово (светлая тема) |
| Streaming | ✅ Готово |
| Password flow | ✅ Готово |
| Smoke tests | ✅ 74 теста |
| Docs | ✅ 12 файлов |
| Scripts | ✅ Готово |
---
## Архитектура эмбеддингов
### Локальная модель (основной режим)
- **Модель**: `all-MiniLM-L6-v2` (sentence-transformers, 384 размерности)
- **Расположение**: `./models/all-MiniLM-L6-v2/` (safetensors формат)
- **Библиотека**: `sentence-transformers` (добавлен в pyproject.toml)
- **Использование**: `VectorMemory._local_embed()` — загрузка модели через `SentenceTransformer`, кодирование в thread pool
### Remote endpoint (fallback)
- **Endpoint**: `/v1/embeddings` на llama-server или OpenAI-совместимом сервере
- **Использование**: `VectorMemory._remote_embed()` — HTTP POST запрос
### Поток данных
1. Зача завершена → `_run_memory_policy()` → LLM классифицирует → `MemoryDecision`
2. Если `should_store=True``MemoryStore.add()` (SQLite) + `VectorMemory.add_memory()` (Qdrant)
3. При следующем запросе → `MemoryStore.relevant()` (SQLite LIKE) + `VectorMemory.search_memory()` (semantic)
4. Recall-роль фильтрует релевантные воспоминания через LLM
### Зависимости
- `sentence-transformers` — для локальной модели
- `qdrant-client` — для Qdrant (уже был)
- Qdrant запускается через `docker-compose.memory.yml` (порт 6333)
| Docker (Qdrant) | ✅ Готово |
**Общий вывод:** Все 4 задачи плана реализованы. Система представляет собой работающий skeleton с полным когнитивным циклом. Основные направления для дальнейшего развития: интеграция рефлексии и summary в основной цикл, LLM-based MemoryPolicy, векторная память, тёмная тема, расширение ContextBuilder.