О проекте
Команда: SDUTIA
Кейс HackAlem.ai: Энергетика — Самрук-Казына
Стадия: Доведённый продукт
Задача кейса: AI-агент, прогнозирующий выработку ветровой электростанции по погодным данным
Суть
AI-агент почасового прогноза выработки ВЭС у Шелека (2 турбины) на 48 ч с интервалом P10–P90: сам загружает архивные NWP-прогнозы, проверяет их, запускает ML-модель, публикует прогноз и пересчитывает при появлении свежих прогонов погоды.
Как устроено
Агентный цикл из 8 инструментов (inspect_scada, list_weather_runs, fetch_weather, validate_weather, run_forecast, analyze_forecast, publish_forecast, check_for_updates) выполняет LLM-политика OpenAI (gpt-5.4-mini) с валидатором аргументов и откатом на детерминированную политику правил; NVIDIA NIM как запасной провайдер. Погода — ECMWF AIFS/IFS, ICON, GFS из Open-Meteo с as-of-гарантией; модель — изотонические кривые мощности по каждой NWP + gradient boosting с квантилями P10/P50/P90 и калибровкой интервала, внутрисуточная коррекция по SCADA (τ=2 ч). Walk-forward валидация, бэктест на февраль 2026, Streamlit-дашборд RU/EN.
Ключевые решения
- function calling
- isotonic regression
- gradient boosting
- квантильная регрессия P10/P50/P90
- калибровка интервалов
- walk-forward validation
- nowcasting
- as-of guard
- rule-based fallback
Метрики
MAE сутки вперёд 0.168 (RMSE 0.228) vs 0.189 кривая NWP, 0.325 климатология, 0.368 персистентность; покрытие P10–P90 0.799 после калибровки; MAE на новой турбине 0.181
Особенности
Строгая as-of-гарантия (используются только прогоны погоды, опубликованные до момента прогноза), пересчёт версии 2 только при реальном изменении входов, криминалистика часового пояса SCADA и проверка всего решения одной командой verify.
Стек
Python · Streamlit · pandas · Parquet · Open-Meteo API · Docker · GitHub Actions
Модели
gpt-5.4-mini · NVIDIA NIM
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-8408bc24-sdutia.
README
README.md1. Название проекта
WindAgent — Agentic AI для почасового прогноза выработки ВЭС на 24–48 часов (кейс «Agentic AI для прогнозирования выработки ВЭС», ВЭС у Шелека, Алматинская область, 2 турбины)
English version: README.en.md
Работающая версия: https://windagent.kunai.kz — тот же Docker-образ, что собирается из этого репозитория (раздел 11). Проверка у себя за 3 минуты:
pip install -r requirements.txt→python -m windagent verify→ 4 × PASS (раздел 8). Ключи и интернет не нужны. Результат без дашборда, одной командой (≈1 мин):python -m windagent demo— шаги агента в консоли, весь февраль пересчитывается заново, таблица по дням и файлresults/forecast_february_2026.csv.

