AAYMA
RU
Войти

Beeline Campaign Planner

Агент планирования тарифных кампаний: через пилоты в симуляторе оценивает эффект переходов «тариф × ARPU-сегмент → целевой тариф» и собирает план…

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

О проекте

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

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

Суть

Агент планирования тарифных кампаний: через пилоты в симуляторе оценивает эффект переходов «тариф × ARPU-сегмент → целевой тариф» и собирает план предложений с каналами и бюджетом.

Как устроено

Кандидаты — тройки (текущий тариф, ARPU-сегмент, целевой тариф) со слабым априором из истории переходов; LLM (gpt-4.1-mini, JSON Schema) выбирает до 4 партий по 5 пилотов через env.run_pilot, агент обновляет среднее и стандартную ошибку. Финальный выбор по консервативной оценке mean − 1.64×SE жадным алгоритмом с учётом стоимости каналов и лимитов бюджета/контактов, без пересечения аудиторий. LLM отдельно объясняет итоговые переходы; есть статистический fallback и replay-кэш ответов.

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

  • explore/exploit пилоты
  • lower confidence bound mean−1.64·SE
  • жадная оптимизация под бюджет
  • JSON Schema structured outputs
  • кэш LLM-ответов по хешу

Особенности

LLM только выбирает эксперименты и объясняет план, а расчёт и лимиты делает код; консервативная нижняя граница эффекта защищает от переходов, снижающих ARPU; воспроизводимость через replay сохранённых LLM-ответов по хешу запроса.

Стек

Python · pandas · NumPy · Streamlit · OpenAI Responses API

Модели

gpt-4.1-mini


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

README

README.md

Beeline Campaign Planner — агент тарифных маркетинговых кампаний

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

Проект решает задачу распределения ограниченного бюджета: переход на другой тариф может как увеличить ARPU — среднюю выручку на абонента, — так и снизить его. Агент учитывает стоимость контактов и неопределённость результатов пилотов, а LLM помогает выбирать эксперименты и объясняет готовый план.

Все данные кейса синтетические. В текущей версии пилоты проходят в симуляторе. Приложение не отправляет реальные SMS, push-уведомления, рекламу или звонки. Результат симуляции не является прогнозом реальной прибыли или балла судейства.

Что реализовано

  • Агент Agent.act(env), совместимый с локальной средой хакатона.
  • Веб-интерфейс на Streamlit: пример из кейса или загрузка трёх CSV в одну область.
  • Определение назначения таблиц по колонкам, ручное сопоставление нестандартных названий на сайте, проверка формата, тарифов, сегментов, числовых значений и уникальности идентификаторов.
  • Выбор гипотез LLM, запуск пилотов через env.run_pilot, повторные проверки перспективных вариантов и обновление статистических оценок.
  • Выбор итоговых кампаний и каналов с учётом бюджета, числа контактов и риска.
  • Показ прогресса, истории пилотов, оценок эффекта, расходов и результата симуляции.
  • Объяснения LLM для каждого итогового перехода: интерес клиента, возможный эффект для компании, подтверждающие факты, риски и следующая проверка.
  • Экспорт плана в CSV и отчёта в JSON; отдельная генерация submission.csv для сдачи.
  • Ввод API-ключа в скрытое поле сайта и резервная статистическая логика при сбое API.

Лимиты локального кейса:

РесурсЛимит
Общий бюджет, включая пилоты100 000 у.е.
Все контакты, включая пилоты и повторные контакты15 000
ПилотыДо 20
Участники одного пилотаОт 10 до 200
Финальные кампанииДо 10
Абоненты одной финальной кампанииДо 5 000

Как работает решение

Пользовательский сценарий

  1. Аналитик открывает сайт и выбирает пример из кейса либо загружает три таблицы.
  2. Приложение проверяет данные и показывает количество абонентов, переходов и тарифов.
  3. Аналитик вводит ключ OpenAI API и разрешает передачу агрегатов для запуска.
  4. После нажатия «Исследовать и собрать план» агент формирует кандидатов на пилоты.
  5. LLM выбирает проверки из предложенного списка; агент запускает их в симуляторе и обновляет оценки эффекта и неопределённости.
  6. Код формирует итоговый план кампаний. Отдельный запрос к LLM создаёт объяснения переходов на основании параметров тарифов, агрегатов сегментов, истории и пилотов.
  7. Аналитик изучает результат и скачивает campaign_plan.csv и campaign_report.json.

