AAYMA
RU
Войти

Агент тарифных маркетинговых кампаний

Агент для Beeline: исследует базу абонентов, проверяет гипотезы пилотами и выдаёт план из не более чем 10 кампаний (сегмент → тариф → канал) в…

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

О проекте

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

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

Суть

Агент для Beeline: исследует базу абонентов, проверяет гипотезы пилотами и выдаёт план из не более чем 10 кампаний (сегмент → тариф → канал) в рамках бюджета и охвата, максимизируя чистый прирост.

Как устроено

PriorBuilder строит априорные оценки по истории смен тарифа, ArmModel ведёт апостериорные среднее и дисперсию эффекта по каждой гипотезе и каналу. PilotPlanner выбирает пилоты по knowledge gradient через env.run_pilot, пока KG>0. Allocator распределяет деньги и контакты через лагранжиан, CampaignPacker и ScoreSimulator собирают план и проверяют его копией правил скоринга. LLM-агенты на Pydantic AI (HypothesisAnalyst, RiskReviewer) опциональны: задают веса гипотез 0,8/1,0/1,2 и вето кампаниям. Guardrails следят за лимитами, при сбое LLM работает детерминированный движок.

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

  • Bayesian posterior updates
  • knowledge gradient
  • Lagrangian allocation
  • Pydantic AI structured output
  • guardrails
  • stress-testing with oracle

Метрики

seed 42: PASS, чистый результат 4 814 532 (+3,196% к baseline), ROI 49,15, риск 0%; 10 seed: медиана 4 418 069, 10/10 в плюс; 381 тест; ~0,63 от оракула на стресс-мирах

Особенности

Пилоты выбираются по knowledge gradient с явной неопределённостью (шум 0.804/√n). Устойчивость проверена на 9 сдвинутых мирах против оракула. LLM влияет на решение только через ограниченные веса и вето, поэтому не может нарушить лимиты, а сабмит побайтно воспроизводится без ключа.

Стек

Python 3.13 · Pydantic AI · pytest · uv

Модели

OpenAI


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

README

README.md

Агент тарифных маркетинговых кампаний — Beeline × HackAlem AI

Агент для задачи «Beeline Tariff Marketing Campaigns». Он сам исследует базу абонентов, проверяет гипотезы пилотами и возвращает план из 10 или меньше кампаний (сегмент → целевой тариф → канал), который укладывается в бюджет и охват. Решения принимает байесовское ядро. LLM-агенты на Pydantic AI (HypothesisAnalyst, RiskReviewer) подключаются опционально: ранжируют гипотезы и проверяют риск плана. При любом сбое LLM агент продолжает работу на детерминированном движке.

Все данные кейса синтетические. Суммы указаны в условных единицах (у.е.).

Финальная проверка и решения перед сдачей: release_review.md. Добавлены численная проверка LLM-вето и tools/preflight.py; варианты настройки аллокатора не включены из-за регрессий на новых seed. Полный набор: 381 тест прошёл.

Краткое резюме

ПроблемаКампания «вслепую» уходит в минус: около половины исторических смен тарифа заканчиваются downsell, а контакт стоит денег. История описывает другую выборку, поэтому истинный эффект можно узнать только из шумных пилотов, которые тоже расходуют бюджет.
РешениеАгентный цикл исследование → гипотезы (LLM) → пилоты по knowledge gradient → обновление posterior → аллокация (лагранжиан по деньгам и охвату) → ревью рисков (LLM) → guardrails → план + отчёт. У каждого эффекта есть апостериорное среднее и дисперсия с шумом 0.804/√n. План проверяется копией правил скоринга.
Результатpython local_eval.py (seed 42): PASS, чистый результат 4 814 532 (+3,196% к baseline 150 641 084), ROI 49,15, риск 0,0%. --runs 10: медиана 4 418 069, в плюс 10 из 10. Результаты стресс-тестов зависят от сценария и seed; см. финальную проверку выше.

Быстрый старт

Нужен Python 3.13 (зафиксирован в .python-version); чистая установка проверена также на Python 3.12.

python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
pip install pytest                    # только для тестов

python tools/build_agent.py           # собрать agent.py после изменений исходников
python local_eval.py                  # прогон seed 42 скриптом организаторов
python local_eval.py --runs 10        # устойчивость на 10 seed
python make_submission.py             # генерирует submission.csv
pytest                                # модульные и интеграционные тесты

