AAYMA
RU
Войти

HackAlem: агент тарифных кампаний и Telegram-бот

Агент для маркетингового аналитика: по истории смен тарифов строит гипотезы, проверяет их SMS-пилотами и формирует план тарифных кампаний с учётом…

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

О проекте

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

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

Суть

Агент для маркетингового аналитика: по истории смен тарифов строит гипотезы, проверяет их SMS-пилотами и формирует план тарифных кампаний с учётом бюджета; Telegram-бот запускает расчёт и хранит историю в SQLite.

Как устроено

Аудитория делится на группы current_tariff × arpu_segment; по истории переходов считается prior_ratio = mean(delta)·min(m/M·0.65,1) и prior_net = N·(A·prior_ratio − 4), для каждой группы берётся лучший целевой тариф (18 кандидатов). До 12 SMS-пилотов по 150 абонентов обновляют оценки с поправкой на неопределённость, затем выбираются финальные кампании и считается скоринг по правилам стартового пакета. Gemini опционально только упорядочивает очередь гипотез перед пилотами.

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

  • исторические оценки эффекта смены тарифа
  • последовательные пилоты (explore/exploit)
  • поправка на неопределённость
  • JSON-schema валидация ответа LLM
  • Telegram bot

Особенности

Прозрачно описаны формулы приоритизации и чётко ограничена роль LLM: она только выбирает порядок пилотов, при ошибке используется исторический порядок. Честная сверка с ТЗ и указание, какие файлы данных не используются. 68 офлайн-тестов.

Стек

Python · pandas · NumPy · Telegram Bot · SQLite · unittest

Модели

Gemini (Google Gen AI SDK)


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

README

README.md

HackAlem: агент тарифных кампаний и Telegram-бот

Прототип для маркетингового аналитика: агент оценивает историю смен тарифов, проверяет гипотезы пилотами и возвращает план кампаний с учётом бюджета и контактов. Telegram-бот запускает расчёт, показывает результат и сохраняет историю в SQLite. Все данные синтетические; суммы указаны в условных единицах.

Основной сценарий ТЗ реализован и работает локально: история → гипотезы → пилоты → обновление оценок → финальные кампании → скоринг. Полную готовность к сдаче пока заявлять нельзя: после переноса кода в scripts/ команды и расположение артефактов отличаются от PDF, а проверка на скрытой среде не проводилась. Подробная сверка приведена в разделе «Соответствие ТЗ».

Быстрый запуск

Команды ниже выполняются из корня репозитория в PowerShell. Используйте проектную .venv. Проверенное окружение: Python 3.14.7; текущие закреплённые зависимости требуют как минимум 3.12 из-за NumPy. Совместимость всего набора с Python 3.10 не подтверждена.

python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
if (-not (Test-Path config.json)) { Copy-Item config.example.json config.json }

# Один прогон с seed=42 и полным отчётом
.\.venv\Scripts\python.exe -X utf8 -m scripts.local_eval

# Десять прогонов: seed от 0 до 9
.\.venv\Scripts\python.exe -X utf8 -m scripts.local_eval --runs 10

# Создать или перезаписать data/submission.csv, seed=42
.\.venv\Scripts\python.exe -X utf8 -m scripts.local_eval --submission

# Запустить агента, получить отчёт бота и сохранить его без Telegram
.\start_bot.cmd --once

# Все автоматические тесты
.\.venv\Scripts\python.exe -X utf8 -m unittest discover -s tests -v

Для этих прогонов токен Telegram не нужен. При gemini.enabled=false не нужен и LLM: расчёт выполняется локально. Если в личном config.json включён Gemini, оценка и --once могут обращаться к API. Для воспроизводимого CSV оставьте Gemini выключенным.

start_bot.cmd сам переходит в папку проекта. Остальные команды python -m scripts.<модуль> нужно запускать из корня, чтобы Python нашёл пакет. Внутри приложения пути к данным, конфигурации и SQLite разрешаются относительно корня проекта, а не рабочей папки процесса.