Скачанный из приложения план не заменяет файл сдачи: submission.csv создаётся командой python make_submission.py на стандартных данных и seed 42.

Роли LLM и статистики

LLM не обучается на загруженных CSV. Используется готовая модель OpenAI. Она выбирает пилоты и пишет объяснения; расчёт оценок, проверку лимитов и распределение кампаний выполняет Python-код.

Кандидат на проверку — сочетание текущего тарифа, ARPU-сегмента и целевого тарифа. История переходов задаёт слабую предварительную оценку. Агент планирует до четырёх партий по пять пилотов: первые две исследуют варианты, следующие две уточняют уже измеренные перспективные варианты. Все пилоты выполняются через канал sms. LLM предлагает выборки по 100–200 человек; код уменьшает их при недостатке аудитории или ресурсов.

После пилотов агент обновляет среднее и стандартную ошибку оценки. Для финального выбора используется консервативная оценка mean − 1.64 × standard_error. Жадный алгоритм сравнивает ожидаемый эффект с расходами на канал и оставшимися лимитами. Между итоговыми кампаниями аудитории не пересекаются; с пилотами пересечения возможны. Если прибыльных по оценке вариантов нет, код может выбрать доступный протестированный сегмент для бесплатного push; положительного эффекта это не гарантирует.

Объяснение LLM не меняет рассчитанный план. Если запрос объяснения завершается ошибкой, план остаётся доступным, а сайт показывает предупреждение.

Технологии

КомпонентИспользование
PythonАгент, обработка данных, симуляция, API-клиент и проверки
pandas >=2.2,<4CSV, группировки, сегментация и отчёты
NumPy >=1.26,<3Численные операции и случайность симуляции
Streamlit >=1.64,<2Веб-интерфейс, загрузка файлов, таблицы и скачивание отчётов
OpenAI Responses APIВыбор пилотов и объяснение итоговых кампаний
gpt-4.1-miniМодель по умолчанию; имя настраивается через OPENAI_MODEL
JSON SchemaСтруктурированный формат ответов модели с проверкой в коде
urllib.request, unittest, Streamlit AppTestHTTP-запросы и автоматические проверки

Отдельного backend-сервиса, базы данных и локальной LLM в проекте нет. OpenAI SDK не требуется: запросы отправляются средствами стандартной библиотеки.

Архитектура

flowchart TD
    D[CSV или пример из кейса] --> UI[app.py — Streamlit]
    UI --> S[campaign_app_service.py — проверка и запуск]
    S --> A[agent.py — Agent.act]
    A <--> P[llm_planner.py — выбор пилотов через API]
    A <--> E[environment.py — публичный интерфейс пилотов]
    S --> M[mock_environment.py и scoring_core.py — симуляция и оценка]
    A --> C[Итоговые кампании]
    C --> X[campaign_explanations.py — объяснения через API]
    X --> UI
    M --> UI
    UI --> R[Таблицы, CSV и JSON]

Агент получает публичный интерфейс среды и наблюдения пилотов. Мок-модель эффектов создаётся адаптером приложения или скриптом проверки; агент не читает её напрямую.

Файл или каталогНазначение
agent.pyКандидаты, пилоты, обновление оценок, финальные кампании
llm_planner.pyОграниченные вызовы API, проверка ответа, кэш и обработка ошибок
app.pyСтраница аналитика и настройки запуска
campaign_app_service.pyРаспознавание CSV, валидация, запуск агента, сбор отчёта
campaign_explanations.pyАгрегаты и объяснения итоговых тарифных переходов
environment.pyИнтерфейс среды и выполнение пилотов
mock_environment.pyЛокальная модель эффектов на основе истории переходов
scoring_core.pyПроверка кампаний, ограничения, стоимость и оценка результата
local_eval.pyПроверка агента на одном или нескольких seed
make_submission.py, submission.csvГенератор и файл кампаний для сдачи
customer_profile.csv, data/Основные данные кейса
llm_responses/10 сохранённых ответов для воспроизводимости командных запусков
test_app.pyПроверки сервиса, интерфейса, API-ошибок и объяснений
requirements.txtОбщие зависимости агента и веб-приложения
.streamlit/config.tomlЦветовая тема интерфейса

