В марте я уже выкладывал материал про свой ранний проект — AAF (Autonomous Agent Framework). Это был базовый эксперимент, на котором я обкатывал концепции локального контекста и вызова инструментов. За прошедшие месяцы я переписал архитектуру с нуля, учтя накопленный опыт, и сформировал новый фреймворк — JAWL (Just A While Loop).

Если посмотреть на актуальный ландшафт опенсорсных автономных агентов, выделяются два интересных проекта: OpenClaw (созданный Питером Штайнбергером) и Hermes Agent (от Nous Research).

  • OpenClaw — это готовое локальное приложение‑ассистент на Node.js. Оно выступает гейтвеем между мессенджерами и вашим ПК, закрывая вопросы привязки сессий (Pairing) и авторизации пользователей. Но это монолитный пользовательский софт, который, к тому же, часто страдает от проблем с безопасностью.

  • Hermes Agent — продвинутый ассистент на Python с механизмом самообучения и формированием профиля пользователя. Однако его память базируется преимущественно на логах сессий и плоском векторном поиске, без жесткой реляционной структуры и графов знаний.

JAWL решает задачу с точки зрения системной инженерии на Python. Это полностью асинхронный фреймворк, работающий через механизм Event‑Driven ReAct Loop, предоставляющий агенту гибридную векторно‑графовую память Vector‑Graph RAG, дедукцию по теореме Байеса, делегирование задач фоновым воркерам и транзакционную самомодификацию кода с автоматическим откатом.


🏗 Архитектура слоев (L0 — L3)

Система спроектирована с четким разграничением ответственности по слоям (SOLID):

┌─────────────────────────────────────────────────────────────┐
│ 🔴 L3: AGENT CORE (ReAct Loop, Heartbeat, LLM Executor)     │
└──────────────────────────────┬──────────────────────────────┘
                               │ (вызовы инструментов + сбор контекста)
┌──────────────────────────────▼──────────────────────────────┐
│ 🟠 L2: INTERFACES (Host OS, Telegram, GitHub, Web, Voice)   │
└──────────────────────────────┬──────────────────────────────┘
                               │ (пассивные хранилища данных)
┌──────────────────────────────▼──────────────────────────────┐
│ 🟢 L0: STATE LAYER (Пассивные кэши интерфейсов)             │
└──────────────────────────────┬──────────────────────────────┘
                               │ (Долгосрочная память)
┌──────────────────────────────▼──────────────────────────────┐
│ 🟡 L1: DATABASES (SQLite + Qdrant + KuzuDB)                 │
└─────────────────────────────────────────────────────────────┘
  1. L0 State: Пассивный кэш данных. Они хранят «снимки состояния» интерфейсов (сообщения, метрики хост‑ПК, открытые файлы) в памяти. Задумка L0: стейты не выполняют I/O операций при каждой сборке промпта, что устраняет задержки на сетевые запросы при каждом запросе к модели.

  2. L1 Databases Layer: Гибридная память.

    • SQL DB (SQLite): Матрица Эйзенхауэра для рабочих задач, гипотезы Байеса, встроенные математические мотиваторы, заметки и CRM субъектов.

    • Vector DB (Qdrant + FastEmbed): Семантическая память: Knowledge (знания) и Thoughts (фоновая рефлексия).

    • Graph DB (KuzuDB): Граф знаний и граф навигации по кодовой базе (Code Graph).

  3. L2 Interfaces Layer: Изолированные плагины взаимодействия с внешним миром (Host OS, Telegram, GitHub, Email, Web, Voice, Calendar и др.). Собираются динамически через паттерн Plugin Discovery.

  4. L3 Agent Layer: Вся высокоуровневая логика (ReactLoop, Heartbeat, LLMExecutor с ротацией ключей, SwarmManager, SubconsciousOrchestrator).


⚡ Событийно‑управляемый Heartbeat

