AAYMA
RU
Войти

NoIdea · Прогноз мощности ветропарка

Приложение для прогноза выработки ветровых турбин: импорт SCADA-CSV, погодные выпуски NOAA GFS и Open-Meteo, обучение моделей с временной…

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

О проекте

Команда: 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.md

NoIdea · Прогноз мощности ветропарка

Приложение для импорта измерений турбин, проверки данных и воспроизводимого прогноза нормализованной мощности. 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. Последовательность команды:

  1. Проверить активные турбины, SHA исходников и наличие полных часов в обучении, валидации и контроле. Нужна история от начала 2025 года до первого выпуска 31.01.2026 12:00 UTC; короткий CSV отклоняется до массовой загрузки.
  2. Загрузить и кешировать ежедневные выпуски погоды для моментов 01.01.2025–28.02.2026 в 12 UTC. Параллельность — до четырёх работников (--workers 1..4); общие ячейки турбин повторно не скачиваются.
  3. Обучить отдельные модели: оценочный fit — до 01.10.2025, валидация — октябрь–декабрь, контроль — январь до 31.01.2026 12 UTC. После проверки создаются отдельные финальные веса до первого выпуска. Модель, не прошедшая отбор на валидации, не активируется и не запускает replay.
  4. Выполнить прогнозы без новой февральской SCADA: ежедневно в 12 UTC, выпуски 31.01–28.02 включительно, горизонт 48 часов.
  5. Сохранить 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.

Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
10
Авторы
2
Файлы
88
Ветки
3

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

ArifOrymbetov 5Arif Orymbetov 5

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

  • 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
Ветки (3)

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

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

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