Установка и запуск

Команды выполняются из корня репозитория. Локальная проверка проекта выполнена на Python 3.14.7. Зависимости устанавливаются из файлов requirements; полной фиксации транзитивных версий в проекте нет.

1. Создать окружение и установить зависимости

python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt

В Windows PowerShell окружение активируется командой .venv\Scripts\Activate.ps1. Один файл requirements.txt устанавливает зависимости агента, сайта и тестов.

2. Запустить приложение

python -m streamlit run app.py --server.address 127.0.0.1 --server.port 8501 --browser.gatherUsageStats false

Открыть http://127.0.0.1:8501. В боковой панели ввести ключ в поле «Ключ OpenAI API» и подтвердить передачу агрегатов. Без ключа и разрешения кнопка запуска недоступна.

Сайт использует только режим новых запросов к API, без подстановки сохранённых ответов. За одно исследование возможно до пяти платных запросов: четыре для выбора пилотов и один для объяснения плана. Для работы API нужен доступ к интернету и ключ с доступом к выбранной модели.

Ключ можно задать на сервере вместо ввода на сайте. В Bash, без записи значения в команду истории:

read -rsp 'OpenAI API key: ' OPENAI_API_KEY
export OPENAI_API_KEY

После этого запустить приложение из того же терминала. Если ключ введён на сайте, он имеет приоритет над серверным. Файл .env автоматически не загружается.

3. Запустить агента и создать файл сдачи

Для воспроизводимой проверки без запросов к API в Bash:

export AGENT_LLM_MODE=replay
python local_eval.py
python make_submission.py

Для проверки разброса результатов:

python local_eval.py --runs 10

Эти команды используют основные CSV репозитория. make_submission.py перезаписывает submission.csv результатом запуска с seed 42.

Настройки командного агента

Эти режимы относятся к Agent() в командной строке. Переключателя режимов на сайте нет.

ПеременнаяНазначение
OPENAI_API_KEYКлюч OpenAI; значение не должно попадать в Git
OPENAI_MODELИмя модели, по умолчанию gpt-4.1-mini
AGENT_LLM_MODE=autoПо умолчанию: точное совпадение с сохранённым ответом, затем API; при недоступности — статистика
AGENT_LLM_MODE=apiНовые вызовы API без чтения сохранённых ответов; при сбое — статистика
AGENT_LLM_MODE=replayСохранённые ответы; при отсутствии совпадения — статистика, без сети
AGENT_LLM_MODE=offТолько статистический выбор пилотов
AGENT_LLM_CACHEКаталог кэша командного запуска, по умолчанию .llm_cache

Сохранённый ответ применяется только при совпадении хеша полного запроса: модели, инструкций, схемы, кандидатов, наблюдений и лимитов. Это кэширование, а не обучение модели. Для воспроизводимости стандартного файла сдачи следует сохранить llm_responses/, исходные данные и настройки модели.

Проверка сайта

  1. Запустить приложение и выбрать «Пример из кейса».
  2. Ввести действующий API-ключ, разрешить передачу агрегатов и запустить исследование.
  3. Дождаться плана. Проверить тарифы и распределение бюджета во вкладке «Кампании и бюджет», обоснования переходов во вкладке «Объяснения LLM» и историю проверок во вкладке «Пилоты».
  4. Скачать CSV и JSON. При успешном ответе API объяснения присутствуют и в JSON.
  5. Выбрать «Загрузить CSV» и добавить вместе три основных файла репозитория: customer_profile.csv, data/change_tariff.csv, data/dict_tariff.csv.
  6. Убедиться, что приложение распознало таблицы. Удаление одного обязательного файла должно блокировать запуск; при изменении набора данных прежний результат очищается.

