О проекте
Команда: NoIdea
Кейс HackAlem.ai: Энергетика — Самрук-Казына
Стадия: Рабочий MVP
Задача кейса: AI-агент, прогнозирующий выработку ветровой электростанции по погодным данным
Суть
Приложение для прогноза выработки ветровых турбин: импорт SCADA-CSV, погодные выпуски NOAA GFS и Open-Meteo, обучение моделей с временной валидацией, почасовой прогноз на 48 ч и прогон по февралю 2026 под управлением OpenAI-агента.
Как устроено
CSV турбин с шагом 10 минут импортируются с версионированием по SHA-256. CLI wind.case загружает и кеширует ежедневные выпуски NOAA GFS в 12 UTC, обучает модели с разбиением fit до 01.10.2025, валидация октябрь–декабрь, контроль январь, и выполняет replay выпусков 31.01–28.02 на 48 ч без февральской SCADA. Модель, не прошедшая отбор на валидации, не активируется. OpenAI-агент (gpt-4.1-mini) оркестрирует инструменты прогноза, режим tools выполняет те же шаги без LLM. Есть телеметрический маршрут с явным persistence baseline, пересчёт в кВт/кВт·ч по номиналам и автоматизация при новых данных. UI на React.
Ключевые решения
- NWP-based forecasting
- temporal train/val/test split
- persistence baseline
- leakage tests
- agent tool orchestration
- forecast replay
Особенности
Много внимания к утечкам и доступности данных: проверяется время публикации погодного выпуска, есть тесты на leakage, аудит часового пояса SCADA, отсутствующий выпуск остаётся пропуском, а не нулём. Режимы tools и llm разделены, чтобы детерминированный прогон не выдавался за решения LLM.
Стек
Python 3.12 · FastAPI · uv · React · TypeScript · Vite · Docker Compose · pytest
Модели
gpt-4.1-mini
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-b7dd4552-noidea.
README
README.mdNoIdea · Прогноз мощности ветропарка
Приложение для импорта измерений турбин, проверки данных и воспроизводимого прогноза нормализованной мощности. OpenAI-агент управляет инструментами, а численные модели рассчитывают прогноз. Погодный маршрут использует NOAA GFS с проверкой публикации исходного выпуска и может работать без новых измерений SCADA за февраль. Телеметрический маршрут использует только прошлые измерения; если подходящей модели нет, возможен явно обозначенный persistence baseline.
В репозитории есть код и проверки, но нет пользовательских полных CSV, готовых весов и секретов. Погодные модели создаются явным запуском CLI после импорта данных и проверки временной валидации. Импорт CSV, открытие страницы и события автоматизации сами по себе не обучают модель. Наличие кода не означает, что у каждой новой турбины уже есть обученная модель или полный локальный архив.
Быстрый запуск
docker compose up --build -d
Приложение: http://localhost:8028, API: http://localhost:8028/docs.
Порт можно изменить: PORT=8030 docker compose up -d.
/turbines— импорт, измерения, полнота, удаление и восстановление./turbines/new— создание турбины с названием и координатами./forecast— разовый прогноз, месячный прогон и автоматизация./weather— диагностический архив ECMWF/Open-Meteo./sources— карточка Open-Meteo и добавление публичных JSON/CSV-источников.
Начальное состояние пустое: примеры автоматически не импортируются.
Создайте турбины и импортируйте их CSV. Данные, модели и кеш сохраняются
в Docker-томе wind-data; docker compose down их не удаляет.
Порты по умолчанию открыты только на localhost, пользовательской авторизации нет.
Для LLM сохраните OPENAI_API_KEY в .env (вне Git); необязательный
OPENAI_MODEL по умолчанию равен gpt-4.1-mini. Ключ не нужен для просмотра,
импорта, обучения ML и режима tools. Кнопки LLM и явно включённая
LLM-автоматизация расходуют API-кредиты. По умолчанию автоматизация выключена.
Локальная разработка
Нужны uv, Python 3.12 и Node.js 24+.
uv sync --frozen
npm --prefix frontend ci
npm --prefix frontend run build
uv run uvicorn wind.app:app --host 127.0.0.1 --port 8027
Адрес: http://localhost:8027. Для React с горячей перезагрузкой запустите
npm --prefix frontend run dev: Vite проксирует /api на порт 8027.
Локальный DATA_DIR по умолчанию — ./data/workspace; для другого каталога
задайте переменную окружения перед запуском. OpenAI-инструменты читают ключ
из .env, но путь к данным для локального сервера задавайте явно в окружении.
Подготовить измерения
CSV: UTF-8, запятая, максимум 25 МБ, исходные русские заголовки организаторов, время без timezone, шаг 10 минут. Мощность нормализована в [0,1]. Некорректный импорт не меняет активный набор. Повторный успешный импорт заменяет активную версию, сохраняя предыдущий исходник и его SHA-256.
Для CLI сначала создайте турбины в интерфейсе. Имена файлов должны
заканчиваться на turbine <id>.csv для соответствующей зарегистрированной турбины.
uv run python -m wind.ingest --source-dir /path/to/csv
Docker, папка подключена только для чтения:
docker compose run --rm -v "/path/to/csv:/input:ro" web /app/.venv/bin/python -m wind.ingest --source-dir /input
Удаление турбины — корзина с явным подтверждением. Новые расчёты для удалённой турбины блокируются, CSV и результаты сохраняются. Восстановление доступно в «Удалённых турбинах»; ID не переиспользуется.
В изученных полных файлах: 142 360 и 149 499 строк, период
11.03.2023–31.01.2026. Февральских фактических значений нет, несмотря на
конечную дату 28.02.2026 в названиях. Семидневные samples/ подходят для проверки
импорта и интерфейса, но не для обучения с указанными ниже периодами.
Воспроизвести весь февральский кейс одной командой
Предварительно нужны активные турбины с координатами и полными CSV в том же
DATA_DIR, что у сервера. Команда не создаёт демо-турбины и не изменяет исходные
измерения. Часовой пояс и семантику выбирайте явно: в примере постоянное UTC+6
и начало 10-минутного интервала, которые пока остаются гипотезами о SCADA.
docker compose exec -T web /app/.venv/bin/python -m wind.case --all --measurement-timezone Etc/GMT-6 --timestamp-semantics interval_start --execution-mode tools
Нативный вариант:
uv run python -m wind.case --all --measurement-timezone Etc/GMT-6 --timestamp-semantics interval_start --execution-mode tools
Для конкретных турбин замените --all на --turbine-ids 1 2.
Последовательность команды:
- Проверить активные турбины, SHA исходников и наличие полных часов в обучении, валидации и контроле. Нужна история от начала 2025 года до первого выпуска 31.01.2026 12:00 UTC; короткий CSV отклоняется до массовой загрузки.
- Загрузить и кешировать ежедневные выпуски погоды для моментов
01.01.2025–28.02.2026 в 12 UTC. Параллельность — до четырёх работников
(
--workers 1..4); общие ячейки турбин повторно не скачиваются. - Обучить отдельные модели: оценочный fit — до 01.10.2025, валидация — октябрь–декабрь, контроль — январь до 31.01.2026 12 UTC. После проверки создаются отдельные финальные веса до первого выпуска. Модель, не прошедшая отбор на валидации, не активируется и не запускает replay.
- Выполнить прогнозы без новой февральской SCADA: ежедневно в 12 UTC, выпуски 31.01–28.02 включительно, горизонт 48 часов.
- Сохранить CSV всех выпусков, CSV покрытия февраля и JSON-отчёты.
Отчёты: DATA_DIR/case-runs/<id>/report.json, forecasts.csv, coverage.csv,
replay.json, а при подтверждённых номиналах всех турбин — также plant.csv.
Точные пути печатаются в консоль. Задача также видна во вкладке
«Месячный прогон». Предыдущие генерируемые артефакты при замене сохраняются
в case-runs/<id>/previous-models/; исходные CSV, Parquet и ключи не переписываются.
Код возврата ненулевой при блокировке, частичном или неуспешном replay.
Ошибки отдельных архивных загрузок сохраняются в отчёте; доступных данных
должно хватить для честной валидации, а отсутствие погодного выпуска в феврале
останется пропуском результата.
Повтор с готовыми совместимыми артефактами, без обучения:
uv run python -m wind.case --all --measurement-timezone Etc/GMT-6 --timestamp-semantics interval_start --execution-mode tools --skip-train
Полностью офлайн по уже загруженному кешу:
uv run python -m wind.case --all --measurement-timezone Etc/GMT-6 --timestamp-semantics interval_start --execution-mode tools --skip-train --offline
--offline распространяется и на погодные запросы во время replay:
отсутствующий кеш становится явной ошибкой. С llm этот флаг несовместим.
Для настоящих решений агента выберите --execution-mode llm без --offline:
это платные обращения OpenAI, до 128 комбинаций «турбина × выпуск» за прогон.
tools выполняет ту же численную последовательность без LLM и не выдаётся
за доказательство автономных решений языковой модели.
В обоих режимах загрузку обучающего архива и обучение запускает команда
wind.case; инструмента обучения у LLM нет. execution_mode определяет
оркестрацию выпусков прогноза после подготовки модели. Успешный месячный
tools-прогон не означает, что весь месяц также проверен через LLM:
для этого нужен отдельный отчёт с execution_mode=llm и журналами запусков.
Прогнозы в интерфейсе
Во вкладке «Разовый прогноз» выберите турбины, явные настройки времени,
первую и последнюю отметки UTC включительно (24–48 часов).
issue_at равен первой отметке минус час. Бесплатная локальная проверка
показывает готовность модели/измерений, но не обещает доступность погодного
выпуска до его загрузки. Платный запуск повторяет проверку и обрабатывает
турбины последовательно. Видны причины остановки/fallback, история и JSON.
Метка прогноза означает начало часового интервала. При горизонте 48 текущая
сетка содержит начала issue_at+1h … issue_at+48h; последний интервал
заканчивается через 49 часов после выпуска. Это явная трактовка 48 почасовых
значений; исходное задание не определяет семантику меток.
Погодный маршрут не требует свежей телеметрии. Модель по измерениям и
persistence требуют полного часа возрастом не более двух часов на issue_at.
Неизвестное время блокирует любой маршрут. Непригодный погодный выпуск
не заменяется реанализом или скрытым baseline.
Во вкладке «Месячный прогон» можно изменить расписание, турбины и режим. По умолчанию 29 ежедневных выпусков на турбину. Счётчики показывают обработанные, успешные и ошибочные выпуски; после завершения доступны CSV выпусков, CSV покрытия и JSON отчёта. Для покрытия 1–28 февраля UTC ожидается 672 часа на турбину: для каждого часа выбирается последний успешный выпуск строго раньше этого часа. Все исходные горизонты сохраняются отдельно. Ошибка остаётся пропуском, а не нулём мощности. Эти числа описывают расписание, не утверждают, что реальный прогон уже успешен. Для прогона из одного дня покрытие относится к следующим суткам UTC.
Если до создания прогона подтверждены номиналы всех выбранных турбин, появляется
экспорт «ВЭС · кВт и кВт·ч CSV». Для часового интервала прогнозная мощность в кВт
равна нормализованной мощности, умноженной на номинал, энергия — кВт × 1 час.
Такой пересчёт предполагает нормировку исходной мощности как P/Pном.
В задании формула нормировки не указана, поэтому физические значения условны
до её подтверждения организаторами; подтверждение номинала в OSM этого не доказывает.
Выгрузка суммирует только выбранные турбины, сохраняя пропуски. Номиналы и
источники фиксируются при создании прогона; позднее подтверждение не меняет
старые результаты. Без подтверждённых номиналов доступны нормализованные ряды.
Реакция на новые данные
Во вкладке «Автоматизация» укажите турбины, время CSV, горизонт и режим,
включите пересчёт и сохраните настройки. Начальное состояние выключено.
tools не расходует OpenAI-кредиты; включённый llm разрешает платные вызовы
при последующих событиях. Простое открытие формы ничего не запускает.
Импорт измерений пересчитывает последний исторический момент прогноза;
без истории используется последний уже доступный полный час измерений.
Кнопка «Обновить погоду и пересчитать» загружает NOAA GFS и создаёт событие
на выбранный момент UTC (POST /api/weather/gfs). При выключенной автоматизации
кнопка только загружает погоду. Обновление старого Open-Meteo через /api/weather
не запускает пересчёт: этот источник не входит в признаки погодной модели.
Повторы одной ревизии и момента не дублируются. В журнале видны состояние,
ошибки и ссылка на прогноз каждого события. Импорт не ждёт агента.
Настройки сохраняются в DATA_DIR/automation.
Новый CSV может сделать прежнюю модель несовместимой по SHA. Автоматизация
не переобучает её: нужно явно повторить обучение. До этого действуют обычные
правила готовности маршрута и явно обозначенного телеметрического baseline;
февральский месячный режим требует пригодную погодную модель.
Перезапуск сервера не приводит к скрытому повтору потенциально платного запроса.
Месячные прогоны можно продолжить командой python -m wind.replay --resume ID.
Это реакция на события и исторический replay, не постоянный cron будущих выпусков.
Характеристики турбины
На странице «Турбины» показана карта по сохранённым координатам. Кнопка «Найти характеристики» ищет ближайшие объекты OpenStreetMap и показывает расстояние, производителя, модель и указанную номинальную мощность. Найденный кандидат применяется только после «Применить к этой турбине»; сервер повторно проверяет объект, сохраняет источник и подтверждение пользователя. OpenStreetMap не является паспортом оборудования. Близость координат сама по себе не подтверждает идентичность объекта, а координаты выбранной турбины не заменяются. Нормированная мощность исходного CSV сохраняется без изменений.
Данные погоды, время и честная оценка
- Старый ECMWF/Open-Meteo Single Runs в
/weatherостаётся диагностическим:available_at=null,historical_eligibility=unverified. Егоrunне означает публикацию, а загрузка сегодня не доказывает доступность в прошлом. - Погодная модель использует NOAA GFS 0.25° через архив Dynamical. Историческая доступность проверяется по Last-Modified нужных оригинальных NOAA GRIB-файлов. Сохраняются версии архива, координаты сетки, SHA и свидетельства публикации. Это подтверждение оригинальных прогнозных объектов, не существования API Dynamical в прошлом; последующие преобразования/округления описаны в provenance.
- Пользовательский «доверенный источник» разрешает загрузку, но не автоматически делает данные verified или подходящими для модели. Реестр поддерживает JSON/CSV и помощь LLM с параметрами; особая авторизация или формат требуют адаптера.
- CSV хранится в исходном времени. Сопоставление ветра поддерживает постоянное UTC+6, но не подтверждает настройку оборудования. Начало/конец интервала также неизвестны. Для ML семантика применяется к 10-минутным данным до агрегации.
- Полный час требует шесть валидных значений каждой метрики. Пропуски не интерполируются; повреждённые данные, дубли и отметки вне сетки отклоняются.
- Погодные признаки — прогнозы, доступные на
issue_at; будущий фактический ветер и будущая мощность не являются признаками. Границы сплитов учитывают доступность целевого полного часа. Внутреннего random early stopping нет. - Валидация выбирает модель до расчёта январского контроля. Финальные NWP-веса переобучаются отдельно до 31.01.2026 12 UTC; метрики относятся к замороженным оценочным весам. Январь ранее изучался при выборе timezone и не называется официальным нетронутым тестом.
- Без фактической мощности февраля нельзя сообщать февральские MAE/RMSE. Нормализованная мощность — не кВт и не кВт·ч. Для энергии парка нужны номинальные мощности турбин; сумма нормализованных значений не заменяет их.
Отдельное обучение телеметрической модели
Для свежих измерений остаётся отдельный HistGradientBoosting по лагам, скользящим статистикам и календарю. Он не использует погодные прогнозы:
uv run python -m wind.ml train --all --measurement-timezone Etc/GMT-6 --timestamp-semantics interval_start --train-end 2025-10-01T00:00:00Z --validation-end 2026-01-01T00:00:00Z --test-end 2026-02-01T00:00:00Z
Его веса не дообучаются на валидации/контроле. Сохраняются отдельно в
DATA_DIR/models/, погодные — в DATA_DIR/nwp-models/. Несовпадение CSV,
времени, версии библиотеки или повреждение весов запрещает использование
несовместимого артефакта. Нельзя применять модель внутри периода её выбора.
API и документация
Полные схемы: /docs. Основные группы:
| API | Назначение |
|---|---|
/api/turbines, /api/turbines/{id}/import | Каталог и измерения |
/api/turbines/deleted, /api/turbines/{id}/restore | Корзина и восстановление |
/api/weather, /api/sources | Диагностическая погода и реестр источников |
/api/ml/models | Готовность артефактов и периоды оценки |
/api/agent/preflight, /api/agent/runs | Локальная проверка и LLM-агент |
/api/forecasts/{id} | Численный прогноз и происхождение |
/api/replays, /api/replays/{id}/export | Месячный прогон и выгрузки |
/api/automation, /api/automation/events | Явная подписка на обновления и журналы |
POST /api/weather/gfs | Проверенный выпуск GFS и событие пересчёта при включённой подписке |
Подробности: агент и события, контракты данных, источники, публикация погодных выпусков, проверка времени, реальные проверки погодной модели, прежний аудит телеметрии.
Проверки
uv run pytest -q
uv run ruff check src tests
uv run ruff format --check src tests
npm --prefix frontend run build
docker compose config --quiet
Автотесты используют изолированные данные и фиктивные внешние ответы: проверяют
временные границы, исключение утечек, публикацию погоды, несовместимые артефакты,
очередь, экспорт, автоматику и отказ до массовой загрузки при неполных CSV.
Проход тестов не является метрикой качества на реальном феврале. Живая проверка
LLM отдельно запускается uv run python -m wind.simulate и расходует кредиты.
Это набор сценариев реакции агента, а не полный февральский replay.
src/wind/ импорт, API, агент, погодный архив, replay, автоматизация, case CLI
src/wind_ml/ признаки, временная оценка, телеметрические и погодные модели
frontend/ React + TypeScript + Vite
tests/ изолированные проверки
samples/ семидневные CSV для интерфейса, не для полного обучения
data/ локальные данные, кеш, модели и результаты; вне Git
Старые данные в родительском data/ и старом Docker-томе не удаляются и
не импортируются автоматически в новое рабочее пространство. Ключи, .env,
полные CSV, погодные архивы и бинарные модели исключены из Git.
Авторы коммитов
История коммитов
- 2026-09-23 17:56Merge pull request #5 from BAITC-Hacks/feature/february-replayArifOrymbetov
- 2026-09-23 17:55feature(forecast): прогноз по архивной погоде и февральский прогонArif Orymbetov
- 2026-09-23 17:02Merge pull request #4 from BAITC-Hacks/feature/data-explorerArifOrymbetov
- 2026-09-23 17:01feature(forecast): подключить ML и разделить страницы приложенияArif Orymbetov
- 2026-09-23 16:02Merge pull request #3 from BAITC-Hacks/feature/data-explorerArifOrymbetov
- 2026-09-23 16:02feature(sources): добавить поиск и настройку погодных источников агентомArif Orymbetov
- 2026-09-23 15:10Merge pull request #2 from BAITC-Hacks/feature/data-explorerArifOrymbetov
- 2026-09-23 15:10feature(data): добавить веб-интерфейс и агента прогнозированияArif Orymbetov
Все коммиты (10)
- 2026-09-23 14:12Merge pull request #1 from BAITC-Hacks/feature/data-explorerArifOrymbetov
- 2026-09-23 14:10feature(data): описать первый этап и исключить локальные данныеArif Orymbetov
Структура репозитория (88 файлов)
.dockerignore .env.example .gitignore .python-version Dockerfile README.md compose.yaml docs/agent.md docs/data-contract.md docs/ml/README.md docs/ml/data-audit.md docs/ml/nwp-audit.md docs/ml/validation-report.json docs/sources.md docs/timezone-audit.md docs/weather-availability.md frontend/index.html frontend/package-lock.json frontend/package.json frontend/src/DiscoveryPanel.tsx frontend/src/ForecastPage.tsx frontend/src/ReplayPanel.tsx frontend/src/SourcesPanel.tsx frontend/src/TurbineActions.tsx frontend/src/TurbineSpecifications.tsx frontend/src/main.tsx frontend/src/style.css frontend/src/vite-env.d.ts frontend/tsconfig.json frontend/vite.config.ts pyproject.toml samples/README.md samples/demo turbine 1.csv samples/demo turbine 2.csv scripts/audit_timezone.py src/wind/__init__.py src/wind/agent.py src/wind/app.py src/wind/archive.py src/wind/automation.py src/wind/case.py src/wind/discovery.py src/wind/ingest.py src/wind/ml.py src/wind/nwp.py src/wind/replay.py src/wind/simulate.py src/wind/sources.py src/wind/storage.py src/wind/turbine_specs.py src/wind/weather.py src/wind_ml/__init__.py src/wind_ml/__main__.py src/wind_ml/artifacts.py src/wind_ml/cli.py src/wind_ml/evaluation.py src/wind_ml/features.py src/wind_ml/io.py src/wind_ml/models.py src/wind_ml/nwp.py src/wind_ml/schemas.py src/wind_ml/telemetry.py src/wind_ml/training.py tests/ml/conftest.py tests/ml/test_artifacts.py tests/ml/test_evaluation.py tests/ml/test_guardrails.py tests/ml/test_leakage.py tests/ml/test_nwp.py tests/ml/test_preprocessing.py tests/ml/test_telemetry.py tests/ml/test_weather_io.py tests/test_agent.py tests/test_archive.py tests/test_automation.py tests/test_case.py tests/test_data.py tests/test_discovery.py tests/test_forecast_preflight.py tests/test_ml_integration.py tests/test_onboarding.py tests/test_replay.py tests/test_sources.py tests/test_turbine_deletion.py tests/test_turbine_specs.py tests/test_weather.py tests/test_weather_agent.py uv.lock
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.