LLM подключать не обязательно: по умолчанию AGENT_LLM_MODE=off, и сабмит не зависит от ключа. Для запуска с LLM создайте .env (он в .gitignore):

# .env — необязательно
OPENAI_API_KEY=sk-...
AGENT_LLM_MODE=decide                 # off | advise | decide

Для жюри: запуск с LLM. Одного OPENAI_API_KEY недостаточно: пока AGENT_LLM_MODE не задан, агент работает без LLM, и make_submission.py побайтно воспроизводит submission.csv из репозитория. Чтобы включить LLM-агентов в контур решений, задайте обе переменные окружения:

export OPENAI_API_KEY=sk-...
export AGENT_LLM_MODE=decide          # или advise
python make_submission.py

С живым LLM ответы модели могут изменить выбор пилотов, и тогда submission.csv будет отличаться от закоммиченного. Вызовы LLM ограничены по времени (30 с на вызов, 90 с суммарно), а при ошибке API агент продолжает работу на детерминированном движке.

КомандаЧто проверяетОжидаемый результат
python local_eval.pyпрогон seed 42, отчёт организаторовСтатус PASS, чистый результат 4 814 532
python local_eval.py --runs 10устойчивость на 10 seedмедиана 4 418 069, в плюс 10 из 10
python make_submission.pyгенерирует submission.csv9 кампаний, побайтно воспроизводится
python tools/preflight.py --checkсборка agent.py и повторный прогон без перезаписи submission.csvсовпадение с существующим файлом
pytestмодульные и интеграционные тесты381 тест проходит
python tools/stress_eval.py --worlds all --runs 39 сдвинутых миров и оракултаблица «медиана / оракул»

Прогон local_eval.py занимает около 1,5 с (LLM выключен). Лимит задачи — 10 минут.

Результаты

Все числа получены неизменёнными скриптами организаторов (local_eval.py, make_submission.py, scoring_core.py) на мок-эффектах. На судействе эффекты другие, поэтому главный критерий — устойчивость на сдвинутых мирах.

python local_eval.py (seed 42):

МетрикаЗначение
СтатусPASS
Чистый результат4 814 532 (+3,196% к baseline 150 641 084)
Gross4 914 516
Затраты99 984 из 100 000
Контактов14 960 из 15 000
Уникальных абонентов10 840
Пилотов18 из 20
Финальных кампаний9
ROI49,15
Риск (доля абонентов с отрицательным эффектом)0,0%
Время прогона≈1,5 с (LLM выключен)

python local_eval.py --runs 10: медиана 4 418 069; минимум 3 670 926; максимум 5 122 772; в плюс 10 из 10.

make_submission.py: submission.csv побайтно воспроизводится в двух запусках — с ключом OPENAI_API_KEY и без него. В плане 9 кампаний, каналы: push ×5, sms ×3, digital_ads ×1.

Стресс-миры (python tools/stress_eval.py --worlds all --runs 3). Оракул (tools/oracle_eval.py) знает истинную модель эффектов мира и упаковывает решение при тех же лимитах — это эвристическая верхняя оценка.

МирЧто меняется относительно мокаМедиана агентаОракул
mockисходные эффекты4,70M5,66M
scale_x2все эффекты ×29,76M10,90M
scale_x0.5все эффекты ×0,52,09M2,79M
high_plus_0.15+0,15 к изменению ARPU у HIGH-гипотез5,46M6,47M
permute_targetsцелевые тарифы переставлены внутри ячейки, история вводит в заблуждение0,55M5,21M
saturationконверсия ×3 со срезкой на 1, перенос между каналами нелинеен11,81M17,34M
flip_sign_30знак эффекта перевёрнут у 30% гипотез1,43M5,07M
prior_misspecifiedэффекты перемешаны независимо от истории11,64M19,76M
price_decorrelatedэффект не связан с ценой тарифа4,24M5,80M

В этой серии (seed 0–2) все прогоны положительные. На свежих seed 100–104 базовая стратегия дала 43/45 положительных результатов; два убытка возникли в flip_sign_30. Средняя доля от оракула в таблице выше — около 0,63. Самые большие разрывы возникают, когда история систематически врёт (permute_targets, flip_sign_30): 18–20 пилотов не хватает, чтобы найти все выгодные цели. tools/loss_breakdown.py раскладывает разрыв до оракула по категориям.

