О проекте
Команда: Gjigi
Кейс HackAlem.ai: Энергетика — Самрук-Казына
Стадия: Доведённый продукт
Задача кейса: AI-агент, прогнозирующий выработку ветровой электростанции по погодным данным
Суть
AI-агент ежедневно выпускает почасовой вероятностный прогноз P10/P50/P90 выработки ВЭС из двух турбин на 48 часов по архивным прогнозам погоды, опубликованным к моменту выпуска. Сделана ретро-симуляция февраля 2026 и Streamlit-дашборд.
Как устроено
SCADA двух турбин (10 мин) служит только целевой переменной. Признаки строятся из архивных NWP-прогонов Open-Meteo (ECMWF IFS 9 км, GFS, ICON, ECMWF 0.25°) с правилом as_of против утечки: ансамбль, соседние часы, плотность воздуха, сдвиг ветра. Модель: квантильный LightGBM, изотоническая кривая мощности и конформная калибровка интервала. Агент на OpenAI function calling (gpt-4.1, 10 инструментов с обязательным полем reason) сам скачивает погоду, отбраковывает источники-выбросы, пересчитывает прогноз при выходе нового прогона, валидирует результат, рассчитывает заявку на балансирующий рынок и пишет сводку диспетчеру. Есть fallback RuleAgent.
Ключевые решения
- LightGBM quantile regression
- изотоническая кривая мощности
- конформная калибровка
- NWP-ансамбль ECMWF/GFS/ICON
- защита от утечки as_of
- function calling
- rule-based fallback agent
- оптимальный квантиль для заявки на БРЭ
- стресс-тест агента
Метрики
MAE 0.150 (D+1) / 0.176 (D+2) доли номинала на январе 2026; −9% MAE к NWP+кривая мощности; агент −7% MAE D+1 (0.162→0.150); при испорченном источнике 0.160 против 0.307; покрытие P10–P90 81%/77%; −58% стоимости небалансов к климатологии
Особенности
Среди лучших по инженерной честности. make verify побитово воспроизводит все 28 выпусков февраля. Отсутствие утечки проверяется тестом (удаление будущих прогонов не меняет прогноз). Вклад агента измерен отдельно. Добавлена экономика небалансов и ограничение в коде: агент не может исключить исправный ECMWF, потому что в стресс-тесте LLM пыталась это сделать.
Стек
Python · pandas · NumPy · PyArrow · LightGBM · scikit-learn · Streamlit · Plotly · Matplotlib · pytest · Open-Meteo API · Make
Модели
gpt-4.1
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-ee3eb0d0-gjigi.
README
README.mdHackAlem AI — Agentic AI для прогноза выработки ВЭС на 24–48 ч
Что решает продукт
Выработка ветровой электростанции зависит от ветра, а ВЭС должна заранее сообщать, сколько энергии выдаст в каждый час следующих суток: по этому плану работают энергосистема и балансирующий рынок. Ошибка стоит денег: за недовыработку против плана станция платит штраф, перевыработка продаётся дешевле. Продукт — AI-агент, который каждый день сам делает почасовой прогноз выработки ВЭС на следующие 24–48 часов по открытым прогнозам погоды и сам следит за качеством этого прогноза.
Задача хакатона: воспроизвести работу так, как если бы прогноз выпускался в прошлом, — 31.01.2026, затем 01.02 и так далее до конца февраля, используя только те прогнозы погоды, которые были опубликованы на каждый момент.
Как это работает — подробнее: Агент каждый день в 10:00 (время станции) выпускает почасовой вероятностный прогноз (P10/P50/P90) выработки ВЭС из двух турбин на следующие двое суток. Для прогноза он использует только архивные погодные прогнозы, опубликованные к моменту выпуска. Агент сам проверяет входные данные, запускает ML-модель, проверяет результат, отбрасывает неисправные источники, пересчитывает прогноз при выходе нового прогона погодной модели и пишет сводку для диспетчера. Каждое решение с объяснением попадает в журнал.
| Итог | Значение |
|---|---|
| Результат (файл сдачи) | outputs/forecast_feb2026.csv: 28 выпусков (31.01–27.02.2026) × 48 ч, почасовой прогноз выработки ВЭС |
| Точность, репетиция на январе 2026 (модели не видели дек–янв) | MAE 0.150 на D+1, 0.176 на D+2 (доля номинала), LLM-агент |
| Выигрыш к классическому «NWP + кривая мощности» | −9% MAE (holdout дек–янв), +6% в среднем за 12 месяцев CV |
| Вклад агента (пересчёт на свежем прогоне, отбраковка источников) | −7% MAE на D+1 (0.162 → 0.150); при испорченном источнике погоды ошибка конвейера ×2 (0.307), агента — 0.160 |
| Экономический эффект (план подачи на БРЭ) | −58% стоимости небалансов к климатологии, −28% за счёт оптимального квантиля вместо медианы |
| Интервал P10–P90 | покрытие 81% / 77% на D+1 / D+2 (цель 80%) |
| Защита от утечки | правило публикации + тесты + аудит каждого выпуска |