Агент в JAWL, как и во многих других фреймворках, крутится в ReAct цикле. Для того, чтобы уменьшить затраты на запросы к модели, я написал локальный Event Bus, который менеджерит просыпания модели. Все L2-интерфейсы подключены к единой асинхронной шине событий (EventBus). Каждое входящее событие имеет свой уровень приоритета (от BACKGROUND и LOW до CRITICAL). Heartbeat на лету умножает оставшееся время сна на коэффициент ускорения:

\text{Time}_{\text{remaining}} = \text{Time}_{\text{remaining}} \times \text{Multiplier}_{\text{event}}

Если поступает событие уровня CRITICAL (например, прямое сообщение от пользователя или вебхук), коэффициент равен 0.0 — остаток времени мгновенно обнуляется, и агент просыпается без задержки.

    [Сон Heartbeat: 600 сек]
                  │
    Входящий ивент (MEDIUM, x0.6)
                  │
    [Сон сокращен: 360 сек]
                  │
    Входящий ивент (CRITICAL, x0.0)
                  │
        ⚡ МОМЕНТАЛЬНЫЙ ВЫЗОВ

🧠 Гибридный Vector‑Graph RAG

Векторный поиск отлично ищет похожие по смыслу фрагменты текста, но не способен отслеживать жесткие логические цепочки. Графовая база (KuzuDB) хранит причинно‑следственные связи и структуры.

В JAWL реализован гибридный поиск:

  1. Из текста вызова извлекаются ключевые сущности.

  2. Параллельно запрашиваются векторы из Qdrant и связанные узлы из KuzuDB.

  3. Кросс‑резолвинг: Найденный векторный факт проверяет наличие своих узлов в графе и подтягивает их соседние связи. И наоборот — текстовые описания найденных графовых узлов отправляются в новый векторный поиск.

Для корректной работы с русским языком и падежами в систему встроен поиск сущностей на базе RapidFuzz и алгоритма Ахо‑Корасик FlashText.


🐝 Оркестрация субагентами

Выполнять тяжелые и рутинные задачи (парсинг 20 сайтов, глубокий рефакторинг кода) именно основной, дорогой моделью давно стало признаком плохого распределения токенов. В JAWL, как и в некоторых других агентских фреймворках главный агент выступает в роли оркестратора. Он формулирует ТЗ и спавнит фоновых субагентов со специализированными ролями (CODER, WEB_RESEARCHER, ARCHIVIST, QA_ENGINEER, SYSADMIN):

       ┌────────────────────────┐
       │      Main Agent        │
       └───────────┬────────────┘
                   │ Делегирование рутины
         ┌─────────┼─────────┐
         ▼         ▼         ▼
         │     Subagents     │
         │         │         │
         └─────────┴─────────┘
                   │ Отформатированные Markdown репорты
                   ▼
       [SUBAGENT_TASK_COMPLETED] -> Пробуждение главного агента

Субагенты работают в изолированных Stateless ReAct циклах на более дешевых и быстрых моделях (например, Gemini 3.5 Flash Lite). Они не имеют доступа к основной памяти и возвращают результат в виде итогового Markdown‑отчета. Нагрузка на API регулируется через asyncio.Semaphore.


🛡 Безопасная самомодификация

При наличии уровня доступа OPERATOR агент имеет право модифицировать исходный код самого фреймворка. Чтобы защитить систему от падения в незапускаемое состояние, внедрен механизм под названием Deploy Sessions:

  1. Перед правками агент открывает деплой‑сессию.

  2. Система создает транзакционный Copy‑on‑Write бэкап всех изменяемых файлов и ведут манифест новых файлов.

  3. При коммите сессии принудительно запускаются синтаксический анализатор compileall и набор тестов pytest.

  4. Если тесты провалены, агент получает Traceback ошибки и списывает одну попытку.

  5. Если лимит попыток исчерпан, система автоматически выполняет Rollback — восстанавливает исходные файлы из бэкапа и удаляет части нерабочего кода.


🚀 Заключение

Фреймворк распространяется под лицензией MIT. Исходный код, архитектурные схемы и документация доступны на GitHub:

👉 GitHub

Буду рад отзывам и пулл‑реквестам в репозитории.