Агентная архитектура

Агент — асинхронный конвейер (Orchestrator) с явным циклом «гипотеза → эксперимент → обновление убеждений → решение». Среда (env) — единственный источник новых фактов; LLM влияет только через провалидированные структурированные ответы.

Схема AI-агентной архитектуры

Агент построен по классической схеме «восприятие → память → рассуждение → действие → рефлексия». Байесовское ядро отвечает за числа, LLM-агенты — за гипотезы и критику, guardrails — за соблюдение лимитов.

flowchart LR
  subgraph ENV["Среда env — публичный API"]
    E1["customer_profile, tariffs, channels"]
    E2["remaining_budget, remaining_contacts, pilots_left"]
    E3["run_pilot — пилотная кампания"]
  end

  subgraph PERCEPTION["1. Восприятие"]
    P1["DataView: ячейки и подъячейки аудитории"]
    P2["PriorBuilder: история смен тарифа"]
  end

  subgraph MEMORY["2. Память и убеждения"]
    M1["ArmModel: posterior по гипотезе и каналу"]
    M2["pilot_history и evidence"]
    M3["llm_cache.json"]
  end

  subgraph REASONING["3. Рассуждение и планирование"]
    R1["LLM-агент HypothesisAnalyst — Pydantic AI"]
    R2["PilotPlanner: knowledge gradient"]
    R3["Allocator: лагранжиан деньги и охват"]
    R4["CampaignPacker и ScoreSimulator"]
  end

  subgraph ACTION["4. Действие"]
    A1["Пилоты через env.run_pilot"]
    A2["Финальный план 1..10 кампаний"]
  end

  subgraph REFLECTION["5. Рефлексия и контроль"]
    C1["LLM-агент RiskReviewer — Pydantic AI"]
    C2["Guardrails: лимиты, схема, fallback"]
    C3["Reporter: agent_report.md"]
  end

  E1 --> P1
  P2 --> M1
  P1 --> R1
  R1 -->|"веса гипотез"| R2
  M1 --> R2
  R2 --> A1
  A1 --> E3
  E3 -->|"шумный эффект"| M2
  M2 --> M1
  M1 --> R3
  E2 --> R3
  R3 --> R4
  R4 --> C1
  C1 -->|"провалидированное вето"| C2
  C2 --> A2
  C2 --> C3
  R1 -.-> M3
  C1 -.-> M3

Роли LLM-агентов. Оба агента реализованы на Pydantic AI: структурированный вывод (pydantic-модели), read-only инструменты над движком, output_validator + ModelRetry, UsageLimits и таймауты. LLM никогда не задаёт числа плана напрямую — он влияет только через ограниченные решения (веса 0,8/1,0/1,2 и вето кампаний), которые затем проверяют симулятор и guardrails. Поэтому ошибка модели не может нарушить лимиты.

Цикл агента

flowchart TD
  A["Исследование данных<br/>DataView, PriorBuilder"] --> B["Гипотезы LLM<br/>HypothesisAnalyst"]
  B -->|"веса 0.8 / 1.0 / 1.2"| C["Пилоты по knowledge gradient<br/>PilotPlanner"]
  C -->|"env.run_pilot"| D["Обновление posterior<br/>ArmModel"]
  D -->|"KG больше 0 и есть пилоты"| C
  D -->|"разведка остановлена"| E["Аллокация<br/>Allocator, CampaignPacker, ScoreSimulator"]
  E --> F["Ревью рисков LLM<br/>RiskReviewer"]
  F -->|"вето только если P net больше 0 ниже 0.9"| G["Guardrails.validate"]
  G --> H["План 1..10 кампаний"]
  G --> I["Отчёт agent_report.md"]
  B -.->|"ошибка или таймаут: нейтральные веса"| C
  F -.->|"ошибка или таймаут: без вето"| G

Один запуск Agent.act

