AAYMA
RU
Войти

PalliPalli

Агент тарифных кампаний Beeline: сначала проверяет гипотезы пилотами, потом формирует до 10 кампаний «тариф × канал × сегмент» с положительным…

Доведённый продукт
Команда «PalliPalli» 19💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: PalliPalli
Кейс HackAlem.ai: Телеком — Beeline
Стадия: Доведённый продукт

Задача кейса: Агент, который решает, каким абонентам какой тариф предложить (чистый прирост ARPU)

Суть

Агент тарифных кампаний Beeline: сначала проверяет гипотезы пилотами, потом формирует до 10 кампаний «тариф × канал × сегмент» с положительным чистым приростом ARPU.

Как устроено

Ячейки «текущий тариф × ARPU-сегмент» (27 ячеек, 540 гипотез) → приор эффекта по change_tariff.csv с shrinkage → байесовская модель доверия истории (эффект ≈ β·h + b + шум; параметры β, b, τ, ρ оцениваются перебором по сетке по всем пилотам) → выбор пилотов по knowledge gradient минус ущерб и стоимость → кампании там, где пессимистичная оценка (mean−1σ) > 0, жадное распределение по каналам и добор бесплатным push. LLM не используется.

Ключевые решения

  • Bayesian grid inference
  • knowledge gradient / value of information
  • shrinkage
  • pessimistic (mean−1σ) selection
  • greedy channel allocation
  • stress testing on alternative worlds

Метрики

Seed 42: чистый результат 5 301 006 (+3.52% к baseline), затраты 96 544/100 000, 19 пилотов, 10 кампаний; 10 прогонов: медиана 5 338 600, мин 4 895 447; стресс-тест 11 миров × 10 seed (напр. 89% от оракула на mock)

Особенности

Агент сам оценивает, насколько верить исторической выборке, и расширяет разведку, если история «врёт». stress_eval.py проверяет его на 11 альтернативных мирах (sign_flip, hostile, fresh и др.) и сравнивает с оракулом и стратегией «только история». Это редкий пример честной проверки устойчивости вне мок-среды.

Стек

Python


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-573aa7a4-pallipalli.

README

README.md

PalliPalli — агент тарифных кампаний (Beeline, трек 04)

Агент решает, каким абонентам какой тариф предложить и через какой канал, чтобы прирост ARPU был больше затрат на коммуникацию. Сначала он проверяет гипотезы пилотами, а потом запускает до 10 кампаний.

Решение — один файл agent.py. Остальные файлы — пакет организаторов и stress_eval.py, наш стресс-тест.

Идея коротко

История переходов описывает других абонентов, и на судействе эффекты другие. Поэтому агент не решает заранее, верить истории или нет, а оценивает это по своим же пилотам:

  1. По истории строит первую оценку эффекта для каждого перехода.
  2. Держит модель «насколько история похожа на правду» и уточняет её после каждого пилота. Подтвердилась — действует узко и уверенно. Врёт — расширяет неопределённость по всем гипотезам, и разведка становится шире.
  3. Выбирает пилоты по ценности информации: какой пилот сильнее всего улучшит итоговый план с учётом его цены.
  4. Запускает кампании только там, где выгода положительна даже по пессимистичной оценке. Свободный охват добирает бесплатным push.

Как делим аудиторию

Ячейка — текущий тариф × ARPU-сегмент (LOW / MID / HIGH), например «tariff_13, LOW». Ячейки меньше 60 абонентов пропускаем: и пилот, и кампания на них слишком шумные. В мок-среде остаётся 27 ячеек. Гипотеза — ячейка плюс любой из 20 других тарифов, всего 540 гипотез. Заранее ничего не отсекаем: если история врёт, лучшим может оказаться любой тариф.

Шаг 1. Оценка по истории

Для каждой гипотезы по data/change_tariff.csv считаем h = средний относительный прирост ARPU у тех, кто сам делал такой переход, × доля этого перехода среди всех переходов из ячейки. К оценке прикладываем её выборочную дисперсию.

  • Малые группы подтягиваем к среднему по той же паре тарифов.
  • Для переходов без истории берём небольшую прикидку по разнице цен тарифов с широкой погрешностью.
  • Если файла истории нет, агент работает только на ценовой прикидке и не падает.

Шаг 2. Насколько верить истории

Истинный эффект гипотезы моделируем как

эффект ≈ β·h + b + шум,  дисперсия шума = τ² + ρ²·h²
  • β — насколько история верна по масштабу (1 — верна, 0 — бесполезна);
  • b — общий сдвиг (например, рынок хуже, чем в истории);
  • τ — общий разброс, ρ — ошибка отдельного перехода, растущая с его силой (например, перевёрнутый знак).

Эти четыре параметра оцениваются по всем пилотам сразу байесовским перебором по сетке значений. Каждая гипотеза получает приор β·h + b с дисперсией из τ, ρ и неопределённости самих β, b. Результаты пилотов этой гипотезы добавляются с весами по точности: пилот на 200 абонентах сдвигает оценку сильно, на 40 — слабо.

