AAYMA
RU
Войти

Beeline Tariff Marketing Campaigns — Adaptive Agent

Статистический агент тарифных кампаний Beeline без LLM: слабые исторические приоры, разведочные и подтверждающие пилоты и распределение бюджета с…

Рабочий MVP
Команда «Moonlight» 7💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: Moonlight
Кейс HackAlem.ai: Телеком — Beeline
Стадия: Рабочий MVP

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

Суть

Статистический агент тарифных кампаний Beeline без LLM: слабые исторические приоры, разведочные и подтверждающие пилоты и распределение бюджета с учётом неопределённости.

Как устроено

Приоры берутся из change_tariff.csv по ячейкам «текущий тариф × целевой × ARPU-сегмент». Затем идут до 12 разведочных пилотов по 50 абонентов с поощрением разнообразия. Оценки пилотов объединяются с приором по точности, используя шум PER_CUSTOMER_STD = 0.804 из environment.py. Ранжирование — по lift минус одна SE, подтверждающие пилоты по 100–200 запускаются по ценности информации. Итоговый план собирает жадный алгоритм с теневыми ценами бюджета; каналы сравниваются с учётом неопределённости множителя call.

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

  • precision-weighted shrinkage
  • exploration/exploitation
  • value of information
  • greedy allocation with shadow prices
  • risk-adjusted lift

Метрики

Seeds 0–9: 10/10 положительных, медиана +1,153,902, мин +714,869, макс +1,509,310; seed 42 net +910,618 при 6 кампаниях и 17 пилотах

Особенности

Подробный аудит использования параметра шума 0.804 со ссылками на строки environment.py. Явные бюджеты на разведку: 15% денег и 2000 контактов. Консервативный fallback не добивает план до 10 кампаний.

Стек

Python · pandas · NumPy


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

README

README.md

Beeline Tariff Marketing Campaigns — Adaptive Agent

Problem

Choose tariff campaigns for 23,441 subscribers with unknown target-audience effects. An offer can produce downsell, and communication costs money. Historical switchers come from another population: the agent must learn through paid pilot experiments before allocating the campaign budget. All supplied data are synthetic.

Solution

Agent.act(env) returns a list of campaign dictionaries. This adaptive decision system combines weak historical priors, diverse exploratory pilots, selective confirmation, and uncertainty-aware economic allocation. It uses only Python, pandas, numpy, the supplied historical CSV, and the documented environment API. There are no external services, API keys, or accesses to hidden environment state.

Why this is Agentic AI

Historical data
      ↓
Prior hypotheses
      ↓
Candidate generation
      ↓
Small exploratory pilots
      ↓
Observe real target-audience response
      ↓
Update beliefs + uncertainty
      ↓
Selective confirmation pilots
      ↓
Economic channel selection
      ↓
Budget-aware campaign selection
      ↓
Final campaign plan

The agent does not simply predict from historical data. It interacts with the environment, performs experiments, observes results, updates its beliefs and changes subsequent decisions. It is a statistical decision agent; no LLM is needed.

Algorithm

  1. Historical prior. Group data/change_tariff.csv by current tariff, target tariff and ARPU segment; calculate relative-lift mean, count and standard deviation. Drop nonfinite values, negative after-ARPU and before-ARPU below 100. Clip relative lift to [-1, 3]. Historical segments use LOW <1000, MID 1000–5000, HIGH >5000; current audience labels come directly from the public profile.
  2. Candidate generation. Pair observed current-tariff/ARPU cells with valid, different target tariffs. Prefer historically positive hypotheses; use neutral hypotheses if none exist. Historical switch frequency is not treated as the hidden campaign conversion probability.
  3. Diversity-aware exploration. Rank prior expected total economic value with penalties for already-tested cells, target tariffs and ARPU segments.
  4. Exploratory pilots. Run up to 12 successful 50-customer experiments. Smaller audiences/resources can reduce the size, but never below 10. Skips and failures do not count as successful experiments.
  5. Prior-informed shrinkage. Accumulate prior and pilot estimates by precision. More pilot customers increase evidence weight. This is not full Bayesian inference.
  6. Uncertainty estimation. Use the public measurement-noise formula below and historical uncertainty. Final ranking subtracts one standard error from the estimated lift. This is a risk penalty, not a calibrated confidence guarantee.
  7. Confirmation pilots. Consider up to six follow-up hypotheses whose intervals can change selection versus zero, same-cell rivals or the ten-campaign boundary. Use 100 customers, or 200 when base-effect SE exceeds 0.15. Stop when estimated information value cannot justify contact opportunity cost or resources run out.
  8. Channel economics. Compare communication costs and conversion multipliers, including the probability cap for call; see Channel Selection.
  9. Final selection. Compare greedy plans at budget shadow prices derived from current option value/cost ratios. Maximize total risk-adjusted net value over reachable customers, using remaining resources after pilots. This is a heuristic, not an exact global optimizer. Final cells are disjoint.
  10. Conservative fallback. If no risk-positive plan remains, return one offer on the cheapest valid channel with positive updated expected net value. It must have promising history or a valid pilot observation. Thus positive pilot evidence can rescue missing/negative history. This minimizes downside under uncertainty; push does not guarantee profit. Do not pad the plan to ten.

