AAYMA
RU
Войти

WindAgent

Агент, который каждые сутки берёт архивный прогноз погоды Open-Meteo и выпускает почасовой прогноз выработки двух турбин ВЭС на 24–48 ч.

Доведённый продукт
Команда «Technokod» 19💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: Technokod
Кейс HackAlem.ai: Энергетика — Самрук-Казына
Стадия: Доведённый продукт

Задача кейса: AI-агент, прогнозирующий выработку ветровой электростанции по погодным данным

Суть

Агент, который каждые сутки берёт архивный прогноз погоды Open-Meteo и выпускает почасовой прогноз выработки двух турбин ВЭС на 24–48 ч. Проверяет качество входа, может отказаться от выпуска, пересчитывает прогноз при изменении данных. Есть дашборд диспетчера.

Как устроено

Инструмент погоды берёт Open-Meteo Previous Runs (только прогноз, известный к концу суток D, без факта погоды). Модель по каждой турбине смешивает эмпирическую кривую мощности, градиентный бустинг и двухэтапную схему (бустинг предсказывает ветер на гондоле, затем измеренная кривая турбины). Интервал P10–P90 строится по квантилям остатков. Агент ForecastAgent с 6 инструментами (get_weather, check_input_quality, prepare_and_predict, analyze, recalculate, save_forecast) управляется через OpenAI function calling, при ошибке переходит на детерминированный планировщик. Бэктест из 30 выпусков за февраль 2026, выход: CSV и JSON-журналы трассы.

Ключевые решения

  • Gradient Boosting
  • эмпирическая кривая мощности
  • двухэтапная модель (ветер гондолы → мощность)
  • ансамбль с весами
  • квантильный интервал P10–P90
  • OpenAI function calling
  • детерминированный fallback-планировщик
  • контроль качества входа (stuck sensor, пропуски)
  • бэктест по дням

Метрики

Январь 2026: nMAE 14.31% / 14.55% (24 ч), 16.33% / 16.66% (48 ч) для турбин 1/2; MAE смеси 0.1431 против 0.1580 у GBM

Особенности

Честный протокол «прогноз как в прошлом»: берётся только прогноз погоды, известный на момент выпуска. Отдельный эксперимент на деградацию данных показывает разницу между агентом и конвейером: на испорченном входе агент 4 раза отказался публиковать прогноз, конвейер молча опубликовал все 6. Живая LLM-оркестрация проверена на 30 выпусках и совпала с детерминированной побитово, расход токенов замерен.

Стек

Python · scikit-learn · FastAPI · Open-Meteo API · Docker · GitHub Actions

Модели

gpt-5.4-mini · gpt-5.4


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-c63c1350-technokod.

README

README.md

WindAgent — Agentic AI для прогнозирования выработки ВЭС

License: MIT Python scikit-learn OpenAI

Агент, который каждые сутки сам берёт прогноз погоды, известный к концу этих суток, и выпускает почасовой прогноз выработки двух турбин ВЭС на следующие 24–48 часов — с контролем качества входа, анализом результата и пересчётом при обновлении входных данных.

Команда Technokod, HackAlem AI 2026. Трек «Энергетика», кейс Самрук-Казына «Agentic AI для прогнозирования выработки ВЭС».

Краткое описание

Агент прогнозирует почасовую выработку двух турбин ветроэлектростанции (Шелекский ветровой коридор, Алматинская область) на 24–48 часов вперёд. Для каждых суток выпуска он сам получает архивный прогноз погоды, известный к концу этих суток, проверяет полученные данные на пригодность, готовит признаки, запускает модель, анализирует результат, сравнивает с предыдущим выпуском и пересчитывает прогноз, если входные данные изменились. Пользователь: диспетчер ВЭС или энергосбытовая служба, которой нужен суточный и двухсуточный график выработки для заявок на рынок и планирования резервов.

Ключевое требование кейса — использовать прогноз погоды, доступный на момент прогнозирования, а не факт. Оно выполнено на уровне источника данных: факт погоды не используется ни в тесте, ни в обучении (docs/validation.md).

Как это выглядит

Дашборд диспетчера

Главный экран: выбор даты выпуска, горизонт 24/48 часов, ожидаемая энергия за период и по суткам, покрытие интервала, изменение к прошлому выпуску. Роторы турбин вращаются со скоростью, пропорциональной прогнозной мощности.

Вывод и трасса агента

Панель агента: сверху вывод по выпуску, ниже отметка о пересчёте с причиной (расхождение 0,164 при пороге 0,15), часы риска и развёрнутая трасса из шести шагов с техническими именами инструментов — видно соответствие коду.

Проверка на январе 2026

Внизу — сводка для заявки с калиброванным коридором и блок надёжности: факт января против прогноза, выпущенного накануне, с метриками nMAE на обоих горизонтах.

Живой дашборд: https://hack.technokod.kz

Что реализовано

  • Инструмент погоды на Open-Meteo Previous Runs API: для каждого часа берётся значение из прогноза, выпущенного за 1 день (сутки D+1) и за 2 дня (сутки D+2) до этого часа. Момент выпуска — конец суток D (операционный дедлайн 23:59, когда диспетчер готовит заявку на следующие сутки), и это определение существенно: слой previous_day1 для часа 23:00 дня D+1 содержит прогноз, выпущенный около 23:00 дня D. Все использованные прогнозы принадлежат суткам D и известны к их концу, но не к их началу — поэтому выпуск датируется концом дня, а не полуночью. Фактическая погода не используется нигде.
  • Модель выработки по каждой турбине — смесь трёх компонентов: эмпирическая кривая мощности по прогнозной скорости ветра на 100 м, градиентный бустинг на признаках прогноза и двухэтапная схема (бустинг «признаки прогноза → скорость ветра на гондоле», затем измеренная кривая турбины «ветер гондолы → мощность»). Веса подобраны на честной валидации января 2026. Дополнительно считается интервал P10–P90 по квантилям остатков валидации, разбитым по лиду и уровню прогноза.
  • Агент с шестью инструментами (get_weather, check_input_quality, prepare_and_predict, analyze, recalculate, save_forecast), класс ForecastAgent в app/agent.py. Оркестрация через function calling OpenAI (gpt-5.4-mini на выбор инструментов, gpt-5.4 на заключение). Без ключа, в режиме DEMO_MODE и при любой ошибке провайдера тот же цикл выполняет детерминированный планировщик, что фиксируется в трассе как llm_fallback. Ошибка инструмента возвращается модели как результат вызова, поэтому модель исправляется сама, а не роняет выпуск.
  • Детектор обновления входных данных: прогноз на пересекающиеся часы сравнивается с выпуском предыдущего дня; при среднем изменении больше 0.15 или при штормовых часах агент выполняет повторный расчёт. Штормовые часы (≥ 22 м/с) обнуляются — буревая защита турбины.
  • Контроль качества входа и отказ от выпуска: прогноз по испорченным данным внешне неотличим от исправного, поэтому вход проверяется отдельным шагом — полнота горизонта, разрывы часовой сетки, пропуски, физически невозможные значения, застрявший датчик, подозрительно ровный ряд. Вердикт fail останавливает выпуск, warn публикует его с пониженной достоверностью. Это и отличает агента от конвейера: воспроизводимый эксперимент scripts/degradation_experiment.py прогоняет оба контура по шести сценариям и показывает, что жёсткий конвейер молча публикует все шесть — в том числе выпуск с 26 строками NaN при 13 часах пропусков и ровный прогноз с КИУМ 0.84 на застрявшем датчике. Агент на тех же данных четырежды отказывает в публикации (missing_values, stuck_sensor, negative_wind, incomplete_horizon) и один раз ловит аномалию пересчётом. Равномерный сдвиг всего поля ветра контролем входа не ловится принципиально — он физически правдоподобен, и его перехватывает второй слой, сравнение с прошлым выпуском. Разбор — в docs/root-analysis/03-agency.md.
  • Бэктест «как в прошлом»: 30 последовательных выпусков с 29.01 по 27.02.2026, итоговый почасовой ряд февраля в outputs/forecast_feb2026.csv, журнал действий агента по каждому дню в outputs/agent_logs/. Задание требует выпуск «на 31 января — прогноз на следующие 24–48 часов»; чтобы сами часы 31 января были закрыты при любой трактовке отсчёта, выпуски начинаются с 29 января. Побочный результат: 1 февраля получает прогноз и на сутки, и на двое суток, поэтому в итоговом файле нет ни одного пропуска.
  • CLI, HTTP-сервис, тесты, контрольный скрипт для жюри и кэш всех внешних запросов, позволяющий запускать решение без сети.

Как работает решение

