О проекте
Команда: Nexcel solution
Кейс HackAlem.ai: Энергетика — Самрук-Казына
Стадия: Рабочий MVP
Задача кейса: AI-агент, прогнозирующий выработку ветровой электростанции по погодным данным
Суть
Почасовой прогноз нормализованной мощности двух ветротурбин на 24–48 ч по архивным прогнозам ECMWF IFS: модель LightGBM и LLM-агент в консоли диспетчера, который объясняет результат.
Как устроено
SCADA с шагом 10 минут усредняется до часа. Погода берётся из Open-Meteo Previous Runs API (ecmwf_ifs025) с учётом 7-часовой задержки публикации, чтобы не было утечки будущего. Из неё строятся 16 физических признаков: скорость ветра на высотах, u/v, v³, сдвиг ветра, sin/cos времени, turbine_id. LightGBM (Huber, 700 деревьев) предсказывает мощность, результат обрезается до [0,1] и обнуляется при ветре >25 м/с, затем прогноз проверяется правилами (flat_forecast, icing_risk, расхождение с прошлым прогнозом). Агент на gpt-4o-mini через tool calling вызывает run_forecast_cycle, check_weather_update и get_weather_tool (до 4 раундов) и пересказывает цифры в FastAPI-консоли.
Ключевые решения
- LightGBM
- Huber loss
- TimeSeriesSplit
- walk-forward backtest
- Model Output Statistics
- leakage-safe as-of weather
- tool calling
- CatBoost (эксперимент)
Метрики
Holdout январь 2026: MAE 0.2468, RMSE 0.3004 (в долях номинала); тесты 23/24
Особенности
Утечка будущего исключена: используются только прогоны ECMWF, опубликованные до момента выпуска прогноза, а не реанализ ERA5. Модель учится на связке «прогноз погоды → факт SCADA» по идее MOS. Кнопка «Проверить обновление погоды» сравнивает хэш входов и пересчитывает прогноз.
Стек
Python · pandas · NumPy · LightGBM · FastAPI · Open-Meteo API · HTML · pytest
Модели
gpt-4o-mini
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-d830a856-nexcel-solution.
README
README.mdGridAgent-Wind — почасовой прогноз выработки ВЭС на 24–48 часов
Проект для хакатона HackAlem AI, трек «Энергетика» (кейс Самрук-Казына).
Система сама берёт архивный прогноз погоды ECMWF IFS по координатам двух ветротурбин. Затем она прогоняет его через локальную ML-модель и выдаёт почасовой прогноз нормализованной мощности на 24 или 48 часов вперёд. LLM-агент в чате только вызывает эти расчёты и объясняет результат, сам он цифры не считает.
1. Проблема
Оператор ВЭС заранее подаёт почасовой график выработки. Если факт расходится с графиком, станция платит за небаланс. Ветер плохо предсказуем, поэтому нужен прогноз на сутки–двое, построенный только на тех данных, которые реально были доступны в момент прогноза.
Объект. ВЭС из двух ветроустановок, Алматинская область (Шелекский ветровой коридор).
| Турбина | Широта | Долгота |
|---|---|---|
| Т1 | 43.643194 | 78.538833 |
| Т2 | 43.645139 | 78.535611 |
Т2 стоит примерно в 340 м от Т1, по азимуту ~310° (на северо-запад). При ветре северо-западного или юго-восточного направления одна турбина может оказаться в аэродинамическом следе другой. Отдельно этот эффект не моделируется: в модель подаются направление ветра и номер турбины, так что она может выучить разницу неявно. Это не проверялось.
2. Демо
TODO: добавить скриншот или GIF HTML-консоли диспетчера (чат, график и журнал шагов агента).
3. SMART-цель
- S — прогнозировать нормализованную активную мощность (0…1 от номинала) двух турбин по часам на 24–48 ч.
- M — на отложенной выборке (январь 2026) модель показала MAE = 0.2468 и RMSE = 0.3004 (в долях номинала).
- A — обучение на SCADA за 11.03.2023–31.01.2026 и архивных прогнозах ECMWF IFS, модель LightGBM.
- R — прогноз нужен оператору ВЭС, чтобы подавать график выработки на следующие сутки.
- T — walk-forward прогноз на 1–28 февраля 2026 строится командой
backtest_runner.py. Метрика за февраль будет посчитана после получения факта SCADA за этот период (TODO).
4. Архитектура: два контура
Модель и физика — детерминированный Python-код. LLM только вызывает этот код и пересказывает результат.
SCADA (10 мин, XLSX/CSV) Open-Meteo ECMWF IFS (1 ч)
│ │
▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ ДЕТЕРМИНИРОВАННЫЙ КОНТУР (pandas / NumPy / LightGBM) │
│ • SCADA → почасовые средние │
│ • погода «на момент выпуска» — без заглядывания в будущее │
│ • физические признаки (сдвиг ветра, u/v, v³, время суток) │
│ • LightGBM: прогноз погоды → фактическая мощность │
│ • ограничения: мощность в [0, 1], 0 при ветре > 25 м/с │
│ • проверки прогноза по правилам │
└───────────────────────────┬──────────────────────────────────┘
│ ForecastResult: цифры, шаги, предупреждения
▼
┌──────────────────────────────────────────────────────────────┐
│ АГЕНТНЫЙ КОНТУР (OpenAI gpt-4o-mini, tool calling) │
│ run_forecast_cycle · check_weather_update · get_weather_tool│
│ не более 4 раундов вызова инструментов │
└───────────────────────────┬──────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ FastAPI + HTML-консоль: чат · график по турбинам · метрики │
│ журнал шагов агента · «Проверить обновление погоды» │
└──────────────────────────────────────────────────────────────┘
Один цикл прогноза (src/agents/pipeline.py)
Прогноз, выпущенный в день D в 12:00 UTC, покрывает часы с D+1 00:00 UTC на 24 или 48 ч.
- context — загрузка модели
models/wind_power_lgb.pklи её метрик. - weather — прогноз погоды для каждой турбины, но только из прогонов ECMWF,
опубликованных до момента выпуска. Учитывается задержка публикации 7 ч. Источник —
Open-Meteo Previous Runs API, модель
ecmwf_ifs025. Нет сети — берётся локальный кэш. Если хоть одного часа нет, цикл останавливается с ошибкой и не подставляет выдуманные значения. - features — 16 признаков (раздел 5).
- model — предсказание, ограничения, сохранение в
outputs/forecasts/forecast_<дата>_t12_<h>h.csv. - validation — проверки по правилам (раздел 6) и сравнение с прогнозом предыдущего дня.
Кнопка «Проверить обновление погоды» заново скачивает погоду и сравнивает хэш входных данных. Если погода изменилась, прогноз пересчитывается и показывается разница с прошлым расчётом.
5. Данные, погода и признаки
Защита от утечки будущего
- В прогнозе и бэктесте используются только архивные прогнозы ECMWF IFS, опубликованные до выпуска прогноза. Реанализ ERA5 не используется: он построен на фактических наблюдениях задним числом и в реальном времени недоступен.
- Модель обучается сопоставлять «прогноз погоды → фактическая мощность SCADA», а не «анемометр → мощность». Так она сразу учитывает систематическую ошибку глобальной модели для этой местности (идея Model Output Statistics).
Шаг по времени
SCADA приходит с шагом 10 минут. Данные усредняются до 1 часа
(resample("1h").mean() в src/train/build_train_set.py), потому что у ECMWF шаг 1 час.
Признаки (src/tools/feature_tool.py)
| Группа | Признаки |
|---|---|
| Ветер по высотам | wind_speed_10m, wind_speed_80m, wind_speed_100m |
| Направление | wind_direction_10m, wind_direction_100m, sin/cos направления на 100 м |
| Сдвиг ветра | delta_v = v100 − v10 |
| Перенос масс | wind_u = −v·sin(θ), wind_v = −v·cos(θ) |
| Энергия потока | v3 = v100³ |
| Температура | temperature_2m (связана с плотностью воздуха) |
| Календарь | hour_sin, hour_cos, day_of_year |
| Турбина | turbine_id (категориальный) |
Previous Runs API не отдаёт ветер на 80 м. В прогнозе он восстанавливается из 10 м и 100 м по логарифмическому профилю.
Модель (src/train/train_model.py)
- LightGBM, функция потерь Huber, 700 деревьев, learning rate 0.03, фиксированный seed.
- Обучение: 11.03.2023–31.12.2025, кросс-валидация по времени (
TimeSeriesSplit). - Отложенная выборка: январь 2026 → MAE 0.2468, RMSE 0.3004.
- Сравнения с базовой моделью (например, «сырой» прогноз ветра через кривую мощности) пока нет — TODO.
6. Проверки прогноза и ограничения
Все проверки считаются в Python. LLM их только пересказывает.
| Проверка | Правило |
|---|---|
| Ограничение мощности | прогноз обрезается до [0, 1] |
shutdown_wind | ветер на 100 м > 25 м/с → мощность = 0 (отключение турбины) |
low_wind_high_power | ветер < 3 м/с при мощности > 0.2 — подозрительно |
flat_forecast | стандартное отклонение прогноза турбины < 0.01 |
icing_risk | температура ≤ 0 °C (влажность пока не запрашивается, поэтому это только информация) |
divergence_from_previous_issue | средняя разница с прогнозом прошлого дня > 0.15 |
Пороги подобраны вручную: паспорта турбин у команды нет.
7. Быстрый запуск (Windows PowerShell, Python 3.11+)
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
python -m src.train.train_model # обучение → models/wind_power_lgb.pkl
python -m src.app # UI на http://127.0.0.1:7860
- Первое обучение скачивает погоду в
data/weather_cache/. Дальше можно обучать без сети:python -m src.train.train_model --offline. - Прогноз из UI в первый раз тоже требует интернета (Open-Meteo Previous Runs API).
После этого погода кэшируется в
data/weather_cache/*_previous_runs.parquet. - Walk-forward бэктест на февраль 2026:
python backtest_runner.py→submissions/forecast_february_2026.csvиbacktest_report.json. - LLM-чат: скопируйте
.env.exampleв.env, укажитеOPENAI_API_KEY, поставьтеMOCK_MODE=false. Без ключа или приMOCK_MODE=trueчат отвечает заглушкой, а прогноз по кнопке работает как обычно.
8. Тесты
pytest -v
tests/test_agent_tools.py— инструменты агента, ограничения мощности, порядок «погода → модель».tests/test_orchestrator_mock.py— чат в демо-режиме без LLM.tests/test_agent_cycle.py— полный цикл прогноза: проверки, обновление погоды, неполная погода.
Сейчас проходят 23 из 24 тестов. test_incomplete_weather_fails_loud ждёт другой текст ошибки (TODO).
9. Почему просто
Мы сознательно не даём LLM считать мощность или погоду: языковая модель может выдумать цифру, а оператору нужны воспроизводимые числа. Поэтому весь расчёт — это обычный Python-конвейер из пяти шагов, который работает и без LLM, и его можно проверить тестами. Агент только выбирает один из трёх инструментов и объясняет результат. Модель — один LightGBM на обе турбины, без ансамблей и нейросетей: её легко переобучить за минуты и объяснить судье.
10. Известные ограничения
- Обучение использует Open-Meteo Historical Forecast API (
ecmwf_ifs), а прогноз — Previous Runs API (ecmwf_ifs025). Источники близкие, но не одинаковые, поэтому ошибка на реальных данных может отличаться от holdout. - Без интернета и без кэша
*_previous_runs.parquetпрогноз останавливается на шаге погоды. - Экономический эффект (сколько экономится на штрафах за небаланс) не считался.
11. Дальше (не реализовано)
- Расчёт стоимости небаланса по тарифам балансирующего рынка РК и рекомендации диспетчеру.
- Явный учёт аэродинамического следа между Т1 и Т2.
- Сценарии для демонстрации (резкий спад ветра, сильный сдвиг ветра).
- Метрика walk-forward за февраль 2026 по факту SCADA.
12. Команда
- Alisher — оркестратор и инструменты агента (
src/agents/). - TODO — обучение модели и обработка данных (
src/train/). - TODO — архивная погода и проверка координат (
src/tools/weather_tool.py).
Авторы коммитов
История коммитов
- 2026-09-23 18:03Merge remote-tracking branch 'origin/main'muhambetovbektas7
- 2026-09-23 18:01Merge remote-tracking branch 'origin/main'muhambetovbektas7
- 2026-09-23 17:58Update README.mdBektasMukhambetov
- 2026-09-23 17:58Add leakage-safe forecast backtestmuhambetovbektas7
- 2026-09-23 17:55Merge remote-tracking branch 'origin/main' into feat/html-uiTarlan
- 2026-09-23 17:55Add files via uploadBektasMukhambetov
- 2026-09-23 17:51Редизайн: HTML-консоль диспетчера на FastAPI вместо GradioTarlan
- 2026-09-23 17:47Delete src/core/power_curve.pyBektasMukhambetov
Все коммиты (20)
- 2026-09-23 17:47Delete src/core/forecast_model.pyBektasMukhambetov
- 2026-09-23 17:46Delete .claude directoryBektasMukhambetov
- 2026-09-23 17:32Merge pull request #1 from BAITC-Hacks/feat/agent-integrationTarlan Baimyrza
- 2026-09-23 17:24Готовый проект без дизайна: связка агента, модели и UITarlan
- 2026-09-23 16:45Merge main and complete forecast integrationmuhambetovbektas7
- 2026-09-23 16:35Integrate deterministic forecast toolsmuhambetovbektas7
- 2026-09-23 16:11Add README/AGENTS.md, fix error handling+logging in orchestrator, add sanity testsTarlan
- 2026-09-23 16:04Merge main and add deterministic wind training pipelinemuhambetovbektas7
- 2026-09-23 15:23Add ML training core and data preprocessingmuhambetovbektas7
- 2026-09-23 15:16Add AI chat dashboard and project skillsTarlan
- 2026-09-23 14:20Remove unused scaffold filesTarlan
- 2026-09-23 14:15Create project skeletonTarlan
Структура репозитория (57 файлов)
.env.example .gitignore AGENTS.md README.md README_1.md backtest_runner.py catboost_info/catboost_training.json catboost_info/learn/events.out.tfevents catboost_info/learn_error.tsv catboost_info/time_left.tsv data/Dataset HackAlemAI для участников 11.03.2023-28.02.2026 - turbine 2.csv data/Dataset_HackAlemAI_для_участников_11_03_2023_28_02_2026_turbine.xlsx data/turbine_22.csv data/turbines.json docs/skills/skill-agentic-loop.md docs/skills/skill-code-quality.md docs/skills/skill-readme.md models/.gitkeep models/forecast_model.pkl models/metrics.json outputs/forecasts/.gitkeep pytest.ini requirements.txt run.sh setup.sh src/agents/__init__.py src/agents/orchestrator.py src/agents/pipeline.py src/agents/tools.py src/app.py src/backtest.py src/core/__init__.py src/core/features.py src/core/train.py src/tools/__init__.py src/tools/feature_tool.py src/tools/inference_tool.py src/tools/weather_tool.py src/train/__init__.py src/train/build_train_set.py src/train/train_model.py src/weather/__init__.py src/weather/archive_client.py src/weather/mock_weather.py src/web/index.html tests/test_agent_cycle.py tests/test_agent_tools.py tests/test_api.py tests/test_as_of_backtest.py tests/test_forecast_model.py tests/test_orchestrator_mock.py tests/test_power_curve.py tests/test_weather_client.py training/eda.ipynb training/evaluate.py training/train.py training/training_report.md
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.