For historical mean h, standard deviation s and count n:

historical_weight = min(20, 2 * log(1 + n))
prior_mean = 0.5 * h
prior_variance = s² / max(n, 1) + 0.20² + h² / 12
prior_precision = min(historical_weight / 0.804², 1 / prior_variance)

The weak uniform [0,1] conversion assumption has mean 0.5 and variance 1/12; historical switcher data cannot identify campaign conversion. The count cap and 0.20 population-shift allowance prevent history from overwhelming pilots. Singleton standard deviations default to 1; missing history has zero precision. These modeling assumptions were not fitted to mock seeds.

For a pilot with channel multiplier m <= 1 and actual sample size n:

pilot_base_estimate = observed_lift_ratio / m
pilot_precision = n * m² / 0.804²
updated_mean = (prior_precision * prior_mean
                + sum(pilot_precision * pilot_base_estimate))
               / (prior_precision + sum(pilot_precision))
updated_SE = sqrt(1 / (prior_precision + sum(pilot_precision)))

Audit of PILOT_STD = 0.804

The supplied environment.py:52, not a fitted mock model, explicitly defines:

PER_CUSTOMER_STD = 0.804

The public pilot mechanics in environment.py:172–173 use:

noise = _rng.normal(0.0, PER_CUSTOMER_STD / np.sqrt(n_actual))
observed_ratio = float(_true_lift_ratio(picked, target_tariff, channel).mean() + noise)

The file's comments document per-person noise and standard error scaling. Its module documentation states that judging uses the same environment class with a different effect model. Relying on 0.804 is therefore justified under the supplied public contract; it is not a hidden effect or a constant tuned from mock scores. If organizers change the noise mechanics, uncertainty must be reassessed. The agent does not import private helpers or inspect the effect model.

run_pilot() exposes noisy relative/total lift, actual sample count, cost and remaining resources, but no customer IDs or confidence interval. Total lift is derived from the same noisy ratio and is not independent evidence.

Exploration vs Exploitation

Exploration is a real campaign cost, not a free rehearsal. Before each call, check pilots_left > 0, remaining_contacts >= pilot_size and remaining_budget >= pilot_size * cost_per_contact. Current call sites request only 50, 100 or 200 people; samples below 10 are skipped.

Exploration consumes at most 15% of starting money and 2,000 contacts, and reserves at least one contact for final selection. Stage 2 stops when further information is unlikely to change a decision. Final plans start from the public remaining budget/contacts after pilots, so those costs are always included.

Channel Selection

The organizer's public scoring rule (scoring_core.py:167–172) is:

lift_ratio = tariff_change * min(conversion_probability * conversion_multiplier, 1)
net = credited incremental ARPU - contacts * cost_per_contact

Both channel fields come from env.channels. HIGH ARPU does not automatically mean call. Expensive channels must justify cost and reduced affordable reach. The planner also considers cheap channels over a larger audience.

Pilots use channels with multiplier <=1, measuring the shared base effect tariff_change * conversion_probability without saturation. For call (1.2), the scale relative to that base lies in [1, 1.2]. Use its midpoint for expectation and add its half-width to the risk penalty. Do not assume an unconditional 1.2x gain.

Exact tariff/ARPU filters, ascending ID_NUMBER, the first 5,000 people, and remaining money/contact caps match public scoring order. Prefix sums use actual audience ARPU. No unsupported final audience-size field is emitted.

Final campaigns use different current-tariff/ARPU cells, preventing final/final overlap. Pilot IDs are unavailable. Risk scores subtract an upper bound on possible pilot-overlap ARPU; expected value uses a conservative random-sampling overlap estimate. Negative effects are never discounted away. All repeated contacts still cost money and reach; the organizer credits each customer once, by their best offer.

Robustness

Current validation after final hardening:

CheckResult
Positive runs, seeds 0–910/10
Median net result+1,153,902
Minimum+714,869
Maximum+1,509,310
Single-run net, seed 42+910,618
Single-run gross972,300
Total communication cost61,682 / 100,000
Total contacts, including pilots4,547 / 15,000
Final campaigns6
Pilots17
Pilot contacts1,100
Pilot communication cost14,900

