Skip to content

Design note — адаптивное планирование (§26, §24)

Статус: спайк (реализовано) · Область: хук на runtime, чтобы реактор мог выбирать среди кандидатов; жёсткие правила отсекают, ранжирование только упорядочивает, LLM разруливает ничьи редко.

Почему это ложится на кодовую базу

Runtime — это реактивный fan-out: arun_once строит список кандидатов work (agent×event) и запускает все матчеры (§24). У вопроса «какой шаг сильнее всего снижает неопределённость» сегодня нет дома — хук это один вызов в arun_once перед _dispatch и перед budget-ограничением (порядок важен и тогда, когда budget обрезает список):

if self.scheduler is not None and work:
    work = await self.scheduler(self.context, work)

По умолчанию None → текущее поведение; примитивы не тронуты.

Контракт политики (три стадии + два guard'а)

filter (жёсткие правила → МОГУТ отсеять) → rank (метрика → только порядок) → LLM-разруливание (редко)
  • filter — доменные правила отсекают кандидатов, которые вообще не подходят, так что они никогда не доходят до ранжирования (например, опровергнутая гипотеза, или capability 'b' не разрешена для тега 'x').
  • rank — метрика упорядочивает кандидатов; она никогда не отсеивает (§26: не каждое решение нуждается в LLM). Опциональный rank_limit=k обрезает до top-k после ранжирования (закреплённые HITL-кандидаты никогда не учитываются; непустой ранжированный список никогда не опустошается → нет голодания).
  • LLM-разруливание — только когда разрыв метрики между топ-2 ≤ llm_tie_break и есть модель; один вызов structured_llm упорядочивает пару. Офлайн → пропускается.
  • HITL pin — любой кандидат, который разрешает отвеченный PendingQuestion, принудительно ставится в начало (§60): подтверждение человека никогда не проигрывает ранжированию.
  • Fallback против голодания — если фильтрация опустошила бы набор кандидатов, сохраняется исходный список: единственный путь к прогрессу обязан выжить.

Адаптивность возникает ограниченно по усилиям: каждая итерация переранжирует относительно свежего контекста, так что события самого ценного кандидата двигают следующий раунд (§24).

API

from reactifact import Runtime
from reactifact.scheduler import uncertainty_policy

runtime = Runtime(
    ctx,
    agents=[...],
    scheduler=uncertainty_policy(
        rules=[not_refuted],        # Rule = (context, agent, event) -> bool
        metric=support_split,       # Metric = (context, agent, event) -> float
        llm_tie_break=0.05,         # LLM при почти-ничьей (редко), system-промпт владеет приложение
        rank_limit=1,               # опциональное "выбрать top-k" (безопасно: никогда не опустошает)
    ),
)

Agent.capabilities (§25) — метаданные, которые политика и LLM-разруливание используют для описания кандидатов; сами по себе они ничего не меняют.

reactifact.scheduler.relation_balance_metric — встроенная uncertainty-driven Metric: ранжирует кандидата по тому, насколько несимметричны связи supports/contradicts у артефакта, к которому относится его событие — детерминированно, без привязки к провайдеру, тот же структурный сигнал, который examples/medic_lab раньше считал вручную только для отчёта (produce/evaluate.py). medic_lab теперь подключает его через собственный Runtime(scheduler=medic_lab_scheduler()), так что самая противоречивая открытая гипотеза реально исследуется первой, а не просто оказывается наверху в итоговом отчёте.

Чем это НЕ является

  • Не альтернативный исполнитель — графа путей нет, runtime по-прежнему реагирует на события.
  • Не «обязательно выбрать одного» — только ранжирование; отсев — работа filter, подстрахованная fallback'ом против голодания.
  • Не «LLM везде» — только ничьи, с бюджетом, безопасно офлайн.
  • Системный промпт для разруливания принадлежит приложению: передайте llm_system= в uncertainty_policy/Scheduler (по умолчанию DEFAULT_TIE_BREAK_SYSTEM), чтобы решение формулировалось в терминах домена.

Что поставляется со спайком

  • reactifact/scheduler.pyScheduler, uncertainty_policy, relation_balance_metric, типы.
  • Хук в runtime (параметр scheduler=), Agent.capabilities.
  • examples/adaptive — два конкурирующих «художника» + HITL-подтверждение.
  • examples/medic_labmedic_lab_scheduler() подключает relation_balance_metric и в консольный (chat.py), и в веб (api/chat.py) вход.
  • tests/test_adaptive.py — ранжирование, отсев, fallback, LLM-разруливание, пропуск офлайн, HITL pin, прогон демо, relation_balance_metric (веса, кастомные имена связей, сквозное планирование).
  • tests/test_medic_lab.pymedic_lab_scheduler() приоритизирует более противоречивую открытую гипотезу.