- harness/es.py: append-only event store + проекции (образец — кооп-портал) - harness/levels.py: уровни 1с/1м/1ч/1д, контекст ∝ масштабу - harness/scheduler.py: уровневые потоки, идемпотентные тики, absorbed-стабы - harness/isolated_runner.py + _llm_worker.py: LLM-тик в сабпроцессе (глобальный asyncio-рантайм Strands не переживает 2+ create_harness) - cli.py: init/task/tasks/status/events + requeue (написан самим минутным агентом) - 9router: отдельный ключ sk-simbiot, модель auto (glm-5.3 первым)
80 lines
No EOL
3.8 KiB
Python
80 lines
No EOL
3.8 KiB
Python
"""Simbiot Harness — уровни и конфигурация тактовой лестницы.
|
|
|
|
Контекст ∝ масштабу: чем крупнее цикл, тем больше контекст и модель.
|
|
"""
|
|
from dataclasses import dataclass, field
|
|
|
|
SEC = 1
|
|
MIN = 60
|
|
HOUR = 3600
|
|
DAY = 86400
|
|
WEEK = 7 * DAY
|
|
MONTH = 30 * DAY
|
|
QUARTER = 91 * DAY
|
|
YEAR = 366 * DAY # округляем год как 366 дней для простоты выравнивания
|
|
|
|
|
|
@dataclass
|
|
class CycleLevel:
|
|
name: str
|
|
label: str
|
|
every: int # секунд между тиками
|
|
uses_llm: bool
|
|
model: str # модель 9router (или "none")
|
|
effort: str # effort Strands: auto/low/medium/high/off
|
|
ctx_tokens: int # бюджет контекста уровня
|
|
task_classes: tuple # какие классы задач может брать
|
|
mandate: str # мандат уровня (в промпт)
|
|
can_shell: bool # инструменты уровня
|
|
|
|
def seq(self, ts: int) -> int:
|
|
"""Номер такта уровня для unix-ts (для идемпотентности тиков)."""
|
|
return ts // self.every
|
|
|
|
|
|
LEVELS = {
|
|
"sec": CycleLevel(
|
|
name="sec", label="Секунда · рефлекс", every=SEC, uses_llm=False,
|
|
model="none", effort="off", ctx_tokens=0, task_classes=(),
|
|
mandate=("Ты — рефлекторный уровень харнеса. Работает без LLM: телеметрия, "
|
|
"heartbeat, алерты. Никаких решений — только наблюдение."),
|
|
can_shell=False,
|
|
),
|
|
"min": CycleLevel(
|
|
name="min", label="Минута · пульс", every=MIN, uses_llm=True,
|
|
model="auto", effort="low", ctx_tokens=4_000, task_classes=("S",),
|
|
mandate=("Ты — минутный пульс харнеса Simbiot. Триаж входящих, быстрые ответы, "
|
|
"мелкие фиксы (S-задачи до 5 минут). Отвечай кратко, не углубляйся. "
|
|
"Если задача больше твоего класса — откажись и предложи промоушен."),
|
|
can_shell=True,
|
|
),
|
|
"hour": CycleLevel(
|
|
name="hour", label="Час · поток", every=HOUR, uses_llm=True,
|
|
model="auto", effort="medium", ctx_tokens=32_000, task_classes=("S", "M"),
|
|
mandate=("Ты — часовой поток харнеса Simbiot. Дайджест часа: что произошло, "
|
|
"статусы фоновых задач, батч мелочей (S/M). Консолидируй минутные "
|
|
"события в выжимку — сырое наверх не поднимай."),
|
|
can_shell=True,
|
|
),
|
|
"day": CycleLevel(
|
|
name="day", label="День · манифест", every=DAY, uses_llm=True,
|
|
model="auto", effort="high", ctx_tokens=128_000, task_classes=("S", "M", "L"),
|
|
mandate=("Ты — дневной цикл харнеса Simbiot. Манифест дня: план, выбор задач "
|
|
"L-класса, дневной отчёт. Думай глубоко, планируй на день вперёд."),
|
|
can_shell=True,
|
|
),
|
|
}
|
|
|
|
# v0.2+: week/month/quarter/year — включаются по мере готовности
|
|
FUTURE_LEVELS = ("week", "month", "quarter", "year")
|
|
|
|
|
|
def size_class(minutes: int) -> str:
|
|
"""Класс задачи по оценке длительности."""
|
|
if minutes <= 5:
|
|
return "S"
|
|
if minutes <= 60:
|
|
return "M"
|
|
if minutes <= 8 * 60:
|
|
return "L"
|
|
return "XL" |