No campaigns were rejected and no execution exceptions occurred. The evaluator's 23-campaign total includes 17 pilots plus 6 final campaigns. Its remaining-resource lines describe the state after pilots; total cost/contacts also include finals. The ten seed results remain unchanged by the fallback correctness fix. All 10 project tests passed (19.8 seconds total, including ten-seed integration and CSV reproduction). A separately measured seed-42 Agent.act took 0.949 seconds on this workspace. The regenerated CSV is nonempty and unchanged.

The mock environment validates mechanics and robustness. Judging effects are different, so the strategy is not tuned to specific mock effects.

Reproducibility

Run from the project root with Python 3.12:

pip install -r requirements.txt
python local_eval.py
python local_eval.py --runs 10
python make_submission.py
python -m unittest -v test_agent

This Windows workspace uses .venv\Scripts\python.exe in place of python. Dependencies remain numpy and pandas only. Candidate order is deterministic; the environment controls seeded sampling. make_submission.py uses seed 42. The test suite validates the generated CSV against a fresh run; run generation before tests. Local environments, caches, .vscode/ and .env are ignored by Git.

Files

FilePurpose
agent.pySubmit: adaptive Agent.act(env) implementation
submission.csvSubmit: reproducible six-campaign output
requirements.txtSubmit: minimal numpy/pandas dependencies
test_agent.pyDeterministic edge-case and ten-seed integration checks
README.mdApproach, public mechanics, commands and limitations
environment.py, mock_environment.pyUnmodified organizer environment and mock factory
scoring_core.py, local_eval.pyUnmodified organizer scoring and evaluation
make_submission.pyUnmodified organizer CSV generator
customer_profile.csv, data/, dictionaries, guides, templateUnmodified participant inputs/reference files

All 15 files from the participant ZIP were compared byte-for-byte and are unchanged.

Constraints

Competition limitEnforcement
1–10 final campaigns when a positive feasible plan is availablePositive-value selection; at most one fallback
5,000 customers per final campaignExplicit planner reach cap matching scorer
15,000 total contacts, pilots includedPreflight pilot checks; finals use remaining contacts
100,000 total communication budgetActual pilot-size cost check; finals use remaining money
20 pilots maximumCheck public pilots_left before every call
10–200 customers per pilotBounded call sites; skip undersized samples
Valid tariffs, channels and filtersPublic dictionaries and observed valid tariff/ARPU cells
Runtime below 10 minutesSmall local computations; exploration deadline at 540 seconds
No hidden state, mock effects or embedded secretsOnly documented environment API and supplied historical data

If every feasible estimate is nonpositive, or no contacts remain, return an empty plan rather than fabricate a profitable offer. Unknown true effects cannot be certified positive in advance. All required local runs return valid nonempty plans.

Failure Handling

Catch RuntimeError, ValueError and OSError only around pilot calls, warn, and continue with other candidates. Reject malformed/nonfinite pilot responses. Any contacts consumed by a failed or malformed response remain accounted for. Historical-file read/schema failures yield neutral hypotheses; invalid historical numbers are filtered. Programming errors are not hidden by a blanket exception handler. There is no misleading partial-plan recovery claim.

Tests cover constrained resources, pilot failures, malformed observations, prior reliability, call saturation, belief reversal after negative pilots, missing/invalid history, positive-pilot fallback without history, deterministic output, valid filters, exploration caps, runtime, full limits across ten seeds, and CSV reproduction.

Known Limitations

  • Historical population differs from the target population. Positive-history screening can miss transitions that become attractive under judging effects.
  • Pilot observations are noisy; uncertainty and information value are approximate. One-standard-error ranking can select false positives after adaptive selection.
  • Overlap can only be handled using public information; pilot identities are not exposed, and confirmation can resample previously contacted customers.
  • Call saturation is bounded, not identified by call pilots. Weak conversion and population-shift priors are assumptions that pilots should override.
  • Allocation is heuristic, with no global optimality guarantee. Data/call subsegments and multiple channels within a final cell are not optimized.
  • Hidden judging effects differ from mock effects. Ten positive mock runs do not guarantee judging profit, and public noise changes would require reassessment.
  • The documented API cannot interrupt an indefinitely blocking run_pilot call; the deadline controls when further exploration may start.
Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
4
Авторы
1
Файлы
21
Ветки
1

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

akhmadiya 4

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

  • 2026-09-23 16:54Harden stable submission and document compliance auditakhmadiya
  • 2026-09-23 16:09Improve Beeline agent with adaptive exploration and validated submissionakhmadiya
  • 2026-09-23 15:21Add files via uploadahhmadiya
  • 2026-09-23 14:01Add files via uploadahhmadiya
Структура репозитория (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
submission.csv
tariff_dictionary.csv
test_agent.py

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

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

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