Быстрый старт для жюри (3 команды, ~2 мин, интернет не обязателен)
python3 -m pip install -r requirements.txt && python3 -m pip install -e .
make verify # агент заново делает все 28 выпусков февраля и сверяет с эталоном → REPRODUCED
make demo # дашборд http://localhost:8501: прогноз, журнал решений агента, репетиция против факта, живой запуск
Итоговый прогноз — outputs/forecast_feb2026.csv. LLM-режим агента включается ключом
в .env (раздел 8). Без ключа тот же агент работает на правилах: это детерминированный режим, его и проверяет make verify.
Нужен Python ≥ 3.11 и make (есть на macOS/Linux; без make — команды из Makefile, например python3 scripts/verify.py).
Технологии
| Что | Чем |
|---|---|
| Язык | Python 3.11+ |
| Погодные данные | Open-Meteo: архив точных прогонов ECMWF IFS 9 км (Single Runs API) и прогнозы GFS, ICON, ECMWF 0.25° (Previous Runs API) |
| Данные и признаки | pandas, NumPy, PyArrow (parquet) |
| ML-модель | LightGBM (квантильная регрессия P10/P50/P90), scikit-learn (изотоническая кривая мощности, baselines), конформная калибровка интервала |
| AI-агент | OpenAI API (gpt-4.1), function calling (10 инструментов + обязательное объяснение каждого шага); запасной режим — агент на правилах |
| Демо | Streamlit + Plotly (дашборд), Matplotlib (графики отчётов) |
| Проверка | pytest (18 тестов), make verify (побитовое воспроизведение февраля) |
Как проверить решение (≈5 минут)
- Установить и воспроизвести результат:
make verify— агент заново делает все 28 выпусков (сам получает погоду → прогноз → проверки → пересчёт) и сверяет с эталономdocs/reference/→REPRODUCED. - Прогнать тесты:
make test— 18 тестов: очистка данных, отсутствие утечки (удаление всех будущих прогонов погоды не меняет прогноз), признаки без данных SCADA, модель, полный цикл агента, работа без интернета и при пропаже любого источника. - Посмотреть, какие прогнозы погоды использовал каждый выпуск:
docs/forecast/issue_log.csv— все прогоны опубликованы до момента выпуска. - Посмотреть решения агента:
docs/agent/AGENT_REPORT.mdи журналыdocs/agent/logs/<дата>.json— каждый шаг с объяснением. - Проверить качество на реальном факте: в данных нет факта за февраль, поэтому та же процедура прогнана на январе 2026
с моделями, не видевшими декабрь–январь:
make rehearsal→docs/agent_rehearsal/AGENT_REPORT.md. - Увидеть всё вживую:
make demo→ http://localhost:8501;make stress— агент против испорченных данных;make live— реальный прогноз на ближайшие двое суток (нужен интернет).
1. Архитектура
flowchart LR
subgraph Data["Данные"]
S[SCADA 2 турбины<br/>10 мин, 2023-03 … 2026-01]
W[Open-Meteo архив прогнозов<br/>ECMWF 9 км точные прогоны<br/>GFS · ICON · ECMWF 0.25°]
end
W -->|fetch_weather:<br/>агент скачивает сам| ST[(Прогнозы в сессии<br/>init_time · valid_time · available_at)]
ST -->|get_forecast as_of<br/>только опубликованное| F[Признаки<br/>ансамбль, соседние часы,<br/>плотность, сдвиг ветра, календарь]
S -->|только как целевая переменная| M
F --> M[LightGBM квантили<br/>P10/P50/P90 + mean<br/>+ кривая мощности<br/>+ конформная калибровка]
M --> A
subgraph A["Агент: OpenAI function calling / правила"]
direction TB
a0[fetch_weather] --> a1[check_weather_inputs] --> a2[run_forecast] --> a3[validate_forecast]
a3 --> a4[compare_with_previous] --> a5[check_for_new_runs]
a5 -->|новый прогон опубликован| a6[rerun_with_update]
a3 -->|проблема: выброс источника| a2
a6 --> ab[recommend_bid]
a5 --> ab
ab --> a7[finalize + сводка диспетчеру]
end
A --> O[Прогноз 48 ч · журнал решений · отчёт]
Полный цикл из ТЗ: получить внешние погодные данные → подготовить данные → запустить модель → сформировать почасовой прогноз
→ проанализировать результат → пересчитать при обновлении входных данных. Каждый шаг — отдельный инструмент агента
(src/hackalem/agent/tools.py).
2. Агент
- Мозг — LLM (OpenAI, function calling) (
agent.py). Модель получает не сырые массивы, а факты от инструментов: покрытие и свежесть каждого источника, расхождение моделей, итоги валидации, правку к вчерашнему прогнозу, появление новых прогонов. Сама решает, что делать дальше. Каждый вызов обязан содержать полеreason— объяснение решения. - Запасной мозг — правила (
RuleAgent) с теми же инструментами. Включается автоматически, если нетOPENAI_API_KEYили LLM недоступна, поэтому проект всегда воспроизводим. - Сам получает погоду (
fetch_weather): сессия выпуска начинается пустой. Агент по координатам ВЭС запрашивает у Open-Meteo прогоны ECMWF, GFS и ICON, опубликованные к моменту выпуска (ответы кэшируются). Если сети нет, берёт те же данные из архива в репозитории и пишет об этом в журнал. Тест проверяет, что оба пути дают одинаковые входные данные. - Какие решения принимает агент:
- исключает NWP-источник, который сильно отклоняется от медианы ансамбля или имеет пропуски (ECMWF 9 км не трогает, только если он сломан);
- при сильном расхождении моделей расширяет интервал (ограниченно: он уже откалиброван);
- пересчитывает прогноз, когда публикуется более свежий прогон (обычно ECMWF 00z в 14:00 по времени станции, тот же день D, до начала целевых суток) и сообщает, насколько изменился прогноз;
- проверяет итог: пропуски, границы [0, 1], порядок квантилей, нереальные скачки, согласованность станции и турбин;
- рассчитывает план подачи на балансирующий рынок (
recommend_bid, раздел 6); - пишет сводку для диспетчера на русском.
- Живой режим (
make live): тот же агент выпускает реальный прогноз на ближайшие двое суток по самым свежим прогонам. - Страховка: агент не может завершиться без прогноза. Если LLM не вызвала
finalize, срабатывает резервный путь. - Ограничения в коде, а не только в инструкции LLM: исправный ECMWF 9 км (главный и самый точный источник) исключить нельзя, инструмент откажет и объяснит почему. Это ограничение появилось после стресс-теста: LLM в запутанных ситуациях иногда пыталась его исключить.
- Журнал решений:
docs/agent/logs/<дата>.json(каждый шаг: инструмент, аргументы, объяснение, результат), общий отчёт —docs/agent/AGENT_REPORT.md.
Пример (выпуск 11.02.2026, LLM gpt-4.1, полный журнал) — каждое решение опирается на цифры:
1 fetch_weather | Начинаю сессии: требуется загрузить прогнозы погоды, опубликованные к 10:00 2026-02-11, чтобы оценить покрытие и свежесть NWP источников.
2 check_weather_inputs | Загружены прогнозы ECMWF (2 прогона: 18z и 12z), GFS, ICON и ECMWF 0.25°, всего 591 строк; необходимо проверить полноту и согласованность источников.
3 run_forecast [-gfs] | GFS отклоняется от медианы на 3.31 м/с (порог 3.0), остальные источники в пределах нормы; исключаю GFS из прогноза.
4 validate_forecast | Прогноз рассчитан без GFS, средняя ширина интервала D+1: 0.448, D+2: 0.328; проверяю корректность и аномалии.
5 compare_with_previous | Доступен прогноз предыдущего дня; необходимо сравнить текущий и предыдущий прогнозы для выявления крупных изменений.
6 check_for_new_runs | Среднее абсолютное изменение прогноза по сравнению с предыдущим днем 0.092, крупных пересмотров нет; проверяю наличие более свежих прогонов NWP.
7 rerun_with_update | Свежие прогоны ECMWF 00z и других моделей изменяют средний ветер на 0.59 м/с (порог 0.3); пересчитываю прогноз с обновленными данными.
8 recommend_bid | Прогноз обновлен с учетом свежих прогонов (ECMWF 00z и др.), среднее изменение ветра 0.59 м/с, средний p50 D+1: 0.19, D+2: 0.055; рассчитываю оптимальную заявку.
9 finalize | Прогноз и заявка обновлены с учетом свежих прогонов и исключения GFS; все проверки пройдены, крупных аномалий нет.
3. Протокол прогноза и защита от утечки
- Выпуск в день D в 10:00 по времени станции (= время SCADA = UTC+6, установлено по данным, см. ниже), прогноз на локальные сутки D+1 и D+2 (48 часов; заблаговременность 14–61 ч). Первый выпуск — 31.01.2026, последний — 27.02.2026. Агент может выпустить обновление в тот же день D после публикации нового прогона.
- Погода — только архивные прогнозы, а не реанализ (ERA5) и не факт:
- ECMWF IFS 9 км — точные прогоны как были выпущены (Open-Meteo Single Runs API, 00/06/12/18z, с 03.2024);
- GFS, ICON, ECMWF 0.25° — Open-Meteo Previous Runs API (значение, спрогнозированное за 1–3 суток).
Мы проверили сравнением с точными прогонами:
previous_dayNдля часа t — это прогонfloor_6h(t) − N суток.
- Правило доступности: строка прогноза используется в момент
as_of, только еслиinit_time + задержка_публикации ≤ as_of(консервативно: ECMWF 8 ч, GFS и ICON 6 ч) —weather/store.py. - Проверки:
- тесты: результат не меняется, если удалить все будущие прогоны; в признаках нет данных SCADA;
- аудит каждого тестового выпуска с использованными прогонами —
docs/forecast/issue_log.csv.
- Обучение на тех же условиях, что и тест: процедура выпуска воспроизведена на истории (723 выпуска с 03.2024), модель учится на прогнозах погоды в том виде, в каком они были доступны в момент выпуска.
- SCADA не используется как вход (например, «вчерашняя мощность»): данные организаторов заканчиваются 31.01, для февральских выпусков такого признака не было бы.
4. Данные и находки
Подробно — docs/eda/EDA.md, docs/weather/WEATHER.md.
- Две турбины в ~350 м друг от друга ведут себя почти одинаково (корреляция 0.997). Цель — средняя станции, плюс отдельные модели T1 и T2.
- Очистка: простои (мощность ≈0 при ветре ≥5 м/с) и ограничения мощности (видны «полки» 0.41/0.61/0.72) исключены из обучения, исходная мощность сохранена для оценки.
- Время SCADA — фиксированное UTC+6: переход Казахстана на UTC+5 в 03.2024 в данных не отражён. Установлено кросс-корреляцией с прогнозами ECMWF. Ошибка в час напрямую испортила бы прогноз.
- Датчик температуры — в гондоле (на 3–5 °C теплее воздуха), поэтому плотность воздуха считается по NWP.
- Ансамбль 4 моделей лучше любой одной: MAE ветра на D+1 1.89 против 2.18 м/с у ECMWF 9 км. ECMWF занижает ветер на площадке на ~1 м/с, зимой сильнее.
5. Модель
src/hackalem/models/gbm.py, отчёт — docs/model/MODEL.md.
- LightGBM, квантильная регрессия P10/P50/P90 и отдельная модель среднего. P50 оптимален для MAE, среднее — для RMSE.
- 73 признака: по каждой NWP-модели (ветер 10/100 м, порывы, направление, температура, плотность воздуха, сдвиг ветра, заблаговременность), статистики ансамбля (среднее, разброс, min/max), соседние часы и значение в середине часа (SCADA — среднее за час, NWP — мгновенное значение), календарь и горизонт, физический признак — кривая мощности турбины по прогнозу ветра.
- Конформная калибровка интервала (CQR) на данных вне выборки.
- Число итераций — по early stopping. Финальные модели обучены на всей истории до 31.01.2026 (
models/).
6. Результаты
Мощность нормализована к номиналу, поэтому MAE = nMAE.
Базовые прогнозы и модель, holdout декабрь 2025 – январь 2026 (обучение только до 01.12.2025):
| Метод | MAE | RMSE |
|---|---|---|
| Persistence («завтра как сегодня») | 0.377 | 0.492 |
| Климатология (месяц × час) | 0.321 | 0.361 |
| ECMWF + кривая мощности | 0.200 | 0.290 |
| Ансамбль 4 NWP + кривая мощности + калибровка (лучший baseline) | 0.192 | 0.268 |
| LightGBM квантили | 0.175 | 0.251 |
Rolling CV по 12 месяцам (02.2025–01.2026) и абляции:
| Вариант | MAE | RMSE | Покрытие 80% |
|---|---|---|---|
| Полная модель | 0.163 | 0.231 | 79% |
| Без физического признака | 0.162 | 0.231 | 80% |
| Только ECMWF (без ансамбля) | 0.172 | 0.243 | 79% |
| Лучший baseline | 0.173 | — | — |
→ Главный источник выигрыша — мультимодельный ансамбль NWP.
Сквозная репетиция на январе 2026 (весь конвейер по выпускам, модели обучены до 01.12.2025, сравнение с фактом SCADA):
| MAE D+1 | MAE D+2 | RMSE D+1 | Покрытие 80% D+1 / D+2 | |
|---|---|---|---|---|
| Конвейер без агента (выпуск в 10:00) | 0.162 | 0.178 | 0.238 | 73% / 75% |
| Агент на правилах (отчёт) | 0.149 | 0.180 | 0.219 | 81% / 77% |
| Агент на LLM, OpenAI gpt-4.1 (отчёт) | 0.150 | 0.177 | 0.221 | 81% / 77% |
LLM принимает решения не строго детерминированно (в прогонах с gpt-4o-mini MAE D+1 была 0.144–0.150). В таблице — последний прогон с gpt-4.1, его журналы лежат в репозитории. Агент пересчитывает не всегда: в 23 из 31 январского выпуска свежий прогон заметно менял ветер (≥ 0.3 м/с), в остальных агент оставил выпуск 10:00 и объяснил почему.
Основной выигрыш агента — пересчёт после публикации свежего прогона (00z, в 14:00 того же дня D) и отбраковка источника-выброса (чаще всего GFS). Порог расширения интервала для агента на правилах подобран по этой же репетиции, поэтому его покрытие немного оптимистично. Остальные решения от январских данных не настраивались.