Структура и взаимодействие компонентов

scripts/
  agent.py           # стратегия, интерфейс JsonLLMClient, адаптер Gemini
  bot.py             # Telegram, отчёты, SQLite и запуск без Telegram
  config.py          # dataclass-настройки, чтение JSON, общее логирование
  environment.py     # API пилотов и локальная модель эффекта
  local_eval.py      # общий оценщик для CLI/бота, экспорт submission
  scoring_core.py    # правила скоринга из стартового пакета
  __init__.py
data/
  *.csv              # аудитория, история и справочники
  submission.csv     # появляется после --submission
  bot/runs.sqlite3   # появляется после запуска бота, исключён из Git
tests/
  bot_eval.py        # прежний оценщик для сравнения в тестах
  test_*.py
start_bot.cmd        # Windows-лаунчер
config.example.json # пример настроек без секретов
config.json         # личные настройки и ключ Gemini, исключён из Git
.env                # личный токен Telegram, исключён из Git
requirements.txt    # прямые и транзитивные зависимости с версиями
README.md           # общая инструкция по агенту и боту
AGENTS.md           # правила разработки
PARTICIPANT_GUIDE.md # текстовая памятка с актуализированными путями
*.pdf               # исходные требования организаторов

Основной поток выполнения:

CLI local_eval ───────────────────────┐
Telegram /run → bot → run_demo(42) ───┤
                                     ↓
                  make_mock_env → Agent.act(env)
                                     ↓
                      env.run_pilot → финальные кампании
                                     ↓
                  пилоты + кампании → scoring_core
                                     ↓
                     отчёт CLI / отчёт бота + SQLite

Стратегия отделена от среды и оценщика: Agent.act(env) -> list[dict] получает доступный агенту объект среды и возвращает кампании. Локальный оценщик дополнительно получает служебный список уже выполненных пилотов, чтобы включить их в итог. Сам агент скрытую модель из среды не извлекает.

КомпонентОсновной контракт
scripts.agent.AgentПринимает settings и заменяемый llm_client; метод act(env) возвращает финальные кампании.
scripts.environment.make_mock_env(seed)Возвращает среду пилотов и служебный объект для локального оценщика.
scripts.local_eval.evaluate_agent(agent, seed)Возвращает seed, campaigns, n_pilots, полный metrics; при исключении агента добавляет безопасный error.
scripts.local_eval.run_demo(seed=42)Создаёт Agent() с настройками из JSON и вызывает общий оценщик без печати. Это точка входа бота.
scripts.local_eval.build_submission(agent, seed=42)Выполняет агента и возвращает DataFrame только финальных кампаний. CLI сохраняет его в CSV.
scripts.bot.RunStoreХранит состояния запусков и результаты, выдаёт историю по chat ID.

Отдельные llm.py, logging_config.py, mock_environment.py и make_submission.py объединены с профильными модулями. Шаблон agent_template.py удалён. scoring_core.py оставлен отдельно: его правила используют и среда, и оценщик. tests/bot_eval.py нужен для регрессионного сравнения и не импортируется при обычной работе бота.

Какие данные действительно используются

Файл в data/СодержимоеИспользование в текущем коде
customer_profile.csv23 441 абонент, 29 колонокГотовая аудитория, ID, текущий тариф, сегменты и predicted_arpu; среда и скоринг работают с этим профилем.
change_tariff.csv14 823 записи переходовАгент рассчитывает исторические оценки; локальная среда отдельно строит на этой же истории модель эффекта.
dict_tariff.csv21 тарифПроверка допустимости тарифа; локальная модель использует цены при отсутствии исторического перехода.
traffic.csvМесячное потреблениеПрисутствует в наборе, но текущий код его не читает.
arpu_monthly.csvМесячная выручкаПрисутствует в наборе, но текущий код его не читает.
tariff_dictionary.csv, feature_dictionary.csvОписания тарифов и признаковСправочные материалы для человека; стратегия их не читает.