Цикл одного выпуска (app/agent.py):

  1. get_weather(issue_date) → 48 часов прогноза по координатам ВЭС из выпусков, сделанных в течение суток issue_date и известных к их концу; возвращает диапазон дат, разбивку по лидам, минимум, среднее и максимум ветра и температуры.
  2. check_input_quality() → проверка полученного кадра: полнота горизонта и непрерывность часовой сетки, пропуски, отрицательная скорость и значения выше физического предела 40 м/с, застрявшее значение (6 часов подряд одинаковой скорости), разброс ниже 0.3 м/с, температура вне −60…60 °C. Вердикт ok / warn / fail; при fail расчёт не запускается и выпуск не публикуется.
  3. prepare_and_predict() → признаки и прогноз по турбинам 1 и 2, значения 0..1 (нормализованная мощность), интервал P10–P90.
  4. analyze() → часы штиля (< 3 м/с), часы номинала (≥ 11.5 м/с), штормовые часы (≥ 22 м/с), разброс между турбинами, среднее и максимальное изменение относительно вчерашнего выпуска на пересекающихся часах, флаг needs_recalculation.
  5. recalculate(reason) → повторный опрос источника, пересчёт модели и обнуление штормовых часов. В результате фиксируется числовой триггер (trigger, trigger_detail, trigger_values) — что именно сработало и с какими значениями, — формулировка модели reason_passed и input_changed. В ретроспективе input_changed всегда false: повторный опрос архива на прошедшую дату возвращает тот же срез, о чём в журнале стоит прямое пояснение note; в промышленном режиме здесь был бы новый выпуск прогноза погоды.
  6. save_forecast() → outputs/forecasts/<дата>.csv; журнал outputs/agent_logs/<дата>.json с трассой инструментов, анализом, вердиктом качества и заключением пишется в конце выпуска.

Порядок вызовов выбирает модель. Пример реальной трассы из outputs/agent_logs_llm/2026-02-15.json: get_weather → check_input_quality → prepare_and_predict → analyze → prepare_and_predict → recalculate → save_forecast — агент увидел обновление входных данных и сам инициировал пересчёт.

Режим оркестрации не влияет на числовой результат, и это измерено, а не заявлено. Скрипт scripts/run_llm_issues.py прогнал живую LLM-оркестрацию на всех 30 выпусках тестового периода: журналы в outputs/agent_logs_llm/, откатов на детерминированный план не было ни разу. Решения модели совпали с решениями планировщика во всех 30 выпусках — пересчёт в тех же четырёх днях (06.02, 15.02, 21.02, 24.02), выпуски побитово те же. Расход на весь прогон: 184 обращения к API, 187 тыс. входных и 6 тыс. выходных токенов, то есть 5–6 вызовов и около 4.5 тыс. prompt-токенов на выпуск. Канонические журналы в outputs/agent_logs/ — детерминированные: сдача воспроизводится без ключей.

Бэктест повторяет цикл для каждого дня тестового периода, передавая выпуску дня N результат дня N−1, затем собирает итоговый ряд: на каждый час февраля берётся самый свежий выпуск (lead 1) и отдельно выпуск за двое суток (lead 2).

Результаты

Честная валидация: обучение до 31.12.2025, проверка на январе 2026 по протоколу «прогноз как в прошлом» (30 выпусков, 720 часов на каждый горизонт). Мощность нормирована на 1, поэтому nMAE — это MAE в процентах от установленной мощности. Числа из docs/metrics.json.

ТурбинаГоризонтMAERMSEnMAE
124 ч0.14310.201514.31 %
148 ч0.16330.229616.33 %
224 ч0.14550.204114.55 %
248 ч0.16660.231616.66 %

Вклад компонентов на горизонте 24 ч (MAE, турбина 1 / турбина 2): кривая мощности 0.1471 / 0.1480, двухэтапная схема 0.1493 / 0.1526, градиентный бустинг «в лоб» 0.1580 / 0.1608, смесь 0.1431 / 0.1455. Подбор весов на валидации дал бустингу нулевой вес на обеих турбинах (турбина 1 — two_stage 0.5 и curve 0.5, турбина 2 — two_stage 0.4 и curve 0.6): прямое предсказание мощности проигрывает обоим физически осмысленным компонентам, а выигрыш даёт именно их усреднение. Объём обучения итоговых моделей — 31 078 и 33 710 часов.

Сравнение с эталонами (январь 2026, горизонт 24 ч, MAE, расчёт в docs/validation.md):

МетодТурбина 1Турбина 2
Модель0.14310.1455
Климатология по часу суток0.30030.3007
Константа (среднее по истории)0.30100.3017
Персистентность (последние полные сутки)0.35680.3463

Модель точнее персистентности на 58–60 % и климатологии на 52 %. Персистентность здесь хуже константы — это объясняет, почему лаги фактической выработки в признаки не вошли.

Два интервала неопределённости, калиброванные по-разному. Почасовой коридор P10–P90 строится по квантилям часовых остатков, разбитым по лиду и уровню прогноза: измеренное покрытие 79.4 % на горизонте 24 ч и 79.7 % на 48 ч при номинале 80 % (docs/validation.md, раздел 7).

Суточный коридор для заявки — отдельная величина, и складывать для него почасовые границы нельзя. Сумма квантилей неявно предполагает, что все 24 часа одновременно лежат на одной границе, то есть полную корреляцию ошибок: такой интервал покрывал 93.3 % при ширине 10.13 норм·ч против средней суточной выработки 7.37. Поэтому квантили суточной энергии калиброваны напрямую, на остатках суточной энергии за январь (scripts/calibrate_daily.py, данные в docs/demo/daily-interval.js):

Горизонтq10q90ШиринаПокрытие (исключение суток)Было: сумма почасовых границ
сутки вперёд−2.554+4.3976.81 норм·ч73.3 %93.3 % при ширине 10.13
двое суток−2.617+4.2516.71 норм·ч76.7 %93.3 % при ширине 12.20

Интервал уже прежнего в 1.5–1.8 раза и асимметричен: факт чаще превышает прогноз в хвосте — модель недооценивает выработку в самые ветреные сутки. Оговорки: выборка 30 суток на лид даёт стандартную ошибку покрытия около 7 пунктов, внутривыборочная оценка 80.0 % завышена по построению, и к истине ближе указанные 73–77 %. Разбор — docs/root-analysis/05-interval-calibration.md.

Проверка на утечку факта: прогнозная скорость ветра на 100 м против факта с гондолы за январь 2026 — корреляция 0.741, MAE 1.91 м/с, смещение −0.93 м/с. Это обычный суточный прогноз, а не переодетый факт. Подстановка фактического ветра в измеренную кривую турбины даёт MAE 0.032, то есть 78 % нынешней ошибки создаёт сам прогноз погоды и только 22 % — модель турбины.

Валидация января 2026 Прогноз на февраль 2026

Что показал разбор корня задачи

Прежде чем улучшать решение, мы разобрали, где в этой задаче вообще лежит ошибка и что её ограничивает. Пять разборов с численными проверками — в docs/root-analysis/; ниже выводы, включая отрицательные: они показывают, что очевидные улучшения проверены, а не пропущены.

  • 78 % ошибки создаёт прогноз погоды, а не модель турбины. Подстановка фактической скорости ветра в измеренную кривую даёт MAE 0.032 против 0.143 у полного контура. Связь почти линейна: 1 м/с ошибки прогноза ветра стоит 6.3 пункта nMAE, и чтобы дойти до nMAE 10 %, нужен прогноз с ошибкой 1.2 м/с вместо нынешних 1.9. Это потолок для любой схемы «прогноз ветра → кривая мощности» (01-weather-input.md).
  • Мультимодельный ансамбль проверен и проиграл. Сравнили пять источников Open-Meteo: наш best_match не побеждён ничем — ИИ-модель ECMWF AIFS хуже на 2.7 %, среднее ветров на 7.7 %, ICON на 19.9 %, GFS на 48.1 %. Причина в том, что best_match уже отобранный продукт: в этой точке он совпадает с ICON в 75.3 % часов, разнообразие мнимое. Линейная калибровка ветра тоже отвергнута — она улучшает ветер (MAE 1.913 → 1.791), но ухудшает мощность (0.1517 → 0.1586), потому что сжимает разброс, а кривая нелинейна.
  • MAE оказалась правильной метрикой именно для рынка РК. Цена положительного дисбаланса — контрактная с повышающим коэффициентом, отрицательного — с понижающим; при зеркальных коэффициентах потери симметричны, а при симметрии оптимум даёт медиана, то есть минимизация MAE эквивалентна минимизации денег. Проверено и обратное: при асимметрии цен 2:1 оптимальная заявка снижает стоимость на 15 %, но увеличивает физический объём небаланса на 3.1 % (02-objective-function.md).
  • Фильтровать простои вредно. Исключение часов с недобором мощности поднимает nMAE с 14.31 до 15.26 %: оценивают нас по фактической выработке с учётом недоступности, а отфильтрованная модель предсказывает доступную мощность — другую величину. Знание плановых остановов дало бы 1.6–2.3 пункта, но требует журнала SCADA (04-data-and-operations.md).
  • Агентность в ретроспективе вырождается, и мы это исправили. На архиве прошлого повторный опрос источника возвращает тот же срез, поэтому пересчёт не может изменить числа: расхождение с конвейером ровно 0.0000000000 во всех выпусках. Единственное место, где агент объективно сильнее жёсткого конвейера, — право заблокировать ненадёжный выпуск. Отсюда шестой инструмент и эксперимент с деградацией входа (03-agency.md).

Технологии