Стресс-тест агента (docs/agent_stress/STRESS.md, make stress): намеренно портим
входные данные на 10 январских выпусках и сравниваем с фактом SCADA.
| Сценарий | Конвейер без агента | Агент (правила) | Агент (LLM) | Агент исключил источник |
|---|---|---|---|---|
| Данные исправны | 0.162 | 0.160 | 0.160 | 0 / 10 |
| GFS выдаёт мусор (×2.5 + 4 м/с) | 0.307 | 0.161 | 0.160 | 10 / 10 — GFS |
| ICON недоступен | 0.173 | 0.169 | 0.171 | 0–1 / 10 (пропуск модель переносит сама) |
| ECMWF 9 км (главный источник) недоступен | 0.211 | 0.205 | 0.205 | 0 / 10 (прогноз продолжается на 3 моделях) |
→ Обычный конвейер «проглатывает» сломанный источник, и ошибка удваивается. Агент замечает выброс и исключает его.
При пропаже любого источника, даже главного, система продолжает выдавать прогноз (тесты test_forecast_survives_any_missing_source).
Экономика балансирующего рынка (docs/economics/ECONOMICS.md, make economics).
Недовыработка против плана стоит дороже перевыработки (иллюстративно 40 и 18 тг/кВт·ч, параметры в config.yaml).
Поэтому план, минимизирующий ожидаемые потери, — квантиль прогноза q* = c₊/(c₊+c₋) ≈ 0.31, а не медиана
(инструмент агента recommend_bid). Стоимость небалансов на январе, на 1 МВт установленной мощности:
| План на сутки вперёд | тыс. тг / МВт·мес | Экономия к климатологии |
|---|---|---|
| Климатология (без прогноза погоды) | 6 240 | — |
| Модель без агента, медиана | 4 007 | 36% |
| Агент, медиана | 3 691 | 41% |
| Агент, оптимальный квантиль P31 | 2 640 | 58% |
7. Результат

