ducklm/CURRENT_STATE.md

28 KiB
Raw Blame History

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.pySettings 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.pyTaskState (Pydantic модель)
  • duck_core/tasks/store.pyTaskStore: create, update_status, complete, fail, cancel, waiting_for_approval, get, list
  • duck_core/events/store.pyEventStore: append (с авто-increment sequence), list_events, list_by_type
  • duck_core/conversations/store.pyConversationStore: create, ensure, get, list, add_message, list_messages, get_conversation_id_for_task
  • duck_core/approvals/service.pyApprovalService: create_pending, pending, get, allow_once, allow_forever, deny, is_allowed_forever
  • duck_core/experience/recorder.pyExperienceRecorder: record, list_records, get_record, write_skill_update_proposal
  • duck_core/memory/store.pyMemoryStore: 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.pyContextBuilder.build_basic_messages(): собирает сообщения из memory records, history и текущего user message

Tools

  • duck_core/tools/base.pyToolResult (Pydantic), Tool (Protocol)
  • duck_core/tools/gateway.pyToolGateway: маршрутизация action → конкретный инструмент
  • duck_core/tools/file_read.pyFileReadTool: чтение файлов внутри workspace, запрет .env/.ssh/shadow
  • duck_core/tools/file_write.pyFileWriteTool: запись внутри workspace, защита от перезаписи
  • duck_core/tools/list_dir.pyListDirTool: листинг директории внутри workspace
  • duck_core/tools/search_files.pySearchFilesTool: текстовый поиск по файлам (glob, case_sensitive)
  • duck_core/tools/shell_exec_safe.pyShellExecSafeTool: allowlist + blocklist + approval
  • duck_core/tools/command_policy.pyCommandPolicy: классификация команд (readonly, system, destructive, dangerous fragments)
  • duck_core/tools/paths.pyresolve_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.pySkillRegistry: загрузка из */skill.yaml, парсинг procedure/examples/notes, поиск по ключевым словам
  • skills/analyze_project/ — единственный скилл: анализ структуры проекта

Experience & Reflection

  • duck_core/experience/recorder.py — запись результатов задач, предложения по обновлению скиллов
  • duck_core/reflection.pyReflection.reflect(): вызов critic-роли для анализа транскрипта задачи

Memory

  • duck_core/memory/store.pyMemoryStore: хранение в SQLite с поддержкой scope (global/workspace/conversation), importance, полнотекстовый поиск (LIKE)
  • duck_core/memory/policy.pyMemoryPolicy: заглушка (всегда should_store=False)
  • duck_core/memory/vector_memory.pyVectorMemory: интеграция с 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 APIPOST /v1/chat/stream с SSE, POST /v1/tasks/{id}/continue/stream, POST /v1/tasks/{id}/password/stream. В плане не было явно указано streaming.

  3. Password flow — полный цикл запроса sudo-пароля: requires_passwordtool_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=TrueMemoryStore.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.