В PDF указано 14 824 перехода; в фактическом CSV при чтении через pandas получается 14 823 строки данных. Для описания реализации выше использован фактический размер файла.

Собственного построения профиля из сырых таблиц и обучения модели ARPU нет: predicted_arpu уже поставляется в customer_profile.csv. Стратегия группирует аудиторию только по текущему тарифу и ARPU-сегменту. Среда поддерживает фильтры потребления интернета и звонков, но агент пока их не выбирает.

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

Как агент выбирает кампании

Ниже описаны фактические шаги scripts/agent.py с настройками по умолчанию. Параметры можно менять через config.json.

1. Построение гипотез из истории

Аудитория разбивается на пары current_tariff × arpu_segment. Для каждой группы считаются число абонентов N и средний predicted_arpu, обозначенный A. Рассматриваются группы от 300 до 5000 абонентов; большие группы не разбиваются на части.

В истории остаются переходы с исходным средним ARPU не меньше 100 и заполненными тарифами/ARPU после перехода. Исторический ARPU делится на три сегмента по порогам 1000 и 5000. Относительное изменение ограничивается диапазоном от −1 до 3:

delta = clip((ARPU_after − ARPU_before) / ARPU_before, −1, 3)

Для каждого перехода из пары «исходный тариф + ARPU-сегмент» на целевой тариф считаются среднее delta и число наблюдений m. Если M — число всех переходов из этой пары после очистки истории:

prior_ratio = mean(delta) × min((m / M) × 0.65, 1)
prior_net   = N × (A × prior_ratio − 4)

Здесь 0.65 — множитель конверсии SMS, 4 — цена контакта. Доля исторических переходов m / M используется как приближение конверсии; в этих данных нет измеренной конверсии маркетингового воздействия.

Переходы с менее чем 10 наблюдениями, на тот же тариф или на неизвестный тариф исключаются. Для каждой пары аудитории остаётся один целевой тариф с максимальным prior_net. На текущем наборе и стандартных настройках получается 18 кандидатов из 63 пар тариф/ARPU-сегмент.

2. Выбор кандидатов для пилотов и роль LLM

Без LLM выбираются первые 12 кандидатов по исторической оценке. Если включён Gemini и кандидатов больше доступного по настройкам числа пилотов, модель выбирает и упорядочивает 12 ID из готового списка.

Модель получает агрегаты: тарифы, ARPU-сегмент, численность, средний ARPU, размер исторической выборки и начальные оценки эффекта. Индивидуальные строки профиля в этот запрос не передаются. Ответ должен содержать ровно нужное число уникальных целочисленных ID существующих кандидатов; тип, длина, диапазон и уникальность проверяются.

LLM участвует только в выборе очереди гипотез до пилотов. Она не придумывает новые фильтры или тарифы, не анализирует последующие наблюдения, не выбирает финальный план и не пишет объяснение результата. При отсутствии ключа, ошибке запроса или неверном JSON используется исторический порядок.

Адаптер GeminiClient использует Google Gen AI SDK за интерфейсом JsonLLMClient.generate_json(prompt, schema). Можно передать свой клиент в Agent(llm_client=my_client). По умолчанию LLM выключен; настройки запроса задают таймаут 10 секунд и одну попытку. Это ограничения запроса, а не общий таймер всего запуска.

3. Пилоты и поправка на неопределённость

Агент последовательно проводит до 12 SMS-пилотов по 150 абонентов. Один такой пилот стоит 600 у.е.; двенадцать — 7200 у.е. и 1800 контактов. Перед вызовом проверяются оставшиеся пилоты, деньги и контакты; в контактах резервируется место хотя бы для самого маленького выбранного сегмента.

Для фактического размера пилота n и наблюдения observed_ratio рассчитываются:

posterior          = (50 × prior_ratio + n × observed_ratio) / (50 + n)
conservative_ratio = posterior − 0.5 × 0.8 / sqrt(n)
estimated_net      = max(N − n, 0) × A × conservative_ratio − N × 4

