О проекте
Команда: 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.mdPalliPalli — агент тарифных кампаний (Beeline, трек 04)
Агент решает, каким абонентам какой тариф предложить и через какой канал, чтобы прирост ARPU был больше затрат на коммуникацию. Сначала он проверяет гипотезы пилотами, а потом запускает до 10 кампаний.
Решение — один файл agent.py. Остальные файлы — пакет организаторов и stress_eval.py, наш стресс-тест.
Идея коротко
История переходов описывает других абонентов, и на судействе эффекты другие. Поэтому агент не решает заранее, верить истории или нет, а оценивает это по своим же пилотам:
- По истории строит первую оценку эффекта для каждого перехода.
- Держит модель «насколько история похожа на правду» и уточняет её после каждого пилота. Подтвердилась — действует узко и уверенно. Врёт — расширяет неопределённость по всем гипотезам, и разведка становится шире.
- Выбирает пилоты по ценности информации: какой пилот сильнее всего улучшит итоговый план с учётом его цены.
- Запускает кампании только там, где выгода положительна даже по пессимистичной оценке. Свободный охват добирает бесплатным 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σ). Если она не больше нуля, ячейку не трогаем. - Для каждого канала считаем выгоду на контакт: средний ARPU ячейки × эффект × эффективность канала − цена контакта.
- Раздаём охват самым выгодным ячейкам через дешёвый канал (push или SMS).
- Остаток бюджета тратим на переход на дорогой канал (реклама, звонок) там, где доплата окупается лучше всего.
- Оставшийся охват бесплатно отдаём push-кампаниям в ячейки с положительной
оценкой (порог мягче:
оценка − 0.5σ), так как контакт ничего не стоит. - Ячейки с одинаковым тарифом, каналом и сегментом собираем в одну кампанию до 5 000 абонентов. Если кампаний больше 10, оставляем 10 самых прибыльных.
Если ни одна ячейка не прошла порог, агент возвращает одну бесплатную push-кампанию на самую перспективную гипотезу.
Надёжность
- Нет зашитых эффектов: тарифы, каналы, остатки бюджета и охвата читаются из
env, эффекты — только из истории и пилотов. - Любая ошибка внутри
act()перехватывается: пилоты уже в зачёте, поэтому агент возвращает запасной план, а не падает. - Случайности нет,
submission.csvпри повторном запуске совпадает побайтно. - LLM и сеть не используются. Прогон занимает 1–3 секунды.
Логика тарифных кампаний (для маркетинг аналитика; как он прочитает)
Цель — предложить клиенту тариф, соответствующий его расходам и потреблению.
Гипотеза: тарифы 8 и 9 — доступные варианты, 10 и 11 — для регулярного потребления, 20 — премиальный. Эти роли необходимо подтвердить условиями тарифов; номера сами по себе ничего не означают.
| Сегмент | Переход | Канал | Предполагаемая ценность для клиента |
|---|---|---|---|
| LOW | 13 → 8 | Digital ads | Базовый пакет по доступной цене |
| MID | 13/3/12/15/9 → 8 | Digital ads | Сбалансированный пакет для повседневных задач |
| LOW | 8/14/4 → 9 | Digital ads | Меньший пакет без оплаты лишних объёмов |
| MID | 4/11 → 8 | SMS | Более выгодное соотношение цены, интернета и звонков |
| LOW | 10 → 9 | SMS | Доступный тариф для небольшого потребления |
| MID | 8 → 10 | SMS | Больше ресурсов для клиентов, которым не хватает текущего пакета |
| LOW | 11 → 8 | SMS | Сохранение необходимых услуг при меньших расходах |
| MID | 10 → 11 | SMS | Более подходящее соотношение интернета и минут |
| MID | 14 → 9 | Push | Более выгодные условия при текущем потреблении |
| HIGH | 8 → 20 | Push | Расширенный пакет и меньше доплат сверх лимитов |
Стратегия по сегментам (для маркетинг аналитика; как он прочитает)
- 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 277k | 89% | 0/10 | 5 957k |
| noisy | шум ±15 п.п. на каждый переход | 5 815k | 79% | 0/10 | 6 320k |
| weaker | эффекты вдвое слабее и −3 п.п. | 1 779k | 68% | 0/10 | 2 553k |
| downsell | −15 п.п. к каждому переходу | 3 691k | 82% | 0/10 | 4 061k |
| sign_flip | у 30% переходов знак обратный | 3 549k | 58% | 0/10 | 3 288k |
| shuffled | лучшие тарифы перемешаны в ячейке | 52k | 1% | 4/10 | 149k |
| segment_swap | эффекты перемешаны между сегментами | 3 463k | 21% | 0/10 | 4 045k |
| sparse_noise | ошибка тем больше, чем меньше истории | 6 401k | 61% | 0/10 | 5 905k |
| mixed | масштаб, шум и 15% ошибок знака | 4 131k | 55% | 0/10 | 4 140k |
| fresh | история бесполезна | 12 259k | 32% | 0/10 | 2 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 или из
текущей папки).
Авторы коммитов
История коммитов
- 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
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.