Поэтому пилот учит агента не только про свою гипотезу, но и про надёжность истории в целом. На мок-среде, где эффекты построены из истории, к концу разведки агент приходит к β ≈ 1, τ ≈ 0.035, то есть видит, что истории можно верить.

Шаг 3. Пилоты

Перед каждым пилотом агент перебирает все гипотезы и размеры (40, 80, 120, 200 абонентов) и для каждой пары считает:

  • ценность информации (knowledge gradient): насколько в среднем вырастет пессимистичная оценка лучшего тарифа ячейки после этого пилота, умноженная на суммарный ARPU ячейки;
  • минус ущерб абонентам пилота: они реальные, и отрицательный эффект идёт в зачёт;
  • минус цена пилота: SMS-контакт плюс 30 у.е. альтернативной стоимости контакта, который мог бы уйти в кампанию.

Запускается лучший вариант. Если ни один не окупается, разведка заканчивается раньше. Пилоты идут через SMS; результат делим на эффективность SMS, чтобы пересчитать его на другие каналы. На разведку уходит не больше 25% охвата, а один пилот берёт не больше половины ячейки.

Шаг 4. План кампаний

  1. Для каждой ячейки берём тариф с лучшей пессимистичной оценкой (оценка − 1σ). Если она не больше нуля, ячейку не трогаем.
  2. Для каждого канала считаем выгоду на контакт: средний ARPU ячейки × эффект × эффективность канала − цена контакта.
  3. Раздаём охват самым выгодным ячейкам через дешёвый канал (push или SMS).
  4. Остаток бюджета тратим на переход на дорогой канал (реклама, звонок) там, где доплата окупается лучше всего.
  5. Оставшийся охват бесплатно отдаём push-кампаниям в ячейки с положительной оценкой (порог мягче: оценка − 0.5σ), так как контакт ничего не стоит.
  6. Ячейки с одинаковым тарифом, каналом и сегментом собираем в одну кампанию до 5 000 абонентов. Если кампаний больше 10, оставляем 10 самых прибыльных.

Если ни одна ячейка не прошла порог, агент возвращает одну бесплатную push-кампанию на самую перспективную гипотезу.

Надёжность

  • Нет зашитых эффектов: тарифы, каналы, остатки бюджета и охвата читаются из env, эффекты — только из истории и пилотов.
  • Любая ошибка внутри act() перехватывается: пилоты уже в зачёте, поэтому агент возвращает запасной план, а не падает.
  • Случайности нет, submission.csv при повторном запуске совпадает побайтно.
  • LLM и сеть не используются. Прогон занимает 1–3 секунды.

Логика тарифных кампаний (для маркетинг аналитика; как он прочитает)

Цель — предложить клиенту тариф, соответствующий его расходам и потреблению.

Гипотеза: тарифы 8 и 9 — доступные варианты, 10 и 11 — для регулярного потребления, 20 — премиальный. Эти роли необходимо подтвердить условиями тарифов; номера сами по себе ничего не означают.

СегментПереходКаналПредполагаемая ценность для клиента
LOW13 → 8Digital adsБазовый пакет по доступной цене
MID13/3/12/15/9 → 8Digital adsСбалансированный пакет для повседневных задач
LOW8/14/4 → 9Digital adsМеньший пакет без оплаты лишних объёмов
MID4/11 → 8SMSБолее выгодное соотношение цены, интернета и звонков
LOW10 → 9SMSДоступный тариф для небольшого потребления
MID8 → 10SMSБольше ресурсов для клиентов, которым не хватает текущего пакета
LOW11 → 8SMSСохранение необходимых услуг при меньших расходах
MID10 → 11SMSБолее подходящее соотношение интернета и минут
MID14 → 9PushБолее выгодные условия при текущем потреблении
HIGH8 → 20PushРасширенный пакет и меньше доплат сверх лимитов

Стратегия по сегментам (для маркетинг аналитика; как он прочитает)

  • LOW: доступность и удержание клиентов.
  • MID: соответствие потреблению и обоснованное расширение пакета.
  • HIGH: премиальная ценность и обслуживание высокого потребления.

LOW/MID/HIGH отражают ARPU — среднюю выручку на клиента, а не его доход или объём потребления.

Роль каналов (для маркетинг аналитика; как он прочитает)

  • Digital ads: знакомство с предложением.
  • SMS: конкретная выгода и ссылка на подключение.
  • Push: сравнение тарифов и быстрый переход в приложении.

Результаты на мок-среде

python local_eval.py (seed 42):

МетрикаЗначение
Чистый результат5 301 006 (+3.52% к baseline)
Затраты96 544 из 100 000 у.е.
Пилотов19 из 20 (3 725 контактов, 14 900 у.е.)
Кампаний10
Контактов15 000 из 15 000
Абонентов с отрицательным эффектом0%

python local_eval.py --runs 10: медиана 5 338 600, минимум 4 895 447, максимум 5 532 704, все 10 прогонов в плюсе.

Стресс-тест: эффекты не как в истории

