28 KiB
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/embeddingsendpoint 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/chatendpoint
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—Settingsdataclass, загрузка из.env, кэширование черезlru_cacheconfig/models.yaml— 5 ролей (thinker, critic, coder, action, summary), все наlocal-main(порт 8081).env/.env.example— полная конфигурация путей, портов, GPU, Qdrant
ModelClient
duck_core/model_client.py— ролевая маршрутизация вызовов к llama-serverchat()— синхронный вызов с измерением latency, usagestream_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, listduck_core/events/store.py—EventStore: append (с авто-increment sequence), list_events, list_by_typeduck_core/conversations/store.py—ConversationStore: create, ensure, get, list, add_message, list_messages, get_conversation_id_for_taskduck_core/approvals/service.py—ApprovalService: create_pending, pending, get, allow_once, allow_forever, deny, is_allowed_foreverduck_core/experience/recorder.py—ExperienceRecorder: record, list_records, get_record, write_skill_update_proposalduck_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/shadowduck_core/tools/file_write.py—FileWriteTool: запись внутри workspace, защита от перезаписиduck_core/tools/list_dir.py—ListDirTool: листинг директории внутри workspaceduck_core/tools/search_files.py—SearchFilesTool: текстовый поиск по файлам (glob, case_sensitive)duck_core/tools/shell_exec_safe.py—ShellExecSafeTool: allowlist + blocklist + approvalduck_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— основной чат с сохранением в conversationPOST /v1/chat/stream— streaming чат через SSEPOST /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|denyGET /v1/skills,GET /v1/skills/{skill_id}GET /v1/experiencePOST /v1/memory,GET /v1/memory,GET /v1/memory/searchGET /v1/models/roles,GET /v1/models/pingGET /health,GET /v1/status- Веб-страницы:
/,/approvals,/skills,/memory,/experience
WebChat UI
duck_core/web/templates/index.html— полноценный WebChat с sidebar, conversation list, activity drawerduck_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 endpointscripts/llama/build_vulkan.sh— сборка llama.cpp с Vulkanscripts/llama/healthcheck.sh— проверка здоровья llama-serverscripts/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.pytest_event_log.py,test_action_directive_schema.pytest_tool_gateway.py,test_approvals.pytest_skill_registry.py,test_experience_recorder.pytest_vector_memory.py,test_memory_store.pytest_chat_api.py,test_conversations.pytest_runtime_reasoning.py,test_runtime_tools.pytest_llama_server_connection.py,test_llama_service_script.py
Документация
- 12 файлов в
docs/:architecture.md,how_to_run.md,how_to_test.mdweb_api.md,model_roles.md,tool_gateway.mdmemory_architecture.md,experience_learning.md,skills.mdlocal_llama_server.md,performance_mtp.mdsuperpowers/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-clientMakefile— цели: duck-up, duck-llama-main, duck-api, duck-dev, duck-smoke, duck-test, duck-verifydata/duck.sqlite3— рабочая БД SQLiteworkspace/— рабочая директория для инструментов
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. Что добавлено сверх плана
-
ConversationStore — полноценное управление диалогами (conversations + conversation_messages таблицы), чего не было явно в плане Task 2. План упоминал только tasks и events.
-
Streaming API —
POST /v1/chat/streamс SSE,POST /v1/tasks/{id}/continue/stream,POST /v1/tasks/{id}/password/stream. В плане не было явно указано streaming. -
Password flow — полный цикл запроса sudo-пароля:
requires_password→tool_password_requested→/v1/tasks/{id}/password/stream. В плане не было детализировано. -
Activity Drawer в WebChat — боковая панель с вкладками Events/Commands/Memory, инлайн-терминалы для инструментов, инлайн-одобрения. Значительно больше, чем «пустая WebChat-страница» из этапа 1 ТЗ.
-
Command Audit — отдельный тип события
command_auditдля shell-команд с полной метаданной (action_type, risk_level, blocked, approved, returncode). -
Scope-aware Memory — трёхуровневая система скоупов (global/workspace/conversation) с автоматическим infer_scope.
-
Skill update proposals — автоматическая запредложений по обновлению скиллов в
skills/_proposals/. -
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. Что не реализовано (или реализовано частично)
-
MemoryPolicy — заглушка.
MemoryPolicy.classify()всегда возвращаетshould_store=False. Нет LLM-классификации для автоматического сохранения памяти. -
ContextBuilder — минимальный. Нет суммаризации старых events, нет обрезки по token budget, нет приоритизации контекста. Просто склеивает memory + history + user message.
-
Critic не вызывается автоматически.
Reflection.reflect()есть, но не интегрирован в RuntimeLoop — нет автоматической рефлексии после завершения задачи. -
Summary роль не используется. Нет автоматической суммаризации контекста при превышении budget.
-
Coder роль не используется в основном потоке. RuntimeLoop вызывает только action и thinker.
-
Recall роль не определена в конфиге. В ТЗ упоминается recall, но в
config/models.yamlеё нет. -
Sys_util роль не определена в конфиге. Аналогично.
-
VectorMemory не интегрирован в RuntimeLoop. Qdrant-поиск не подключён к основному циклу (MemoryStore использует LIKE-поиск, а не векторный).
-
WebChat — светлая тема. В памяти пользователя указано предпочтение тёмной темы, но CSS реализован светлый (
color-scheme: light, белый фон). -
Нет CLI. Упомянуто в ТЗ как необязательное, но если понадобится — нужно делать.
-
Нет автоматического применения 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)
- Пользователь отправляет сообщение →
POST /v1/chatили/v1/chat/stream - Создаётся Task + событие
task_created - ContextBuilder собирает сообщения (memory + history + user message)
- Action loop (до 4 итераций):
- Модель
actionгенерирует JSON directive (schema: action_directive.schema.json) - ToolGateway выполняет каждый action через соответствующий инструмент
- Если команда требует approval → пауза, создание Approval, ожидание решения
- Если sudo → запрос пароля
- Результаты собираются как tool_observations
- Модель
- Thinker получает все tool_observations и формирует финальный ответ
- Задача завершена →
task_completed - (Опционально) 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 запрос
Поток данных
- Зача завершена →
_run_memory_policy()→ LLM классифицирует →MemoryDecision - Если
should_store=True→MemoryStore.add()(SQLite) +VectorMemory.add_memory()(Qdrant) - При следующем запросе →
MemoryStore.relevant()(SQLite LIKE) +VectorMemory.search_memory()(semantic) - Recall-роль фильтрует релевантные воспоминания через LLM
Зависимости
sentence-transformers— для локальной моделиqdrant-client— для Qdrant (уже был)- Qdrant запускается через
docker-compose.memory.yml(порт 6333) | Docker (Qdrant) | ✅ Готово |
Общий вывод: Все 4 задачи плана реализованы. Система представляет собой работающий skeleton с полным когнитивным циклом. Основные направления для дальнейшего развития: интеграция рефлексии и summary в основной цикл, LLM-based MemoryPolicy, векторная память, тёмная тема, расширение ContextBuilder.