Seed в параметрах задаёт случайность пилотов. Одинаковый seed воспроизводит случайность симулятора при одинаковой последовательности действий, но новые ответы OpenAI могут изменить эту последовательность и итоговый план.

Автоматические проверки

python -m unittest test_app -v

В репозитории 17 тестов: проверка входных данных, форматов CSV, ручного сопоставления, общей области загрузки, полного сценария интерфейса, изоляции ключей, блокировки запуска, обхода кэша в API-режиме, объяснений и сохранения плана при ошибке API. Сетевые ответы подменяются тестовыми; реальные платные обращения не выполняются. Один из тестов сравнивает воспроизведённый CSV со стандартным submission.csv.

Данные и интеграции

Основной комплект в репозитории:

ФайлСтрокСодержимое
customer_profile.csv23 441Профили, текущие тарифы, признаки потребления, сегменты и predicted_arpu
data/change_tariff.csv14 823Исторические переходы и ARPU до/после
data/dict_tariff.csv21Коды, цены и параметры тарифных пакетов

Формат загрузок

CSV в UTF-8, UTF-16 с BOM или Windows-1251; разделитель — запятая, точка с запятой, табуляция или |; до 20 МБ на файл и до 100 000 строк в каждой таблице. Порядок и имена файлов произвольны: приложение определяет таблицы по обязательным колонкам. Лишние пробелы, регистр и пробелы вместо подчёркиваний в известных названиях исправляются автоматически. Поддерживается строка Excel sep=;, пробелы между разрядами и десятичная запятая в числовых полях (при CSV с запятой-разделителем такие числа должны быть в кавычках).

Для других названий откройте «Настроить распознавание файлов и колонок», выберите назначение таблицы и сопоставьте поля с существующими колонками. Лишний файл можно явно пропустить. Недостающие значения не выдумываются: если обязательных данных действительно нет или они некорректны, запуск остаётся недоступным с указанием причины.

ТаблицаОбязательные колонки
ПрофилиID_NUMBER, current_tariff, arpu_segment, data_segment, call_segment, predicted_arpu
ПереходыID_NUMBER, tariff_plan_code_from, tariff_plan_code_to, AVG_ARPU_PREV_3M, AVG_ARPU_NEXT_3M
Тарифыtariff_plan_code, price_tariff

Для содержательного сравнения пакетов используются также Data_in_PKG, Min_another_operator_in_PKG, Min_another_operator_and_city_in_PKG. Если этих колонок нет, соответствующие параметры для объяснения считаются неизвестными.

Сегменты: ARPU — LOW/MID/HIGH, интернет — NON_USER/LITE/HEAVY, звонки — LOW/MEDIUM/HIGH. Коды тарифов должны состоять из латинских букв, цифр, дефиса или подчёркивания и существовать в справочнике. Нужны минимум два тарифа и хотя бы один сегмент «текущий тариф × ARPU» от 10 абонентов. История с ARPU до перехода менее 100 исключается из оценки эффекта. Дубли таблиц и неоднозначные схемы блокируют загрузку; неподходящие дополнительные файлы отмечаются как неиспользуемые.

API и хранение