В папке outputs/ лежит только то, что требует задание, — outputs/forecast_feb2026.csv:
почасовой прогноз выработки ВЭС на 24–48 ч для каждого ежедневного выпуска с 31.01 по 27.02.2026 (28 × 48 = 1344 строки).
| Колонка | Смысл |
|---|---|
issue_date | день выпуска D (31.01 — первый, 27.02 — последний) |
issue_time | момент выпуска по времени станции: 10:00, или время пересчёта агентом после публикации свежего прогона (тот же день D) |
datetime | прогнозируемый час (время станции, начало часа), сутки D+1 и D+2 |
horizon_h | часов от выпуска до прогнозируемого часа |
power_forecast | прогноз нормализованной активной мощности станции (среднее двух турбин, 0..1), медиана P50 |
Каждый час февраля покрыт двумя выпусками: за сутки (D+1) и за двое суток (D+2).
Всё остальное — материалы для проверки решения в docs/: отчёты этапов (EDA, погодный архив, baselines, модель),
журнал решений агента (docs/agent/logs/*.json, AGENT_REPORT.md), аудит прогонов NWP по выпускам, стресс-тест, экономика, живой прогноз (docs/live/),
прогноз без агента и репетиции на январе.
8. Запуск
git clone https://github.com/BAITC-Hacks/hack-ee3eb0d0-gjigi.git && cd hack-ee3eb0d0-gjigi
python3 -m pip install -r requirements.txt && python3 -m pip install -e .
# (необязательно) LLM-агент: положите свой ключ в .env (файл в .gitignore)
echo 'OPENAI_API_KEY=sk-...' > .env # модель: OPENAI_MODEL, по умолчанию gpt-4.1
make agent # агент по февралю 2026 → outputs/forecast_feb2026.csv + журнал в docs/agent/ (~1 мин на правилах, ~5 мин с LLM)
make rehearsal # агент на январе 2026 + сравнение с фактом
make forecast # тот же прогноз без агента (для сравнения) → docs/forecast/
make test # 18 тестов (в т.ч. на отсутствие утечки) + make verify
make demo # дашборд Streamlit → http://localhost:8501
make live # реальный прогноз на ближайшие 2 суток по свежим прогонам (нужен интернет)
make stress # стресс-тест: испорченные/пропавшие источники погоды, конвейер против агента
make economics # стоимость небалансов на январе: медиана против оптимального квантиля
Дашборд (app.py): прогноз на февраль с интервалами (станция, T1, T2); любой выпуск с журналом решений агента и сводкой
для диспетчера; репетиция на январе против факта SCADA; живой запуск агента на выбранную дату (правила или LLM).
Архив погодных прогнозов (data/weather/) и обученные модели (models/) лежат в репозитории, поэтому команды выше
работают без интернета. Полное воспроизведение с нуля: make all (скачивание архива ~15 мин, обучение с CV ~20 мин).
| Этап | Команда | Результат |
|---|---|---|
| 0. EDA и очистка | make eda | docs/eda/ |
| 1. Архив прогнозов погоды | make weather | data/weather/, docs/weather/ |
| 2. Признаки и baselines | make baselines | docs/baselines/ |
| 3. Модель | make train | models/, docs/model/ |
| 4. Прогноз по выпускам | make forecast | docs/forecast/ |
| 5. Агент | make agent / make rehearsal | outputs/forecast_feb2026.csv, docs/agent/, docs/agent_rehearsal/ |
| 6. Демо | make demo | дашборд |
| Проверка | make verify | воспроизведение февраля и сверка с docs/reference/ |
Python ≥ 3.11. Зависимости: pandas, numpy, scikit-learn, LightGBM, pyarrow, requests, openai, python-dotenv, matplotlib, streamlit, plotly.
9. Структура
config.yaml все параметры: координаты, протокол выпуска, модели NWP, задержки, пороги агента
src/hackalem/
data/turbines.py SCADA: загрузка, флаги качества, часовая агрегация
weather/openmeteo.py HTTP-клиент (кэш, ограничение частоты, повторы)
weather/sources.py Single Runs / Previous Runs → единый формат
weather/store.py архив + get_forecast(as_of) без утечки
weather/issued.py воспроизведение протокола выпуска на истории
features/build.py признаки (только NWP + календарь)
models/baselines.py климатология, persistence, кривая мощности, изотоника
models/gbm.py квантильный LightGBM + CQR
evaluation.py метрики, holdout, rolling CV
forecast.py run_issue(): один выпуск прогноза
agent/tools.py инструменты агента
agent/agent.py LLM-агент (OpenAI) и агент на правилах
weather/fetch.py загрузка погоды агентом по требованию (с запасным вариантом из архива)
economics.py план подачи на БРЭ: оптимальный квантиль, стоимость небалансов
app.py дашборд Streamlit
outputs/forecast_feb2026.csv итоговый прогноз (единственный файл результата)
docs/ отчёты, графики, журналы агента, репетиции
scripts/ run_eda · build_weather_archive · check_weather · run_baselines ·
train_model · run_backtest · run_agent · forecast_live ·
agent_stress_test · economics_eval
tests/ 18 тестов (данные, утечка, признаки, модель, агент)
10. Ограничения
- Факта за февраль 2026 в данных нет, поэтому качество оценено на декабре–январе (holdout, репетиция) и 12-месячной CV.
- Задержки публикации NWP заданы консервативно, а не измерены. Реальная доступность может быть раньше, это только в пользу честности.
- В 03–08.2024 в архиве нет части прогонов ECMWF 06/18z. Тогда используется предыдущий опубликованный прогон.
- Простои и ограничения мощности не прогнозируются: модель прогнозирует выработку при исправной работе турбин.
11. Потенциал развития
- Экономика БРЭ уже работает (
recommend_bid, раздел 6). Дальше: реальные тарифы и цены по часам, месячные ограничения плана и автоматическая подача плана оператору рынка. - Масштабирование: координаты, модели NWP и пороги агента — в
config.yaml. Новая станция = новый конфиг + архив её SCADA. - Больше источников (ансамбли ECMWF ENS/GEFS для интервалов), учёт плановых ремонтов и ограничений мощности как отдельной модели доступности.
- Живой режим: тот же
run_issue(as_of=now)по расписанию, агент реагирует на каждый новый прогон.
Авторы коммитов
История коммитов
- 2026-09-23 17:32Dashboard: 48-hour forecast table (plant, T1, T2, interval, market plan) after a live agent runkelpopope
- 2026-09-23 17:22Dashboard redesign (Wind48 style): light green theme, sidebar, KPI cards, agent steps panel; same functionalitykelpopope
- 2026-09-23 17:09Dashboard: remove legend block, keep clear labelskelpopope
- 2026-09-23 17:07Dashboard: legend of all terms (T1/T2, P10-P90, D+1/D+2, MAE...), clearer labels and captionskelpopope
- 2026-09-23 16:57Agent explains decisions with concrete numbers; skips pointless steps; gpt-4.1kelpopope
- 2026-09-23 16:21Stress test: 4 scenarios incl. ECMWF outage, with code guardrail; figures from result fileskelpopope
- 2026-09-23 16:14Code guardrail: agent cannot exclude a healthy ECMWF 9 km (LLM did it in stress scenarios)kelpopope
- 2026-09-23 16:02README: what the product solves, technologies, step-by-step verification for the jurykelpopope
Все коммиты (23)
- 2026-09-23 15:56Forecast survives any missing NWP source (incl. ECMWF); remove pitch noteskelpopope
- 2026-09-23 15:55Quick start for the jury, make verify (bit-exact reproduction of February), README figureskelpopope
- 2026-09-23 15:42Pin dependency versions; force numeric NWP dtypes (fix fresh-install failure)kelpopope
- 2026-09-23 15:39Final results: LLM agent with fetch_weather + recommend_bid, stress test, economicskelpopope
- 2026-09-23 15:13Agent fetches weather itself; live mode, stress test, balancing-market economicskelpopope
- 2026-09-23 14:55Keep only the required deliverable in outputs/kelpopope
- 2026-09-23 14:47Stage 6: Streamlit demo dashboard (forecast, agent decisions, rehearsal, live agent run)kelpopope
- 2026-09-23 14:44Stage 5: forecasting agent (OpenAI function calling + rule fallback), final READMEkelpopope
- 2026-09-23 14:30Stage 3-4: quantile LightGBM, February rolling forecast, agent tools draftkelpopope
- 2026-09-23 14:09Stage 2: features, baselines, validation schemekelpopope
- 2026-09-23 14:02Stage 1: leak-free archived NWP forecastskelpopope
- 2026-09-23 14:02Merge web upload into main historykelpopope
- 2026-09-23 14:02Remove duplicate scripts from web upload (real ones live in scripts/)kelpopope
- 2026-09-23 13:58Add files via uploadkelpopope
- 2026-09-23 13:53Stage 0-1: project skeleton, SCADA cleaning, EDA, NWP archive fetcherskelpopope
Структура репозитория (206 файлов)
.gitignore .streamlit/config.toml Makefile README.md app.py config.yaml data/raw/turbine_1.csv data/raw/turbine_2.csv data/weather/forecast_store.parquet data/weather/issued_forecasts.parquet docs/agent/AGENT_REPORT.md docs/agent/agent_forecasts.csv docs/agent/decisions.csv docs/agent/logs/2026-01-31.json docs/agent/logs/2026-02-01.json docs/agent/logs/2026-02-02.json docs/agent/logs/2026-02-03.json docs/agent/logs/2026-02-04.json docs/agent/logs/2026-02-05.json docs/agent/logs/2026-02-06.json docs/agent/logs/2026-02-07.json docs/agent/logs/2026-02-08.json docs/agent/logs/2026-02-09.json docs/agent/logs/2026-02-10.json docs/agent/logs/2026-02-11.json docs/agent/logs/2026-02-12.json docs/agent/logs/2026-02-13.json docs/agent/logs/2026-02-14.json docs/agent/logs/2026-02-15.json docs/agent/logs/2026-02-16.json docs/agent/logs/2026-02-17.json docs/agent/logs/2026-02-18.json docs/agent/logs/2026-02-19.json docs/agent/logs/2026-02-20.json docs/agent/logs/2026-02-21.json docs/agent/logs/2026-02-22.json docs/agent/logs/2026-02-23.json docs/agent/logs/2026-02-24.json docs/agent/logs/2026-02-25.json docs/agent/logs/2026-02-26.json docs/agent/logs/2026-02-27.json docs/agent_rehearsal/AGENT_REPORT.md docs/agent_rehearsal/agent_forecasts.csv docs/agent_rehearsal/decisions.csv docs/agent_rehearsal/logs/2025-12-31.json docs/agent_rehearsal/logs/2026-01-01.json docs/agent_rehearsal/logs/2026-01-02.json docs/agent_rehearsal/logs/2026-01-03.json docs/agent_rehearsal/logs/2026-01-04.json docs/agent_rehearsal/logs/2026-01-05.json docs/agent_rehearsal/logs/2026-01-06.json docs/agent_rehearsal/logs/2026-01-07.json docs/agent_rehearsal/logs/2026-01-08.json docs/agent_rehearsal/logs/2026-01-09.json docs/agent_rehearsal/logs/2026-01-10.json docs/agent_rehearsal/logs/2026-01-11.json docs/agent_rehearsal/logs/2026-01-12.json docs/agent_rehearsal/logs/2026-01-13.json docs/agent_rehearsal/logs/2026-01-14.json docs/agent_rehearsal/logs/2026-01-15.json docs/agent_rehearsal/logs/2026-01-16.json docs/agent_rehearsal/logs/2026-01-17.json docs/agent_rehearsal/logs/2026-01-18.json docs/agent_rehearsal/logs/2026-01-19.json docs/agent_rehearsal/logs/2026-01-20.json docs/agent_rehearsal/logs/2026-01-21.json docs/agent_rehearsal/logs/2026-01-22.json docs/agent_rehearsal/logs/2026-01-23.json docs/agent_rehearsal/logs/2026-01-24.json docs/agent_rehearsal/logs/2026-01-25.json docs/agent_rehearsal/logs/2026-01-26.json docs/agent_rehearsal/logs/2026-01-27.json docs/agent_rehearsal/logs/2026-01-28.json docs/agent_rehearsal/logs/2026-01-29.json docs/agent_rehearsal/logs/2026-01-30.json docs/agent_rehearsal_rules/AGENT_REPORT.md docs/agent_rehearsal_rules/agent_forecasts.csv docs/agent_rehearsal_rules/decisions.csv docs/agent_rehearsal_rules/logs/2025-12-31.json docs/agent_rehearsal_rules/logs/2026-01-01.json docs/agent_rehearsal_rules/logs/2026-01-02.json docs/agent_rehearsal_rules/logs/2026-01-03.json docs/agent_rehearsal_rules/logs/2026-01-04.json docs/agent_rehearsal_rules/logs/2026-01-05.json docs/agent_rehearsal_rules/logs/2026-01-06.json docs/agent_rehearsal_rules/logs/2026-01-07.json docs/agent_rehearsal_rules/logs/2026-01-08.json docs/agent_rehearsal_rules/logs/2026-01-09.json docs/agent_rehearsal_rules/logs/2026-01-10.json docs/agent_rehearsal_rules/logs/2026-01-11.json docs/agent_rehearsal_rules/logs/2026-01-12.json docs/agent_rehearsal_rules/logs/2026-01-13.json docs/agent_rehearsal_rules/logs/2026-01-14.json docs/agent_rehearsal_rules/logs/2026-01-15.json docs/agent_rehearsal_rules/logs/2026-01-16.json docs/agent_rehearsal_rules/logs/2026-01-17.json docs/agent_rehearsal_rules/logs/2026-01-18.json docs/agent_rehearsal_rules/logs/2026-01-19.json docs/agent_rehearsal_rules/logs/2026-01-20.json docs/agent_rehearsal_rules/logs/2026-01-21.json docs/agent_rehearsal_rules/logs/2026-01-22.json docs/agent_rehearsal_rules/logs/2026-01-23.json docs/agent_rehearsal_rules/logs/2026-01-24.json docs/agent_rehearsal_rules/logs/2026-01-25.json docs/agent_rehearsal_rules/logs/2026-01-26.json docs/agent_rehearsal_rules/logs/2026-01-27.json docs/agent_rehearsal_rules/logs/2026-01-28.json docs/agent_rehearsal_rules/logs/2026-01-29.json docs/agent_rehearsal_rules/logs/2026-01-30.json docs/agent_stress/STRESS.md docs/agent_stress/stress_results.csv docs/baselines/01_baseline_mae.png docs/baselines/02_cv_by_month.png docs/baselines/03_holdout_sample.png docs/baselines/BASELINES.md docs/baselines/baselines_summary.json docs/baselines/cv_monthly_metrics.csv docs/baselines/holdout_metrics.csv docs/economics/ECONOMICS.md docs/economics/economics.csv docs/eda/01_power_curve_flags.png docs/eda/02_monthly_coverage.png docs/eda/03_monthly_cf_by_year.png docs/eda/04_month_hour_heatmap.png docs/eda/05_tz_check_tmax_hour.png docs/eda/06_turbine_agreement.png docs/eda/07_acf.png docs/eda/08_distributions.png docs/eda/09_jan_2026.png docs/eda/EDA.md docs/eda/eda_summary.json docs/forecast/01_feb2026_forecast.png docs/forecast/backtest_summary.json docs/forecast/forecast_no_agent_feb2026.csv docs/forecast/forecasts_all_issues.csv docs/forecast/issue_log.csv docs/forecast/rehearsal_jan2026.csv docs/img/feb_forecast.png docs/img/jan_rehearsal.png docs/img/key_results.png docs/live/forecast_2026-09-23.csv docs/live/log_2026-09-23.json docs/model/01_holdout_sample.png docs/model/02_feature_importance.png docs/model/03_cv_by_month.png docs/model/MODEL.md docs/model/cv_metrics.csv docs/model/feature_importance_p50.csv docs/model/holdout_metrics.csv docs/model/model_summary.json docs/model/train.log docs/reference/forecast_feb2026_rules.csv docs/weather/01_tz_offset.png docs/weather/02_nwp_vs_measured.png docs/weather/03_ecmwf_error_by_month.png docs/weather/WEATHER.md docs/weather/check_weather.log docs/weather/nwp_skill_wind.csv docs/weather/test_issue_runs.csv docs/weather/weather_summary.json models/gbm_y_T1.pkl models/gbm_y_T2.pkl models/gbm_y_plant.pkl models/metadata.json outputs/forecast_feb2026.csv pyproject.toml requirements.txt scripts/agent_stress_test.py scripts/build_weather_archive.py scripts/check_weather.py scripts/economics_eval.py scripts/forecast_live.py scripts/make_figures.py scripts/run_agent.py scripts/run_backtest.py scripts/run_baselines.py scripts/run_eda.py scripts/train_model.py scripts/verify.py src/hackalem/__init__.py src/hackalem/agent/__init__.py src/hackalem/agent/agent.py src/hackalem/agent/tools.py src/hackalem/config.py src/hackalem/data/__init__.py src/hackalem/data/turbines.py src/hackalem/economics.py src/hackalem/evaluation.py src/hackalem/features/__init__.py src/hackalem/features/build.py src/hackalem/forecast.py src/hackalem/models/__init__.py src/hackalem/models/baselines.py src/hackalem/models/gbm.py src/hackalem/timeutils.py src/hackalem/weather/__init__.py src/hackalem/weather/fetch.py src/hackalem/weather/issued.py src/hackalem/weather/openmeteo.py src/hackalem/weather/sources.py src/hackalem/weather/store.py tests/test_agent.py tests/test_features.py tests/test_gbm.py tests/test_turbines.py tests/test_weather_store.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.