При n=150 история имеет вес 25%, наблюдение — 75%. Поправка уменьшает относительную оценку примерно на 0,03266. Это простая эвристика осторожности с заданным уровнем шума, а не откалиброванный доверительный интервал.

Оценка дополнительного результата учитывает, что финальная кампания снова контактирует со всем сегментом: оплата нужна за все N контактов, но участникам пилота нельзя второй раз начислить эффект того же предложения. Уже состоявшийся эффект пилота отдельно учитывает оценщик.

Результаты пилотов меняют финальный выбор, но не меняют очередь следующих пилотов, их канал, целевой тариф или размер. Остановка по остаткам лимитов реализована; адаптивная разведка по уже полученным наблюдениям пока отсутствует.

4. Формирование финального плана

После пилотов кандидаты сортируются по estimated_net / N. Агент жадно выбирает положительные SMS-кампании, целиком помещающиеся в оставшиеся бюджет и контакты, до 10 штук. Сегменты финальных кампаний не пересекаются, поскольку на каждую пару тариф/ARPU-сегмент оставлен один кандидат. Пересечения с пилотами при этом есть.

Если подходящих SMS-кампаний нет, агент пытается вернуть одну бесплатную push-кампанию для доступного сегмента. Бесплатный контакт не гарантирует положительного эффекта смены тарифа. Если кандидатов или места даже для одного сегмента нет, возможен пустой результат; гарантия «минимум одна кампания» для любых входных данных отсутствует.

Пример структуры возвращаемой кампании:

{
    "campaign_name": "sms_tariff_13_MID_tariff_8",
    "filter_current_tariff": "tariff_13",
    "filter_arpu_segment": "MID",
    "target_tariff": "tariff_8",
    "channel": "sms",
}

Выход CSV также содержит колонки filter_data_segment и filter_call_segment; при текущей стратегии они пустые. Это означает отсутствие соответствующего фильтра.

Что делает локальная среда и как считается результат

environment.py создаёт модель эффекта из выданной истории и симулирует пилот на случайной подвыборке выбранного сегмента. Пилот возвращает средний модельный эффект с шумом; стандартное отклонение шума равно 0.804 / sqrt(n). Контакты, расходы и конкретные ID участников пилота сохраняются для скоринга.

КаналЦена контакта, у.е.Множитель конверсииИспользование агентом
push00,50Только запасная финальная кампания.
sms40,65Все обычные пилоты и финальные кампании.
digital_ads220,85Поддерживается средой, стратегия не выбирает.
call1601,20Поддерживается средой, стратегия не выбирает.

В scoring_core.py ожидаемый эффект для абонента считается так:

lift = predicted_arpu × model_arpu_change
       × min(model_conversion × channel_multiplier, 1)

gross_arpu_lift = сумма лучшего lift для каждого уникального абонента
total_cost     = стоимость всех контактов, включая повторные и пилотные
net_arpu_gain  = gross_arpu_lift − total_cost

Эффекты нескольких кампаний для одного абонента не складываются. Повторные контакты расходуют и деньги, и лимит контактов. Базовая выручка — сумма готового predicted_arpu, примерно 150 641 084 у.е.; итог после кампаний равен базе плюс net_arpu_gain.

Общие ограничения: 100 000 у.е., 15 000 контактов вместе с пилотами, до 5000 абонентов на кампанию, до 10 финальных кампаний и до 20 пилотов по 10–200 абонентов. Среда расходует ресурсы на пилотах, агент планирует финальные кампании по остаткам, а скоринг последовательно применяет общие ограничения.

PASS в отчёте означает только net_arpu_gain > 0. Это не подтверждение всех требований сдачи. Поле n_campaigns полного отчёта включает пилоты: например, 22 означает 12 пилотов и 10 финальных кампаний. risk_score_pct — доля уникальных контактированных абонентов с отрицательным ожидаемым эффектом в модели, а не вероятность будущего бизнес-убытка.