Единственная реализованная внешняя AI-интеграция — OpenAI Responses API (https://api.openai.com/v1/responses). Передаются агрегаты кандидатов и пилотов; для объяснения — также параметры тарифов и агрегированная история переходов. Идентификаторы абонентов и произвольные дополнительные колонки CSV в запросы не входят. Запросы используют store: false; это параметр API, а не обещание отсутствия любой обработки или хранения на стороне внешнего провайдера.

Загрузки и введённый на сайте ключ хранятся в текущей сессии. Ключ не записывается в файлы, кэш или отчёты и не меняет окружение сервера. Кнопка очистки удаляет его из поля и сбрасывает разрешение. Серверный ключ в браузер не подставляется. Временный кэш планировщика приложения удаляется после работы агента; исходные CSV не перезаписываются. .env и .llm_cache/ исключены через .gitignore.

Ограничения текущей версии

  • Симуляция, а не запуск кампаний. Интеграций с биллингом, CRM и системами рассылок нет. Эффекты на судействе отличаются от локальной мок-модели.
  • Нет гарантии прибыли. Скоринг оценивает прирост выручки за вычетом контактов, без полной себестоимости услуг. Пилоты тоже расходуют бюджет; при слабом или отрицательном сигнале результат может оказаться убыточным.
  • Ограниченный поиск. До четырёх партий пилотов, ограниченный список кандидатов и жадное распределение ресурсов не гарантируют глобально оптимальный план. При недостатке подходящих данных итоговый список может быть пустым.
  • Перенос эффекта между каналами приближённый. Пилоты проводятся через SMS; финальные каналы сравниваются по множителям среды, без отдельных пилотов каждого канала.
  • Признаки должны быть готовы. Проект не обучает модель predicted_arpu, не подготавливает произвольные сырые выгрузки и не дообучает LLM.
  • Ответы LLM требуют проверки. Структура ответа проверяется кодом, но достоверность всех текстовых выводов автоматически не доказывается. При сбое выбора пилотов используется статистика; при сбое объяснения оно не показывается.
  • Ограничения времени программные. Новая партия не начинается после 210 секунд работы агента, новый пилот — после 240. Таймаут каждого API-вызова — 25 секунд, без повторов. Запрос итогового объяснения не начинается после 540 секунд работы приложения. Общего принудительного прерывания на 10-й минуте нет: зависший вызов среды или долгая локальная обработка этими проверками не прерываются.
  • Локальное приложение. Нет авторизации сотрудников, фоновой очереди задач, постоянного хранилища исследований и конфигурации промышленного развёртывания.
  • Воспроизводимость зависит от режима. Новые ответы API, изменение модели, данных или версий зависимостей могут изменить план и submission.csv.
Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
8
Авторы
1
Файлы
30
Ветки
1

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

Impala4c 8

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

  • 2026-09-23 16:25deletion of test filesImpala4c
  • 2026-09-23 16:23new designImpala4c
  • 2026-09-23 16:06some changes in reqImpala4c
  • 2026-09-23 16:04some files for test was deletedImpala4c
  • 2026-09-23 16:01New READMEImpala4c
  • 2026-09-23 15:54appImpala4c
  • 2026-09-23 14:37deleted filesImpala4c
  • 2026-09-23 14:01first commitImpala4c
Структура репозитория (30 файлов)
.gitignore
.streamlit/config.toml
README.md
agent.py
app.py
campaign_app_service.py
campaign_explanations.py
customer_profile.csv
data/change_tariff.csv
data/dict_tariff.csv
environment.py
llm_planner.py
llm_responses/1ab968bbd902280c05bdf945a6af8a7cac55616605073c1c8665aeecdd679cd3.json
llm_responses/1bfec62bf8b496806565a39cff321107fe144cd82446c060b091f38d0f87efc1.json
llm_responses/26856e4d171a8582dc750da4c5e78c62442aceed67bb3a3e67ff9a7d33f17974.json
llm_responses/2cbbef767a2dc30752eb82c8e4bd690056ed581678a39b96f761ab289943d492.json
llm_responses/471daf1c7eba805cba61307fa538d76051f278eaa5e7fa5ea82e97301a572490.json
llm_responses/5a33c6d60d7e312e49c88f64e104ec7ba8cbdd96583870df2a4892154fa027ae.json
llm_responses/5e7f842771cb9e0cd6a32e47867f07ad373fd1ede58b9bdb93de7fb88905e726.json
llm_responses/README.md
llm_responses/b36368abcadf4bd9df17e11506fd6bfc7c743fd1274ec25a800f57a96d68941f.json
llm_responses/e3a6a7559b71e383fd38e474ac2105929d502702e461e6661014f3ee2d223efa.json
llm_responses/f9aa2a84473617046f701f862801a8e1c37f28b224010235a663b337f9185728.json
local_eval.py
make_submission.py
mock_environment.py
requirements.txt
scoring_core.py
submission.csv
test_app.py

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

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

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