sequenceDiagram
  participant AG as Agent.act
  participant OR as Orchestrator
  participant TH as to_thread
  participant LLM as HypothesisAnalyst
  participant PP as PilotPlanner
  participant ENV as env
  participant AM as ArmModel
  participant AL as Allocator, Packer, Simulator
  participant RR as RiskReviewer
  participant GR as Guardrails
  participant RP as Reporter
  AG->>OR: запуск asyncio-конвейера
  OR->>TH: DataView, история, описания тарифов
  TH-->>OR: ячейки, подъячейки, история
  OR->>TH: PriorBuilder.build
  TH-->>OR: prior по гипотезам
  OR->>LLM: батчи гипотез через asyncio.gather
  LLM-->>OR: веса правдоподобия или нейтральные веса
  loop пока KG больше 0, есть пилоты и время
    OR->>PP: next_pilot
    PP-->>OR: гипотеза, канал, n, подъячейка
    OR->>ENV: run_pilot
    ENV-->>OR: наблюдённый эффект
    OR->>AM: add и calibrate, обновление posterior
  end
  OR->>AL: аллокация, упаковка, симуляция
  AL-->>OR: кандидат плана
  OR->>RR: план и evidence по кампаниям
  RR-->>OR: вето и резюме или пустое вето
  OR->>GR: validate
  GR-->>OR: валидный план
  OR->>RP: agent_report.md
  OR-->>AG: list of dict

CPU-стадии идут в asyncio.to_thread, пилоты — строго последовательно. Каждая стадия ловит свои исключения; при сбое конвейер переходит к аллокации с тем, что уже известно, а при сбое всего конвейера Agent.act возвращает валидный запасной план из guardrails.

Компоненты

МодульФайлРоль
Config, resolve_llm_modeagent_src/contract.pyлимиты, риск-параметры, типы, выбор режима LLM
DataViewagent_src/m10_dataview.pyячейки и подъячейки, цены тарифов, каналы из env
PriorBuilderagent_src/m20_prior.pyиерархический prior по истории смен тарифа
ArmModelagent_src/m30_arm_model.pyгауссов posterior по гипотезе и каналу, перенос между каналами
ScoreSimulatoragent_src/m40_simulator.pyвекторная копия правил скоринга
Allocatoragent_src/m50_allocator.pyлагранжиан по деньгам и охвату
CampaignPackeragent_src/m60_packer.pyупаковка в ≤ 10 кампаний, пересимуляция
PilotPlanneragent_src/m70_planner.pyвыбор пилота по knowledge gradient и правило остановки
LLMLayeragent_src/m80_llm.pyHypothesisAnalyst, RiskReviewer, валидация, кэш, fallback
Guardrails, Reporteragent_src/m85_guardrails.pyсанитизация, лимиты, запасной план, отчёт
Orchestrator, Agentagent_src/m90_orchestrator.pyasyncio-конвейер, дедлайны, verification pilots

Реализованные методы

Ниже — какие методы реализованы, зачем каждый нужен и где он находится в коде.

№МетодЗачемГде в коде
1Иерархическая байесовская усадка (shrinkage)Устойчивая стартовая оценка эффекта при малом числе наблюдений в историиagent_src/m20_prior.py
2Beta-неопределённость доли переходаУчесть, что конверсия в истории оценена неточноagent_src/m20_prior.py
3Гауссов posterior с шумом 0.804/√nУчёт надёжности пилота: 150 клиентов точнее 30agent_src/m30_arm_model.py
4Empirical Bayes калибровка prior (β, τ)Автоматически понять, насколько история «врёт» для целевой аудиторииArmModel.calibrate
5Перенос между каналами с защитой от насыщенияПилот в одном канале информирует решение в другом с учётом min(conv·mult, 1)agent_src/m30_arm_model.py
6Knowledge gradient + квадратура Гаусса–ЭрмитаВыбрать пилот с максимальной ценностью информации за вычетом стоимостиagent_src/m70_planner.py
7Verification pilotsПроверить гипотезы, которые план собирается развернуть, до крупных затратagent_src/m90_orchestrator.py
8Лагранжева релаксация по двум ресурсамОптимально поделить деньги и охват между сегментамиagent_src/m50_allocator.py
9Нижняя доверительная граница (LCB) и P(net > 0)Защита от winner's curse: не вкладываться в случайно удачный пилотagent_src/m50_allocator.py
10Точный симулятор скорингаПроверять каждый план по тем же правилам, что и при оценкеagent_src/m40_simulator.py
11Упаковка в ≤ 10 кампанийУложить план подъячеек в лимит кампаний с минимальной потерейagent_src/m60_packer.py
12LLM-агенты на Pydantic AIРанжирование гипотез и ревью рисков со структурированным выводомagent_src/m80_llm.py
13Guardrails и fallbackГарантия валидного плана при любом сбоеagent_src/m85_guardrails.py