Если Agent.act падает, оценщик сохраняет уже выполненные пилоты в расчёте и возвращает безопасное описание типа ошибки. В режиме --runs аварийные seed выводятся отдельно; сводная статистика устойчивости рассчитывается только по запускам без исключений. Отрицательный результат без исключения остаётся обычным завершённым прогоном.

Настройки и секреты

Стартовый шаблон — config.example.json. Полные поля, значения по умолчанию и промпт определены dataclass-объектами в scripts/config.py. Личные значения хранятся в корневом config.json: отсутствующие файл или поля заменяются стандартными значениями, неизвестные поля и некорректные настройки отклоняются.

СекцияОсновные параметры
historyПуть data/change_tariff.csv, минимальный исходный ARPU 100, минимум 10 наблюдений, пороги сегментов и ограничения относительного эффекта.
strategyСегменты 300–5000, максимум 12 пилотов по 150, максимум 10 кампаний, вес истории 50, шум 0.8, штраф 0.5.
geminienabled=false, ключ api_key, имя model, таймаут, попытки, параметры генерации и ranking_prompt.
loggingУровень INFO и формат сообщений общего логгера.
botbackend=real, db_path=data/bot/runs.sqlite3.

Промпт gemini.ranking_prompt поддерживает {max_pilots}, {pilot_size}, {sms_cost}, {candidates_json}. Для LLM нужны enabled=true и собственный ключ; имя модели из шаблона не подтверждено живым API-вызовом в описанных ниже проверках. Логи внешних ошибок содержат тип ошибки без сырого ответа SDK. Настройка логирования повторно использует один обработчик и не перенастраивает корневой логгер.

По принятому в проекте соглашению только токен Telegram читается из TELEGRAM_BOT_TOKEN в окружении или корневом .env. Ключ Gemini и остальные настройки берутся из JSON. Оба личных файла исключены из Git; ключи не нужно добавлять в config.example.json.

Старый параметр bot.runner_module удалён: значение bot_eval ещё принимается для совместимости, любое другое вызывает ошибку с просьбой убрать поле. Бот всегда использует scripts.local_eval.run_demo для режима real.

Telegram: запуск, команды и история

После установки зависимостей создайте в корне .env с токеном своего бота от @BotFather:

TELEGRAM_BOT_TOKEN=ваш_токен

Запустите .\start_bot.cmd или откройте файл двойным щелчком. Бот проверит соединение через Telegram getMe и выведет ссылку на себя. Отправьте /start, затем /run. Терминал должен оставаться открытым; остановка — Ctrl+C.

КомандаДействие
/start, /helpСправка и текущий режим.
/runОдин расчёт с seed=42, метрики и финальные кампании. Аргументы команды не поддерживаются.
/lastПоследний запуск текущего чата, в том числе выполняющийся или ошибочный.
/historyПоследние пять запусков текущего чата, новые сверху.

backend=real запускает настоящую стратегию на локальной симуляции. backend=mock возвращает фиксированные демонстрационные цифры без агента. Ни один режим не отправляет маркетинговые сообщения абонентам оператора. Бот — дополнительный интерфейс к локальному расчёту; PDF не требует Telegram-интеграции.

На процесс разрешён один одновременный /run, общий для всех чатов. Расчёт вынесен в рабочий поток через asyncio.to_thread, поэтому история доступна во время выполнения. Используется polling; публичный сервер и webhook не нужны. Накопленные команды при старте отбрасываются. Для одного токена и одной базы запускайте один процесс.

SQLite хранит chat ID, время начала/завершения в UTC, seed, backend, состояние running/done/error, финальные кампании, число пилотов, ошибку и четыре метрики: net_arpu_gain, total_cost, total_contacts, status. Полный pilot_history, промежуточные оценки и объяснения решений не сохраняются. В группе история общая для группы, в личном чате — для этого чата.

Результат сохраняется до отправки отчёта в Telegram. Падение агента помечает запуск ошибкой, но доступные метрики пилотов сохраняются. Отрицательный результат или скоринговый FAIL сами по себе не означают техническую ошибку. При повторном запуске polling незавершённые записи получают состояние ошибки.