2. Краткое описание
Проблема. Выработка ВЭС зависит от ветра, который меняется за часы. Оператору ВЭС и диспетчеру нужно заранее знать почасовой профиль выработки на следующие сутки: для графика, резервов и снижения небалансов. Прогноз должен строиться только на тех данных, которые реально были доступны в момент прогноза.
Для кого: дежурный персонал ВЭС, диспетчер энергосистемы, участник рынка, который отвечает за баланс.
Что делает решение. AI-агент каждый день в 00:00 (время данных, UTC+6) самостоятельно выполняет весь цикл:
- получает архивные прогнозы погоды 4 моделей, опубликованные до момента прогноза;
- проверяет их качество;
- запускает ML-модель;
- публикует почасовой прогноз на 48 ч с интервалом неопределённости P10–P90 и аналитикой;
- через 6 ч заново загружает прогнозы погоды и по каждому оставшемуся часу проверяет, появились ли более свежие прогоны; если входные данные изменились, пересчитывает прогноз (версия 2) и записывает причину.
Тестовый период 01–28.02.2026 воспроизведён «как в прошлом»: 29 выпусков, с 31.01 по 28.02.
Ключевые результаты (блок генерируется из файлов в outputs/):
Валидация walk-forward (2025-10, 2025-11, 2025-12, 2026-01; 123 ежедневных выпусков, ВЭС = среднее двух турбин, нормированная мощность 0–1):
| Метод | MAE сутки вперёд (24–47 ч) | RMSE сутки вперёд | MAE внутрисуточно (0–23 ч) |
|---|---|---|---|
| WindAgent (наша модель) | 0.168 | 0.228 | 0.157 |
| Кривая мощности по лучшей NWP-модели | 0.189 | 0.243 | 0.182 |
| Климатология (месяц × час) | 0.325 | 0.362 | 0.326 |
| Персистентность (последний час) | 0.368 | 0.478 | 0.299 |
Улучшение MAE на сутки вперёд: 48.3% vs климатологии, 54.3% vs персистентности, 11.3% vs кривой мощности NWP. Интервал P10–P90: покрытие до калибровки 0.717; коэффициент расширения k = 1.07 (подобран на 2025-10, 2025-11, 2025-12); проверка на отложенном месяце 2026-01: 0.713 → 0.799 (цель 0.80). Перенос на «новую» турбину: обучение только на t1 → MAE t2 = 0.181 (при обучении на обеих турбинах 0.178).
Тестовый период: 29 выпусков (31.01–28.02.2026, 00:00 по времени данных), 58 опубликованных версий, политика агента: llm/openai (gpt-5.4-mini); исключений моделей погоды: 0; строк в сабмите: 672. Журналы агента: 834 событий LLM; валидатор аргументов отклонил 4 некорректных вызова(ов) инструментов LLM (2026-02-08_0000, 2026-02-12_0000, 2026-02-18_0000), после чего LLM повторил их корректно; автоматических переходов на политику правил: 1 (2026-02-08_0000). Пост-проверка выпуска 31.01 (внутрисуточная часть, факт есть): MAE 0.185 за 24 ч.
Часы SCADA: оценка UTC+6, в конфигурации UTC+6; время пика температуры стабильно в пределах 10 мин между годами; на 01.03.2024 00:00 нет дублей/пропусков.
Блок сгенерирован командой python -m windagent report из outputs/shelek/…; не редактировать вручную.
3. Что реализовано
| Возможность | Где в коде | Чем подтверждается |
|---|---|---|
| Агентный цикл. 8 инструментов, решения агента, публикация версий, трассировка каждого шага | src/windagent/agent/ (tools.py, runner.py) | tests/test_agent.py, outputs/shelek/runs/*/trace.jsonl |
| LLM-политика (OpenAI; NVIDIA NIM как запасной провайдер) с вызовом инструментов и автоматическим откатом на детерминированную политику правил | agent/llm.py, agent/rules.py | run.json → policy/provider/model; тест отката без ключа |
| Гарантия as-of. Используются только прогоны погоды, опубликованные до момента прогноза (проверка во время выполнения + тесты) | src/windagent/asof.py | tests/test_asof.py, weather.csv каждого выпуска (init_time_utc) |
| Получение архивных прогнозов погоды по координатам ВЭС (ECMWF AIFS, ECMWF IFS, ICON, GFS), только в UTC, с ретраями и кэшем | src/windagent/weather.py | data/cache/shelek/*.parquet, run.json → weather_source |
| ML-модель. Изотонические «кривые мощности» по каждой NWP-модели + gradient boosting, P10/P50/P90, калибровка интервала | model.py, features.py, forecast.py | outputs/shelek/validation/metrics.json |
| Внутрисуточная коррекция по свежим данным SCADA (только если данные моложе 2 ч); параметр τ = 2 ч выбран на валидации | forecast.py, validation.py | run.json → nowcast; metrics.json → nowcast |
| Пересчёт при обновлении данных. Через 6 ч погода загружается заново и повторяется as-of отбор. Для каждого оставшегося часа сравнивается, из какого прогона берутся входы. Пересчёт только если входы изменились; LLM может оставить версию 1 с обоснованием | check_for_updates в tools.py | run.json → versions[].reason (доля часов с новыми прогонами), kpis.revision_mae_v2_vs_v1 |
| Анализ результата. КИУМ против климатической нормы, рампы, штиль и сильный ветер, ширина интервала, уровень уверенности | analyze_forecast в tools.py | analysis.md каждого выпуска |
| Скользящий бэктест 31.01–28.02.2026 и файл сабмита | agent/runner.py | outputs/shelek/test_period/ |
| Walk-forward валидация против трёх бейзлайнов + перенос на «новую» турбину | src/windagent/validation.py | outputs/shelek/validation/ |
| Оценка по факту (формат CSV организаторов) с детекцией сдвига часов | src/windagent/evaluate.py | tests/test_evaluate_and_clock.py |
| Криминалистика часов SCADA (данные в фиксированном UTC+6) | src/windagent/clock.py | outputs/shelek/clock/clock_report.json |
| Анализ данных SCADA: полнота и пропуски, распределение и сезонность, ветер и мощность, флаг доступности, связь между турбинами | scripts/eda.py | docs/DATA_ANALYSIS.md, docs/figures/eda/ |
| Режим реального времени: прогноз на ближайшие 48 ч от текущего момента | run_now в agent/runner.py | python -m windagent forecast --now |
| Дашборд Streamlit, RU/EN: пояснение проекта и ключевые результаты; вкладки «Прогноз», «Агент» (запуск агента с журналом шагов и решений), «Валидация», «Тестовый период», «Как это работает» (с блоком «Запустить у себя») | app/ | tests/ui/, docs/figures/ui/ |
| Надёжность. Проверка входных данных, коды выхода, понятные сообщения об ошибках | errors.py, cli.py | tests/test_agent.py, tests/test_timeutil.py |
Воспроизводимость. Одна команда python -m windagent verify запускает тесты, заново выполняет все 29 выпусков агента во временной папке (на копиях модели и кэша погоды) и сверяет результат с сабмитом, проверяет схему и часы. Полное переобучение: python -m windagent all --offline. Есть Docker (образ собран и работает на сервере) и CI-workflow; GitHub Actions в организации сейчас заблокирован биллингом, поэтому workflow запускается вручную | cli.py (verify), Dockerfile, .github/workflows/ci.yml | вывод verify: 4 × PASS |
4. Как работает решение
Вход: 10-минутные данные SCADA двух турбин (data/raw/), координаты (config/sites.yaml) и момент выпуска прогноза.
Выход: почасовой прогноз по ВЭС и по каждой турбине (среднее, P10, P50, P90) плюс аналитика и полный журнал действий агента.
Протокол выпуска.
- Прогноз выпускается в день D в 00:00 по времени данных (UTC+6). Это D−1 18:00 UTC или D−1 23:00 по официальному времени Астаны.
- Горизонт 48 ч:
- часы 0–23 — внутрисуточный прогноз на день D;
- часы 24–47 — прогноз на сутки вперёд, на день D+1. Это и есть «прогноз на следующие 24–48 часов».
- Выпуск 31.01 даёт прогноз на 01.02, выпуск 01.02 — на 02.02, и так далее до выпуска 27.02 с прогнозом на 28.02.
- Сабмит на сутки вперёд содержит 28 × 24 = 672 часа.
Цикл агента (одинаковый для LLM-политики и политики правил):
inspect_scada → свежесть и доступность данных SCADA до момента прогноза
list_weather_runs → какие прогоны погоды уже опубликованы (правило as-of)
fetch_weather → загрузка архивных прогнозов Open-Meteo (API, при сбое — кэш)
validate_weather → покрытие, неправдоподобные значения, разброс ансамбля, модель-выброс
(агент решает, исключать ли её; остаётся минимум 2 модели)
run_forecast → ML-модель: P10/P50/P90 по турбинам и ВЭС
analyze_forecast → КИУМ против нормы, рампы, неопределённость, пересмотр, уровень уверенности
publish_forecast → версия 1 + аналитика для оператора (RU/EN)
-- часы симуляции +6 ч --
check_for_updates → погода загружается заново, as-of отбор на новый момент: изменились ли входы оставшихся часов?
да → validate → run → analyze → publish v2; нет (или несущественно) → версия 1 остаётся
Правило as-of.
- Значение из прогона погоды с началом t0 разрешено использовать в момент T, только если
t0 + задержка_публикации(модель) ≤ T. - Для каждой модели и каждого часа берётся самый свежий прогон, который удовлетворяет правилу.
- Задержки публикации измерены по метаданным Open-Meteo (
meta.json) и заданы с запасом +1.2…+1.5 ч. Командаpython -m windagent provenanceизмеряет их заново и сохраняет результат вoutputs/provenance/provenance.json(таблица в разделе 9). - То же правило применяется при построении обучающей выборки, поэтому модель обучается на прогнозах того же «возраста», что и в работе.
Что решает LLM, а что — модель.
- Решает LLM: порядок действий, исключение моделей по флагам проверки, повторный запуск, нужен ли пересчёт при обновлении (или оставить версию с обоснованием), текст аналитики. Если LLM ответил текстом без действий, его один раз просят либо выполнить шаги, либо явно ответить KEEP с причиной.
- Пример реального решения LLM (выпуск 10.02, уточнение в 06:00, текст написан моделью
gpt-5.4-mini, файлoutputs/shelek/runs/2026-02-10_0000/trace.jsonl): «На 2026-02-10 06:00 зафиксированы новые входы: для ecmwf_aifs025_single, ecmwf_ifs025 и gfs_seamless обновлено 28% оставшихся часов, для icon_seamless — 16%, поэтому прогноз пересчитан.» - Защиты сработали на реальных запусках: некорректные вызовы инструментов LLM отклонялись валидатором аргументов и повторялись моделью; при тайм-ауте API работу завершала политика правил (счётчики — в блоке результатов).
- Считает ML-модель: все числа прогноза. LLM не может изменить момент прогноза или запросить будущие данные.
- Фактические значения из окна прогноза агенту не показываются. Сверка с фактом (пост-проверка) делается после публикации и помечается отдельно.
5. Технологии
| Категория | Что используется | Где |
|---|---|---|
| Язык | Python ≥ 3.11 (проверено: 3.12 в Docker-образе, 3.14 локально) | весь проект |
| Данные | pandas, NumPy, PyArrow (parquet) | src/windagent/ |
| ML | scikit-learn: HistGradientBoostingRegressor (среднее и квантили 0.1/0.5/0.9), IsotonicRegression | model.py |
| AI-агент | OpenAI Python SDK: chat completions с вызовом инструментов. Модели: OpenAI gpt-5.4-mini (по умолчанию, настраивается), NVIDIA NIM (OpenAI-совместимый API, запасной провайдер) | agent/llm.py |
| Прогнозы погоды | Open-Meteo Previous Runs API. Модели: ECMWF AIFS 0.25° (AI-модель погоды), ECMWF IFS 0.25°, DWD ICON, NOAA GFS | weather.py |
| Интерфейс | Streamlit, Plotly | app/ |
| Конфигурация | YAML (config/sites.yaml), .env (python-dotenv) | config.py |
| Тесты и проверка | pytest, windagent verify, Docker, GitHub Actions (workflow; запуск вручную) | tests/, .github/workflows/ci.yml, Dockerfile |
6. Архитектура проекта
flowchart LR
SCADA[data/raw: SCADA 10 мин] --> S[scada.py<br/>часовые средние, флаги простоя]
OM[Open-Meteo<br/>Previous Runs API] --> W[weather.py<br/>UTC, ретраи, кэш]
W --> A[asof.py<br/>только опубликованные прогоны]
A --> F[features.py<br/>интервальное выравнивание,<br/>ансамбль, плотность воздуха]
S --> M
F --> M[model.py<br/>кривые мощности + HGB<br/>P10/P50/P90]
M --> FC[forecast.py<br/>выбор модели по cutoff,<br/>nowcast, агрегация ВЭС]
subgraph Agent[agent/]
R[runner.py] --> P{политика<br/>LLM или правила}
P --> T[tools.py: 8 инструментов]
end
T --> FC
T --> O[outputs/:<br/>forecast, weather, trace,<br/>analysis, run.json]
O --> UI[app/: Streamlit]
O --> CLI[cli.py / evaluate.py]
| Компонент | Ответственность |
|---|---|
config.py, config/sites.yaml | площадки, турбины, часы данных, модели погоды и их задержки |
scada.py | чтение CSV организаторов, перевод времени в UTC, часовые средние (≥ 4 из 6 записей), флаг «турбина недоступна» |
weather.py, asof.py | архивные прогнозы, правило as-of, векторный выбор для обучения и для работы |
features.py, model.py | признаки и модель |
forecast.py | прогноз одного выпуска, выбор модели с cutoff ≤ T (при необходимости обучается as-of модель) |
agent/ | инструменты, политики, раннер, трассировка, бэктест |
validation.py, evaluate.py, clock.py, report.py | валидация, оценка по факту, проверка часов, генерация блоков README |
api.py | единственный интерфейс для UI (docs/CONTRACTS.md) |
Масштабирование.
- Турбины и площадки задаются в
config/sites.yaml; добавление турбины — это изменение конфигурации, а не кода. - Погода запрашивается один раз на координаты площадки, а не на каждую турбину.
- Одна модель на площадку (турбина — признак модели).
- LLM вызывается на выпуск, а не на турбину.
- Проверка переноса на «новую» турбину приведена в блоке результатов.
Путь к 1000+ турбинам (не реализован, план):
- собственный сервер Open-Meteo или коммерческий API;
- поток SCADA в БД временных рядов;
- оркестратор по событию «вышел новый прогон»;
- реестр моделей и мониторинг дрейфа;
- агрегирование квантилей портфеля с учётом корреляций.
7. Установка и запуск
Всё нужное уже в репозитории: данные SCADA, кэш архивных прогнозов погоды, обученная модель, результаты. Ключ LLM не обязателен: без ключа агент работает на политике правил с теми же инструментами.
Вариант A — Docker (самый простой):
docker compose up --build
Затем откройте http://localhost:8501. Полный пересчёт внутри контейнера (офлайн, политика правил):
docker compose run --rm pipeline
Вариант B — Python ≥ 3.11 (проверено на 3.12 и 3.14).
Linux / macOS:
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python -m windagent info
streamlit run app/streamlit_app.py
Windows PowerShell:
python -m venv .venv; .\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python -m windagent info
streamlit run app/streamlit_app.py
Включение LLM (опционально).
- Скопируйте
.env.exampleв.env. - Укажите
OPENAI_API_KEYи/илиNVIDIA_API_KEY..envв git не попадает.
Команды CLI:
| Команда | Что делает |
|---|---|
python -m windagent forecast --issue "2026-02-10 00:00" [--policy auto|llm|rules] [-v] | один выпуск (время данных); -v — трассировка агента |
python -m windagent forecast --now | прогноз от текущего момента (нужен интернет) |
python -m windagent backtest [--offline] [--policy rules] | 29 выпусков 31.01–28.02 → outputs/shelek/test_period/ |
python -m windagent validate | walk-forward валидация → outputs/shelek/validation/ |
python -m windagent train | обучить рабочую модель (cutoff = первый выпуск тестового периода) |
python -m windagent all [--offline] | validate → train → backtest |
python -m windagent evaluate --actuals t1=ФАЙЛ --actuals t2=ФАЙЛ | оценка по факту в формате организаторов |
python -m windagent detect-clock | проверка часов SCADA |
python -m windagent verify | все проверки одной командой (тесты, воспроизведение сабмита, схема, часы) |
python -m windagent demo | результат в консоли без дашборда: шаги агента, весь февраль заново, таблица по дням, CSV в results/ |
python -m windagent fetch-history | заново скачать архив прогнозов в кэш |
python -m windagent provenance | заново измерить задержки публикации и проверить сопоставление прогонов архива (нужен интернет) |
Коды выхода: 0 — успех, 2 — неверный ввод, 3 — нет данных, 4 — внешний сервис недоступен и нет кэша. Ошибка выводится одной строкой ERROR: ….
8. Как проверить решение
Примерно 5–10 минут. Все шаги работают офлайн и без ключа LLM.
-
Всё одной командой (около 3 минут):
python -m windagent verifyОжидается 4 строки
PASS:- тесты (
pytest -q); - все 29 выпусков заново выполнены во временной папке, и сабмит совпадает с закоммиченным (
max abs diff 0.00e+00); - схема сабмита: 672 ч, значения 0–1, P10 ≤ P50 ≤ P90;
- часы SCADA — фиксированный UTC+6.
Ниже те же проверки по шагам.
- тесты (
-
Тесты:
pytest -qОжидается: все тесты проходят. Среди них правило as-of на реальном кэше, агент end-to-end, исключение модели-выброса, откат LLM → правила, оценка и проверка часов.
-
Один выпуск с полной трассировкой агента:
python -m windagent forecast --issue "2026-01-31 00:00" --policy rules --offline -vОжидается 30 шагов:
- проверка SCADA → прогоны → загрузка → проверка → прогноз → анализ → публикация v1;
- через 6 ч: «newer runs change the inputs of … 28% … of the remaining hours» → пересчёт → v2;
- в конце аналитика и «Пост-проверка» по факту 31.01 (MAE 0.1855 за 24 ч).
Результат пишется в
outputs/shelek/adhoc/; закоммиченные выпуски тестового периода не перезаписываются. -
Воспроизведение февральского прогноза в отдельную папку и сверка с сабмитом:
Linux / macOS:
WINDAGENT_OUTPUTS_DIR=repro python -m windagent backtest --offline --policy rulesWindows PowerShell:
$env:WINDAGENT_OUTPUTS_DIR="repro"; python -m windagent backtest --offline --policy rules; Remove-Item Env:WINDAGENT_OUTPUTS_DIRЗатем сверка:
python -m windagent compare outputs/shelek/test_period/submission_day_ahead.csv repro/shelek/test_period/submission_day_ahead.csvОжидается:
"submission_rows": 672, затемOK: identical within 0.0002. Числа прогноза не зависят от того, какая политика вела агента: LLM не исключала модели. -
Часы данных:
python -m windagent detect-clockОжидается:
estimated UTC+6 … Fixed UTC+6 logger clock; no change at the 2024-03-01 switch. -
Оценка по факту февраля (если у жюри есть файлы факта в формате датасета):
python -m windagent evaluate --actuals t1=feb_turbine_1.csv --actuals t2=feb_turbine_2.csvКоманда выводит MAE, RMSE и bias по турбинам и ВЭС, а также покрытие P10–P90. Если файл факта сдвинут по часам (например, записан в UTC+5), выводится предупреждение и метрики после выравнивания.
-
Дашборд:
streamlit run app/streamlit_app.pyВкладки: «Прогноз», «Агент» (запуск агента с живой трассировкой), «Валидация», «Тестовый период», «Как это работает».
-
Некорректные запросы обрабатываются. Например:
python -m windagent forecast --issue "31.01.2026"Ожидается
ERROR: Invalid time …и код выхода 2. Также проверьте дату в будущем,--horizon 500и неизвестную турбину вevaluate. -
(Опционально) LLM-режим: задайте
OPENAI_API_KEYв.env, затем:python -m windagent forecast --issue "2026-02-10 00:00" --policy llm -vВ трассировке появятся сообщения LLM, а в
run.json—"policy": "llm", "provider": "openai".
9. Данные и интеграции
SCADA (предоставлено организаторами), файлы data/raw/turbine_1.csv и turbine_2.csv.
- 10-минутные записи с 11.03.2023 по 31.01.2026: средний ветер (м/с), нормированная активная мощность (0–1), температура (°C).
- Фактических данных за февраль 2026 нет: тестовый период прогнозируется вслепую.
- Часы данных — фиксированный UTC+6. Регистратор не перешёл на UTC+5 при смене часового пояса 01.03.2024. Доказательства воспроизводит
detect-clock:- на 01.03.2024 00:00 нет дублей и пропусков;
- время суточного пика температуры не сместилось между годами;
- корреляция ветра SCADA с прогнозами погоды по кварталам максимальна при сдвиге 5.3–6.2 ч (медиана 5.75, ближайшее целое — UTC+6); смену часов исключают два теста выше.
- Именованные часовые пояса (например,
Asia/Almaty) в коде не используются: это сдвинуло бы все данные после марта 2024 на час. - Во всех выходных файлах три времени:
*_data_clock(UTC+6, как в датасете),*_utc,*_kz_official(UTC+5). - Часы, где мощность меньше 0.05 при ветре больше 6 м/с, помечаются как «турбина недоступна» (простой/ограничение) и исключаются из обучения.
Open-Meteo Previous Runs API (https://previous-runs-api.open-meteo.com/v1/forecast), без ключа.
- Переменные: ветер 10 м и 100 м, направление 100 м, температура 2 м, приземное давление.
- Сдвиги
previous_day1…4: значение на час v берётся из прогона, начатого вfloor_6ч(v) − N суток. Это проверено сверкой с точными прогонами ECMWF IFS HRES (Single Runs API): 48 из 48 значений совпадают (python -m windagent provenance→outputs/provenance/provenance.json). - Запросы только с
timezone=UTC; ответы с ненулевымutc_offset_secondsотклоняются.
| Модель | Архив с | Задержка публикации (с запасом) |
|---|---|---|
| ECMWF AIFS 0.25° (AI-модель погоды) | 19.02.2025 | 7 ч |
| ECMWF IFS 0.25° | 07.03.2024 | 9 ч |
| DWD ICON | 17.02.2024 | 5 ч |
| NOAA GFS | 17.02.2024 | 8 ч |
LLM (опционально): OpenAI API и NVIDIA NIM (OpenAI-совместимый endpoint). Ключи задаются только через .env.
Данные о погоде — Open-Meteo, лицензия CC BY 4.0.
10. Ограничения
- Нет факта за февраль, поэтому точность в тестовом периоде мы оценить не можем. Точность показана на walk-forward валидации (10.2025–01.2026). Февраль оценивается командой
evaluate, когда появится факт. - Задержки публикации — оценки. Они измерены по текущим метаданным Open-Meteo и взяты с запасом; фактические исторические задержки могли отличаться.
- Консервативный выбор прогонов. Архив Previous Runs устроен по суточным сдвигам, поэтому для части часов берётся чуть более старый прогон, чем был бы доступен оператору. Мы жертвуем долей точности ради отсутствия утечки.
- Короткая история лучшей модели. ECMWF AIFS в архиве только с 19.02.2025, так что по ней около 11 месяцев обучения.
- Грубая сетка прогноза в сложном рельефе. Сетка моделей погоды 9–25 км, а площадка находится в долинном коридоре. ML исправляет систематические ошибки, но не все.
- Нет номинальной мощности. «ВЭС» здесь — среднее двух турбин в нормированных единицах. МВт выдаются только если задать
rated_mwвconfig/sites.yaml. - Простои, ограничения и обледенение не прогнозируются по погоде. Модель прогнозирует выработку при доступных турбинах.
- Нет переобучения в феврале, потому что новых фактических данных нет. Команда
trainподдерживает переобучение. - Соглашения о времени. Сдвиг UTC+6 установлен статистически, а не по документации. Метка 10-минутного интервала (начало или конец, ±10 мин) принята как начало часа.
- Редкие экстремумы изучены слабо. Ветер около порога отключения и штормы в истории редки.
- Текст аналитики от LLM может отличаться между запусками. Числа прогноза от LLM не зависят.
- Интервал P10–P90 для ВЭС — среднее квантилей турбин. Это допустимо, так как турбины сильно коррелированы (≈ 0.97), но это приближение.
- Бесплатный API Open-Meteo предназначен для некоммерческого использования. Режим реального времени требует интернета; бэктест работает офлайн из кэша.
- У демо нет авторизации, запуски LLM в интерфейсе ограничены лимитом.
- GitHub Actions в организации BAITC-Hacks заблокирован биллингом, поэтому CI-workflow не запускается автоматически. Те же проверки выполняет
python -m windagent verify(раздел 8).
11. Ссылка на deployed-версию
- Дашборд Streamlit из этого репозитория: Docker-образ на сервере команды, nginx, HTTPS (Let's Encrypt).
- Показывает закоммиченные результаты тестового периода и валидации.
- Позволяет запустить агента для любого выпуска и сделать прогноз от текущего момента. Во время хакатона запуски LLM ограничены лимитом; без ключа на сервере работает политика правил.
- Это тот же образ, что
docker compose up --buildсобирает из репозитория (раздел 7); развёрнутая версия не заменяет запуск у себя.
| Прогноз | Агент | Валидация |
|---|---|---|
![]() | ![]() | ![]() |
Приложение A. Структура репозитория
config/sites.yaml площадки, турбины, часы данных
data/raw/ SCADA организаторов (ASCII-имена файлов)
data/cache/shelek/ кэш архивных прогнозов погоды (parquet)
models/shelek/ рабочая модель (cutoff 31.01.2026 00:00 времени данных)
outputs/shelek/runs/ 29 выпусков: forecast.csv, weather.csv, trace.jsonl, analysis.md, run.json
outputs/shelek/test_period/ submission_day_ahead.csv (672 ч), all_issues.csv
outputs/shelek/validation/ metrics.json, predictions.csv
outputs/shelek/clock/ clock_report.json
outputs/provenance/ измеренные задержки публикации и проверка сдвигов архива
src/windagent/ ядро (см. раздел 6)
app/ дашборд Streamlit
scripts/eda.py генератор отчёта docs/DATA_ANALYSIS.md
tests/ тесты ядра (tests/*.py), UI (tests/ui) и чёрного ящика CLI (tests/qa)
docs/CONTRACTS.md схемы файлов, API и CLI
docs/DATA_ANALYSIS.md анализ данных SCADA с рисунками (docs/figures/eda/)
docs/presentation/ сценарий демо и ответы на вопросы жюри
Приложение B. Команда
SDUTIA. Разработка велась в этом репозитории в рамках 5-часового ограничения хакатона.
Авторы коммитов
История коммитов
- 2026-09-23 17:53feat: `python -m windagent demo` - results in the console and CSV without the dashboardkazakhbala01
- 2026-09-23 17:30docs: live site and 3-minute check at the top of both READMEs; real agent evidencekazakhbala01
- 2026-09-23 17:22test: importlib import mode so tests/, tests/ui/ and tests/qa/ can reuse file names (157 tests pass)kazakhbala01
- 2026-09-23 17:17ui: explain forecasts, agent decisions and validation for the jurykazakhbala01
- 2026-09-23 16:58ui: prevent clipped metrics and separate chart legendskazakhbala01
- 2026-09-23 16:52feat: operator-friendly analysis text (model names, Russian reasons, smaller headings)kazakhbala01
- 2026-09-23 16:37ui: preserve canonical selections across language and clock changeskazakhbala01
- 2026-09-23 16:23docs: English README (faithful translation) + README consistency fixeskazakhbala01
Все коммиты (32)
- 2026-09-23 16:17docs: deployed version at https://windagent.kunai.kz (README section 11)kazakhbala01
- 2026-09-23 16:13docs: confirm resolved QA findings and sync final presentation evidencekazakhbala01
- 2026-09-23 16:09docs: add reproducible SCADA analysis and five data figureskazakhbala01
- 2026-09-23 16:07docs: record core resolutions of the QA requestskazakhbala01
- 2026-09-23 16:07fix: QA findings in the CLI contract (all 7 categories); drop resolved xfail markerskazakhbala01
- 2026-09-23 16:02data+docs: final LLM backtest, provenance evidence, README claims matched to the repokazakhbala01
- 2026-09-23 16:00docs: refresh jury materials for current validation and adhoc runskazakhbala01
- 2026-09-23 16:00test: align CLI QA with updated core and flag stale artifactskazakhbala01
- 2026-09-23 15:55test: add offline black-box CLI QA and document contract defectskazakhbala01
- 2026-09-23 15:45fix: AI-judge review findings (reliability, honest update check, validated nowcast)kazakhbala01
- 2026-09-23 15:45docs: add sourced jury presentation and offline demo materialskazakhbala01
- 2026-09-23 15:22feat: one-command local verification; CI manual-only while Actions is billing-lockedkazakhbala01
- 2026-09-23 15:14feat: Docker, CI (3.11-3.13 + reproduce + docker), core tests, README (RU)kazakhbala01
- 2026-09-23 15:14data: official February backtest by the LLM agent (gpt-5.4-mini) + README metricskazakhbala01
- 2026-09-23 15:13ui: verify revised forecasts and capture real dashboard previewskazakhbala01
- 2026-09-23 15:07ui: support version-specific weather and remaining-hour revisionskazakhbala01
- 2026-09-23 15:03ui: verify isolated Python 3.12 runtime and forecast layoutkazakhbala01
- 2026-09-23 15:02feat: forecasting agent, rolling backtest, evaluate, clock forensics (S1)kazakhbala01
- 2026-09-23 15:00ui: document isolated dashboard checkout and publishing workflowkazakhbala01
- 2026-09-23 14:55ui: build bilingual forecasting dashboard and offline testskazakhbala01
- 2026-09-23 14:55ui: add deterministic schema-exact dashboard fixtureskazakhbala01
- 2026-09-23 14:42feat: weather archive, as-of guard, features, model, walk-forward validationkazakhbala01
- 2026-09-23 14:32docs: point change requests to per-engineer fileskazakhbala01
- 2026-09-23 14:30chore: skeleton + contracts (S0)kazakhbala01
Структура репозитория (266 файлов)
.dockerignore .env.example .gitattributes .github/workflows/ci.yml .gitignore .streamlit/config.toml Dockerfile README.en.md README.md app/README.md app/__init__.py app/components/__init__.py app/components/agent.py app/components/analytics.py app/components/forecast.py app/copy_agent.py app/copy_analytics.py app/i18n.py app/presentation.py app/rate_limit.py app/services.py app/streamlit_app.py app/widgets.py config/sites.yaml data/cache/shelek/nwp_ecmwf_aifs025_single.parquet data/cache/shelek/nwp_ecmwf_ifs025.parquet data/cache/shelek/nwp_gfs_seamless.parquet data/cache/shelek/nwp_icon_seamless.parquet data/raw/turbine_1.csv data/raw/turbine_2.csv docker-compose.yml docs/CONTRACTS.md docs/DATA_ANALYSIS.md docs/figures/eda/01_data_coverage.png docs/figures/eda/02_power_distribution.png docs/figures/eda/03_monthly_seasonality.png docs/figures/eda/04_wind_power.png docs/figures/eda/05_cross_turbine.png docs/figures/ui/agent.png docs/figures/ui/forecast-cards.png docs/figures/ui/forecast.png docs/figures/ui/hourly-values.png docs/figures/ui/how-it-works.png docs/figures/ui/intro.png docs/figures/ui/run-locally.png docs/figures/ui/test-period.png docs/figures/ui/validation-charts.png docs/figures/ui/validation.png docs/presentation/DEMO_SCRIPT.md docs/presentation/QA_CHEATSHEET.md docs/presentation/SLIDES_OUTLINE.md docs/requests/README.md docs/requests/codex-qa.md docs/requests/codex-ui.md models/shelek/model_production.pkl outputs/provenance/provenance.json outputs/shelek/clock/clock_report.json outputs/shelek/runs/2026-01-31_0000/analysis.md outputs/shelek/runs/2026-01-31_0000/forecast.csv outputs/shelek/runs/2026-01-31_0000/run.json outputs/shelek/runs/2026-01-31_0000/trace.jsonl outputs/shelek/runs/2026-01-31_0000/weather.csv outputs/shelek/runs/2026-02-01_0000/analysis.md outputs/shelek/runs/2026-02-01_0000/forecast.csv outputs/shelek/runs/2026-02-01_0000/run.json outputs/shelek/runs/2026-02-01_0000/trace.jsonl outputs/shelek/runs/2026-02-01_0000/weather.csv outputs/shelek/runs/2026-02-02_0000/analysis.md outputs/shelek/runs/2026-02-02_0000/forecast.csv outputs/shelek/runs/2026-02-02_0000/run.json outputs/shelek/runs/2026-02-02_0000/trace.jsonl outputs/shelek/runs/2026-02-02_0000/weather.csv outputs/shelek/runs/2026-02-03_0000/analysis.md outputs/shelek/runs/2026-02-03_0000/forecast.csv outputs/shelek/runs/2026-02-03_0000/run.json outputs/shelek/runs/2026-02-03_0000/trace.jsonl outputs/shelek/runs/2026-02-03_0000/weather.csv outputs/shelek/runs/2026-02-04_0000/analysis.md outputs/shelek/runs/2026-02-04_0000/forecast.csv outputs/shelek/runs/2026-02-04_0000/run.json outputs/shelek/runs/2026-02-04_0000/trace.jsonl outputs/shelek/runs/2026-02-04_0000/weather.csv outputs/shelek/runs/2026-02-05_0000/analysis.md outputs/shelek/runs/2026-02-05_0000/forecast.csv outputs/shelek/runs/2026-02-05_0000/run.json outputs/shelek/runs/2026-02-05_0000/trace.jsonl outputs/shelek/runs/2026-02-05_0000/weather.csv outputs/shelek/runs/2026-02-06_0000/analysis.md outputs/shelek/runs/2026-02-06_0000/forecast.csv outputs/shelek/runs/2026-02-06_0000/run.json outputs/shelek/runs/2026-02-06_0000/trace.jsonl outputs/shelek/runs/2026-02-06_0000/weather.csv outputs/shelek/runs/2026-02-07_0000/analysis.md outputs/shelek/runs/2026-02-07_0000/forecast.csv outputs/shelek/runs/2026-02-07_0000/run.json outputs/shelek/runs/2026-02-07_0000/trace.jsonl outputs/shelek/runs/2026-02-07_0000/weather.csv outputs/shelek/runs/2026-02-08_0000/analysis.md outputs/shelek/runs/2026-02-08_0000/forecast.csv outputs/shelek/runs/2026-02-08_0000/run.json outputs/shelek/runs/2026-02-08_0000/trace.jsonl outputs/shelek/runs/2026-02-08_0000/weather.csv outputs/shelek/runs/2026-02-09_0000/analysis.md outputs/shelek/runs/2026-02-09_0000/forecast.csv outputs/shelek/runs/2026-02-09_0000/run.json outputs/shelek/runs/2026-02-09_0000/trace.jsonl outputs/shelek/runs/2026-02-09_0000/weather.csv outputs/shelek/runs/2026-02-10_0000/analysis.md outputs/shelek/runs/2026-02-10_0000/forecast.csv outputs/shelek/runs/2026-02-10_0000/run.json outputs/shelek/runs/2026-02-10_0000/trace.jsonl outputs/shelek/runs/2026-02-10_0000/weather.csv outputs/shelek/runs/2026-02-11_0000/analysis.md outputs/shelek/runs/2026-02-11_0000/forecast.csv outputs/shelek/runs/2026-02-11_0000/run.json outputs/shelek/runs/2026-02-11_0000/trace.jsonl outputs/shelek/runs/2026-02-11_0000/weather.csv outputs/shelek/runs/2026-02-12_0000/analysis.md outputs/shelek/runs/2026-02-12_0000/forecast.csv outputs/shelek/runs/2026-02-12_0000/run.json outputs/shelek/runs/2026-02-12_0000/trace.jsonl outputs/shelek/runs/2026-02-12_0000/weather.csv outputs/shelek/runs/2026-02-13_0000/analysis.md outputs/shelek/runs/2026-02-13_0000/forecast.csv outputs/shelek/runs/2026-02-13_0000/run.json outputs/shelek/runs/2026-02-13_0000/trace.jsonl outputs/shelek/runs/2026-02-13_0000/weather.csv outputs/shelek/runs/2026-02-14_0000/analysis.md outputs/shelek/runs/2026-02-14_0000/forecast.csv outputs/shelek/runs/2026-02-14_0000/run.json outputs/shelek/runs/2026-02-14_0000/trace.jsonl outputs/shelek/runs/2026-02-14_0000/weather.csv outputs/shelek/runs/2026-02-15_0000/analysis.md outputs/shelek/runs/2026-02-15_0000/forecast.csv outputs/shelek/runs/2026-02-15_0000/run.json outputs/shelek/runs/2026-02-15_0000/trace.jsonl outputs/shelek/runs/2026-02-15_0000/weather.csv outputs/shelek/runs/2026-02-16_0000/analysis.md outputs/shelek/runs/2026-02-16_0000/forecast.csv outputs/shelek/runs/2026-02-16_0000/run.json outputs/shelek/runs/2026-02-16_0000/trace.jsonl outputs/shelek/runs/2026-02-16_0000/weather.csv outputs/shelek/runs/2026-02-17_0000/analysis.md outputs/shelek/runs/2026-02-17_0000/forecast.csv outputs/shelek/runs/2026-02-17_0000/run.json outputs/shelek/runs/2026-02-17_0000/trace.jsonl outputs/shelek/runs/2026-02-17_0000/weather.csv outputs/shelek/runs/2026-02-18_0000/analysis.md outputs/shelek/runs/2026-02-18_0000/forecast.csv outputs/shelek/runs/2026-02-18_0000/run.json outputs/shelek/runs/2026-02-18_0000/trace.jsonl outputs/shelek/runs/2026-02-18_0000/weather.csv outputs/shelek/runs/2026-02-19_0000/analysis.md outputs/shelek/runs/2026-02-19_0000/forecast.csv outputs/shelek/runs/2026-02-19_0000/run.json outputs/shelek/runs/2026-02-19_0000/trace.jsonl outputs/shelek/runs/2026-02-19_0000/weather.csv outputs/shelek/runs/2026-02-20_0000/analysis.md outputs/shelek/runs/2026-02-20_0000/forecast.csv outputs/shelek/runs/2026-02-20_0000/run.json outputs/shelek/runs/2026-02-20_0000/trace.jsonl outputs/shelek/runs/2026-02-20_0000/weather.csv outputs/shelek/runs/2026-02-21_0000/analysis.md outputs/shelek/runs/2026-02-21_0000/forecast.csv outputs/shelek/runs/2026-02-21_0000/run.json outputs/shelek/runs/2026-02-21_0000/trace.jsonl outputs/shelek/runs/2026-02-21_0000/weather.csv outputs/shelek/runs/2026-02-22_0000/analysis.md outputs/shelek/runs/2026-02-22_0000/forecast.csv outputs/shelek/runs/2026-02-22_0000/run.json outputs/shelek/runs/2026-02-22_0000/trace.jsonl outputs/shelek/runs/2026-02-22_0000/weather.csv outputs/shelek/runs/2026-02-23_0000/analysis.md outputs/shelek/runs/2026-02-23_0000/forecast.csv outputs/shelek/runs/2026-02-23_0000/run.json outputs/shelek/runs/2026-02-23_0000/trace.jsonl outputs/shelek/runs/2026-02-23_0000/weather.csv outputs/shelek/runs/2026-02-24_0000/analysis.md outputs/shelek/runs/2026-02-24_0000/forecast.csv outputs/shelek/runs/2026-02-24_0000/run.json outputs/shelek/runs/2026-02-24_0000/trace.jsonl outputs/shelek/runs/2026-02-24_0000/weather.csv outputs/shelek/runs/2026-02-25_0000/analysis.md outputs/shelek/runs/2026-02-25_0000/forecast.csv outputs/shelek/runs/2026-02-25_0000/run.json outputs/shelek/runs/2026-02-25_0000/trace.jsonl outputs/shelek/runs/2026-02-25_0000/weather.csv outputs/shelek/runs/2026-02-26_0000/analysis.md outputs/shelek/runs/2026-02-26_0000/forecast.csv outputs/shelek/runs/2026-02-26_0000/run.json outputs/shelek/runs/2026-02-26_0000/trace.jsonl outputs/shelek/runs/2026-02-26_0000/weather.csv outputs/shelek/runs/2026-02-27_0000/analysis.md outputs/shelek/runs/2026-02-27_0000/forecast.csv outputs/shelek/runs/2026-02-27_0000/run.json outputs/shelek/runs/2026-02-27_0000/trace.jsonl outputs/shelek/runs/2026-02-27_0000/weather.csv outputs/shelek/runs/2026-02-28_0000/analysis.md outputs/shelek/runs/2026-02-28_0000/forecast.csv outputs/shelek/runs/2026-02-28_0000/run.json outputs/shelek/runs/2026-02-28_0000/trace.jsonl outputs/shelek/runs/2026-02-28_0000/weather.csv outputs/shelek/test_period/all_issues.csv outputs/shelek/test_period/submission_day_ahead.csv outputs/shelek/validation/metrics.json outputs/shelek/validation/predictions.csv pyproject.toml requirements.txt scripts/eda.py src/windagent/__init__.py src/windagent/__main__.py src/windagent/agent/__init__.py src/windagent/agent/llm.py src/windagent/agent/rules.py src/windagent/agent/runner.py src/windagent/agent/tools.py src/windagent/agent/trace.py src/windagent/api.py src/windagent/asof.py src/windagent/cli.py src/windagent/clock.py src/windagent/config.py src/windagent/dataset.py src/windagent/errors.py src/windagent/evaluate.py src/windagent/features.py src/windagent/forecast.py src/windagent/model.py src/windagent/provenance.py src/windagent/report.py src/windagent/scada.py src/windagent/timeutil.py src/windagent/validation.py src/windagent/weather.py tests/fixtures/README.md tests/fixtures/make_fixtures.py tests/fixtures/outputs/shelek/runs/2026-02-01_0000/analysis.md tests/fixtures/outputs/shelek/runs/2026-02-01_0000/forecast.csv tests/fixtures/outputs/shelek/runs/2026-02-01_0000/run.json tests/fixtures/outputs/shelek/runs/2026-02-01_0000/trace.jsonl tests/fixtures/outputs/shelek/runs/2026-02-01_0000/weather.csv tests/fixtures/outputs/shelek/runs/2026-02-02_0000/analysis.md tests/fixtures/outputs/shelek/runs/2026-02-02_0000/forecast.csv tests/fixtures/outputs/shelek/runs/2026-02-02_0000/run.json tests/fixtures/outputs/shelek/runs/2026-02-02_0000/trace.jsonl tests/fixtures/outputs/shelek/runs/2026-02-02_0000/weather.csv tests/fixtures/outputs/shelek/test_period/all_issues.csv tests/fixtures/outputs/shelek/test_period/submission_day_ahead.csv tests/fixtures/outputs/shelek/validation/metrics.json tests/fixtures/outputs/shelek/validation/predictions.csv tests/qa/README.md tests/qa/conftest.py tests/qa/test_cli_errors.py tests/qa/test_cli_workflows.py tests/test_agent.py tests/test_asof.py tests/test_evaluate_and_clock.py tests/test_timeutil.py tests/ui/test_agent.py tests/ui/test_analytics.py tests/ui/test_app.py tests/ui/test_fixture_contract.py tests/ui/test_forecast.py tests/ui/test_presentation.py tests/ui/test_rate_limit.py tests/ui/test_services.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.