Мок-среда построена из той же истории, что и наш приор, поэтому агент, который просто верит истории, выглядит на моке идеально. Чтобы проверить агента в условиях судейства, stress_eval.py подменяет эффекты «альтернативными мирами» и считает результат официальными environment.py и scoring_core.py. 11 миров × 10 seed, средний чистый результат:

МирЧто измененоАгент% от оракулаПрогонов в минусТолько история
mockничего5 277k89%0/105 957k
noisyшум ±15 п.п. на каждый переход5 815k79%0/106 320k
weakerэффекты вдвое слабее и −3 п.п.1 779k68%0/102 553k
downsell−15 п.п. к каждому переходу3 691k82%0/104 061k
sign_flipу 30% переходов знак обратный3 549k58%0/103 288k
shuffledлучшие тарифы перемешаны в ячейке52k1%4/10149k
segment_swapэффекты перемешаны между сегментами3 463k21%0/104 045k
sparse_noiseошибка тем больше, чем меньше истории6 401k61%0/105 905k
mixedмасштаб, шум и 15% ошибок знака4 131k55%0/104 140k
freshистория бесполезна12 259k32%0/102 443k
hostileвсе переходы убыточны−705k—10/10−5 517k

«Оракул» знает истинные эффекты мира и строит план без пилотов — это ориентир потолка. «Только история» верит истории как истине. Где история верна, он выигрывает, но в мире fresh получает в 5 раз меньше агента, а в hostile теряет 5.5M против 0.7M у агента. Шаблон организаторов уходит в минус в 78 из 110 прогонов.

Воспроизвести: python stress_eval.py --seeds 10.

Как запустить

pip install -r requirements.txt
python local_eval.py              # один прогон с отчётом
python local_eval.py --runs 10    # 10 прогонов с разными seed
python make_submission.py         # собрать submission.csv
python stress_eval.py --seeds 10  # стресс-тест на альтернативных мирах

Для сдачи: agent.py, submission.csv, requirements.txt. Агент читает data/change_tariff.csv из пакета организаторов (рядом с agent.py или из текущей папки).

Звёзды
⭐ 2
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
24
Авторы
3
Файлы
21
Ветки
9

Авторы коммитов

kobeisinov 12Galammadin 7Nursultan 5

История коммитов

  • 2026-09-23 17:57Update README.mdNursultan
  • 2026-09-23 17:50Delete docs directoryGalammadin
  • 2026-09-23 17:50Update README.mdNursultan
  • 2026-09-23 17:45Update README.mdNursultan
  • 2026-09-23 17:44Update README.mdNursultan
  • 2026-09-23 17:34Merge pull request #1 from BAITC-Hacks/feat/agent-history-trustNursultan
  • 2026-09-23 17:30Merge origin/main into feat/agent-history-trustkobeisinov
  • 2026-09-23 17:20fix: return a fallback plan instead of crashing in act()kobeisinov
Все коммиты (24)
  • 2026-09-23 17:20test: add stress_eval.py for worlds where effects differ from historykobeisinov
  • 2026-09-23 17:20docs: describe the history-trust agent and its stress-test resultskobeisinov
  • 2026-09-23 17:15chore: add submission.csv generated by make_submission.pykobeisinov
  • 2026-09-23 17:07feat: agent learns how much to trust history, pilots all tariffs, fills spare reach with pushkobeisinov
  • 2026-09-23 16:48Integrate information-value pilots and grouped campaign planningGalammadin
  • 2026-09-23 16:46feat: V3 portfolio agent — V2 learning plus joint audience/tariff/channel portfolio selectionkobeisinov
  • 2026-09-23 16:46feat: V2 adaptive agent — Bayesian priors with value-of-information follow-up pilotskobeisinov
  • 2026-09-23 16:46feat: V1 rules agent — eight hypothesis SMS pilots, disjoint campaigns ranked by observed net valuekobeisinov
  • 2026-09-23 16:12Add grounded OpenAI analyst and solution tacticsGalammadin
  • 2026-09-23 15:46feat: compare complete campaign plans on galammadinGalammadin
  • 2026-09-23 15:38docs: describe agent approach in READMEkobeisinov
  • 2026-09-23 15:33feat: validate history-informed campaign planner for galammadinGalammadin
  • 2026-09-23 15:04feat: Bayesian pilot exploration agent with channel-aware campaign planningkobeisinov
  • 2026-09-23 14:55docs: link initial project brief from READMEGalammadin
  • 2026-09-23 14:06chore: import official participant package and template-based agent baselinekobeisinov
  • 2026-09-23 13:58docs: add initial Beeline Track 04 project briefGalammadin
Структура репозитория (21 файлов)
.gitignore
PARTICIPANT_GUIDE.md
PARTICIPANT_GUIDE.pdf
README.md
agent.py
agent_template.py
customer_profile.csv
data/arpu_monthly.csv
data/change_tariff.csv
data/dict_tariff.csv
data/traffic.csv
environment.py
feature_dictionary.csv
local_eval.py
make_submission.py
mock_environment.py
requirements.txt
scoring_core.py
stress_eval.py
submission.csv
tariff_dictionary.csv
Ветки (9)

Обсуждение 0 комментариев

Войдите, чтобы комментировать

Пока нет комментариев — будьте первым.