1–2. Prior из истории (PriorBuilder)

По data/change_tariff.csv для каждой гипотезы «текущий тариф × ARPU-сегмент → целевой тариф» считаются среднее относительное изменение ARPU m и доля перехода share. Точечная оценка эффекта: e = m · share. Гипотез много, а наблюдений у многих мало, поэтому оценка сжимается к среднему по ячейке, а ячейка — к глобальному среднему (иерархическая усадка). Неопределённость доли перехода задаётся распределением Beta(c + 1, tot − c + 1). Итоговая дисперсия prior: σ0² = var(m)·share² + m²·var(share) + τ². История описывает другую выборку, поэтому prior только ранжирует кандидатов и задаёт стартовую точку, а решения принимаются по пилотам.

3–5. Обновление убеждений по пилотам (ArmModel)

Пилот на n абонентах возвращает y = r + ε, где ε ~ N(0, 0.804²/n). Это сопряжённая гауссова модель: posterior-среднее — взвешенное среднее prior и наблюдения с весами, обратными дисперсиям. Поэтому пилот на 150 клиентах сдвигает оценку сильнее, чем пилот на 30. Если пилоты систематически расходятся с prior, empirical Bayes калибровка оценивает масштаб β и добавочную дисперсию τ и пересчитывает все гипотезы, включая ещё не проверенные. Эффект канала: r_c = r_p · k(c, p), где k = min(mult_c·ĉ, 1) / min(mult_p·ĉ, 1). Перенос вверх (например, с push на call) дисконтируется, чтобы не переоценить эффект при насыщении конверсии.

6–7. Адаптивная разведка (PilotPlanner)

Кандидат на пилот — это (гипотеза, канал, n ∈ {30, 60, 100, 150, 200}, подъячейка). Для каждого считается knowledge gradient: насколько вырастет ценность лучшего финального плана после наблюдения y. Ожидание по y берётся численно, квадратурой Гаусса–Эрмита. Из KG вычитаются стоимость контактов пилота и цена израсходованного охвата, а польза пилота как мини-кампании прибавляется. Разведка останавливается, когда лучший KG ≤ 0 или закончились пилоты, резерв бюджета или время. Размер и число пилотов не зашиты: они зависят от того, что уже известно. До 8 пилотов зарезервировано на проверку гипотез с наибольшим ожидаемым убытком перед развёртыванием.

8–9. Распределение бюджета (Allocator)