Переезд старой базы

При обновлении остановите прежний процесс. Если стандартная новая база data/bot/runs.sqlite3 ещё не существует, бот автоматически копирует историю из bot_data/runs.sqlite3 через SQLite backup, включая подтверждённые записи WAL. Исходная база остаётся резервной копией. Существующая новая база или база по другому явно заданному пути не заменяется.

Диагностика

# Проверить настройки и наличие токена без сети и вывода секретов
.\.venv\Scripts\python.exe -m scripts.bot --check-config

# Проверить токен через getMe без запуска polling
.\.venv\Scripts\python.exe -m scripts.bot --check-telegram

# Фиксированный пример отчёта, без токена и без агента
.\start_bot.cmd --demo

# Агент без Telegram, с отдельным файлом истории
.\start_bot.cmd --once --backend real --db data/bot/check.sqlite3

--check-config возвращает ненулевой код, если токен не задан; это не мешает запуску --once. Флаги --backend и --db переопределяют JSON на один запуск. Относительный путь базы считается от корня проекта. Запуски без Telegram записываются с chat_id=0 и не появляются в истории Telegram-чатов.

Токен окружения терминала имеет приоритет над .env. Чтобы использовать обновлённый .env, удалите старое значение из текущего PowerShell:

Remove-Item Env:TELEGRAM_BOT_TOKEN -ErrorAction SilentlyContinue

При InvalidToken / Unauthorized получите токен через BotFather: /mybots → ваш бот → API Token, обновите .env и повторите проверку. Раскрытый токен сначала отзовите через Revoke current token. Сетевая ошибка не подтверждает недействительность токена: проверьте доступ к Telegram. Ключ Gemini не связан с авторизацией Telegram.

Что проверено локально

Проверка от 23.09.2026, Windows, Python 3.14.7, зависимости из requirements.txt. Для измерений явно передавался Agent(settings=AgentConfig()): стандартная стратегия, Gemini выключен, без влияния личного JSON.

Показатель, seed=42Результат
Статус локального скорингаPASS
Чистый прирост после расходов3 329 109,82 у.е.
Расходы вместе с пилотами52 876 из 100 000 у.е.
Контакты вместе с пилотами13 219 из 15 000
Уникальные абоненты11 719
Пилоты12 по 150 контактов
Финальные кампании10
Время создания агента и локальной оценкиОколо 0,55 секунды, без запуска интерпретатора и внешних API

На seed 0–9 все десять результатов положительны: минимум 2 674 766,25, медиана 3 395 859,57, максимум 3 643 829,83 у.е. Два вызова build_submission(Agent(settings=AgentConfig())) с seed=42 дали одинаковое CSV-представление из 10 кампаний. Это проверка генератора; обязательный файл data/submission.csv на момент этой сверки ещё не подготовлен.

Для проверки с гарантированно стандартной конфигурацией, независимо от личного JSON:

from scripts.agent import Agent
from scripts.config import AgentConfig
from scripts.local_eval import evaluate_agent

result = evaluate_agent(Agent(settings=AgentConfig()), seed=42)

Команда python -m unittest discover -s tests -v из .venv прошла: 38 тестов. Проверяются команды и конкурентные запросы бота, изоляция истории чатов, перезапуск, миграция SQLite с WAL, ошибки агента, защита логов, конфигурация и совпадение результатов общего и прежнего оценщиков. Telegram и Gemini в тестах подменяются; живые подключения этими тестами не подтверждаются.

Локальный эффект нельзя переносить на бизнес оператора или скрытое судейство. Данные синтетические, а модель симулятора и исторический приоритет агента построены по одному источнику change_tariff.csv. Положительные прогоны показывают работоспособность и поведение в этой симуляции, но не независимую проверку качества стратегии.

Соответствие ТЗ

