Причина: find_task возвращал ОДНУ ready-задачу за такт — при 5+ ready очередь росла быстрее исполнения (1 задача/такт). Теперь: - select_task_batch: ВСЕ ready-задачи класса уровня, старейшая первой; - кап пачки MAX_TASK_BATCH (min=3, hour=6, day=6; env SIMBIOT_BATCH_MAX_<LEVEL>); - бюджет контекста: секция задач <= ctx_tokens*3*0.5 символов, мандат/выжимки/эскалации/инбокс остаются в тике; старейшая берётся всегда; - run_level_tick: claim всего батча ДО воркера; TaskCompleted/TaskFailed ПО каждой отдельно (смерть воркера не теряет батч в running); - build_context: секция 'ТЕКУЩАЯ РАБОТА — БАТЧ из N' с постановками, инструкция закрывать каждую задачу cli.py complete <id> -a; - CLI complete: закрытие running-задачи с результатом (commands.complete_task_with_delivery — доставка в TG идемпотентна). Тесты: tests/test_task_batch.py — 28 проверок (ALL PASS), набор 8/8 зелёный. |
||
|---|---|---|
| docs | ||
| harness | ||
| scripts | ||
| tests | ||
| .gitignore | ||
| architecture.html | ||
| cli.py | ||
| events.db | ||
| README.md | ||
| simbiot-presentation.html | ||
Simbiot Harness
Ритмичный агент: живёт тактами, как человек. CQRS + Event Sourcing на спине, Strands Harness SDK как исполнительное ядро, модели через 9router.
Лестница (v0.1)
| Уровень | Интервал | Контекст | Модель | Задачи |
|---|---|---|---|---|
| sec | 1 с | 0 (без LLM) | — | рефлекс: телеметрия, алерты |
| min | 1 мин | ~4K | auto→flash | S-триаж: входящие, быстрые фиксы |
| hour | 1 ч | ~32K | auto | S/M-задачи |
| day | 1 д | ~128K | auto | M/L-задачи, манифест дня |
Принципы: тик идемпотентен (UNIQUE(level, seq)); занятый уровень поглощает
тик (TickAbsorbed + детерминированная заглушка дайджеста, без LLM);
запущенная задача не прерывается; наверх идут только дайджесты.
События (ES-спина)
CycleTicked, TickAbsorbed, ReflexObserved, DigestWritten,
TaskSubmitted, TaskClaimed, TaskCompleted, TaskFailed, TaskPromoted,
TaskAwaitDelivery, DeliverySent, InitiativeSent, EscalationRaised,
EscalationAcknowledged, BudgetSpent, CycleError, HarnessInit,
PolicySet — append-only, проекции пересобираются replay'ем, стейт =
производное от событий.
Структура
harness/es.py— event store + проекции (checkpoint + replay)harness/levels.py— конфигурация уровнейharness/scheduler.py— демон лестницыharness/isolated_runner.py+_llm_worker.py— LLM-тик в сабпроцессе (глобальный asyncio-рантайм Strands не переживает 2+create_harness)harness/commands.py— command sideharness/tg_commands.py— без-LLM команды бота (сек-рефлекс)cli.py— init/task/task-await/tasks/status/events/requeue
CLI
./venv/bin/python cli.py init --identity "…"
./venv/bin/python cli.py task "…" -m 240 # -m minutes-to-live estimate: S/M/L
./venv/bin/python cli.py task "…" -a # автодоставка результата в TG при готовности
./venv/bin/python cli.py task-await <task-id> # пометить существующую: доставить в TG
./venv/bin/python cli.py tasks
./venv/bin/python cli.py status
./venv/bin/python cli.py events -n 50
./venv/bin/python cli.py requeue <task-id> -r "причина"
./venv/bin/python cli.py promote <task-id> -r "вне класса уровня" # S→M→L
./venv/bin/python cli.py escalate "причина" -f min -c "контекст" -s critical
./venv/bin/python cli.py escalations --open # открытые эскалации
./venv/bin/python cli.py escalation-ack <esc-id> -n "что сделал" # закрыть
./venv/bin/python cli.py tg-reply <msg-id> "текст" # ответ на входящее TG
# батч-режим (t-260930064924): один сводный ответ закрывает несколько входящих
./venv/bin/python cli.py tg-reply m-1,m-2,m-3 -c "текст" # ReplySent на каждое, одна отправка
# идемпотентные уведомления/файлы (t-260930083937): повтор — SKIP, дублей в чат нет
./venv/bin/python cli.py tg-notify "текст" -k <ключ>
./venv/bin/python cli.py tg-deliver-file <путь> -c "подпись" [--force]
Автодоставка артефактов (t-260929205927)
Задача, заказанная из TG (cli.py task -a / task-await), помечается
TaskAwaitDelivery. При завершении (TaskCompleted) результат (answer)
уходит в чат сам — DeliverySent, без повторного запроса. Push в тике
(сразу после завершения) + pull-страховка (delivery-демон каждые 15с:
досылает недоставленное, если тик умер между завершением и отправкой).
Идемпотентно: один раз доставлено — повторно не шлётся; после requeue
доработка доставляется снова. Формат: «✅ Готово: