О проекте
Команда: OpenToWork
Кейс HackAlem.ai: Энергетика — Самрук-Казына
Стадия: Доведённый продукт
Задача кейса: AI-агент, прогнозирующий выработку ветровой электростанции по погодным данным
Суть
Цифровой двойник ВЭС Нурлы с почасовым прогнозом на 48 ч и интервалом P10–P90 по архивным прогнозам 7 погодных моделей, многошаговым агентом, Copilot и черновиком заявки на продажу в РФЦ; дополнительно карта ВЭС и СЭС Казахстана и расчёт панелей на крышах.
Как устроено
SCADA турбин → признаки «на момент выпуска» с тестом утечек → архив Open-Meteo (7 моделей, только опубликованные выпуски) → каскад: поправка ветра и кривая мощности через Монте-Карло + прямая LightGBM + ансамбль с конформной калибровкой → граф агента (Планировщик → Сборщик погоды → Контроль качества → Прогнозист → Критик → Аналитик → Объяснитель → Ревизор → Отчёт) с пересчётом по выпускам погоды. TreeSHAP объясняет вклад факторов; Copilot отвечает на вопросы и считает what-if; из прогноза собирается заявка в РФЦ по приложению 7 (PDF/DOCX/CSV/JSON). Годовой walk-forward бэктест.
Ключевые решения
- LightGBM
- конформная калибровка
- Монте-Карло по кривой мощности
- TreeSHAP
- walk-forward бэктест
- RAG
- семантический кэш
- prompt injection guard
Метрики
Годовой бэктест (365 прогнозов): почасовая точность 84,2% от номинала, 84,0% для суток заявки, 90,8% по суточной выработке; ошибка на 8% ниже baseline «погода + кривая мощности» (в январе–феврале на 17–19%)
Особенности
Прогноз доведён до бизнес-результата: черновик суточной заявки в РФЦ по нормативной форме приложения 7. Каскадная модель с конформной калибровкой, объяснения через TreeSHAP и причины каждой ревизии; при этом сильно выходит за рамки кейса (СЭС, крыши, 36 ВЭС).
Стек
Python · FastAPI · PostgreSQL · Redis · MinIO · Alembic · React · TypeScript · Docker Compose · Prometheus · Grafana · GitHub Actions · Open-Meteo · OpenStreetMap
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-9013145c-opentowork.
README
README.mdRenewable Twin — прогноз выработки ветроэлектростанции
Команда OpenToWork · трек «Энергетика» · задача «Agentic AI для прогнозирования выработки ВЭС».
Система строит почасовой прогноз выработки ВЭС на 48 часов вперёд из любого момента с интервалом неопределённости P10–P90. Агент сам берёт архивные прогнозы 7 мировых погодных моделей — только те выпуски, что уже были опубликованы к моменту прогноза, — проверяет их, запускает ML-модель, объясняет результат, пересчитывает прогноз при выходе новой погоды и собирает черновик заявки на продажу в РФЦ.
На годовом бэктесте (365 прогнозов) почасовая точность — 84.2% от номинала, для суток заявки — 84.0%, по суточной выработке — 90.8%. Ошибка на 8% ниже, чем у базового подхода «метеопрогноз + кривая мощности» (в январе–феврале — на 17–19%), выигрыш статистически значим.
Демо-стенд: http://89.126.192.240:3000 (API: http://89.126.192.240:8000/docs) ·
вход [email protected] / demo1234
Содержание
- Что умеет
- Как это выглядит
- Быстрый запуск
- Как проверить
- ML-часть: как мы прогнозируем выработку ВЭС
- Черновик заявки в РФЦ
- Как считается выработка новой станции
- Архитектура
- Масштабируемость
- Надёжность и безопасность
- Сильные стороны
- План развития
- Для разработчика
- Сторонние компоненты и лицензии
Что умеет
| Раздел | Что делает | Нужен интернет |
|---|---|---|
| Прогноз ВЭС Нурлы | почасовой прогноз на 24/48 ч с P10–P90 по двум турбинам, 3D-сцена станции, история из SCADA, what-if «ветер на N% сильнее/слабее» | нет — прогнозы тестового периода 31.01–27.02.2026 лежат в репозитории |
| Живой прогноз «Сейчас» | прогноз Нурлы из текущего часа по свежей погоде; «Обновить» пересчитывает от текущего часа | да, Open-Meteo |
| Почему прогноз такой | разбор по шагам каскада, вклады факторов (TreeSHAP), причины каждой ревизии | нет для тестового периода |
| Точность прогноза | бэктест 02.2025–01.2026 в сравнении с базовыми подходами | нет |
| Агент и Copilot | шаги агента, происхождение прогноза, ответы на вопросы о прогнозе со ссылкой на выпуск | нет (режим mock) |
| Черновик заявки в РФЦ | суточная заявка на продажу и корректировки, выгрузка PDF/DOCX/CSV/JSON | нет |
| Прогнозы по всем ВЭС | 36 ВЭС Казахстана на карте: у Нурлы — выработка по ML-модели, у остальных — номинал, ветер и штормовые предупреждения (истории их турбин нет, выработку не выдумываем) | да |
| Новая турбина | выработка виртуальной турбины в любой точке по текущей погоде | да |
| Солнечные станции | 26 СЭС Казахстана из OpenStreetMap, прогноз по радиации | да |
| Панели на крышах | Астана, Алматы, Шымкент: сколько панелей поместится, выработка за год с учётом теней | расчёт — нет; подложка карты — да |
| AI-платформа | чат с RAG, семантический кэш, проверка ответов LLM, фоновые задачи, метрики | нет (режим mock) |
Как это выглядит
1. Обзор. Стартовая страница: станция из ТЗ — ВЭС Нурлы (2 × Goldwind GW109/2500) — и три сценария: ветровые станции, солнечные станции, панели на крышах.

2. Прогноз ВЭС Нурлы. Цифровой двойник станции: 3D-сцена с двумя турбинами, текущая мощность каждой, направление и сила ветра. Справа — мощность станции с диапазоном P10–P90, выработка и загрузка за сутки, средний ветер. Внизу — таймлайн на 48 часов: можно проиграть прогноз и увидеть, как меняется выработка по часам. Выбор даты прогноза, переключатель 24/48 ч, пересчёт кнопкой «Обновить».

3. Точность прогноза. Годовой walk-forward-бэктест 02.2025–01.2026: итоговая модель (каскад + калибровка) против прямой LightGBM, «прогноза погоды + кривой мощности», «как вчера» и климатологии. Ошибка по горизонту 1–48 ч и по дням теста.

4. Как считается — агент и Copilot. Лог последнего прогона агента по шагам: Планировщик → Сборщик погоды → Контроль качества → Прогнозист → Критик → Аналитик → Объяснитель → Ревизор → Отчёт, с результатом и временем каждого шага. Справа — AI Copilot: отвечает на вопросы о выбранном выпуске («Когда ожидается пик выработки?», «Что будет, если ветер окажется на 15% слабее?») и сам вызывает расчёт сценария.

5. Заявка в РФЦ. Черновик заявки на продажу по форме приложения 7: почасовой объём на операционные сутки из прогноза, опубликованного до 08:00 предыдущих суток, интервал P10–P90, ветер и горизонт по каждому часу, число корректировок из ревизий агента. Скачивание в PDF, DOCX, CSV и JSON.

Так выглядит выгруженный PDF: пометка «черновик», шапка по приложению 7 (отправитель, контрагент — РФЦ, операция «Продажа», операционные сутки, срок подачи по п. 51), 24 часовых объёма в МВт и итог за сутки. Ниже — справочное обоснование, в заявку не входит: ID прогноза, версия модели, погодные модели и выпуск, P10/P50/P90 и ветер по каждому часу.

6. Живой прогноз по всем ВЭС. Считается от текущего часа по свежим выпускам Open-Meteo. У Нурлы — выработка по ML-модели с коридором P10–P90 и текстовой сводкой агента (пики, перепады, часы низкой уверенности, главные факторы поправки); по остальным 36 ВЭС с координатами — ветер на 100 м.

7. Панели на крышах. Астана, Алматы, Шымкент на реальной 3D-карте: потенциал расчётного района, мощность панелей, лучшие крыши по выработке за год с учётом теней. Клик по зданию открывает его расчёт.

8. AI-платформа. Экономика AI-слоя: сколько запросов снято семантическим кэшем, задержка живого ответа против ответа из кэша, сэкономленные деньги по тарифу модели, число degraded-ответов. Во вкладках — запрос к AI, база знаний и асинхронный анализ.

Быстрый запуск
Подробная инструкция для человека, который видит проект впервые, — RUN.md.
Нужен Docker с Compose v2 и свободные порты 3000, 8000, 5432, 6379, 9000, 9001.
API-ключи и внешние аккаунты не нужны.
git clone https://github.com/BAITC-Hacks/hack-9013145c-opentowork.git
cd hack-9013145c-opentowork
cp .env.example .env # Windows: copy .env.example .env
docker compose up -d --build
Первая сборка занимает несколько минут: Docker ещё и обучает ML-модель (около 2 минут).
Стек готов, когда docker compose ps показывает healthy у api, frontend и worker.
| Что | Адрес |
|---|---|
| Веб-интерфейс | http://localhost:3000 ([email protected] / demo1234) |
| API и Swagger | http://localhost:8000/docs |
| Health / готовность | http://localhost:8000/health, http://localhost:8000/ready |
| Метрики Prometheus | http://localhost:8000/metrics |
Демо-пользователь и данные создаются при первом старте. По умолчанию LLM_MODE=mock:
всё работает без интернета и ключей. Настоящая LLM — LLM_MODE=live и LLM_API_KEY в .env.
Мониторинг: docker compose --profile observability up -d → Grafana http://localhost:3001
(admin / admin) с готовым дашбордом. Сброс: docker compose down -v.
Как проверить
Автоматически
python3 scripts/smoke_test.py # нужен только Python 3, без пакетов
Скрипт проходит основной путь и печатает PASS/FAIL по каждой проверке: вход и отказ
без токена, AI-ответ и попадание в кэш, блокировка prompt injection, фоновая задача,
метрики, прогноз ВЭС (48 согласованных квантилей, горизонт 24 ч, what-if, Copilot,
история SCADA, отсутствие выдуманного факта за февраль).
В интерфейсе
- http://localhost:3000 → вход демо-пользователем.
- Ветровая энергия → ВЭС Нурлы (или http://localhost:3000/#/forecast/wind/nurly).
- «Дата прогноза» → 07.02.2026: график с коридором P10–P90 и 3D-сцена станции. Пункт «Сейчас · живой прогноз» — прогноз из текущего часа (нужен интернет).
- Переключите 24 ч / 48 ч; нажмите на турбину — её история из SCADA (за февраль 2026 измерений нет, и интерфейс так и показывает).
- «Что если» — пересчёт того же выпуска при другом ветре.
- «Объяснить прогноз →» под графиком — разбор по шагам, вклады факторов, причины ревизий.
- «Точность прогноза» — бэктест в сравнении с базовыми подходами.
- «Как считается» — шаги агента и AI Copilot: «Когда ожидается пик выработки?», «Что будет, если ветер окажется на 15% слабее?». В ответе — ID выпуска и выполненные расчёты.
- «Заявка РФЦ» — черновик заявки на сутки, корректировки, скачивание PDF/DOCX/CSV/JSON.
- Прогнозы в верхнем меню — все ВЭС на текущей погоде.
- Панели на крышах — город и здание: число панелей, выработка, тени.
- «Платформа и инструменты» внизу — задайте вопрос, затем его же другими словами: второй ответ придёт из семантического кэша, задержка упадёт на порядок.
Через API
Сохранённые прогнозы, метрики и заявки открыты без токена:
curl "http://localhost:8000/api/v1/forecast/latest?origin=2026-02-07" # P10/P50/P90, шаги агента, ревизии
curl "http://localhost:8000/api/v1/forecast/latest?origin=2025-12-15" # прогноз бэктеста вместе с фактом
curl http://localhost:8000/api/v1/metrics # метрики бэктеста
curl -o zayavka.pdf "http://localhost:8000/api/v1/bids/2026-02-07/file?format=pdf"
Живые расчёты, объяснение и AI требуют токен:
TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"[скрыто]"}' | python3 -c "import json,sys;print(json.load(sys.stdin)['access_token'])")
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8000/api/v1/explain?origin=2026-02-07"
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8000/api/v1/predictions/run?station_id=nurly&origin=now"
curl -X POST http://localhost:8000/api/v1/ai/chat \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"query": "Как рассчитывается прогноз?", "context": {"language": "ru"}}'
Повторите вопрос к чату другими словами: meta.source станет semantic_cache, а
meta.latency_ms заметно уменьшится. Все эндпоинты — в Swagger.
ML-часть: как мы прогнозируем выработку ВЭС
Коротко
Система строит почасовой прогноз выработки ВЭС на 48 часов вперёд из любого момента, по самой свежей погоде, доступной на этот момент. Прогноз даёт не только ожидаемую мощность, но и интервал неопределённости P10–P90 — диапазон, в который реальная выработка попадает в 80% случаев. Прогноз строится на архивных прогнозах семи мировых погодных моделей и ML-модели, обученной на истории турбин. Агент проверяет результат, объясняет его и сам пересчитывает прогноз, когда выходит новый выпуск погоды (каждые 6 часов).
- Для заявки в РФЦ берётся последний прогноз до срока подачи — 08:00 по Астане накануне операционных суток.
- В бэктесте и тестовом периоде прогноз выпускается ежедневно в 00:00 UTC, чтобы результаты были сопоставимы и воспроизводимы.
Результаты
Бэктест повторяет условия тестового периода: каждый месяц с 02.2025 по 01.2026 модель переобучается только на прошлом, прогноз выпускается ежедневно в 00:00 UTC на 48 часов. Итого 365 прогнозов, две турбины. Ошибка — в долях номинала, по всем часам с фактом, включая простои.
| Модель | MAE | Точность (1 − MAE) | Покрытие P10–P90 | Покрытие P5–P95 |
|---|---|---|---|---|
| Итоговая: каскад + калибровка | 0.158 | 84.2% | 80.9% (цель 80%) | 90.2% (цель 90%) |
| Прямая модель (второе мнение) | 0.161 | 83.9% | 66.6% | 81.4% |
| Базовый подход: метеопрогноз + кривая мощности | 0.172 | 82.8% | — | — |
| Климатология (средняя мощность по месяцу и часу) | 0.310 | 69.0% | — | — |
| «Как вчера» | 0.363 | 63.7% | — | — |
Точность = 1 − MAE в долях номинала. Она считается по всем часам, включая штиль, где
прогноз нуля тривиален; другие определения точности дадут другие числа. Все цифры ниже —
из artifacts/reports/backtest_summary.json (ключи operational_day, by_month,
significance), формулы — в докстринге operational_day в backtest.py.
Точность:
- для суток заявки (уровень станции, горизонты 19–43 выпуска 00 UTC) — 84.0% (базовый подход 82.6%). Для сравнения: Самрук-Энерго ставит цель «точность 80%» к концу 2026 года; их определение точности не опубликовано, поэтому сравнение ориентировочное;
- по суточной выработке суток заявки — 90.8% (базовый подход 89.6%), по 337 полным суткам: 1 − среднее |E_факт − E_прогноз| / 24. Медианный прогноз занижает выработку за сутки на 6.9%: распределение мощности скошено, и медиана ниже среднего. Для оценки энергии за сутки правильнее брать средний прогноз, а не P50;
- против «как вчера» ошибка на 56% меньше;
- против базового подхода — на 8% меньше в среднем за год: в январе–феврале на 17–19% (январь 2026: 0.151 против 0.187; февраль 2025: 0.178 против 0.216), в декабре на 7%, в июле 2025 — на уровне метеопрогноза (0.158 против 0.154);
- чем старше выпуск погоды, тем больше ошибка: MAE 0.148 / 0.161 / 0.180 для выпусков за 1 / 2 / 3 суток.
Выигрыш не случайный. Тест Diebold–Mariano на 365 сутках с поправкой на перекрытие прогнозов соседних дней: против прогноза погоды с кривой мощности p = 5·10⁻¹², против «как вчера» p < 10⁻¹⁵.
Интервалы честные и адаптивные. Средняя ширина P10–P90 — 47 п.п. номинала, но это смесь очень разных часов. Интервал узкий там, где погода предсказуема, и широкий там, где нет, а покрытие остаётся близким к 80% в каждом режиме:
| Часы (по предсказанной сложности) | Ширина P10–P90 | Покрытие |
|---|---|---|
| Самые предсказуемые 25% | 14 п.п. | 80.4% |
| Ниже среднего | 36 п.п. | 82.0% |
| Выше среднего | 59 п.п. | 82.2% |
| Самые сложные 25% | 80 п.п. | 83.2% |
- Интервал климатологии при том же покрытии — 94 п.п.: наш вдвое уже, а общая оценка вероятностного прогноза (CRPS) лучше почти на 40%.
- Широкие интервалы — это часы, где ветер попадает на крутой участок кривой мощности (7–11 м/с) или погодные модели расходятся. Там неопределённость реальная, и интервал обязан её показать.
- На уровне станции (куда идёт заявка) покрытие 82.2%: интервал немного консервативный.
Пересчёты агента в течение суток (январь 2026, одни и те же часы):
| Выпуск | Точность относительно утреннего | Ширина P10–P90 |
|---|---|---|
| 00 UTC (утро) | — | 49 п.п. |
| 06 UTC | −2.2% | 48 п.п. |
| 12 UTC | +1.1% | 48 п.п. |
| 18 UTC | +1.6% | 46 п.п. |
Механизм ревизий работает, но его выигрыш на архиве измерить нельзя: архив хранит
прогнозы погоды с шагом в сутки, поэтому пересчёты в 06/12/18 UTC почти не получают новых
данных. На одном месяце разброс от −2.2% до +1.6%, значимость не проверялась
(artifacts/reports/revision_value.json). Поэтому Ревизор публикует новую версию, только
если прогноз заметно сдвинулся, и мелкие колебания не превращаются в лишние корректировки.
Главная идея: учимся на прогнозах погоды, а не на фактической погоде
Как проще, но неправильно. В данных турбин есть фактический ветер, и он почти идеально связан с мощностью: корреляция 0.95. Модель «ветер → мощность» на таких данных очень точна на обучении. Но в момент прогноза завтрашнего фактического ветра ещё нет — есть только прогноз погоды, а он ошибается. Модель, обученная на идеальном ветре, ничего не знает об этих ошибках, и на реальном прогнозе её точность резко падает. Мы это измерили:
| Что с чем сравниваем | Корреляция |
|---|---|
| Фактический ветер у турбины ↔ мощность | 0.95 |
| Прогноз погоды за сутки ↔ фактический ветер | 0.78 |
| Прогноз погоды за двое суток ↔ фактический ветер | 0.74 |
| Прогноз погоды за трое суток ↔ фактический ветер | 0.68 |
Реальная отправная точка — не 0.95, а 0.68–0.78.
Как сделали мы. Для обучения берём не фактическую погоду, а архивные прогнозы погоды — то, что метеоцентры предсказывали заранее. Источник — Open-Meteo Previous Runs API: для каждого часа он хранит значения, предсказанные за 1, 2, 3 суток до него. Модель видит на обучении ровно то же, что увидит в работе, и учится исправлять типичные ошибки погодных моделей именно на этой площадке. Например, она выучила, что зимой прогноз здесь завышает ветер в среднем на 0.45 м/с.
Защита от «подглядывания в будущее». Выпуск погоды публикуется с задержкой в несколько
часов, поэтому для горизонта h берётся только выпуск, гарантированно опубликованный
к моменту прогноза:
N(h) = ⌈(h + 6) / 24⌉ — за сколько суток до нужного часа выпущен прогноз
Правило живёт в одной функции (windcast/weather.py) и считается от момента запроса, поэтому одинаково работает в бэктесте и в живом режиме. Это требование кейса, и оно проверено тестом, а не обещанием: test_windcast_leakage.py подменяет мусором все данные погоды, опубликованные позже момента прогноза, и проверяет, что признаки модели не изменились ни на бит. Живой API тоже не даёт финальной модели считать прогноз из момента раньше границы её обучения (31.01.2026): такие даты отдаются только из walk-forward-бэктеста.
Данные
- Турбины: две турбины, 10-минутные записи с марта 2023 по январь 2026: ветер, мощность, температура. Приводим к часовому шагу, местное время (UTC+5) переводим в UTC.
- Простои: часы простоя и ограничений (сильный ветер при нулевой мощности, выработка ниже кривой на 30% номинала) помечаются и исключаются из обучения. Модель прогнозирует мощность, которую станция может выдать при такой погоде.
- Погода: 7 моделей — ECMWF IFS, ECMWF AIFS (погодная модель на нейросети), ICON, GFS, UKMO, GEM, CMA. Ветер на 10/80/100/120 м, направление, порывы, температура, влажность, давление. Архив лежит в репозитории, поэтому всё воспроизводится без интернета.
Как устроена модель
graph LR
OM["Open-Meteo<br/>архив прогнозов<br/>7 погодных моделей"] --> PIT["Только выпуски,<br/>опубликованные к моменту T"]
PIT --> F["Признаки: ансамбль,<br/>разброс, сдвиг ветра,<br/>плотность воздуха"]
F --> A["A. Поправка ветра<br/>квантильный LightGBM"]
A -->|Монте-Карло, 400 вариантов| B["B. Кривая мощности<br/>2023–2026"]
B --> E["Калибровка P10–P90<br/>по сложности часа"]
F --> C["Прямая модель<br/>(второе мнение)"]
E --> AG[Агент]
C -.->|сверка у Критика| AG
1. Признаки. Главный — ансамбль погодных моделей: среднее по моделям точнее лучшей одиночной (корреляция с фактом 0.78 против 0.74 у ECMWF AIFS). К нему добавляются разброс между моделями (мера неопределённости), направление ветра, сдвиг ветра по высоте (признак ночной инверсии), плотность воздуха, риск обледенения, время суток и сезон.
2. Каскад — итоговая модель. Два понятных шага:
- A. Поправка ветра: LightGBM переводит прогноз погоды в реальный ветер у турбины. На выходе не одно число, а распределение (7 квантилей).
- B. Кривая мощности: «ветер → мощность», обучена на всей истории турбин (~50 тыс. часов). Монотонна по ветру — мощность физически не может падать с ростом ветра до номинала. Учитывает температуру: зимой плотный воздух даёт больше мощности при том же ветре.
- Связка — Монте-Карло: 400 вариантов ветра из шага A прогоняются через кривую B. Так получается честное распределение мощности с правильным учётом нелинейности кривой.
3. Калибровка интервалов. Интервалы доводятся до честного покрытия конформной калибровкой (CQR), отдельно для каждого режима: по давности выпуска погоды (1, 2 или 3 суток) и по сложности часа (ширине интервала до калибровки). Поправки подбираются только на прогнозах прошлых месяцев, поэтому интервал честный в каждом режиме, а не только в среднем.
4. Прямая модель — второе мнение. LightGBM переводит погоду сразу в мощность. Раньше итог был смесью каскада и прямой модели, но проверка показала, что смесь не даёт выигрыша ни по точности (каскад значимо точнее прямой модели: тест Diebold–Mariano, p = 0.00076), ни по интервалам: обе модели учатся на одних признаках и ошибаются одинаково. Итог упрощён до каскада, а прямая модель работает у Критика: если две по-разному устроенные модели сильно расходятся, час помечается для проверки.
5. Агент (windcast/agent/) — граф с явными проверками между шагами:
Планировщик → Сборщик погоды → Контроль качества → Прогнозист → Критик → Аналитик → Объяснитель → Ревизор → Отчёт
| Шаг | Что делает |
|---|---|
| Планировщик | определяет самый свежий выпуск погоды на момент запуска (интерфейс, API или CLI) |
| Сборщик погоды | собирает прогнозы доступных погодных моделей |
| Контроль качества | проверяет погоду до расчёта; нет данных — откат на предыдущий выпуск; непроверенная погода в расчёт не идёт |
| Прогнозист | запускает каскад и калибровку; часы без погоды закрывает климатологией с флагом degraded |
| Критик | ловит пересечение квантилей, выход за номинал, пропуски часов, расхождение с прямой моделью; при сбое — запасной прогноз, тоже перепроверенный |
| Аналитик | ищет резкие перепады выработки (≥ 30% номинала за 3 ч) и риск обледенения |
| Объяснитель | раскладывает прогноз на причины (см. ниже) |
| Ревизор | при новом выпуске погоды (00/06/12/18 UTC) пересчитывает и публикует версию, только если она заметно отличается |
| Отчёт | сводка оператору: шаблоном или через LLM; каждое число в тексте LLM сверяется с расчётом |
Модель не чёрный ящик
Для каждого часа можно ответить на три вопроса: как получилось число, почему модель так
решила и почему прогноз изменился. Кнопка «Объяснить прогноз» под графиком открывает
страницу, где всё это видно (API: GET /api/v1/explain, GET /api/v1/explain/global).
1. Как получилось число — разбор по шагам. Пример: 07.02, 11:00 по Астане.
| Шаг | Ветер | Мощность, % номинала |
|---|---|---|
| Сырой прогноз погоды (среднее 4 моделей) → кривая мощности | 5.66 м/с | 18.7% |
| Модель поправила ветер под площадку → кривая | 4.83 м/с | 10.7% |
| Учли неопределённость ветра (Монте-Карло) → итог | — | 9.8% |
| Калибровка интервала под сложность часа | — | P10–P90: 0–60% |
В этот час погодные модели сильно расходились: ECMWF IFS обещал 2.5 м/с, GFS — 11.4 м/с. Модель знает, какие из них точнее на этой площадке, поэтому снизила прогноз, а интервал сделала широким: час сложный.
2. Почему модель так решила — вклад факторов. Вклады считаются методом SHAP, встроенным в LightGBM: для деревьев решений он точный и аддитивный — базовое значение плюс сумма вкладов равно прогнозу (проверено тестом). Около 50 признаков сведены в 10 понятных групп. Для того же часа: типичная поправка — 6.31 м/с, итог — 4.83 м/с. Разница складывается так:
| Фактор | Вклад в ветер | В мощности |
|---|---|---|
| Тенденция ветра (последние часы ветер слабел) | −0.92 м/с | −7.1 п.п. |
| Уровень прогноза ветра | −0.57 м/с | −4.4 п.п. |
| Температура, влажность, давление | +0.34 м/с | +2.7 п.п. |
| Время суток | −0.32 м/с | −2.5 п.п. |
3. Почему прогноз изменился. Для каждой новой версии агент объясняет сдвиг. Например, выпуск погоды в 06 UTC 06.02 изменил прогноз на 07.02 04:00 с 49% до 22% номинала: ECMWF IFS снизил ветер на 2.9 м/с, ECMWF AIFS — на 1.4 м/с, среди факторов сильнее всего изменилась тенденция ветра. То же обоснование попадает в черновик корректировки заявки в РФЦ.
4. Свойства модели в целом: выученная кривая мощности зимой и летом; на что модель опирается (уровень и тенденция прогноза ветра — около 70% вклада); точность каждой погодной модели на площадке; честность интервалов.
Все объяснения считаются кодом из самой модели, без LLM. LLM, если подключена, только пересказывает готовые факты, и каждое число в её тексте сверяется с ними.
Где модель даёт больше всего
- Зимой, когда ошибки прогноза стоят дороже всего: в январе–феврале ошибка ниже базового подхода на 17–19%, в декабре на 7%. Зимой прогноз погоды на этой площадке систематически завышает ветер (в среднем на 0.45 м/с), и модель выучивает и снимает эту ошибку.
- Летом точность держится на уровне лучших метеопрогнозов. Летний ветер здесь больше зависит от локальных процессов (дневной прогрев, горно-долинная циркуляция), которые глобальные модели описывают хуже: корреляция прогноза с фактом летом 0.64 против 0.80 зимой. Систематической ошибки, которую можно выучить, летом почти нет — резерв здесь в более детальных погодных данных (см. План развития).
Как воспроизвести
ML-часть — пакет windcast/, запускается без веб-стека (Python 3.12+).
Архив погоды уже в artifacts/weather/, сеть не нужна.
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev,ml]"
python -m windcast backtest # бэктест 02.2025–01.2026, 365 выпусков (~6 мин)
python -m windcast train # итоговая модель на данных до 31.01.2026 00:00 UTC
python -m windcast test-period # прогноз на тестовый период 31.01–27.02.2026 (~3 мин)
python -m windcast agent --origin 2026-02-07 # один день с выводом шагов агента
python -m windcast bids # черновики заявок в РФЦ на 01.02–28.02
pytest -q tests/test_windcast_*.py # утечка будущего, агент, объяснения, API
Главный результат: artifacts/forecasts/test_period_forecast.csv —
28 выпусков (31.01…27.02.2026, каждый в 00:00 UTC = 05:00 Астаны) × 48 часов, P10/P50/P90
в долях номинала. Все версии с пересчётами агента — test_period_all_versions.csv,
полный прогон за день — artifacts/forecasts/<дата>.json, метрики —
artifacts/reports/backtest_summary.json.
Черновик заявки в РФЦ
Из прогноза агента собирается черновик заявки на продажу по форме приложения 7 Правил организации и функционирования оптового рынка электрической энергии (приказ Минэнерго РК, V1500010531): получатель — единый закупщик (РФЦ), операция «Продажа», 24 часовых объёма в МВт.
- Срок подачи — до 08:00 по Астане накануне суток поставки. В заявку идёт выпуск 00 UTC (05:00 Астаны), горизонты 19–43 ч.
- Корректировки — пересчёты агента в 06, 12 и 18 UTC становятся черновиками корректировок, если прогноз сдвинулся не меньше чем на 10% установленной мощности, с обоснованием из раздела «Почему прогноз изменился».
- Все объёмы считает код, LLM к заявке не допускается. Допущения напечатаны в документе.
Файлы: artifacts/bids/<дата>/zayavka_<дата>.{pdf,docx,csv,json}, в интерфейсе — вкладка
«Заявка РФЦ», через API — GET /api/v1/bids/2026-02-07/file?format=pdf.
Как считается выработка новой станции
Вы задаёте точку на карте, модель турбины, высоту башни, горизонт (24 или 48 ч) и технические потери. Дальше — пять шагов, все детерминированные.
1. Погода. По координатам запрашивается почасовой прогноз Open-Meteo: скорость ветра на 10, 80, 120 и 180 м, температура на 2 м, приземное давление. Ответ проверяется на единицы измерения, UTC и непрерывность часов.
2. Ветер на высоте втулки. Скорость интерполируется по логарифму высоты между четырьмя уровнями прогноза — ветер растёт с высотой нелинейно, и брать значение с 10 м для башни в 100 м нельзя.
3. Плотность воздуха на втулке. Температура пересчитывается по градиенту −0.0065 °C/м, давление — по барометрической формуле, затем ρ = p / (R·T), R = 287.05 Дж/(кг·К). Холодный плотный воздух даёт больше мощности при той же скорости ветра — без этой поправки зимние часы недооцениваются.
4. Мощность по кривой турбины. Берётся паспортная кривая мощности производителя (каталог OEDB, библиотека windpowerlib) с поправкой на плотность относительно опорных 1.225 кг/м³. При скорости от 25.5 м/с срабатывает штормовая остановка — мощность строго ноль.
5. Энергия. Чистая мощность = валовая × (1 − потери). Часовая мощность принимается постоянной в течение часа, поэтому выработка за час в кВт·ч численно равна мощности в кВт, а за период — их сумма. КИУМ = средняя чистая мощность ÷ номинальная мощность турбины.
Что модель НЕ учитывает
Считается одна исправная турбина. Не моделируются: аэродинамический след соседних турбин, обледенение лопастей, плановые и аварийные простои, ограничения сети. Потери задаются пользователем одним числом.
Архитектура
Модульный монолит на FastAPI, фоновые воркеры, ML-контур windcast и AI-слой
с семантическим кэшем и проверкой ответов LLM.
graph LR
U[Браузер] --> FE["Frontend<br/>nginx :3000"]
FE -->|/api| API["API × N<br/>FastAPI :8000"]
API --> PG[("PostgreSQL 16<br/>+ pgvector")]
API --> R[("Redis 7<br/>кэш, лимиты, очередь")]
API --> S3[("MinIO / S3<br/>файлы")]
R --> W["Worker × N"]
API --> WC["windcast<br/>прогноз и агент"]
WC --> OM["Open-Meteo"]
API --> ORCH[AI Orchestrator]
W --> ORCH
ORCH --> SC[Семантический кэш]
ORCH --> RAG[RAG]
ORCH --> LLM[LLM или mock]
ORCH --> VAL[Проверка ответа]
Путь AI-запроса: проверка ввода (prompt injection, персональные данные, длина) → точный кэш в Redis → эмбеддинг → семантический кэш в pgvector → поиск по базе знаний → LLM → проверка ответа по схеме и источникам с повтором → запасной ответ при неудаче → запись в кэш.
Фронтенд (React + TypeScript, 3D на Three.js, карты на MapLibre) ходит к API по
относительному /api через nginx — ни CORS, ни зашитого адреса.
| Путь | Что там |
|---|---|
app/ | бэкенд: API (app/api/v1/), AI-слой (app/ai/), кэш, RAG, воркеры, крыши (app/solar/), новые турбины (app/wind/) |
windcast/ | ML-модель и агент прогноза ВЭС, CLI python -m windcast |
frontend/ | веб-интерфейс |
datasets/ | данные турбин от организатора (03.2023–01.2026) |
artifacts/ | архив погоды, модели, прогнозы, отчёты бэктеста, заявки, объяснения |
infra/ | Dockerfile, Prometheus, Grafana, манифесты Kubernetes |
tests/, scripts/smoke_test.py | тесты и сквозная проверка |
docs/, context/ | архитектурные решения и навигация по коду |
Масштабируемость
По нагрузке.
- API без состояния — масштабируется репликами. Всё общее (лимиты запросов, кэш, очередь) лежит в Redis, поэтому лимит не умножается на число подов, а ответ из кэша доступен с любой реплики.
- Воркеры масштабируются отдельно от API. Очередь — Redis Streams с группой потребителей: задачу умершего воркера забирает другой, битые сообщения уходят в dead-letter, ключ идемпотентности защищает от двойной постановки, а захват задачи атомарный — два воркера не обработают её дважды.
- Kubernetes: автомасштабирование API 2→10 реплик, воркеров 1→20, пробы
liveness/readiness/startup, миграции отдельной задачей, обновление без простоя,
PodDisruptionBudget. Локальный вариант (
infra/k8s-local/) развёрнут в кластере: smoke-тест прошёл, автомасштабирование подняло API с 2 до 4 реплик за 30 секунд. - Семантический кэш отвечает на повтор и перефразированный вопрос без вызова LLM:
меньше денег и задержки при росте числа пользователей. Экономию показывает
/ai/statsи дашборд Grafana. - ML дёшев на чтении: обучение идёт офлайн (CLI или при сборке образа), готовые прогнозы
отдаются из артефактов. Живой пересчёт тяжелее, поэтому доступен только с токеном и
ограничен 6 запусками в минуту на пользователя (
RATE_LIMIT_FORECAST_LIVE).
По станциям и регионам.
- Новая ВЭС — нужны координаты и история выработки: погодный архив, признаки, обучение,
бэктест и агент собираются теми же командами
windcast. Обучение модели — около 2 минут. - Станции без истории уже на карте: 36 ВЭС получают прогноз ветра и штормовые предупреждения по координатам.
- Не только ветер: тот же каркас уже считает 26 СЭС по радиации и солнечные крыши трёх городов; доменная логика подключается модулем, инфраструктура не меняется.
Надёжность и безопасность
- Деградация вместо падения. Пропала погодная модель — ансамбль перевешивается на
остальные; погоды нет совсем — часы закрываются климатологией с флагом
degraded, и интерфейс честно показывает «—» вместо ветра; основной и запасной прогноз не прошли Критика — прогноз не публикуется. - Проверка ответов LLM в четыре слоя: схема, опора на источники, выдуманные источники, выдуманные числа. Числа в заявке и объяснениях LLM не генерирует.
- Изоляция пользователей: JWT, роли, доступ к чужим объектам, задачам и документам закрыт и проверен интеграционными тестами; кэш по умолчанию свой у каждого пользователя.
- Защита ввода: prompt injection, персональные данные, лимит длины; лимиты частоты на вход, AI-запросы и живые расчёты.
- Наблюдаемость: структурные логи с
request_id, метрики Prometheus, готовый дашборд Grafana (кэш, задержки по стадиям, отказы валидации, очередь). - CI: на каждый пуш — линтер и юнит-тесты; на pull request — интеграционные тесты с PostgreSQL и Redis, поиск секретов, аудит зависимостей, сканирование образов.
Сильные стороны
- Честная постановка. Обучение на архивных прогнозах погоды и тест на утечку будущего: модель проверена в тех же условиях, в которых будет работать.
- Проверенный результат. Бэктест на 365 прогнозах по протоколу теста, выигрыш над базовым подходом статистически значим.
- Прогноз по запросу. Из любого момента, по самой свежей доступной погоде, с автоматическим пересчётом при каждом новом выпуске.
- Интервалы, которым можно верить. Покрытие близко к обещанному в каждом режиме — диспетчер видит, в какие часы держать резерв.
- Ансамбль мировых погодных моделей, включая нейросетевую ECMWF AIFS — лучшую одиночную модель на этой площадке по корреляции с фактом.
- Физика внутри ML. Каскад отделяет ошибку погоды от физики турбины, кривая мощности монотонна, учитывается плотность воздуха.
- Простота. Каждый компонент оправдан замером; то, что не давало выигрыша (смесь двух моделей), убрано из итога.
- Прозрачность. Каждый час раскладывается на шаги и факторы, каждая ревизия объяснена.
- Устойчивость. Сбой данных ведёт к помеченному деградированному прогнозу или к отказу публиковать, а не к тихой ошибке.
- Готовые документы. Черновик заявки в РФЦ по форме Правил оптового рынка и черновики корректировок внутри суток — автоматически.
- Воспроизводимость. Одна команда, без интернета и API-ключей; результаты бэктеста совпадают при перезапуске.
- Готовность к продакшену. Независимое масштабирование API и воркеров, Kubernetes, мониторинг, CI, изоляция пользователей.
План развития
| Направление | Сейчас | Дальше |
|---|---|---|
| Точность летом и ширина интервалов | глобальные погодные модели, выпуски с шагом в сутки | согласованность выпусков за 1–3 суток как признак (данные уже есть), сетка точек погоды вокруг станции, региональные модели с мелкой ячейкой, ансамбль ECMWF ENS, внутрисуточные выпуски |
| Выработка в МВт | номинал турбин в данных не указан: прогноз в долях номинала, в заявке — допущение 2.5 МВт | паспортные данные станции |
| Проверка на факте февраля 2026 | факта за тестовый период в кейсе нет; качество оценено бэктестом на тех же зимних месяцах прошлых лет | сверка с фактом при его получении |
| ML для других ВЭС | ML-прогноз — у Нурлы; у остальных станций ветер и предупреждения | подключение истории выработки → обучение той же командой |
| Подача заявки | черновик PDF/DOCX/CSV/JSON | интеграция с системой рынка и ЭЦП |
| Живые прогнозы под нагрузкой | пересчёт в API с лимитом; кэш погоды и живых прогнозов — в памяти процесса | вынести пересчёт в очередь воркеров, общий кэш в Redis, резервный источник погоды |
| Продакшен-кластер | базовые манифесты infra/k8s/ проходят проверку API-сервером | развёртывание с registry, доменом и секретами заказчика |
| Солнечная энергетика | СЭС и крыши по физике, без факта выработки | калибровка на данных реальных СЭС |
Для разработчика
pip install -e ".[dev,ml]"
docker compose up -d postgres redis minio && alembic upgrade head
uvicorn app.main:app --reload # API на :8000
cd frontend && npm install && npm run dev # http://localhost:5173
pytest -q && ruff check . # юнит-тесты и линтер, база и сеть не нужны
make test-integration # проверки доступа, нужен поднятый стек
Для локального API в .env замените хосты postgres, redis, minio на localhost.
На Windows без Python: make test-docker. Все переменные окружения с комментариями —
в .env.example; при APP_ENV=prod приложение не стартует
с секретами по умолчанию. Kubernetes: kubectl apply -k infra/k8s-local/.
Подробности устройства — docs/ и context/.
| Симптом | Что сделать |
|---|---|
port is already allocated | освободите порт или поменяйте левую часть ports: в docker-compose.yml |
api долго не становится healthy | первая сборка обучает модель; docker compose logs -f api |
| Не открываются «Прогнозы» или «Сейчас» | нужен интернет у контейнера api (Open-Meteo); тестовый период работает офлайн |
| Нет подложки карты городов | её грузит браузер из OpenFreeMap; расчёт крыш от неё не зависит |
| Нужна чистая установка | docker compose down -v && docker compose up -d --build |
Сторонние компоненты и лицензии
| Компонент | Лицензия | Как используется |
|---|---|---|
| FastAPI, SQLAlchemy, Alembic | MIT | HTTP-слой, ORM, миграции |
| pgvector | PostgreSQL License | векторный поиск |
| Redis | RSALv2/SSPL (образ), клиент MIT | кэш, очередь |
| MinIO | AGPLv3 (только локальный стенд) | S3-совместимое хранилище |
| Prometheus, Grafana | Apache 2.0 / AGPLv3 | мониторинг |
| React, Vite, TypeScript | MIT / Apache 2.0 | веб-интерфейс |
| Three.js | MIT | 3D-сцены станции и крыш |
| MapLibre GL JS | BSD-3-Clause | карты городов |
| OpenFreeMap, Mapterhorn | данные OSM (ODbL) / рельеф с атрибуцией | подложка и рельеф карт |
Inter (@fontsource-variable/inter) | SIL OFL 1.1 | шрифт интерфейса |
| nginx | BSD-2-Clause | раздача фронтенда и прокси /api |
| LightGBM | MIT | квантильные модели |
| NumPy, pandas, scikit-learn, PyArrow | BSD-3-Clause / Apache 2.0 | обработка данных |
| python-docx, ReportLab | MIT / BSD | заявка в DOCX и PDF |
| DejaVu Sans | свободная (Bitstream Vera / public domain) | кириллица в PDF |
| windpowerlib (oemof) | MIT, текст в app/wind/UPSTREAM-LICENSE.txt | кривые мощности новых турбин |
| Open-Meteo | данные CC BY 4.0 | архив прогнозов и текущая погода |
| ECMWF IFS/AIFS, DWD ICON, NOAA GFS, UKMO, GEM, CMA | через Open-Meteo, CC BY 4.0 | исходные погодные модели |
| OpenStreetMap | ODbL | здания городов, справочник ВЭС и СЭС |
| PVGIS-ERA5 (JRC, Еврокомиссия) | свободно со ссылкой на источник | радиация для крыш |
| Данные турбин 1 и 2 (03.2023–01.2026) | предоставлены организатором | обучение и бэктест |
Команда OpenToWork
Авторы коммитов
История коммитов
- 2026-09-23 17:54Fix formatting in README overview sectionNuralyAb
- 2026-09-23 17:51docs: развёртываемость — Compose, демо-сервер, Kubernetes; риск Redis без тома в K8sNuridin28
- 2026-09-23 17:50docs(readme): скриншот PDF заявки в РФЦ с описаниемNuridin28
- 2026-09-23 17:49docs(context): журнал — скриншоты в READMENuridin28
- 2026-09-23 17:49docs(readme): скриншоты интерфейса с описанием каждого экранаNuridin28
- 2026-09-23 17:45docs(readme): убрана строка о сроках разработкиNuridin28
- 2026-09-23 17:43docs(readme): как считается выработка новой станции — пять шагов и ограничения моделиNuridin28
- 2026-09-23 17:42feat(wind): справочник турбин действующих ВЭС Казахстана без кривой мощностиauyezkhanov
Все коммиты (86)
- 2026-09-23 17:42feat(api): GET /wind/reference-turbines — паспорт и источники моделей без расчётаauyezkhanov
- 2026-09-23 17:42test(wind): справочные модели не попадают в расчёт, simulate отвечает 422auyezkhanov
- 2026-09-23 17:42feat(frontend): модели казахстанских ВЭС в выборе турбины — паспорт без расчётаauyezkhanov
- 2026-09-23 17:42docs(wind): справочник турбин ВЭС Казахстана и почему по ним нет расчётаauyezkhanov
- 2026-09-23 17:42docs(context): журнал — справочник турбин ВЭС Казахстана без расчётаauyezkhanov
- 2026-09-23 17:42docs(context): журнал — ревью по критериям ВЭС, открытые рискиNuridin28
- 2026-09-23 17:35docs(context): журнал — остаточные замечания ревью закрытыNuridin28
- 2026-09-23 17:35fix(worker): номер попытки как fencing token — истёкшая аренда не перетирает задачуNuridin28
- 2026-09-23 17:35fix(windcast): NaN в прогнозах по кривой и СЭС, общий лимит живого ML-расчёта для /predictions/runNuridin28
- 2026-09-23 17:35docs(readme): ML-часть — только цифры из отчётов, базовый подход, честно про ревизииNuralyAb
- 2026-09-23 17:35feat(windcast): метрики суток заявки в отчёте бэктестаNuralyAb
- 2026-09-23 17:31fix(frontend): переходы между разделами — выбор станции вместо Нурлы, лоадер до загрузки справочникаNuridin28
- 2026-09-23 17:31docs(context): журнал — повторное ревью исправлений, три остаточных рискаNuridin28
- 2026-09-23 17:29feat(wind): ветер сейчас — Open-Meteo current и METAR ближайшего аэродромаauyezkhanov
- 2026-09-23 17:28docs(readme): ML-часть, масштабируемость, сильные стороны и план развития; убрано лишнееNuridin28
- 2026-09-23 17:23fix(frontend): маркер выбранной крыши на карте города ставится во внутреннюю точку контураNuridin28
- 2026-09-23 17:23docs: RUN.md — пошаговый запуск проекта для постороннегоNuridin28
- 2026-09-23 17:21fix(frontend): «Сейчас» пересчитывается при обновлении, часы без погоды показываются как «—»Nuridin28
- 2026-09-23 17:21docs(context): журнал — шесть замечаний ревью закрытыNuridin28
- 2026-09-23 17:20fix(windcast): токен и лимит для живого пересчёта, null вместо NaN, запрет прогноза раньше границы обученияNuridin28
- 2026-09-23 17:20fix(jobs): проверка владельца entity_id в /analysis до постановки в очередьNuridin28
- 2026-09-23 17:20fix(worker): атомарный захват задачи, повторная доставка не дублирует обработкуNuridin28
- 2026-09-23 17:18fix(windcast): итог — каскад + калибровка по сложности часа, проверка значимостиNuralyAb
- 2026-09-23 17:13fix(frontend): выработка только у ВЭС Нурлы, у остальных станций — ветерauyezkhanov
- 2026-09-23 17:10docs(readme): актуализация под текущий код — прогнозы, объяснение, заявка РФЦ, крыши трёх городовNuridin28
- 2026-09-23 17:07docs(context): журнал — синхронизация git и сервераNuridin28
- 2026-09-23 17:05feat(solar): крыши трёх городов — Астана, Алматы, Шымкент, карта MapLibreNuridin28
- 2026-09-23 17:05feat(frontend): вкладка «Заявка РФЦ» и прогноз новых турбин в разделе прогнозовNuridin28
- 2026-09-23 17:05docs(context): журнал — крыши городов, заявка РФЦ, ручной деплой на серверNuridin28
- 2026-09-23 16:49fix: номинал станции по единому правилу, реестр важнее OSMauyezkhanov
- 2026-09-23 16:45fix(frontend): подпись прогноза других ВЭС — кривая мощности Нурлыauyezkhanov
- 2026-09-23 16:35feat(windcast): интерпретация прогноза — шаги каскада, TreeSHAP, причины ревизийNuralyAb
- 2026-09-23 16:35feat(frontend): кнопка «Объяснить прогноз» и страница интерпретацииNuralyAb
- 2026-09-23 16:32docs: README и инженерная документация под текущее состояние проектаNuridin28
- 2026-09-23 16:32fix(frontend): плашка загрузки не выходит за край узкого экранаNuridin28
- 2026-09-23 16:32docs(context): журнал состояния — исправление QA агента и прогон проверокNuridin28
- 2026-09-23 16:31fix(windcast): агент не считает на непроверенной погоде, запасной прогноз перепроверяетсяNuridin28
- 2026-09-23 16:25fix(frontend): доработка экрана загрузки станцииNuridin28
- 2026-09-23 16:24ci: быстрый путь пуш → тесты → деплой, долгие проверки в checks.ymlNuridin28
- 2026-09-23 16:24feat(frontend): экран загрузки и доработки панели ветра станцииNuridin28
- 2026-09-23 16:15fix(infra): образ MinIO из quay.io — minio/minio удалён с Docker HubNuridin28
- 2026-09-23 16:15feat(solar): СЭС Казахстана из OpenStreetMap вместо виртуальной станцииNuridin28
- 2026-09-23 16:13docs: README и разбор кейса под текущее состояниеNuridin28
- 2026-09-23 16:06feat(windcast): черновик заявки на продажу в РФЦ по форме приложения 7NuralyAb
- 2026-09-23 15:59Merge origin/main: расчёт новых турбинNuridin28
- 2026-09-23 15:57fix(windcast): пересчёт бэктеста и прогонов агента после исправления утечки в калибровкеNuridin28
- 2026-09-23 15:57feat(api): проверяемый Copilot прогноза и почасовая погода ветра у станцииNuridin28
- 2026-09-23 15:57feat(solar): справочник солнечных станций Казахстана из OSMNuridin28
- 2026-09-23 15:57feat(frontend): панель ветра, выбор турбин в 3D-сцене, запасной 2D-режим и CopilotNuridin28
- 2026-09-23 15:57docs: журнал состояния, карта проекта и путь запросаNuridin28
- 2026-09-23 15:42docs(wind): document turbine placement, API and calculation assumptionsauyezkhanov
- 2026-09-23 15:41feat(wind): calculate output for new turbine models from live weatherauyezkhanov
- 2026-09-23 15:41feat(frontend): add turbine placement and model selection to Kazakhstan mapauyezkhanov
- 2026-09-23 15:32feat(windcast): живой пересчёт прогноза и эндпоинт предсказанийNuridin28
- 2026-09-23 15:32build: ML-стек и обучение модели в Docker-образеNuridin28
- 2026-09-23 15:32feat(frontend): экран прогнозов и доработки 3D-сценNuridin28
- 2026-09-23 15:32docs: журнал состояния и AGENTS.md для CodexNuridin28
- 2026-09-23 15:17feat(stations): каталог ветровых станций Казахстана и эндпоинт /stationsNuridin28
- 2026-09-23 15:17feat(frontend): карта Казахстана, 3D-сцена крыш, редизайн навигацииNuridin28
- 2026-09-23 15:17docs(context): журнал состояния после pull windcast и новых экрановNuridin28
- 2026-09-23 15:09feat(windcast): прогноз агента на тестовый период 31.01–27.02.2026 и READMENuralyAb
- 2026-09-23 15:09feat(api): эндпоинты прогноза ВЭС под контракт фронтендаNuralyAb
- 2026-09-23 15:09feat(windcast): пошаговый бэктест по протоколу кейса за 12 месяцевNuralyAb
- 2026-09-23 15:09feat(windcast): агент прогноза — граф шагов с пересчётом по выпускам погодыNuralyAb
- 2026-09-23 15:09feat(windcast): метрики и baseline-моделиNuralyAb
- 2026-09-23 15:09feat(windcast): каскад — поправка ветра + кривая мощности через Монте-КарлоNuralyAb
- 2026-09-23 15:09feat(windcast): прямая модель и ансамбль с конформной калибровкойNuralyAb
- 2026-09-23 15:09feat(windcast): архив прогнозов погоды Open-Meteo с доступом «на момент времени»NuralyAb
- 2026-09-23 15:09feat(windcast): признаки «на момент выпуска» и тест утечек будущегоNuralyAb
- 2026-09-23 15:09feat(windcast): загрузка SCADA турбин — час, UTC, флаги простоевNuralyAb
- 2026-09-23 15:02some changesNuridin28
- 2026-09-23 14:59design changesNuridin28
- 2026-09-23 14:55design changesNuridin28
- 2026-09-23 14:43design changesNuridin28
- 2026-09-23 14:32some changesNuridin28
- 2026-09-23 14:17some changesNuridin28
- 2026-09-23 13:25feat: delete arch artefactNuridin28
- 2026-09-23 13:21OpenToWork: MVP (backend, frontend, infra, docs)auyezkhanov
Структура репозитория (430 файлов)
.dockerignore .env.example .gitattributes .github/workflows/checks.yml .github/workflows/ci.yml .gitignore AGENTS.md CLAUDE.md Makefile README.md RUN.md alembic.ini app/__init__.py app/ai/__init__.py app/ai/embedder.py app/ai/guard.py app/ai/orchestrator.py app/ai/prompts.py app/ai/providers.py app/ai/schemas.py app/ai/validator.py app/api/__init__.py app/api/v1/__init__.py app/api/v1/ai.py app/api/v1/auth.py app/api/v1/entities.py app/api/v1/files.py app/api/v1/forecast.py app/api/v1/jobs.py app/api/v1/knowledge.py app/api/v1/predictions.py app/api/v1/router.py app/api/v1/solar.py app/api/v1/stations.py app/api/v1/wind.py app/auth/__init__.py app/auth/security.py app/cache/__init__.py app/cache/ratelimit.py app/cache/semantic.py app/config.py app/db.py app/deps.py app/domain/__init__.py app/domain/rules.py app/errors.py app/files/__init__.py app/files/storage.py app/main.py app/middleware.py app/models.py app/observability.py app/queue/__init__.py app/queue/redis_queue.py app/rag/__init__.py app/rag/pipeline.py app/scripts/__init__.py app/scripts/seed.py app/solar/__init__.py app/solar/catalog.py app/solar/data/almaty_center.json app/solar/data/astana_left_bank.json app/solar/data/kz_solar_farms.json app/solar/data/shymkent_center.json app/solar/model.py app/wind/UPSTREAM-LICENSE.txt app/wind/__init__.py app/wind/catalog.py app/wind/copilot.py app/wind/data/kz_wind_farms.json app/wind/data/wind_snapshot.json app/wind/forecast_store.py app/wind/model.py app/wind/reference_turbines.json app/wind/schemas.py app/wind/station_weather.py app/wind/turbines.json app/wind/turbines.py app/wind/weather.py app/workers/__init__.py app/workers/main.py artifacts/bids/2026-02-01/zayavka_2026-02-01.csv artifacts/bids/2026-02-01/zayavka_2026-02-01.docx artifacts/bids/2026-02-01/zayavka_2026-02-01.json artifacts/bids/2026-02-01/zayavka_2026-02-01.pdf artifacts/bids/2026-02-02/zayavka_2026-02-02.csv artifacts/bids/2026-02-02/zayavka_2026-02-02.docx artifacts/bids/2026-02-02/zayavka_2026-02-02.json artifacts/bids/2026-02-02/zayavka_2026-02-02.pdf artifacts/bids/2026-02-03/zayavka_2026-02-03.csv artifacts/bids/2026-02-03/zayavka_2026-02-03.docx artifacts/bids/2026-02-03/zayavka_2026-02-03.json artifacts/bids/2026-02-03/zayavka_2026-02-03.pdf artifacts/bids/2026-02-04/zayavka_2026-02-04.csv artifacts/bids/2026-02-04/zayavka_2026-02-04.docx artifacts/bids/2026-02-04/zayavka_2026-02-04.json artifacts/bids/2026-02-04/zayavka_2026-02-04.pdf artifacts/bids/2026-02-05/zayavka_2026-02-05.csv artifacts/bids/2026-02-05/zayavka_2026-02-05.docx artifacts/bids/2026-02-05/zayavka_2026-02-05.json artifacts/bids/2026-02-05/zayavka_2026-02-05.pdf artifacts/bids/2026-02-06/zayavka_2026-02-06.csv artifacts/bids/2026-02-06/zayavka_2026-02-06.docx artifacts/bids/2026-02-06/zayavka_2026-02-06.json artifacts/bids/2026-02-06/zayavka_2026-02-06.pdf artifacts/bids/2026-02-07/zayavka_2026-02-07.csv artifacts/bids/2026-02-07/zayavka_2026-02-07.docx artifacts/bids/2026-02-07/zayavka_2026-02-07.json artifacts/bids/2026-02-07/zayavka_2026-02-07.pdf artifacts/bids/2026-02-08/zayavka_2026-02-08.csv artifacts/bids/2026-02-08/zayavka_2026-02-08.docx artifacts/bids/2026-02-08/zayavka_2026-02-08.json artifacts/bids/2026-02-08/zayavka_2026-02-08.pdf artifacts/bids/2026-02-09/zayavka_2026-02-09.csv artifacts/bids/2026-02-09/zayavka_2026-02-09.docx artifacts/bids/2026-02-09/zayavka_2026-02-09.json artifacts/bids/2026-02-09/zayavka_2026-02-09.pdf artifacts/bids/2026-02-10/zayavka_2026-02-10.csv artifacts/bids/2026-02-10/zayavka_2026-02-10.docx artifacts/bids/2026-02-10/zayavka_2026-02-10.json artifacts/bids/2026-02-10/zayavka_2026-02-10.pdf artifacts/bids/2026-02-11/zayavka_2026-02-11.csv artifacts/bids/2026-02-11/zayavka_2026-02-11.docx artifacts/bids/2026-02-11/zayavka_2026-02-11.json artifacts/bids/2026-02-11/zayavka_2026-02-11.pdf artifacts/bids/2026-02-12/zayavka_2026-02-12.csv artifacts/bids/2026-02-12/zayavka_2026-02-12.docx artifacts/bids/2026-02-12/zayavka_2026-02-12.json artifacts/bids/2026-02-12/zayavka_2026-02-12.pdf artifacts/bids/2026-02-13/zayavka_2026-02-13.csv artifacts/bids/2026-02-13/zayavka_2026-02-13.docx artifacts/bids/2026-02-13/zayavka_2026-02-13.json artifacts/bids/2026-02-13/zayavka_2026-02-13.pdf artifacts/bids/2026-02-14/zayavka_2026-02-14.csv artifacts/bids/2026-02-14/zayavka_2026-02-14.docx artifacts/bids/2026-02-14/zayavka_2026-02-14.json artifacts/bids/2026-02-14/zayavka_2026-02-14.pdf artifacts/bids/2026-02-15/zayavka_2026-02-15.csv artifacts/bids/2026-02-15/zayavka_2026-02-15.docx artifacts/bids/2026-02-15/zayavka_2026-02-15.json artifacts/bids/2026-02-15/zayavka_2026-02-15.pdf artifacts/bids/2026-02-16/zayavka_2026-02-16.csv artifacts/bids/2026-02-16/zayavka_2026-02-16.docx artifacts/bids/2026-02-16/zayavka_2026-02-16.json artifacts/bids/2026-02-16/zayavka_2026-02-16.pdf artifacts/bids/2026-02-17/zayavka_2026-02-17.csv artifacts/bids/2026-02-17/zayavka_2026-02-17.docx artifacts/bids/2026-02-17/zayavka_2026-02-17.json artifacts/bids/2026-02-17/zayavka_2026-02-17.pdf artifacts/bids/2026-02-18/zayavka_2026-02-18.csv artifacts/bids/2026-02-18/zayavka_2026-02-18.docx artifacts/bids/2026-02-18/zayavka_2026-02-18.json artifacts/bids/2026-02-18/zayavka_2026-02-18.pdf artifacts/bids/2026-02-19/zayavka_2026-02-19.csv artifacts/bids/2026-02-19/zayavka_2026-02-19.docx artifacts/bids/2026-02-19/zayavka_2026-02-19.json artifacts/bids/2026-02-19/zayavka_2026-02-19.pdf artifacts/bids/2026-02-20/zayavka_2026-02-20.csv artifacts/bids/2026-02-20/zayavka_2026-02-20.docx artifacts/bids/2026-02-20/zayavka_2026-02-20.json artifacts/bids/2026-02-20/zayavka_2026-02-20.pdf artifacts/bids/2026-02-21/zayavka_2026-02-21.csv artifacts/bids/2026-02-21/zayavka_2026-02-21.docx artifacts/bids/2026-02-21/zayavka_2026-02-21.json artifacts/bids/2026-02-21/zayavka_2026-02-21.pdf artifacts/bids/2026-02-22/zayavka_2026-02-22.csv artifacts/bids/2026-02-22/zayavka_2026-02-22.docx artifacts/bids/2026-02-22/zayavka_2026-02-22.json artifacts/bids/2026-02-22/zayavka_2026-02-22.pdf artifacts/bids/2026-02-23/zayavka_2026-02-23.csv artifacts/bids/2026-02-23/zayavka_2026-02-23.docx artifacts/bids/2026-02-23/zayavka_2026-02-23.json artifacts/bids/2026-02-23/zayavka_2026-02-23.pdf artifacts/bids/2026-02-24/zayavka_2026-02-24.csv artifacts/bids/2026-02-24/zayavka_2026-02-24.docx artifacts/bids/2026-02-24/zayavka_2026-02-24.json artifacts/bids/2026-02-24/zayavka_2026-02-24.pdf artifacts/bids/2026-02-25/zayavka_2026-02-25.csv artifacts/bids/2026-02-25/zayavka_2026-02-25.docx artifacts/bids/2026-02-25/zayavka_2026-02-25.json artifacts/bids/2026-02-25/zayavka_2026-02-25.pdf artifacts/bids/2026-02-26/zayavka_2026-02-26.csv artifacts/bids/2026-02-26/zayavka_2026-02-26.docx artifacts/bids/2026-02-26/zayavka_2026-02-26.json artifacts/bids/2026-02-26/zayavka_2026-02-26.pdf artifacts/bids/2026-02-27/zayavka_2026-02-27.csv artifacts/bids/2026-02-27/zayavka_2026-02-27.docx artifacts/bids/2026-02-27/zayavka_2026-02-27.json artifacts/bids/2026-02-27/zayavka_2026-02-27.pdf artifacts/bids/2026-02-28/zayavka_2026-02-28.csv artifacts/bids/2026-02-28/zayavka_2026-02-28.docx artifacts/bids/2026-02-28/zayavka_2026-02-28.json artifacts/bids/2026-02-28/zayavka_2026-02-28.pdf artifacts/bids/index.json artifacts/explain/2026-01-31.json artifacts/explain/2026-02-01.json artifacts/explain/2026-02-02.json artifacts/explain/2026-02-03.json artifacts/explain/2026-02-04.json artifacts/explain/2026-02-05.json artifacts/explain/2026-02-06.json artifacts/explain/2026-02-07.json artifacts/explain/2026-02-08.json artifacts/explain/2026-02-09.json artifacts/explain/2026-02-10.json artifacts/explain/2026-02-11.json artifacts/explain/2026-02-12.json artifacts/explain/2026-02-13.json artifacts/explain/2026-02-14.json artifacts/explain/2026-02-15.json artifacts/explain/2026-02-16.json artifacts/explain/2026-02-17.json artifacts/explain/2026-02-18.json artifacts/explain/2026-02-19.json artifacts/explain/2026-02-20.json artifacts/explain/2026-02-21.json artifacts/explain/2026-02-22.json artifacts/explain/2026-02-23.json artifacts/explain/2026-02-24.json artifacts/explain/2026-02-25.json artifacts/explain/2026-02-26.json artifacts/explain/2026-02-27.json artifacts/explain/global.json artifacts/forecasts/2026-01-31.json artifacts/forecasts/2026-02-01.json artifacts/forecasts/2026-02-02.json artifacts/forecasts/2026-02-03.json artifacts/forecasts/2026-02-04.json artifacts/forecasts/2026-02-05.json artifacts/forecasts/2026-02-06.json artifacts/forecasts/2026-02-07.json artifacts/forecasts/2026-02-08.json artifacts/forecasts/2026-02-09.json artifacts/forecasts/2026-02-10.json artifacts/forecasts/2026-02-11.json artifacts/forecasts/2026-02-12.json artifacts/forecasts/2026-02-13.json artifacts/forecasts/2026-02-14.json artifacts/forecasts/2026-02-15.json artifacts/forecasts/2026-02-16.json artifacts/forecasts/2026-02-17.json artifacts/forecasts/2026-02-18.json artifacts/forecasts/2026-02-19.json artifacts/forecasts/2026-02-20.json artifacts/forecasts/2026-02-21.json artifacts/forecasts/2026-02-22.json artifacts/forecasts/2026-02-23.json artifacts/forecasts/2026-02-24.json artifacts/forecasts/2026-02-25.json artifacts/forecasts/2026-02-26.json artifacts/forecasts/2026-02-27.json artifacts/forecasts/backtest_runs.json artifacts/forecasts/index.json artifacts/forecasts/test_period_all_versions.csv artifacts/forecasts/test_period_forecast.csv artifacts/models/power_curve.json artifacts/reports/backtest_frontend.json artifacts/reports/backtest_predictions.parquet artifacts/reports/backtest_summary.json artifacts/reports/revision_value.json artifacts/weather/cma_grapes_global.parquet artifacts/weather/ecmwf_aifs025_single.parquet artifacts/weather/ecmwf_ifs025.parquet artifacts/weather/gem_seamless.parquet artifacts/weather/gfs_seamless.parquet artifacts/weather/icon_seamless.parquet artifacts/weather/ukmo_seamless.parquet context/00-state.md context/01-map.md context/02-features.md context/03-recipes.md context/04-troubleshooting.md context/05-request-flow.md context/06-kubernetes.md context/07-review-fixes.md context/README.md datasets/turbine_1.csv datasets/turbine_2.csv docker-compose.yml docs/00-index.md docs/01-architecture-overview.md docs/02-ai-llm-pipeline.md docs/03-database-schema.md docs/04-api-specification.md docs/05-deployment-and-kubernetes.md docs/06-security-observability.md docs/07-hackathon-execution-plan.md docs/case-mapping.md docs/city-fidelity.md docs/deployability.md docs/new-wind-turbines.md docs/screenshots/01-home.png docs/screenshots/02-forecast-nurly.png docs/screenshots/03-accuracy.png docs/screenshots/04-agent.png docs/screenshots/05-bid.png docs/screenshots/05b-bid-pdf.jpg docs/screenshots/06-predictions.png docs/screenshots/07-roofs.png docs/screenshots/08-platform.png frontend/.dockerignore frontend/index.html frontend/nginx.conf frontend/package-lock.json frontend/package.json frontend/src/App.tsx frontend/src/api.ts frontend/src/components.tsx frontend/src/main.tsx frontend/src/styles.css frontend/src/twin/Agent.tsx frontend/src/twin/Backtest.tsx frontend/src/twin/Bids.tsx frontend/src/twin/Explain.tsx frontend/src/twin/Flow.tsx frontend/src/twin/KzMap.tsx frontend/src/twin/LegacyWindMap.tsx frontend/src/twin/Loading.tsx frontend/src/twin/Placement.tsx frontend/src/twin/Predictions.tsx frontend/src/twin/Rooftops.tsx frontend/src/twin/SceneBoundary.tsx frontend/src/twin/StationView.tsx frontend/src/twin/UnitPanel.tsx frontend/src/twin/WindMap.tsx frontend/src/twin/WindPanel.tsx frontend/src/twin/WindPlacement.tsx frontend/src/twin/charts.tsx frontend/src/twin/cities.ts frontend/src/twin/data.ts frontend/src/twin/demo.ts frontend/src/twin/design.css frontend/src/twin/explain.css frontend/src/twin/flow-refresh.css frontend/src/twin/kz_outline.json frontend/src/twin/kzmap.css frontend/src/twin/loading.css frontend/src/twin/predictions.css frontend/src/twin/rooftops.css frontend/src/twin/scene/CityMap.tsx frontend/src/twin/scene/RooftopScene.tsx frontend/src/twin/scene/Scene.tsx frontend/src/twin/scene/buildings.ts frontend/src/twin/scene/city-map.css frontend/src/twin/scene/cityEnvironment.ts frontend/src/twin/scene/infrastructure.ts frontend/src/twin/scene/picking.ts frontend/src/twin/scene/terrain.ts frontend/src/twin/scene/turbines.ts frontend/src/twin/twin.css frontend/src/twin/wind-drafts.ts frontend/src/twin/wind-placement.css frontend/src/twin/windpanel.css frontend/tsconfig.json frontend/vite.config.ts infra/docker/backend.Dockerfile infra/docker/frontend.Dockerfile infra/docker/grafana/dashboards/hackalem.json infra/docker/grafana/provisioning/dashboards/dashboards.yml infra/docker/grafana/provisioning/datasources/prometheus.yml infra/docker/initdb/00-extensions.sql infra/docker/prometheus.yml infra/k8s-local/kustomization.yaml infra/k8s/api-deployment.yaml infra/k8s/api-service.yaml infra/k8s/configmap.yaml infra/k8s/frontend.yaml infra/k8s/hpa.yaml infra/k8s/ingress.yaml infra/k8s/kustomization.yaml infra/k8s/migrate-job.yaml infra/k8s/namespace.yaml infra/k8s/secret.yaml infra/k8s/stateful-services.yaml infra/k8s/worker-deployment.yaml migrations/env.py migrations/script.py.mako migrations/versions/0001_initial.py migrations/versions/0002_feedback_and_cache_accounting.py migrations/versions/0003_isolation_and_embedding_version.py migrations/versions/0004_wind_farms.py pyproject.toml scripts/build_wind_catalog.py scripts/entrypoint.sh scripts/fetch_solar_data.py scripts/fetch_solar_farms.py scripts/fetch_wind_farms.py scripts/smoke_test.py scripts/smoke_wind.py tests/test_ai_layer.py tests/test_app_boot.py tests/test_authz_integration.py tests/test_forecast_copilot.py tests/test_security_unit.py tests/test_solar.py tests/test_solar_catalog.py tests/test_solar_geodata.py tests/test_wind.py tests/test_wind_weather.py tests/test_windcast_agent.py tests/test_windcast_api.py tests/test_windcast_explain.py tests/test_windcast_leakage.py tests/test_windcast_submission.py tests/test_worker_redelivery.py windcast/__init__.py windcast/__main__.py windcast/agent/__init__.py windcast/agent/graph.py windcast/agent/report.py windcast/agent/runner.py windcast/agent/tools.py windcast/assets/fonts/DejaVuSans-Bold.ttf windcast/assets/fonts/DejaVuSans.ttf windcast/assets/fonts/README.md windcast/backtest.py windcast/config.py windcast/explain.py windcast/features.py windcast/live.py windcast/metrics.py windcast/models/__init__.py windcast/models/baselines.py windcast/models/cascade.py windcast/models/direct.py windcast/models/ensemble.py windcast/pipeline.py windcast/scada.py windcast/submission.py windcast/weather.py
Ветки (2)
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.