Для каждой подъячейки есть варианты «ничего» или (целевой тариф, канал). Ценность варианта: net = mult_ch · r · ΣP − cost_ch · n, где ΣP — сумма predicted_arpu подъячейки. Два глобальных ограничения (деньги и охват) снимаются лагранжевой релаксацией: каждой единице денег и каждому контакту назначаются теневые цены λ_money и λ_reach, и каждая подъячейка выбирает лучший вариант независимо. Цены подбираются вложенной бисекцией, пока план не уложится в остатки, затем идёт жадная дозагрузка. Отбор идёт по нижней доверительной границе net и по P(net > 0): так агент не вкладывается в сегмент, который выглядел выгодным из-за шума пилота (winner's curse).

Экономика каналов. Из стоимостей 0/4/22/160 у.е. и множителей 0,5/0,65/0,85/1,2 следуют пороги по ожидаемому приросту на абонента r·P: sms выгоднее push при r·P > 26,7; digital_ads выгоднее sms при r·P > 90; call выгоднее digital_ads при r·P > 394. Поэтому дорогие каналы достаются только дорогим сегментам.

10–11. Симуляция и упаковка (ScoreSimulator, CampaignPacker)

Симулятор векторно повторяет правила оценки: фильтры сегментов, сортировку по ID_NUMBER, лимит 5000 на кампанию, затем лимиты охвата и денег, дедупликацию «каждый абонент — по лучшей кампании», учёт уже проведённых пилотов. Упаковщик объединяет подъячейки с одинаковыми (target, channel, сегменты) в кампании с filter_current_tariff="a;b;c". Если кампаний больше 10, он укрупняет группы и пересимулирует план. Из нескольких вариантов упаковки выбирается тот, у которого симулированный net больше. Совпадение с эталоном проверяют tests/test_simulator.py и tests/test_refsim_diff.py.

12–13. LLM-агенты и guardrails

HypothesisAnalyst оценивает правдоподобие гипотез по описаниям тарифов и истории: три батча LOW/MID/HIGH идут параллельно через asyncio.gather. RiskReviewer видит план и evidence по каждой кампании и может предложить вето с обоснованием. What-if проверку он делает через инструмент simulate_without. Guardrails проверяют схему, тарифы, каналы, сегменты и все лимиты; при любой ошибке возвращается валидный запасной план. Подробности — в разделах «LLM-слой» и «Guardrails и лимиты».

LLM-слой (Pydantic AI)

АгентВход и read-only toolsВыход и влияние
HypothesisAnalystбатчи гипотез параллельно через asyncio.gather; tools get_cell, get_tariff, compare_tariffs, get_history_armHypothesisAssessment: оценка low/mid/high → веса 0,8/1,0/1,2 при ранжировании кандидатов на пилот
RiskReviewerплан и evidence по кампаниям; tools get_campaign_evidence, simulate_without (what-if через ScoreSimulator)PlanReview: вето рискованных кампаний и резюме для аналитика
  • Структурированный вывод и валидация. Выход — pydantic-модели; output_validator + ModelRetry требуют, чтобы каждая гипотеза батча была оценена ровно один раз, вето касалось только кампаний с P(net>0) < 0.9 и после вето оставалась хотя бы одна кампания.
  • Численная проверка вето. После перепаковки кандидат должен соблюдать лимиты и сохранять или повышать ожидаемый net с учётом пересечения с пилотами. Иначе исходный план сохраняется, а причина отказа попадает в отчёт.
  • Лимиты. UsageLimits ограничивает число запросов и вызовов tools; таймаут на вызов — 30 с, на весь LLM-слой — 90 с; temperature = 0; модель по умолчанию gpt-4.1-mini, меняется через OPENAI_MODEL.
  • Кэш. Провалидированные ответы сохраняются в llm_cache.json по хэшу входа и промпта.
  • Режимы AGENT_LLM_MODE. off — LLM не вызывается (по умолчанию, см. resolve_llm_mode в agent_src/contract.py), чтобы сабмит не зависел от ключа. advise — вызовы идут и логируются, решения не применяются. decide — при наличии ключа применяются веса гипотез и провалидированное вето. Без ключа любой режим становится off.
  • Fallback. Любая ошибка или таймаут (нет ключа, нет пакета, сеть, лимит, невалидный вывод) → нейтральные веса, пустое вето, работа детерминированного движка.
AGENT_LLM_MODE=decide python local_eval.py   # решения LLM видны в agent_report.md

Guardrails и лимиты

  • Лимиты задачи. ≤ 10 кампаний, ≤ 5000 абонентов в кампании, 15 000 контактов и 100 000 у.е. вместе с пилотами, 20 пилотов по 10–200 абонентов. Планы строятся от env.remaining_* и перед выдачей проверяются симулятором.
  • Валидация вывода (Guardrails.validate). Только ключи формата организаторов; тарифы, каналы и сегменты проверяются по env; кампании, где цель входит в список исходных тарифов, удаляются; имена дедуплицируются. Если валидных кампаний нет, возвращается корректная кампания, которая не тратит бюджет.
  • Честность среды. Только публичный API env (customer_profile, tariffs, channels, remaining_*, pilots_left, run_pilot, pilot_history) и данные участника. tools/build_agent.py не соберёт agent.py, если в коде есть __closure__, gc, inspect или internals.
  • Секреты. Ключ читается только из окружения (.env подгружает python-dotenv, не перезаписывая переменные), не логируется, .env не в git.
  • Время. time_budget_s = 420 (7 из 10 минут), отдельные дедлайны стадий и резерв на аллокацию и отчёт.
  • Детерминизм. RNG от seed, ничьи разрешаются по ключам, словари обходятся в отсортированном порядке.
  • Без констант мока. Эффекты тарифов и сегменты в коде не зашиты; решения зависят от пилотов и posterior.

Соответствие требованиям ТЗ

ТребованиеКак выполненоКоманда проверки
1. agent.py, Agent.act(env) работаетAgent в agent_src/m90_orchestrator.py, сборка в agent.py через tools/build_agent.pypython local_eval.py → Статус PASS
2. 1–10 корректных кампанийCampaignPacker + Guardrails.validate; seed 42 — 9 кампанийpython local_eval.py; pytest tests/test_guardrails.py
3. Пилоты используются и влияют на планцикл PilotPlanner → env.run_pilot → ArmModel; seed 42 — 18 пилотовpytest tests/test_pilot_feedback.py; таблица пилотов в agent_report.md
4. Лимиты бюджета, охвата, пилотовсимулятор + guardrails; 99 984 / 100 000 у.е., 14 960 / 15 000 контактов, 18 / 20 пилотовpython local_eval.py; pytest tests/test_simulator.py tests/test_refsim_diff.py
5. submission.csv воспроизводитсядетерминизм по seed, LLM по умолчанию выключенpython make_submission.py дважды; python tools/preflight.py --check
Опц. Учёт неопределённостиposterior со шумом 0.804/√n, отбор по нижней границе netpytest tests/test_arm_model.py tests/test_pilot_feedback.py
Опц. Адаптивная разведкаknowledge gradient, правило остановки, verification pilotspytest tests/test_planner.py
Опц. Выбор каналаканал выбирается по posterior на канал; в плане push, sms, digital_adssubmission.csv, agent_report.md
Опц. Устойчивость10 из 10 seed в плюс, все стресс-прогоны положительныеpython local_eval.py --runs 10; python tools/stress_eval.py --worlds all --runs 3
Опц. LLM-агенты в контуреPydantic AI, структурированный вывод, валидация, fallbackpytest tests/test_llm_layer.py; AGENT_LLM_MODE=decide python local_eval.py
Файлы организаторов не измененыпобайтно совпадают с пакетом участникаdiff с исходным архивом участника

Структура репозитория

agent.py                  # сдаваемый файл: автосборка из agent_src/
submission.csv            # результат python make_submission.py
agent_report.md           # отчёт последнего запуска: пилоты, план, решения LLM, тайминги
llm_cache.json            # кэш провалидированных ответов LLM
requirements.txt, pyproject.toml, uv.lock, .python-version
agent_src/                # исходники агента, модули m10…m90, контракт в INTERFACES.md
tools/build_agent.py      # agent_src/*.py → agent.py, py_compile и проверка запрещённых токенов
tools/preflight.py        # проверка сборки и повторный прогон без перезаписи submission.csv
tools/eval_checks.py      # вспомогательные проверки сдачи
tools/stress_eval.py      # 9 сдвинутых миров
tools/oracle_eval.py      # оракул с истинными эффектами
tools/loss_breakdown.py   # разложение разрыва до оракула
tools/analyze_history.py  # анализ исторических смен тарифа
tests/                    # pytest, ~360 тестов
reports/                  # stress_eval.json, requirements_audit.md
# файлы организаторов без изменений: environment.py, scoring_core.py, mock_environment.py,
# local_eval.py, make_submission.py, agent_template.py, customer_profile.csv, *_dictionary.csv, data/

Цикл разработки: правка agent_src/*.py → python tools/build_agent.py → agent.py.

Ограничения

  • Эффект моделируется на уровне «тариф, ARPU-сегмент, цель». Зависимость от data/call-сегмента видна только как шум подъячейки.
  • 20 пилотов на сотни гипотез: большая часть решений опирается на prior. Поэтому агент консервативен и может упустить прибыльные сегменты — это видно по разрыву с оракулом в permute_targets и flip_sign_30.
  • Модель пилота гауссова с σ = 0,804 на абонента; при другом шуме доверительные интервалы будут смещены, калибровка частично это компенсирует.
  • LLM не повышает метрику на моке: в режиме decide на seed 42 измерено 4,37M против 4,81M в режиме off. Ценность LLM-слоя — объяснимость и проверка рисков, поэтому по умолчанию он выключен.
  • Все результаты получены на синтетических мок-мирах и не являются прогнозом судейского балла.

Развитие

  1. LLM-судья выбора плана среди кандидатов, уже проверенных движком и симулятором.
  2. Многоэтапные кампании: пилот → частичный запуск → дозапуск по фактическим данным.
  3. Признаки абонента в модели эффекта: иерархическая регрессия по data/call-сегментам и трафику вместо плоского эффекта ячейки.
  4. Интерфейс аналитика: вопросы на естественном языке («почему не call для HIGH?») с ответами из evidence и симулятора через те же read-only tools; ручная фиксация или запрет сегментов.
  5. Масштаб: симулятор и лагранжиан линейны по числу подъячеек; новый канал задаётся стоимостью и множителем.
  6. Jev (RLCD) для LLM-агентов: улучшить ответы HypothesisAnalyst и RiskReviewer с помощью Jev (RLCD). Сейчас режим decide на моке уступает off (4,37M против 4,81M, см. «Ограничения»), поэтому качество ответов LLM — главное узкое место этого слоя. Контур не меняется: ответы по-прежнему проходят output_validator, численную проверку вето и guardrails. Эффект нужно замерить в режимах decide и off через python local_eval.py --runs 10 и python tools/stress_eval.py. Пока не реализовано.
Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
9
Авторы
1
Файлы
76
Ветки
2

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

Eldan Abdrashim 9

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

  • 2026-09-23 17:59docs: направление развития — Jev (RLCD) для LLM-агентовEldan Abdrashim
  • 2026-09-23 17:56docs: инструкция для жюри по запуску с LLMEldan Abdrashim
  • 2026-09-23 17:42Merge remote-tracking branch 'origin/main'Eldan Abdrashim
  • 2026-09-23 17:42fix: воспроизводимый сабмит — проверенный планировщикEldan Abdrashim
  • 2026-09-23 17:38Merge pull request #2 from BAITC-Hacks/masterEldan Abdrashim
  • 2026-09-23 17:32updates: русская документация, агентная архитектура, финальная проверка сабмитаEldan Abdrashim
  • 2026-09-23 17:00Merge pull request #1 from BAITC-Hacks/masterEldan Abdrashim
  • 2026-09-23 16:59wip: checkpoint prepared tariff campaign solution with CodexEldan Abdrashim
Все коммиты (9)
  • 2026-09-23 16:20chore: initialize Python environment with CodexEldan Abdrashim
Структура репозитория (76 файлов)
.env.example
.gitignore
.python-version
README.md
agent.py
agent_report.md
agent_src/INTERFACES.md
agent_src/__init__.py
agent_src/contract.py
agent_src/m10_dataview.py
agent_src/m20_prior.py
agent_src/m30_arm_model.py
agent_src/m40_simulator.py
agent_src/m50_allocator.py
agent_src/m60_packer.py
agent_src/m70_planner.py
agent_src/m80_llm.py
agent_src/m85_guardrails.py
agent_src/m90_orchestrator.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
llm_cache.json
local_eval.py
main.py
make_submission.py
mock_environment.py
pyproject.toml
reports/release_experiments.json
reports/release_review.md
reports/requirements_audit.md
reports/stress_eval.json
requirements.txt
scoring_core.py
submission.csv
tariff_dictionary.csv
task/HackAlem AI_ Beeline Tariff Marketing Campaigns Case.docx
task/HackAlem AI_ Beeline Tariff Marketing Campaigns Case.pdf
task/PARTICIPANT_GUIDE.md
task/PARTICIPANT_GUIDE.pdf
tests/__init__.py
tests/conftest.py
tests/fakes_guardrails.py
tests/fakes_orchestrator.py
tests/fakes_packer.py
tests/fakes_planner.py
tests/ref_simulator_codex.py
tests/test_allocator.py
tests/test_arm_model.py
tests/test_coarse_verify.py
tests/test_contract.py
tests/test_dataview.py
tests/test_guardrails.py
tests/test_integration.py
tests/test_llm_layer.py
tests/test_orchestrator.py
tests/test_packer.py
tests/test_pilot_feedback.py
tests/test_planner.py
tests/test_prior.py
tests/test_refsim_diff.py
tests/test_simulator.py
tests/test_submission_checks.py
tools/analyze_history.py
tools/build_agent.py
tools/eval_checks.py
tools/loss_breakdown.py
tools/oracle_eval.py
tools/preflight.py
tools/stress_eval.py
uv.lock
Ветки (2)

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

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

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