Python 3.14 (эталонное окружение, на нём выполнены все замеры; в Dockerfile указан python:3.11-slim, но Docker на машине сборки не устанавливался, поэтому контейнерный путь не проверялся), pandas, scikit-learn (HistGradientBoosting), httpx, OpenAI SDK, FastAPI, matplotlib, pytest, Docker. Внешние сервисы: Open-Meteo Previous Runs API и Historical Forecast API (открытые, без ключа), OpenAI API. Резервный провайдер LLM — NVIDIA NIM через совместимый с OpenAI эндпоинт, переключается переменной LLM_PROVIDER=nvidia; вживую этот путь не проверялся, ключа NVIDIA у команды не было.

Архитектура

data/raw/*.csv ──► app/data.py (часовая агрегация)
                                        │
Open-Meteo ──► app/weather.py (архив прогнозов, кэш) ──► app/features.py ──► app/model.py
                                        │                    (кривая + GBM + двухэтапная схема, P10–P90)
                                        │                                          │
                              app/agent.py: get_weather → check_input_quality → prepare_and_predict → analyze → [recalculate] → save_forecast
                                        │           оркестратор: LLM function calling или детерминированный план
                              app/backtest.py (30 выпусков) ──► outputs/forecast_feb2026.csv, outputs/agent_logs/
                                        │
                              app/cli.py (train | forecast | backtest | report), app/serve.py (HTTP)

Подробнее: docs/architecture.md, устройство агентного контура — docs/agent-design.md, решения с обоснованием — docs/adr/.

Установка

Системные требования: Python 3.12 или новее (эталон — 3.14.2), около 400 МБ под зависимости и 250 МБ под репозиторий с данными и кэшем. Сеть нужна только для pip install и для выпусков на даты вне кэша. ОС любая: замеры выполнены на Windows 11, контрольный сценарий проверен и в bash, и в PowerShell.

git clone https://github.com/BAITC-Hacks/hack-c63c1350-technokod.git
cd hack-c63c1350-technokod
python -m venv .venv
# Windows: .venv\Scripts\activate      Linux/macOS: source .venv/bin/activate
pip install -r requirements.lock.txt   # эталонные версии; гибкая установка — requirements.txt
cp .env.example .env        # Windows: copy .env.example .env
python scripts/check_env.py

Зависимости

Два файла на выбор:

ФайлКогда использовать
requirements.lock.txtДля сверки чисел. 52 пакета с точными версиями эталонного прогона: именно на них получены метрики в docs/metrics.json и артефакты в outputs/. Версия scikit-learn критична — формат pickle обученных моделей привязан к ней
requirements.txtДля обычной работы: прямые зависимости с гибкими нижними границами

Прямые зависимости: pandas, scikit-learn, httpx, python-dotenv, pydantic, openai, fastapi, uvicorn, matplotlib, joblib, duckdb, pypdf, rich, pytest.

Параметры окружения

Задаются в .env (файл в .gitignore, в репозиторий не попадает), образец — .env.example:

ПеременнаяНазначениеПо умолчанию
LLM_PROVIDERПровайдер оркестрации: openai, nvidia или demoopenai
OPENAI_API_KEYКлюч OpenAI; без него работает детерминированный планировщикпусто
OPENAI_MODELМодель выбора инструментовgpt-5.4-mini
OPENAI_REASONING_MODELМодель для заключенияgpt-5.4
NVIDIA_API_KEY, NVIDIA_BASE_URL, NVIDIA_MODELРезервный провайдер через OpenAI-совместимый эндпоинтпусто
DEMO_MODEПринудительно детерминированный режим без обращений к провайдеруfalse
APP_HOST, APP_PORTАдрес и порт HTTP-сервиса0.0.0.0, 8000

Для полного контрольного сценария ключи не нужны: числовой прогноз считает обученная модель, а LLM отвечает только за порядок вызова инструментов и текст заключения.

Docker (.env должен существовать, его требует docker-compose.yml; этот путь не проверялся — Docker на машине сборки не устанавливался):

docker compose run --rm app bash scripts/demo.sh   # контрольный сценарий целиком, включая обучение
docker compose up                                  # HTTP-сервис на порту 8000

В docker-compose.yml смонтирована только ./data, поэтому обученные модели и результаты остаются внутри контейнера и между отдельными запусками не сохраняются. Готовый блок volumes для models/ и outputs/ приведён в docs/runbook.md.

Запуск

python -m app.cli check                        # проверка окружения, данных, моделей, кэша
python -m app.cli train                        # обучение моделей, около 15 секунд, создаёт models/
python -m app.cli forecast --date 2026-01-31   # выпуск агентом (с LLM, если задан ключ)
python -m app.cli forecast --live              # ОПЕРАТИВНЫЙ выпуск на завтра по текущему прогнозу
python -m app.cli forecast --date 2026-01-31 --no-llm
python -m app.cli backtest --no-llm            # все выпуски 29.01–27.02.2026
python -m app.cli report                       # сводка по метрикам и артефактам
python -m app.cli fetch-weather --start 2026-01-29 --end 2026-02-27   # прогрев кэша

Вспомогательные скрипты:

python scripts/degradation_experiment.py   # агент против конвейера на шести деградациях входа
python scripts/run_llm_issues.py           # живая LLM-оркестрация на всех выпусках (нужен ключ)
python scripts/calibrate_daily.py          # калибровка интервала суточной энергии
python scripts/make_dashboard_data.py      # пересборка данных дашборда из артефактов
python scripts/make_charts.py              # графики в assets/ из артефактов

HTTP-сервис (python -m app.serve, порт из APP_PORT): GET /health, GET /forecast/{issue_date}?horizon=48&llm=false, GET /submission. Если контроль качества входа даёт вердикт fail, /forecast возвращает 422 с причиной отказа и находками, а не пустой прогноз.

Оперативный режим: прогноз на завтра, а не только воспроизведение прошлого

Тестовый протокол кейса требует воспроизвести выпуски февраля 2026 по архивным прогнозам. Но то же решение работает и на сегодняшнюю дату: python -m app.cli forecast --live берёт текущий прогон Open-Meteo Forecast API по тем же координатам и выпускает прогноз на следующие двое суток.

Разница только в источнике входа, всё остальное общее — те же признаки, та же модель, тот же агентный цикл с контролем качества:

РежимИсточник погодыКогда применим
Ретроспектива (--date)Previous Runs API, архивные слои «что предсказывали сутки и двое назад»воспроизведение прошлого, оценка качества на известном факте
Оперативный (--live)Forecast API, текущий прогон модели погодырабочий выпуск на завтра

Архивные слои для будущего не существуют — они появляются только когда прогноз становится прошлым, поэтому для работы на сегодня нужен именно текущий прогон. Проверено 23.09.2026: выпуск покрыл 24–25 сентября, 96 строк, полная трасса get_weather → check_input_quality → prepare_and_predict → analyze → save_forecast, 8 часов штиля, ожидаемая выработка 8.5 и 9.0 норм·ч по турбинам.

Оперативные выпуски в репозиторий не коммитятся: сдаваемые артефакты — только февральские, чтобы их не смешивать.

Как проверить решение

Полный контрольный сценарий одной командой: bash scripts/demo.sh (Linux/macOS/Git Bash) или powershell -File scripts/demo.ps1 (Windows). Скрипт сам выбирает интерпретатор (PYTHON, затем .venv, затем python из PATH), останавливается на первой ошибке и выполняет семь шагов: проверка окружения → обучение (пропускается, если модели уже есть) → выпуск прогноза → бэктест февраля → сводка → автотесты → живая LLM-оркестрация (пропускается, если ключ не задан). Замерено на обученных моделях: 27 секунд в bash и 24 в PowerShell, оба прогона с кодом возврата 0; на чистом клоне с GitHub — 38 секунд вместе с обучением. Сеть не требуется: архивные прогнозы лежат в data/cache/weather/ (223 файла, 6.9 МБ; часть файлов обеспечивает воспроизводимость проверок комиссии из docs/root-analysis/ — валидацию на ноябре и декабре 2025, калибровку суточного интервала и сравнение пяти погодных моделей).

Две детали, которые важны на чужой машине. Тесты запускаются с --basetemp=.pytest_cache/tmp: системный временный каталог pytest на Windows может быть недоступен по правам, и шаг падал бы не по вине решения. Седьмой шаг после прогона с LLM восстанавливает канонический журнал детерминированным выпуском — иначе проверка с ключом переписала бы артефакт outputs/agent_logs/2026-02-15.json.

На свежем клоне первый прогон сценария занимает 38 секунд, из них 14 секунд — обучение (отдельно от этого установка зависимостей pip install идёт 1–2 минуты): файлы models/*.joblib не коммитятся, потому что pickle привязан к версии scikit-learn и на другой машине не загрузится. Если модели уже обучены, шаг 2 пропускается и сценарий проходит за 20–30 секунд.

Ожидаемый результат:

  • python -m app.cli train печатает по каждой турбине веса смеси и ошибку на январе 2026: nMAE 14.31 % и 14.55 % на горизонте 24 ч, 16.33 % и 16.66 % на 48 ч (docs/metrics.json).
  • python -m app.cli forecast --date 2026-01-31 --no-llm печатает трассу get_weather → check_input_quality → prepare_and_predict → analyze → save_forecast, анализ и заключение; появляется outputs/forecasts/2026-01-31.csv на 96 строк (48 часов × 2 турбины, 13 колонок).
  • python -m app.cli backtest --no-llm создаёт 30 файлов выпусков, outputs/forecast_all_issues.csv на 2 880 строк, outputs/forecast_feb2026.csv на 672 часа без единого пропуска и outputs/backtest_summary.csv на 30 строк. Агент выполнил повторный расчёт в четыре дня: 06.02, 15.02, 21.02, 24.02.
  • python -m pytest -q tests → 22 теста проходят примерно за 4 секунды: агрегация часов, честность выборки прогноза, признаки, выпуск агента, сборка итогового ряда февраля, буревое обнуление, откат при отказе LLM, границы и ширина интервала, вердикты контроля качества на пяти деградациях входа, блокировка публикации при fail, отказ сохранять прогноз с NaN, явный признак отсутствия вчерашнего выпуска и числовой триггер пересчёта.

Проверка LLM-оркестрации: задать OPENAI_API_KEY в .env и выполнить python -m app.cli forecast --date 2026-02-15. Порядок вызова инструментов выбирает модель, заключение пишет она же. Журналы всех 30 таких выпусков уже лежат в репозитории (outputs/agent_logs_llm/, по одному на каждую дату с 29.01 по 27.02) — их можно читать без ключа и сравнивать с детерминированными журналами в outputs/agent_logs/: решения совпадают во всех 30 выпусках. Повторить прогон целиком — python scripts/run_llm_issues.py (нужен ключ). При ошибке сети или ключа агент откатывается на детерминированный план, это фиксируется в трассе как llm_fallback и покрыто тестом.

Проверка отказа от выпуска на плохих данных: python scripts/degradation_experiment.py — печатает таблицу «сценарий → конвейер → агент» по шести деградациям входа, ключи и сеть не нужны, артефакты в outputs/ не затрагиваются.

Проверка честности прогноза (ключевое требование кейса) описана отдельным разделом в docs/test-plan.md, численное доказательство — в docs/validation.md.

Выходные файлы

ФайлСодержание
outputs/forecast_feb2026.csvИтоговый почасовой ряд февраля 2026, 672 строки, 13 колонок: ts, turbine_1_lead1, turbine_2_lead1, turbine_1_lead2, turbine_2_lead2, turbine_1_p10, turbine_2_p10, turbine_1_p90, turbine_2_p90, farm_mean_lead1, farm_mean_lead2, farm_p10_lead1, farm_p90_lead1. Станция — среднее долей номинала двух турбин, то есть в тех же единицах
outputs/forecast_all_issues.csvВсе 30 выпусков по часам, 2 880 строк: issue_date, ts, horizon_hour (1–48), lead_day, turbine, power_norm_pred, power_gbm_pred, power_curve_pred, power_p10, power_p90, power_two_stage_pred, wind_speed_100m, wind_nacelle_pred
outputs/backtest_summary.csvСводка по выпускам, 30 строк: часы, отметки пересчёта и режима LLM, часы штиля, номинала и шторма, средний КИУМ по турбинам, изменение относительно прошлого выпуска
outputs/forecasts/<дата>.csvОтдельный выпуск, 96 строк
outputs/agent_logs/<дата>.jsonЖурнал выпуска: issue_date, llm_used, published, confidence, quality (вердикт контроля входа и перечень находок), recalculated, analysis, conclusion, trace
outputs/agent_logs_llm/<дата>.jsonЖурналы всех 30 выпусков с живой LLM-оркестрацией, по одному на дату

Значения — нормализованная мощность 0..1, как в исходных данных; время местное (Алматы). Пропусков в итоговом файле нет: выпуски начинаются с 29 января, поэтому каждый час февраля закрыт и прогнозом на сутки, и прогнозом на двое суток. Перевод в МВт·ч описан в docs/user-guide.md.

Документация

ДокументО чём
docs/architecture.mdАрхитектура, контракты данных, масштабирование
docs/agent-design.mdАгентный контур: инструменты, промпт, пороги, журнал, бюджет LLM
docs/adr/Восемь архитектурных решений с обоснованием и последствиями
docs/model-card.mdКарточка модели: данные, признаки, метрики, ограничения
docs/validation.mdПротокол честной валидации, доказательство отсутствия утечки, эталоны
docs/data-quality.mdКачество исходных данных: разрывы, выбросы, кривая мощности
docs/analysis.mdРазбор ТЗ и первичный анализ данных
docs/metrics.md, docs/metrics.jsonМетрики в читаемом виде и машинном формате
docs/business-value.mdЦенность для эксплуатирующей организации, расчёт эффекта
docs/pitch.mdСценарий защиты и ответы на вопросы
docs/user-guide.mdРуководство пользователя: установка, команды, разбор выходных файлов
docs/runbook.mdРегламент эксплуатации, мониторинг, реакции на отказы
docs/test-plan.mdПрограмма и методика испытаний с критериями приёмки
docs/compliance.mdСоответствие Положению: хронология коммитов, лицензии, раскрытие ИИ
docs/api.mdСправочник интерфейсов: HTTP-сервис и команды CLI с примерами ответов
docs/root-analysis/Пять разборов корня задачи: метеорологический вход и физический потолок точности (01), целевая функция и рынок небалансов (02), смысл агентности (03), данные и эксплуатация станции (04), калибровка интервалов (05)
docs/tz-checklist.mdТребования ТЗ по подзадачам: как исполнено, чем доказывается, самооценка по критериям
docs/audit.mdЖурнал независимых проверок «обещано против сделано» с историей прогонов
docs/submission-checklist.mdТребования Положения хакатона и что чем закрыто
docs/roadmap.mdПлан работ и хронология этапов
docs/brand.mdФирменный стиль: палитра с проверкой контраста, типографика, знак
docs/index.mdКарта документации и маршруты чтения

Данные и интеграции

  • data/raw/turbine_1.csv, data/raw/turbine_2.csv — данные организаторов, 10-минутные записи 11.03.2023–31.01.2026 (скорость ветра с гондолы, нормализованная мощность, температура). Разбор в docs/analysis.md и docs/data-quality.md.
  • Open-Meteo Previous Runs API (previous-runs-api.open-meteo.com) — архивные прогнозы, покрытие с 16.02.2024. Обучение и тест идут на них.
  • Open-Meteo Historical Forecast API — использовался при разведке данных, в итоговую модель не входит.
  • OpenAI API — оркестрация агента и заключения. Ключи только через переменные окружения, .env в .gitignore.
  • data/cache/weather/ — кэш архивных прогнозов, закоммичен ради запуска без сети.

Часовые пояса: данные ВЭС в местном времени, погода запрашивается с timezone=Asia/Almaty, стыковка по локальному часу (переход Казахстана на UTC+5 в марте 2024 учитывается источником погоды).

Стороннее и подготовленное до старта

Раскрытие по п. 5.4.4, 5.4.4.2 и 5.4.12 Положения, подробная хронология — в docs/compliance.md.

Коммиты f992796 и 573e6b8 (13:24 по времени Астаны) содержат инфраструктурную заготовку, подготовленную до начала хакатона: структура папок, .gitignore, .env.example, requirements.txt, Dockerfile, docker-compose.yml, app/config.py, app/llm.py (обёртка над OpenAI SDK), scripts/check_env.py, tests/test_smoke.py. Зафиксировать её раньше было физически нельзя: репозиторий команды выдан после старта. Логики кейса в заготовке нет, это проверяется командой git show f992796.

Вся предметная логика — загрузка и агрегация данных ВЭС, инструмент архивных прогнозов, признаки, модель, агент, бэктест, CLI, сервис — написана после старта, начиная с коммита b2b3b5b, и прослеживается по истории.

Open-source: библиотеки из requirements.txt (BSD, MIT, Apache-2.0; перечень с лицензиями в docs/compliance.md). Данные: организаторов и Open-Meteo (CC BY 4.0). Разработка велась с применением ИИ-агентов для генерации и анализа кода (п. 5.4.12 Положения); архитектурные решения, выбор источника данных и протокол валидации приняты командой, результаты проверены автотестами и численными экспериментами.

Ограничения

  • Нулевой вес градиентного бустинга — не ошибка, а результат честного подбора. Прямое предсказание мощности проигрывает обоим физически осмысленным компонентам (MAE 0.158 против 0.147 у кривой и 0.149 у двухэтапной схемы), поэтому в рабочем прогнозе участвуют два компонента из трёх. Бустинг оставлен в модели как контрольный: он обучается и считается при каждом выпуске, его ошибка видна в docs/metrics.json, а на площадке, где физическая кривая работает хуже (другая турбина, другой рельеф, ограничения по мощности), подбор весов даст ему ненулевой вклад без изменения кода.

  • Погода запрашивается одной точкой на обе турбины — серединой между ними. Координаты из задания (43.645150, 78.535604 и 43.643198, 78.538828) разделяют 400 метров, это глубоко внутри одной ячейки метеосетки: любой глобальный источник вернёт по ним идентичный прогноз, а лишний запрос удвоил бы нагрузку на бесплатный API без единого нового числа. Различаются турбины не погодой, а собственными кривыми мощности и простоями — поэтому модель обучается для каждой отдельно. Обоснование в docs/adr/0007-one-weather-point-two-models.md.

  • Архив Previous Runs начинается с 16.02.2024, поэтому по календарю обучение охватывает 23 месяца, а не всю историю с марта 2023 года. Модель учится на парах «архивный прогноз → факт», и для компонентов, зависящих от прогноза, раннего архива не существует ни у кого. Строк при этом больше, а не меньше: на каждый час приходится два примера (прогноз за сутки и за двое), итого 31 078 и 33 710 против 23 тысяч часов факта.

    Данные до 16.02.2024 в проекте используются — на них построены анализ качества данных, статистика простоев, оценка затенения по направлениям ветра и эталон климатологии. Не используются они только в обучении, и мы проверили четыре способа это изменить (протокол парных сравнений, три месяца, docs/root-analysis/04-data-and-operations.md):

    Способ задействовать историю до 16.02.2024Результат
    Кривая «ветер гондолы → мощность» на всей истории14.33 против 14.31 % — в пределах шума
    Взвешивание по давности (полураспад 6, 12, 24 мес.)ноль, максимум 0.08 пункта, знак разный по турбинам
    Кривая по холодным месяцам всей историивыигрыш 0.02–0.36 пункта, но контрольный опыт снял его: та же кривая внутри архивного периода, без единой старой точки, даёт то же и местами лучше
    Гибрид: верх кривой из полной истории, низ из свежего периоданоль, ±0.02 пункта

    Причина: медианы по бинам скорости расходятся между полной историей и архивным периодом не более чем на 0.008 во всём рабочем диапазоне, а редкие бины выше 12 м/с лежат там, где кривая уже на полке 0.97–0.99. Выборка в критичной зоне 3–12 м/с насыщена — 960–1500 часов на бин, удвоение объёма медиану не двигает.

  • Факт выработки за февраль 2026 участникам не выдан, поэтому качество на тесте не измерялось. Все приведённые метрики относятся к январю 2026 и получены по тому же протоколу «прогноз как в прошлом».

  • Январь 2026 оказался лёгким месяцем. Тот же контур по тому же протоколу даёт nMAE 16.2 % на ноябре 2025 и 19.2–19.7 % на декабре 2025 против 14.3–14.6 % на январе. Стандартная ошибка оценки на 720 часах — около 1 пункта (блочный бутстрап по суткам), то есть 14.31 % это результат на одном месяце, а не характеристика модели. Январь выбран не нами: это последний месяц перед тестовым периодом. Разбор в docs/root-analysis/04-data-and-operations.md.

  • Потолок точности задаёт сам прогноз погоды: на идеальном ветре ошибка модели турбины составляет 0.032 MAE против нынешних 0.143. Дальнейший выигрыш даст ансамбль метеомоделей или коммерческий источник прогноза, а не усложнение модели турбины.

  • Кривая мощности не учитывает плановые остановы и ограничения по команде диспетчера: в выданных данных нет журнала простоев. При внедрении нужны события SCADA.

  • Оба интервала неопределённости построены на остатках января 2026 и на новых данных требуют перекалибровки. Почасовой покрывает 79.4 и 79.7 % при номинале 80 %, суточный — 73–77 % по проверке с исключением суток: выборка в 30 суток даёт стандартную ошибку покрытия около 7 пунктов, то есть суточный интервал слегка недопокрывает номинал. Январь — месяц низкого КИУМ, на ветреном месяце калибровку нужно повторить. Промышленный вариант — блочный conformal на отдельной калибровочной выборке (docs/root-analysis/05-interval-calibration.md).

  • В бэктесте по архиву повторный опрос источника возвращает тот же срез (у архива суточная гранулярность), поэтому пересчёт меняет результат только через буревую защиту. В промышленной эксплуатации агент должен запускаться при каждом новом выпуске прогноза погоды, 4 раза в сутки; вызова оперативного API в коде нет.

  • Путь NVIDIA NIM реализован переключением провайдера, но вживую не проверялся.

  • Workflow .github/workflows/ci.yml настроен и корректен, но в этом репозитории не выполняется: организация-владелец заблокирована по биллингу, GitHub отвечает «The job was not started because your account is locked due to a billing issue». Поэтому бейджа сборки в шапке нет — красный значок вводил бы в заблуждение, а проверка выполняется локально контрольным сценарием.

  • LLM-оркестрация зависит от внешнего API и лимитов, поэтому предусмотрен откат на детерминированный план; числовой результат от режима не зависит.

Развёрнутая версия

https://hack.technokod.kz — дашборд диспетчера: выбор даты выпуска из всех выпусков бэктеста, горизонт 24 или 48 часов, почасовой прогноз станции и обеих турбин с коридором P10–P90 и вчерашним выпуском для сравнения, панель трассы агента с вердиктом контроля входа и причиной пересчёта, сводка для заявки на рынок и блок надёжности по январю 2026. Методика решения — на /about. Статика на Cloudflare Workers, от рабочей машины не зависит, ключи не требуются. Исходник страницы — docs/index.html, данные — docs/demo/dashboard-data.js (собирается scripts/make_dashboard_data.py), docs/demo/validation-data.js (scripts/make_validation_data.py) и docs/demo/daily-interval.js (scripts/calibrate_daily.py), выкат командой npx wrangler deploy по конфигурации wrangler.jsonc. Страница не делает ни одного внешнего запроса: данные подключаются обычными <script>, поэтому она открывается и по ссылке, и прямо из файла.

Само решение остаётся исполняемым из репозитория по инструкции выше: развёрнутого промышленного сервиса прогнозирования нет, страница показывает результат его работы.

Куда развивать

Потолок нынешней точности измерен: 78 % ошибки создаёт сам прогноз погоды, а не модель турбины, и 1 м/с ошибки прогноза ветра стоит 6.3 пункта nMAE. Поэтому развитие идёт не в подбор гиперпараметров, а в данные, которых сейчас нет.

  1. Телеметрия ветроагрегатов — поток SCADA: флаг доступности, уставки ограничений, обороты и угол лопастей, температуры узлов, вибрации. Снимает неопределённость, которую сегодня приходится восстанавливать статистически.
  2. Учёт технического обслуживания — график регламентных работ, наряды, журнал отказов. Плановый ремонт это известное заранее обнуление выработки; его учёт даёт измеренные 1.6–2.3 пункта nMAE.
  3. Прогнозирование на телеметрии и обслуживании — выработка с учётом состояния парка и деградации кривых, прогноз доступности, предиктивное обслуживание, оптимизация заявки на рынок.

Подробно, с числами и оценкой каждого шага: docs/product-roadmap.md.

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

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

m34959203 56

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

  • 2026-09-23 17:58docs: в карту документации добавлены дорожная карта, чек-лист ТЗ и фирменный стильm34959203
  • 2026-09-23 17:52fix: на свежем клоне падали 9 тестов — pytest не создаёт родителя для --basetempm34959203
  • 2026-09-23 17:41docs: скриншоты дашборда в README и дорожная карта развития продуктаm34959203
  • 2026-09-23 17:36fix: подсказки открываются после задержки удержания, а не при любом движении мышиm34959203
  • 2026-09-23 17:34style: убран бейдж «Данные актуальны», строка управления в одну линиюm34959203
  • 2026-09-23 17:32feat: единое поле даты вместо списка выпусков и переключателя режимовm34959203
  • 2026-09-23 17:25style: блок ограничений свёрнут до одной строкиm34959203
  • 2026-09-23 17:24fix: в строке управления показывались оба поля выпуска сразуm34959203
Все коммиты (56)
  • 2026-09-23 17:22feat: выбор любой даты с прогнозом на 24–48 часов вместо диапазона периодаm34959203
  • 2026-09-23 17:17fix: убрана пустота в правой колонке после сворачивания трассыm34959203
  • 2026-09-23 17:14fix: ветер из Open-Meteo приходил в км/ч и принимался за м/с; панель агента перестроена под диспетчераm34959203
  • 2026-09-23 17:09feat: произвольная дата и диапазон — витрина считает прогноз самаm34959203
  • 2026-09-23 17:01feat: экспорт кривых мощности в JS для расчёта произвольных дат на витринеm34959203
  • 2026-09-23 16:56docs: в подсказке выпуска объяснён диапазон дат и как получить прогноз на другую датуm34959203
  • 2026-09-23 16:55feat: пояснения к показателям и исправления контраста по UI-аудитуm34959203
  • 2026-09-23 16:49feat: оперативный режим — прогноз на завтра по текущему прогону погодыm34959203
  • 2026-09-23 16:44fix: три дефекта UX-аудита, дававшие противоречивые числа на экранеm34959203
  • 2026-09-23 16:38docs: README сведён с финальным состоянием и дополнен разделами по 5.4.15m34959203
  • 2026-09-23 16:37fix: отпечаток модели вместо коммита репозитория, аудиторский след в паспорт выпускаm34959203
  • 2026-09-23 16:33fix: суточный коридор калиброван отдельно, история до 16.02.2024 проверена четырьмя способамиm34959203
  • 2026-09-23 16:30fix: момент выпуска определён как конец суток D, а не полночьm34959203
  • 2026-09-23 16:09docs: чек-лист по ТЗ и ответы на три вероятных вопроса жюриm34959203
  • 2026-09-23 15:54feat: живая LLM-оркестрация на всех 30 выпусках вместо трёх показательныхm34959203
  • 2026-09-23 15:46feat: усилие ветра в анимации — поток, направление и режимы по порогам агентаm34959203
  • 2026-09-23 15:37feat: роторы турбин вращаются со скоростью, пропорциональной прогнозуm34959203
  • 2026-09-23 15:30style: дашборд переверстан по макету заказчикаm34959203
  • 2026-09-23 15:21fix: HTTP-сервис отдаёт 422 с причиной вместо падения при забракованном входеm34959203
  • 2026-09-23 15:20docs: документация сведена с кодом после контроля качества и сдвига бэктестаm34959203
  • 2026-09-23 15:16feat: контроль качества входных данных — агент отказывается публиковать прогноз на плохих данныхm34959203
  • 2026-09-23 15:07docs: разбор корня задачи комиссией экспертов; бэктест стартует с 29 январяm34959203
  • 2026-09-23 15:00feat: дашборд диспетчера на hack.technokod.kz, витрина решения уведена на /aboutm34959203
  • 2026-09-23 14:47test: добавлены проверки логики, правленной после первой сборкиm34959203
  • 2026-09-23 14:44feat: фирменный стиль WindAgent — знак, палитра, руководствоm34959203
  • 2026-09-23 14:43feat: сборщик данных дашборда и фирменные знакиm34959203
  • 2026-09-23 14:28docs: убран бейдж CI, который не может стать зелёнымm34959203
  • 2026-09-23 14:27fix: устранены расхождения в замерах и ориентирах качества между документамиm34959203
  • 2026-09-23 14:25docs: расчёт эффекта пересчитан на той же выборке, что и метрики моделиm34959203
  • 2026-09-23 14:22docs: в карту документации добавлены api, журнал аудита и исходник демо-страницыm34959203
  • 2026-09-23 14:19feat: демонстрационная страница результата и выкат на hack.technokod.kzm34959203
  • 2026-09-23 14:15chore: оформить репозиторий по стандарту и уточнить замеры по чистому клонуm34959203
  • 2026-09-23 14:13docs: привести документацию в соответствие с кодом и положением хакатонаm34959203
  • 2026-09-23 14:03Агент: один класс, устойчивая оркестрация, честный интервал; проектная документация; перегенерация артефактов февраляm34959203
  • 2026-09-23 13:48Локальное окружение и воспроизводимость: кэш архивных прогнозов в репозитории, метрики пересчитаны на чистой установкеm34959203
  • 2026-09-23 13:50Восстановлен рабочий агент (ForecastAgent), черновик WindAgent второго агента сохранён в docs/proposals, тесты и сервис снова проходятm34959203
  • 2026-09-23 13:49HTTP-сервис прогноза (FastAPI), запуск python -m app, Dockerfile запускает сервисm34959203
  • 2026-09-23 13:48Интервал P10–P90 из остатков валидации, бэктест с явной сеткой часов февраля, графики в READMEm34959203
  • 2026-09-23 13:45Оркестрация агента через app.llm.chat_tools и available(), .gitattributes для единых окончаний строкm34959203
  • 2026-09-23 13:45Двухэтапная модель (прогноз ветра гондолы + измеренная кривая), смесь трёх компонентов, метрики в docs/metrics.mdm34959203
  • 2026-09-23 13:42check_env: отсутствие ключа LLM не считается ошибкой, контрольный сценарий проходит без ключейm34959203
  • 2026-09-23 13:41README: описание решения, установка, проверка, данные, раскрытие подготовленного до старта, ограниченияm34959203
  • 2026-09-23 13:40Тесты основного сценария, контрольные скрипты demo.sh и demo.ps1, понятная ошибка без обученных моделейm34959203
  • 2026-09-23 13:39Агент прогнозирования (инструменты, анализ, пересчёт), бэктест по дням февраля 2026, CLIm34959203
  • 2026-09-23 13:37Признаки и модель выработки: GBM + кривая мощности, обучение на архивных прогнозах, честная валидация января 2026m34959203
  • 2026-09-23 13:32Анализ ТЗ и данных, дорожная карта, загрузчик ВЭС, инструмент архивных прогнозов Open-Meteom34959203
  • 2026-09-23 13:24check_env: запуск из любой директорииm34959203
  • 2026-09-23 13:24Каркас проекта до старта: структура, конфиг, обёртка LLM, проверка окруженияm34959203
Структура репозитория (427 файлов)
.editorconfig
.env.example
.gitattributes
.github/ISSUE_TEMPLATE/bug_report.md
.github/ISSUE_TEMPLATE/feature_request.md
.github/PULL_REQUEST_TEMPLATE.md
.github/workflows/ci.yml
.gitignore
CHANGELOG.md
CONTRIBUTING.md
Dockerfile
LICENSE
README.md
app/__init__.py
app/__main__.py
app/agent.py
app/backtest.py
app/cli.py
app/config.py
app/data.py
app/features.py
app/llm.py
app/model.py
app/serve.py
app/weather.py
assets/brand/apple-touch-icon-180.png
assets/brand/favicon-32.png
assets/brand/favicon.svg
assets/brand/logo-dark.svg
assets/brand/logo.svg
assets/brand/mark-dark.svg
assets/brand/mark.svg
assets/forecast_feb2026.png
assets/screens/agent-trace.jpg
assets/screens/dashboard.jpg
assets/screens/validation.jpg
assets/validation_jan2026.png
data/README.md
data/cache/weather/0066fcb72c1869f59bee23f7.json
data/cache/weather/00b23abe1dd5145ab024f45a.json
data/cache/weather/019f114ed167aa2a0ea12a87.json
data/cache/weather/03ebf7fbfd35851fafc09124.json
data/cache/weather/042b2d526965e5226f3311d6.json
data/cache/weather/06e40ed173b575d000a8bd4c.json
data/cache/weather/078d030a392108e4e93d4d4b.json
data/cache/weather/07e654925de79eaf3d4f6c48.json
data/cache/weather/08ffe114d4920d0a8e609742.json
data/cache/weather/0aa76478400edd13a1ba2915.json
data/cache/weather/0b3cc73c4934fb18e4a69812.json
data/cache/weather/0b6b900b630bec66897cad04.json
data/cache/weather/0c2d946b963958c4542a25e6.json
data/cache/weather/0dce2e965f6c4cc9c0e0317e.json
data/cache/weather/0e22092ce07d4be3a78928d9.json
data/cache/weather/0e4a786ba0f4b93241d83398.json
data/cache/weather/10582d9ace7bd0d69dc9eeda.json
data/cache/weather/1137c56e9c663867336b384e.json
data/cache/weather/1719cbd0aa756ec38683eb05.json
data/cache/weather/1885e3bde29f3a4b125f1164.json
data/cache/weather/1a619d9dafccd026cf02aa11.json
data/cache/weather/1a76578deee103c8f5d7ee62.json
data/cache/weather/1fa5c6834a4222dcb54ebed2.json
data/cache/weather/21114d7e9517c0a4b6c063e0.json
data/cache/weather/21a4c6d2d3d46f9ceb2ba2fa.json
data/cache/weather/2239224596eed6accda4069a.json
data/cache/weather/25946b68e867f13db9cd238d.json
data/cache/weather/26b58604efb81bd639747b7b.json
data/cache/weather/29a49fc02096facec53b0d3a.json
data/cache/weather/2aa50879765b935b019eebfa.json
data/cache/weather/2b15f63b5d6c7f0655eb5eca.json
data/cache/weather/2b4aa9efc363d173becc71df.json
data/cache/weather/2bbaf230ee95a517c874f155.json
data/cache/weather/2d356d05881c1417ad934667.json
data/cache/weather/2e308f8c08c53dd304a83021.json
data/cache/weather/2e576ca408b3e915f3b60949.json
data/cache/weather/2ff99016068ccdb9d7100999.json
data/cache/weather/329d8a74bdfaab2de6db5da4.json
data/cache/weather/3468d9703b0cd1ba76231428.json
data/cache/weather/3534d1fcbcd5c6c6e7ec5a7e.json
data/cache/weather/35f5da94ebf657c9be9e22dc.json
data/cache/weather/361b9cfbd59f1b6fd36699b5.json
data/cache/weather/369a928a6f4a44193781d3d7.json
data/cache/weather/36cb8e9f8aa74beea24752e2.json
data/cache/weather/37de0d8cdecd9c1d290a57be.json
data/cache/weather/3847bd04069788e8e588756c.json
data/cache/weather/38c9ae96d117b2b943d15f7c.json
data/cache/weather/38dd9bf03fe77d850a5ca340.json
data/cache/weather/39763bbfcc65e183188055bb.json
data/cache/weather/3b860db4c945e1e12d6aeb64.json
data/cache/weather/3c028c57253fa8fa86eea08e.json
data/cache/weather/3ff2c2cce0615ba1ad4f0edc.json
data/cache/weather/42d423fd38ffacd92e951fb9.json
data/cache/weather/453250d9793d2c6da89ce17a.json
data/cache/weather/458ff03c92550df70dd6f12b.json
data/cache/weather/472cf37488b6f7299c7bd845.json
data/cache/weather/4b09a14673672162b7ffe873.json
data/cache/weather/4bceb7fc382b38237129338f.json
data/cache/weather/505bce61f9cbe2bb64e9c6c1.json
data/cache/weather/50c3bbfa31c3a929a7f79948.json
data/cache/weather/512343e449adbf98303dc5e7.json
data/cache/weather/51cc567b3ea5ea160093a2ad.json
data/cache/weather/52988a917a155d506239c495.json
data/cache/weather/54c84375013c47339f6dae06.json
data/cache/weather/55da7936af7ccbd77beed084.json
data/cache/weather/567a889669bfa473395e981a.json
data/cache/weather/5831f88cf0829d2f048da161.json
data/cache/weather/5842cbc5d87a9c525a80682c.json
data/cache/weather/59a9b0ba55f64ce2af04ba06.json
data/cache/weather/5cd688fc9180e3bdd3082c7c.json
data/cache/weather/5e965a3b64a48f1a7f355765.json
data/cache/weather/5ede4d9cfe6af36e2cf819cc.json
data/cache/weather/5fd078bbf37b75d342f20df4.json
data/cache/weather/6209b07ef4a3dc5d77b3712e.json
data/cache/weather/624d1039caa455bfd9aa9b63.json
data/cache/weather/63d036786ee29aae402ede86.json
data/cache/weather/649bbacc3e0b0764406ad9cf.json
data/cache/weather/657262614fa91fa797af600b.json
data/cache/weather/6705ce7f35e3210eeef149d2.json
data/cache/weather/6922d38daa5118a1e87be39d.json
data/cache/weather/693c1da6b5e335234fe36c34.json
data/cache/weather/6bc73ad9c512814c25747c0e.json
data/cache/weather/70532ce524c0b147aa956e36.json
data/cache/weather/71763aba29e51bd38bbc7509.json
data/cache/weather/71975ae410e2d615ca9ce98a.json
data/cache/weather/72c2b0f3b932805bd10aa2aa.json
data/cache/weather/7474be30abbffe5f550ab688.json
data/cache/weather/7648aec51b0fd73b24737296.json
data/cache/weather/76b82cc5ff314f4311c52a37.json
data/cache/weather/7938d562d75339e0ed9f99a6.json
data/cache/weather/7a2f0d5e9d7ae968e76e23b3.json
data/cache/weather/7acd385d4d06cdfe96742b75.json
data/cache/weather/7b2946922d67d8397dd31858.json
data/cache/weather/7c61126df96741dda4c29e7c.json
data/cache/weather/7cbf0840c142343f221ccbdf.json
data/cache/weather/7d77aa2eff7cdada5030ba49.json
data/cache/weather/7e61d76c162f613035804652.json
data/cache/weather/7f2cabb2baccf6694452507b.json
data/cache/weather/7f50df5a62574f1af293a50e.json
data/cache/weather/80217832e8d96fb82c978224.json
data/cache/weather/810471fa29e45eebbae84ec2.json
data/cache/weather/8107fbe46ee6211fdccfc48a.json
data/cache/weather/82a20665812eed522f76cd4c.json
data/cache/weather/83b56616f9e6a5a83acd3dcc.json
data/cache/weather/84fa7f8cbb6d56d24148162f.json
data/cache/weather/85af0f74f655fd26b700d062.json
data/cache/weather/85e7228dc5b907e15eab4ee0.json
data/cache/weather/86446da40e6cb209491b2a79.json
data/cache/weather/86cbca1e26e7ffaeb2638932.json
data/cache/weather/87124fdb8dcc5d2691e37619.json
data/cache/weather/8745beb5a726137ea9c9ccbf.json
data/cache/weather/88174a6b4d9a34d4b2cf76da.json
data/cache/weather/8f4a0037d2ac1dda0c5184e6.json
data/cache/weather/906e55aae4668dca2636d09d.json
data/cache/weather/93f3439af00badcf018c9d0d.json
data/cache/weather/94176390bbf052f4e82500f8.json
data/cache/weather/942cdabf8e4fbb9afff90f56.json
data/cache/weather/95495dc3a9b6e964843039a7.json
data/cache/weather/9593597071a5631b67e89b1c.json
data/cache/weather/961578e07204799fc321c1fc.json
data/cache/weather/96570d551e733c74ec639e84.json
data/cache/weather/979eb6bc936e01747eb1bedf.json
data/cache/weather/97d847d329ee080cc695f9b3.json
data/cache/weather/98d2bc30382271cc43b26401.json
data/cache/weather/99a7602a5a9e09e809b9f06b.json
data/cache/weather/9a069c56da999368d79971f1.json
data/cache/weather/9abf3dfe2d26a71f762c1873.json
data/cache/weather/9d80cc5864d06f3bda3b1a23.json
data/cache/weather/9e22b58e9342bee4e27f71ba.json
data/cache/weather/9ea8432af8670e0a95bf3500.json
data/cache/weather/a04b5a984db557b2fb173b05.json
data/cache/weather/a0f2d057c5295da2232f4a63.json
data/cache/weather/a14da7501ba941f88a6cb932.json
data/cache/weather/a29f2335ad1c8a2101a13f90.json
data/cache/weather/a2db4ccd9c01246a05cd078b.json
data/cache/weather/a3469dcaaef1042fe14f9818.json
data/cache/weather/a40aba57651abd4883c80372.json
data/cache/weather/a429506ecaaa5d08b6c7cada.json
data/cache/weather/a4d382f0690342173396def1.json
data/cache/weather/a6170de199cc27cf09030823.json
data/cache/weather/a633fd3b071c366239f39235.json
data/cache/weather/a9d41402556d5df5789cc596.json
data/cache/weather/a9fcb5e9023feed55beb7187.json
data/cache/weather/aaf985832f146d5ca63479c6.json
data/cache/weather/ab87b77c9735819b9ba0a002.json
data/cache/weather/abda5a718dd73f40f4d6e0de.json
data/cache/weather/ac3700dbc0b99977ce85c0b6.json
data/cache/weather/adfdc43eb00b8b9278fa5873.json
data/cache/weather/af871292c5b1ab1ba10ad9bd.json
data/cache/weather/afbc3f756639e12529d3f556.json
data/cache/weather/b0a2dd2aa4cd21c294799ba9.json
data/cache/weather/b0c7138054a8d552dde036a8.json
data/cache/weather/b0e28292ac26a18835e7cc63.json
data/cache/weather/b4cabe9c77253c51d6f5a191.json
data/cache/weather/b586edcdffc3246ba762caa2.json
data/cache/weather/bb7a8265fb7ac39dda6e3b86.json
data/cache/weather/bfe9d2a5fa35ee14905ded27.json
data/cache/weather/c100cf613d13696353eb8920.json
data/cache/weather/c197fb2e23d55ae3829e0c8a.json
data/cache/weather/c4448a0fc53a4ce938859721.json
data/cache/weather/c5d3a3ee32b4a0a22db6a04f.json
data/cache/weather/c698b9907117c975efe43dc3.json
data/cache/weather/c6e97506bb84b6a8348658d4.json
data/cache/weather/c7cd99935f778cfb859ba804.json
data/cache/weather/c7f7348057f31725129955b4.json
data/cache/weather/c83a0a69e247f85e5378baed.json
data/cache/weather/c8412f44f407d9a7496d02bb.json
data/cache/weather/c874eed4570aa6d141fc8476.json
data/cache/weather/c9b774579e194a7800285793.json
data/cache/weather/c9dd9a46b94a503d5248f3f8.json
data/cache/weather/ca0911a80c6dadec687ac72c.json
data/cache/weather/ca38258aa3dcae6c01519870.json
data/cache/weather/cc0c33933d5e15e1801a2070.json
data/cache/weather/cdf2ed89405722be4c283c80.json
data/cache/weather/ce41aa18994485b4c8519010.json
data/cache/weather/ce8bb5ec7a4f2a595c9814d5.json
data/cache/weather/ce99c207c8a6980f896f0d83.json
data/cache/weather/d3f518e3f8ddca150535e442.json
data/cache/weather/d6672ca715bb33f6cf710f42.json
data/cache/weather/d7076d5319a222d167b22804.json
data/cache/weather/d707d8b7f712fd551fa2bc82.json
data/cache/weather/d9c4777ef976057dabf943c1.json
data/cache/weather/da230108dc86d3f860df4c8f.json
data/cache/weather/dc1c2d4a70b6d92943a0ec97.json
data/cache/weather/dc780e17f566fdf19033e739.json
data/cache/weather/dedf998115290d51cb7a9a5d.json
data/cache/weather/df883dab34f86e49ccc06f06.json
data/cache/weather/dfeacb9953ce3ea5e47607b1.json
data/cache/weather/e3a866694690713db9b7962c.json
data/cache/weather/e3d5c0db6eab363165631e9c.json
data/cache/weather/e410d3ad4e9b37986bc9b2f3.json
data/cache/weather/e47b4f35eab47deb23718312.json
data/cache/weather/e4b7ff38a71fa32694f352dd.json
data/cache/weather/e710dc3a8c0e1d2b406ee994.json
data/cache/weather/e7814128314302ef9720c992.json
data/cache/weather/e849bb85c82f587cc0453597.json
data/cache/weather/e9cea80424bf0d7305d4b968.json
data/cache/weather/ea188a5f9ac0d64425faa40b.json
data/cache/weather/ea3f80f94f6f79c827bee4c0.json
data/cache/weather/ea7ea60999d4666a42480100.json
data/cache/weather/ebf3aaaa535718cb2c131572.json
data/cache/weather/ec753d906ac08080d5ce9361.json
data/cache/weather/ecf5549e5ddea88c97c25238.json
data/cache/weather/ed07cfeb6d146be40aa46914.json
data/cache/weather/ed30f554b9195049d6b46e1f.json
data/cache/weather/ed4d6b413a1abdaea901fd92.json
data/cache/weather/ed6a694d513f773379c79d39.json
data/cache/weather/f22999b79b90c8a46c1333c2.json
data/cache/weather/f25a2c645505ad42f5bcd374.json
data/cache/weather/f33341a5570a05b8fdff48e9.json
data/cache/weather/f388302c6e50fff226d2e994.json
data/cache/weather/f3b320f2873b65676a0e8fba.json
data/cache/weather/f48f4a455c8a9357cd0cb1a3.json
data/cache/weather/f556cc92c260004cb821a50e.json
data/cache/weather/f68f310476de0be4857b1d81.json
data/cache/weather/f786d0e6c392dabf996397bb.json
data/cache/weather/f7a5017fb45be00b0d154683.json
data/cache/weather/f7a975d1c277e0b0891084f1.json
data/cache/weather/f7be44e27fbe4981a6d87e44.json
data/cache/weather/f82eb9884d52f49268bc8b71.json
data/cache/weather/f853da01b06d5ce535bf50bc.json
data/cache/weather/f994ed9d15c7b4c801bba5df.json
data/cache/weather/fa3a08ae11b4898fc4112279.json
data/cache/weather/fb6f3a783e85e7795a7f0328.json
data/cache/weather/fcb7e473021a6369cc6b49f6.json
data/raw/turbine_1.csv
data/raw/turbine_2.csv
docker-compose.yml
docs/.nojekyll
docs/about.html
docs/adr/0001-weather-source.md
docs/adr/0002-model-blend.md
docs/adr/0003-nacelle-wind-two-stage.md
docs/adr/0004-deterministic-fallback.md
docs/adr/0005-cache-in-repo.md
docs/adr/0006-hourly-aggregation.md
docs/adr/0007-one-weather-point-two-models.md
docs/adr/0008-prediction-interval.md
docs/adr/README.md
docs/agent-design.md
docs/analysis.md
docs/api.md
docs/architecture.md
docs/audit.md
docs/brand.md
docs/business-value.md
docs/compliance.md
docs/data-quality.md
docs/demo/apple-touch-icon-180.png
docs/demo/daily-interval.js
docs/demo/dashboard-data.js
docs/demo/favicon-32.png
docs/demo/favicon.svg
docs/demo/forecast-feb2026.js
docs/demo/mark-dark.svg
docs/demo/mark.svg
docs/demo/model-curves.js
docs/demo/validation-data.js
docs/index.html
docs/index.md
docs/metrics.json
docs/metrics.md
docs/model-card.md
docs/pitch.md
docs/product-roadmap.md
docs/roadmap.md
docs/root-analysis/01-weather-input.md
docs/root-analysis/02-objective-function.md
docs/root-analysis/03-agency.md
docs/root-analysis/04-data-and-operations.md
docs/root-analysis/05-interval-calibration.md
docs/runbook.md
docs/submission-checklist.md
docs/test-plan.md
docs/tz-checklist.md
docs/user-guide.md
docs/validation.md
outputs/agent_logs/2026-01-29.json
outputs/agent_logs/2026-01-30.json
outputs/agent_logs/2026-01-31.json
outputs/agent_logs/2026-02-01.json
outputs/agent_logs/2026-02-02.json
outputs/agent_logs/2026-02-03.json
outputs/agent_logs/2026-02-04.json
outputs/agent_logs/2026-02-05.json
outputs/agent_logs/2026-02-06.json
outputs/agent_logs/2026-02-07.json
outputs/agent_logs/2026-02-08.json
outputs/agent_logs/2026-02-09.json
outputs/agent_logs/2026-02-10.json
outputs/agent_logs/2026-02-11.json
outputs/agent_logs/2026-02-12.json
outputs/agent_logs/2026-02-13.json
outputs/agent_logs/2026-02-14.json
outputs/agent_logs/2026-02-15.json
outputs/agent_logs/2026-02-16.json
outputs/agent_logs/2026-02-17.json
outputs/agent_logs/2026-02-18.json
outputs/agent_logs/2026-02-19.json
outputs/agent_logs/2026-02-20.json
outputs/agent_logs/2026-02-21.json
outputs/agent_logs/2026-02-22.json
outputs/agent_logs/2026-02-23.json
outputs/agent_logs/2026-02-24.json
outputs/agent_logs/2026-02-25.json
outputs/agent_logs/2026-02-26.json
outputs/agent_logs/2026-02-27.json
outputs/agent_logs_llm/2026-01-29.json
outputs/agent_logs_llm/2026-01-30.json
outputs/agent_logs_llm/2026-01-31.json
outputs/agent_logs_llm/2026-02-01.json
outputs/agent_logs_llm/2026-02-02.json
outputs/agent_logs_llm/2026-02-03.json
outputs/agent_logs_llm/2026-02-04.json
outputs/agent_logs_llm/2026-02-05.json
outputs/agent_logs_llm/2026-02-06.json
outputs/agent_logs_llm/2026-02-07.json
outputs/agent_logs_llm/2026-02-08.json
outputs/agent_logs_llm/2026-02-09.json
outputs/agent_logs_llm/2026-02-10.json
outputs/agent_logs_llm/2026-02-11.json
outputs/agent_logs_llm/2026-02-12.json
outputs/agent_logs_llm/2026-02-13.json
outputs/agent_logs_llm/2026-02-14.json
outputs/agent_logs_llm/2026-02-15.json
outputs/agent_logs_llm/2026-02-16.json
outputs/agent_logs_llm/2026-02-17.json
outputs/agent_logs_llm/2026-02-18.json
outputs/agent_logs_llm/2026-02-19.json
outputs/agent_logs_llm/2026-02-20.json
outputs/agent_logs_llm/2026-02-21.json
outputs/agent_logs_llm/2026-02-22.json
outputs/agent_logs_llm/2026-02-23.json
outputs/agent_logs_llm/2026-02-24.json
outputs/agent_logs_llm/2026-02-25.json
outputs/agent_logs_llm/2026-02-26.json
outputs/agent_logs_llm/2026-02-27.json
outputs/backtest_summary.csv
outputs/backtest_summary.json
outputs/forecast_all_issues.csv
outputs/forecast_feb2026.csv
outputs/forecasts/2026-01-31.csv
outputs/forecasts/2026-02-01.csv
outputs/forecasts/2026-02-02.csv
outputs/forecasts/2026-02-03.csv
outputs/forecasts/2026-02-04.csv
outputs/forecasts/2026-02-05.csv
outputs/forecasts/2026-02-06.csv
outputs/forecasts/2026-02-07.csv
outputs/forecasts/2026-02-08.csv
outputs/forecasts/2026-02-09.csv
outputs/forecasts/2026-02-10.csv
outputs/forecasts/2026-02-11.csv
outputs/forecasts/2026-02-12.csv
outputs/forecasts/2026-02-13.csv
outputs/forecasts/2026-02-14.csv
outputs/forecasts/2026-02-15.csv
outputs/forecasts/2026-02-16.csv
outputs/forecasts/2026-02-17.csv
outputs/forecasts/2026-02-18.csv
outputs/forecasts/2026-02-19.csv
outputs/forecasts/2026-02-20.csv
outputs/forecasts/2026-02-21.csv
outputs/forecasts/2026-02-22.csv
outputs/forecasts/2026-02-23.csv
outputs/forecasts/2026-02-24.csv
outputs/forecasts/2026-02-25.csv
outputs/forecasts/2026-02-26.csv
outputs/forecasts/2026-02-27.csv
requirements.lock.txt
requirements.txt
scripts/__init__.py
scripts/calibrate_daily.py
scripts/check_env.py
scripts/degradation_experiment.py
scripts/demo.ps1
scripts/demo.sh
scripts/export_curves.py
scripts/make_charts.py
scripts/make_dashboard_data.py
scripts/make_validation_data.py
scripts/run_llm_issues.py
scripts/train.py
tests/conftest.py
tests/test_agent_logic.py
tests/test_input_quality.py
tests/test_pipeline.py
tests/test_smoke.py
wrangler.jsonc

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

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

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