Сверка выполнена по исходному PDF кейса: контракт и данные — страницы 2–3, обязательные требования и ограничения — страницы 4–5, критерии оценки — страница 6. Дополнительный PARTICIPANT_GUIDE.pdf подтверждает обязательные требования на странице 3. Текстовый PARTICIPANT_GUIDE.md уже адаптирован к новой структуре проекта, поэтому совпадение с ним не доказывает совместимость с оригинальными командами из PDF.

Обязательные требования

Требование PDFСтатусЧто есть и чего не хватает
agent.py, класс Agent, метод act(env); запуск python local_eval.pyЛогика выполнена, упаковка частичноКласс работает в scripts/agent.py, оценка запускается через python -m scripts.local_eval. Корневых agent.py и local_eval.py нет; буквальные команды и импорт организатора требуют совместимых точек входа или согласования структуры.
От 1 до 10 корректных кампаний с допустимыми тарифами/каналамиВыполнено на проверенных данныхПри seed=42 получено 10 кампаний без отбрасывания и обрезки по лимитам. Для произвольной аудитории остаётся ветка с пустым результатом при отсутствии кандидатов или контактов.
Использовать env.run_pilot и его результатыВыполнено12 реальных вызовов API локальной среды; наблюдения меняют оценку и финальный выбор, кампании не зашиты готовым списком.
Соблюдать бюджет, контакты и число пилотовВыполнено в локальном сценарииСтратегия учитывает остатки и повторные контакты; среда/скоринг применяют лимиты. Контрольный прогон укладывается в них.
submission.csv, воспроизводимый через python make_submission.pyЧастичноГенерация реализована и детерминирована при выключенном LLM. Но команда теперь python -m scripts.local_eval --submission, путь data/submission.csv; корневого make_submission.py и подготовленного файла для сдачи на дату сверки нет.

Поэтому содержательное ядро задания уже сделано, а формальная сдача ещё требует подготовки. Переименование команд не устраняет требования PDF: нужно отдельно проверить запуск именно тем способом, которым будет пользоваться организатор, и передать все необходимые файлы пакета scripts/ и data/.

Ограничения и надёжность

Условие PDFСостояние реализации
Пилоты и финальные кампании считаются вместе, эффект по абоненту одинРеализовано в общем оценщике и scoring_core.py; повторные контакты оплачиваются.
Не извлекать скрытые эффекты из внутренностей средыАгент использует открытый профиль, справочники, лимиты, выданную историю и run_pilot; introspection скрытой модели не используется.
Не больше 10 минут, включая LLMЛокальный прогон без LLM существенно быстрее лимита. Запросы SDK ограничены, но общего принудительного дедлайна 10 минут нет; произвольные настройки/внедрённый клиент требуют отдельной проверки.
Запасная логика при ошибке LLM; учитывать пилоты при падении агентаОбе ветки реализованы. Живой запрос к Gemini в текущей проверке не выполнялся.
Секреты и ключ, предоставляемый на судействеСекреты исключены из Git. Однако PDF описывает OPENAI_API_KEY из окружения, а проектный Gemini использует config.json; автоматической совместимости с этим способом выдачи ключа нет. Сдача с выключенным LLM не требует ключа, поскольку LLM необязателен.
Эффекты скрытой среды отличаются от локальныхУчтено как ограничение оценки; фактического результата на скрытой среде нет.

Необязательные улучшения из PDF

УлучшениеСтатус
Учёт неопределённости пилотаЧастично: смешивание с историей и штраф 1/sqrt(n) есть; калибровка по данным и проверка покрытия интервалов отсутствуют.
Адаптивная разведкаНе реализована: очередь, канал и размер пилотов заданы до наблюдений; результаты влияют только на финальный план.
Осмысленный выбор канала по сегментуНе реализован как сравнение вариантов: обычный путь всегда SMS, push используется как запасной вариант, реклама/звонки не рассматриваются.
Устойчивость по нескольким seedПодтверждена локально на 10 seed: знак результата не меняется; это не гарантия для скрытых эффектов.
LLM в контуре решенийОграниченно реализован: заменяемый Gemini-адаптер ранжирует готовые гипотезы до пилотов. По умолчанию выключен; влияние на качество не измерено.

