{"title":"getNumberSelected2 algorithm versions","audience":"cursor-agent","note":"Add or edit algorithm descriptions in data/versions.json or static/algo-versions.json. getNumberSelected2 requires version=.","versions":[{"id":"v1","title":"Эталон %All","status":"active","etalon":"Поле «эталон %all» на /v2, 0–100, хранится отдельно для каждого кода сервиса (ig/fb). По умолчанию 50.","description":"Случайная выдача среди отмеченных операторов, как getNumberSelected, но веса считаются от эталона и колонки %All. Цель — чаще брать страны/операторов выше эталона и реже тех, кто ниже, не отключая плохих полностью.","rules":["Сравнивается %All оператора (success / (success+fail+fail_other) за последние 4 часа, только выдачи via=selected2 и version=v1) с эталоном T.","Чем сильнее %All выше T, тем больше вес. Чем сильнее ниже T, тем меньше вес. Отклонение влияет нелинейно (экспонента): большая дыра режет выдачу сильнее, чем маленькая.","Если никто не достигает эталона и никто не выше его, выдача не останавливается: веса просто смещаются к ближайшим к T (наименее плохим). NO_NUMBERS из-за этого не возвращается.","Тестовый период: пока у оператора меньше 8 завершённых статусов, он считается «без статистики». Его вес близок к нейтральному explore-весу, ранние неудачи почти не наказывают. По мере накопления выборки вес плавно переходит к формуле %All. Байесовское сглаживание тянет оценку к T, чтобы малая выборка не дёргала приоритеты.","Скорость выдачи: много «ожидают» на операторе снижает вес (не заваливать его, пока нет SMS/статуса). Частые выдачи за последние 8 минут при %All ниже эталона дополнительно режутся.","Тренд: %All за последние 40 минут против предыдущих 40. Рост успешности поднимает вес, падение опускает. Так алгоритм догоняет улучшение и быстрее уходит с проседающих операторов.","Плохие операторы всегда имеют ненулевой recovery-пол (около 7% относительного веса), чтобы иногда попадать в выдачу. Слабый оператор — это зрелый (%All посчитан после теста) с %All ≤ T/2. Если такой оператор получает success, алгоритм (не таблица) забывает его прошлые результаты и заново открывает тестовый период с этого success. Операторы между T/2 и T не сбрасываются. Повторный провал снова копит статистику; очередной success при %All ≤ T/2 снова сбрасывает.","Кризис: если у ВСЕХ операторов, вышедших из теста, %All в 2 раза хуже эталона или ниже (порог T/2; при T=70 это 35%), почти все запросы отвечают NO_NUMBERS. Реально номер берётся только с вероятностью ~4–14% (больше, если тренд уже растёт; меньше, если продолжает падать). Эти редкие выдачи — тесты по тем же весам, в том числе плохих и ещё тестируемых операторов. Когда %All снова поднимается выше T/2 хотя бы у одного зрелого оператора, обычная выдача возвращается.","Кнопка «Сбросить алгоритм» на /v2 обнуляет только внутреннюю память алгоритма для текущего кода и version (по умолчанию v1). Таблица не трогается: все операторы снова в тестовом периоде. Слабый success после этого копит историю уже от новой отсечки."]},{"id":"baseline","title":"Baseline random","status":"reference","description":"Равномерный random среди отмеченных операторов v2, как getNumberSelected. Эталон не используется. Нужен для сравнения с v1."}]}