Отдельной ML-модели, обучения между запусками, поиска по признакам интернета/звонков и доказательства оптимальности распределения бюджета нет. Веб-интерфейс и реальная отправка кампаний также отсутствуют; PDF не требует их для базовой сдачи.

Как это соотносится с критериями жюри

В основном PDF на странице 6 приведены максимальные баллы. Это веса критериев, не выставленная проекту оценка.

КритерийМаксимумПодтверждённая основа и ограничения
Соответствие задаче и работоспособность25Полный локальный цикл от данных до кампаний работает; остаётся совместимость команд и артефактов сдачи.
Техническая реализация25Разделены стратегия, внешний клиент, среда и скоринг; есть тесты и обработка ошибок. Стратегия эвристическая, адаптивная разведка отсутствует.
README и воспроизводимость25Описаны установка, алгоритм, данные, команды и измеренные результаты. Живые API и запуск в инфраструктуре жюри ещё не проверены.
Ценность и применимость15Аналитик может получить план и историю расчётов через CLI/Telegram. Бизнес-эффект подтверждён только синтетической симуляцией.
Потенциал развития и оригинальность10Следующие направления конкретны: адаптация пилотов, сравнение каналов, расширение сегментации. Пока это направления развития, а не готовые функции.

Что довести до сдачи в первую очередь

  1. Обеспечить совместимость с точками входа из PDF: корневые тонкие лаунчеры/экспорт Agent либо явно согласованный способ запуска пакета. Воспроизвести проверку из чистой копии репозитория.
  2. С выключенным LLM сформировать финальный submission.csv, проверить его путь и состав комплекта для организаторов. Текущий генератор экспортирует только финальные кампании; пилоты воспроизводятся при запуске агента.
  3. Проверить стратегию на неблагоприятных и отличающихся от истории эффектах, включая пустой набор кандидатов; затем улучшать адаптивность пилотов и выбор каналов, сравнивая со стандартной стратегией.
Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
10
Авторы
3
Файлы
30
Ветки
2

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

Saga818 5NoyanTM 3shinbuu 2

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

  • 2026-09-23 17:28Integrate simplified main and consolidate QA under tests; validate submission entry pointsSaga818
  • 2026-09-23 17:07qa: verify simplified main with 68 offline tests and SQLite recoverySaga818
  • 2026-09-23 16:42docs: unify project guide and include reviewed reliability fixesNoyanTM
  • 2026-09-23 16:29feat: add single-run demo evaluation and bot integration checksSaga818
  • 2026-09-23 16:16refactor: reorganize data and simplify bot and agent modulesNoyanTM
  • 2026-09-23 15:36con2shinbuu
  • 2026-09-23 15:34con2shinbuu
  • 2026-09-23 15:29test: add agent resilience checks and document verified QA resultsSaga818
Все коммиты (10)
  • 2026-09-23 14:08Add tariff campaign agent with Gemini integration and configurationNoyanTM
  • 2026-09-23 14:05test: add strict mock QA and demo contract checksSaga818
Структура репозитория (30 файлов)
.gitignore
AGENTS.md
PARTICIPANT_GUIDE.md
PARTICIPANT_GUIDE.pdf
README.md
config.example.json
data/arpu_monthly.csv
data/change_tariff.csv
data/customer_profile.csv
data/dict_tariff.csv
data/feature_dictionary.csv
data/tariff_dictionary.csv
data/traffic.csv
hackalem_ai_beeline_tariff_marketing_campaigns_case.pdf
requirements.txt
scripts/__init__.py
scripts/agent.py
scripts/bot.py
scripts/config.py
scripts/environment.py
scripts/local_eval.py
scripts/scoring_core.py
start_bot.cmd
tests/__init__.py
tests/bot_eval.py
tests/test_bot.py
tests/test_db_migration.py
tests/test_integration.py
tests/test_local_eval.py
tests/test_startup.py
Ветки (2)

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

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

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