О проекте
Команда: INC.
Кейс HackAlem.ai: Энергетика — Самрук-Казына
Стадия: Рабочий MVP
Задача кейса: AI-агент, прогнозирующий выработку ветровой электростанции по погодным данным
Суть
Агентная система почасового прогноза нормализованной мощности двух турбин ВЭС на 24–48 ч по архивным прогнозам NOAA GFS с анализом через OpenAI и пересчётом при обновлении погоды.
Как устроено
История двух турбин (291 859 строк, март 2023 – январь 2026) и 30 пакетов NOAA GFS (GRIB через ecCodes) с проверкой доступности на момент запуска → эмпирическая кривая «ветер–мощность» → OpenAI Responses API (gpt-4o-mini) анализирует результат → обновление погоды и пересчёт. Бэкенд на stdlib HTTP, UI на Node ES-модулях; пакетный режим воспроизводит февраль (28 запусков × 48 ч). Температурная поправка опциональна и по умолчанию выключена.
Ключевые решения
- эмпирическая кривая мощности
- NOAA GFS GRIB (ecCodes)
- проверка доступности погоды по S3 Last-Modified
- пакетный бэктест февраля
- опциональная температурная поправка
Метрики
MAE 0.267–0.388 (нормализованная шкала) на январских проверках; 80 backend + 41 data + 30 UI тестов
Особенности
Строгий контроль утечки будущего: доступность погодного выпуска проверяется по Last-Modified объектов S3, provenance и контрольные суммы для каждого пакета, побайтово воспроизводимый CSV февраля; честно перечислены допущения (роль временной метки, высота анемометра).
Стек
Python · Node.js · JavaScript · Docker Compose · ecCodes · NOAA GFS S3
Модели
GPT-4o-mini (OpenAI Responses API)
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-8df1dc64-inc.
README
README.mdVento — Agentic AI для двух турбин ВЭС
Почасовой прогноз нормализованной мощности на 24–48 часов: архив NOAA GFS → проверка доступности на момент запуска → подготовка реальной истории → эмпирическая кривая мощности → анализ OpenAI → обновление погоды и повторный расчёт при изменении входов.
В общей версии объединены Data Ильи, интерфейс Богдана (22Malish22) и backend с интеграцией Codex (сессия smmaff). Полный текст ТЗ и критерии оценки сохранены в docs/task-board.md. Архитектура: docs/architecture.md, интерфейсы: docs/api-contract.md.
Что работает
Температурная поправка доступна как опциональная надстройка через apply_temperature_correction: true в запросе /api/forecasts или /api/agent/forecasts, выключена по умолчанию и не меняет базовый расчёт по кривой ветер–мощность.
- История двух турбин за март 2023 — январь 2026: 291 859 исходных строк с контрольными суммами.
- 30 проверяемых погодных пакетов NOAA, включая 28 ежедневных запусков для покрытия февраля.
- API расчёта и агентного цикла; реальный OpenAI Responses API, без подстановки фиктивных ответов.
- UI: выбор даты/часа UTC, турбин, 24/48 часов; график, таблица, CSV, анализ и выполненные этапы.
- Февральский результат: 28 запусков по 48 часов, 2 688 точек; CSV первых суток каждого запуска содержит 1 344 строки, по 672 уникальных часа на турбину.
Запуск из чистого клона
Нужны Git, Python 3.11+ (проверено на 3.13), Node.js 22+. UI не требует npm install.
git clone https://github.com/BAITC-Hacks/hack-8df1dc64-inc.git
cd hack-8df1dc64-inc
python -m venv .venv
Активировать окружение:
# Linux/macOS
source .venv/bin/activate
# Windows PowerShell вместо строки выше
.\.venv\Scripts\Activate.ps1
python -m pip install -r data/requirements.txt
Куда проверяющему вставить ключ OpenAI
В корне репозитория (рядом с этим README.md) создайте локальный файл .env из шаблона .env.example, если файла ещё нет:
# Linux/macOS
cp .env.example .env
# Windows PowerShell: вместо команды выше
Copy-Item .env.example .env
Откройте .env и вставьте свой ключ в строку OPENAI_API_KEY после знака =:
OPENAI_API_KEY=ваш_ключ_OpenAI
OPENAI_MODEL=gpt-4o-mini
Не перезаписывайте существующий .env. OPENAI_MODEL можно изменить, если у ключа есть доступ к другой модели OpenAI. Сохраните файл и запустите backend по инструкции ниже; если сервер уже работает, перезапустите его. Для Docker Compose используется тот же .env. Ключ остаётся на backend: не вставляйте его в UI, код или Git.
Проверяющий использует свой ключ: ключ не входит в поставку проекта.
Первый терминал, из корня репозитория:
python -m backend.server
Второй терминал, из того же корня:
node ui/server.mjs
Открыть http://127.0.0.1:3000. Backend: http://127.0.0.1:8000/api/health. Остановить процессы: Ctrl+C. Серверы предназначены для локального демо.
Сценарий проверки
- Оставить 31 января 2026, 19:00 UTC, 24 часа, две турбины и явное допущение
end. - Нажать «Сформировать прогноз». Ожидаются 48 точек, график, таблица, реальный анализ OpenAI и длительности этапов. Первый расчёт истории может занять 10–20 секунд.
- Выбрать 14 февраля, 19:00 UTC, 48 часов и турбину 2: ожидаются 48 точек другого запуска.
- «Обновить погоду и пересчитать» повторно загружает NOAA. Это может занять несколько минут. Неизменившийся архив может дать тот же прогноз: это нормальный результат проверки обновлений.
- Скачать CSV и проверить поля времени, нормализованной мощности, ветра и температуры.
Без ключа остаются доступны численный /api/forecasts, пакетный расчёт и автоматические тесты. Агентный endpoint честно возвращает ошибку настройки, а не имитацию анализа.
Проверки
Финальная объединённая версия проверена из чистого клона с новым Python-окружением: 80 backend + 41 data + 30 UI тестов, два реальных HTTP-запроса с OpenAI (второй с новой загрузкой NOAA) и полный повтор февраля с побайтово одинаковым CSV; подробности — отчёт интеграции.
python -m unittest discover -s backend/tests -t . -v
python -m unittest discover -s data/tests -v
node --test ui/tests/*.test.mjs
Эквивалентные Linux/Git Bash команды: ./backend/test.sh, ./data/test.sh, ./ui/test.sh.
Реальная проверка UI-клиента через HTTP при работающем backend (выполняет платные вызовы OpenAI):
node ui/smoke-api.mjs http://127.0.0.1:8000
Для проверки повторной загрузки NOAA установить LIVE_REFRESH=1 перед этой командой.
Внешние API изолируются только в unit-тестах; этот smoke-сценарий использует настоящие сервисы.
Повторить февраль без OpenAI
python -m backend.batch_forecast --output-dir backend/runtime/february-recheck --timestamp-role end
Каталог результата должен быть новым. Сохраняются 28 JSON, february.csv и summary.json. Используются реальные архивы; февральских фактических мощностей нет, поэтому результат не содержит метрик точности февраля. Это численный пакетный режим, агентный анализ доступен отдельно через UI/API.
Docker Compose
Подготовлены два контейнера и общий запуск:
docker compose up --build
Предварительно заполнить локальный .env. UI: http://127.0.0.1:3000. Остановка: docker compose down.
Погодный кэш хранится в именованном томе; .env исключён из контекста сборки и ключ передаётся только backend.
Сборка и запуск контейнеров ещё не проверены: на рабочей машине отсутствует Docker Engine. Нативный запуск описан выше.
Настройки
| Переменная | Значение по умолчанию / назначение |
|---|---|
| OPENAI_API_KEY | Требуется для реального анализа OpenAI |
| OPENAI_MODEL | gpt-4o-mini |
| WEATHER_ARCHIVE_DIR | data/weather |
| WEATHER_CACHE_DIR | backend/runtime/weather |
| UI_ORIGINS | http://127.0.0.1:3000,http://localhost:3000 |
| PORT, HOST | UI: 3000, 127.0.0.1; задаются в окружении Node |
Стек: Python stdlib HTTP/JSON, ecCodes 2.48.0 для GRIB, JavaScript ES modules/Node static server, OpenAI Responses API, NOAA GFS public S3. Постоянное хранение — исходные gzip, JSON с provenance и CSV; отдельный сервер БД не нужен.
Известные ограничения
-
Смысл временной метки в исходных данных выбран как допущение, не подтверждён организатором.
-
Высота исторического измерения ветра неизвестна, поправка высоты не применена.
-
Время публикации телеметрии неизвестно, используется допущение "конец интервала".
-
Часть скоростей ветра в прогнозе выходит за диапазон исторической кривой обучения, используется ближайшее крайнее значение (экстраполяция).
-
Прогнозируется нормализованная мощность 0–1; без номинальной мощности нет честного перевода в кВт/кВт·ч.
-
Роль метки 10-минутного интервала пока не подтверждена организатором.
end— рабочее допущение; ответ всегда содержитtimestamp_role_confirmed=false. -
Архив GFS имеет сетку 0.5°, поэтому близкие турбины получают одинаковую погоду. Историческая высота анемометра неизвестна, поправка высоты не применяется.
-
Историческая доступность погоды проверяется по выпуску и Last-Modified конкретных объектов S3. Это доказательство времени текущей версии объекта, не журнал фактических запросов в прошлом.
-
Baseline — эмпирическая кривая ветер–мощность, без обучения сложной ML-модели. Январские проверки Data показывают MAE около 0.267–0.388 в нормализованной шкале; это ограниченная проверка, не оценка февраля. Подробнее: Data handoff.
-
Февральская фактическая мощность отсутствует. Тесты совместимости не доказывают точность прогнозирования.
-
Автоматические проверки DOM/HTTP не заменяют визуальную приёмку в браузере; она ещё требуется.
Проверка малых скоростей: распределение истории и отклик модели. Около 13,4% обучающих часов имеют ветер ≤2 м/с; низкие значения мощности согласуются с историей.
Доступ для экспертов и вклад
История, погодные пакеты, provenance и февральский расчёт доступны без аккаунта. Для live-анализа команда должна передать экспертам тестовый ключ отдельным закрытым каналом либо показать запущенный локальный стенд с настроенным ключом. Общедоступный стенд пока не опубликован; секретов в репозитории нет.
Шаблоны инструкций и .claude/ подготовлены заранее. Код Data, backend и UI создан во время работы над кейсом при помощи Codex; история и объяснение изменений — docs/progress.md. Исходный шаблон README и план сохранены в docs/archive. Все изменения ведутся в репозитории организатора.
Авторы коммитов
История коммитов
- 2026-09-23 17:54Подтвердить финальную интеграцию из чистого клона и реальные NOAA/OpenAI проверкиsmmaff
- 2026-09-23 17:44Объединить все модули: backend B7, Data D2 и UI Ventosmmaff
- 2026-09-23 17:39Добавить опциональную температурную поправку без изменения базовой кривойsmmaff
- 2026-09-23 17:35Уточнить NOAA, единицы времени и проверить модель при слабом ветреsmmaff
- 2026-09-23 17:32feat(ui): brand Vento and improve forecast chart readability22Malish22
- 2026-09-23 17:28docs: explain where reviewers set OpenAI API keysmmaff
- 2026-09-23 16:49Зафиксировать локальный запуск и проверку двух прогнозовsmmaff
- 2026-09-23 16:35Подтвердить чистый запуск и воспроизводимость февральского расчётаsmmaff
Все коммиты (33)
- 2026-09-23 16:30Подключить UI к агентному циклу и рассчитать февраль по NOAAsmmaff
- 2026-09-23 16:06Объединить опубликованный UI и сохранить актуальные инструкции модулейsmmaff
- 2026-09-23 16:05Объединить Data D2 с backend и сохранить контракты обоих модулейsmmaff
- 2026-09-23 15:55feat(data): deliver audited NOAA forecast archive and backend integration checksMcPon4ik
- 2026-09-23 15:55feat(ui): animate wind turbines and breeze lines22Malish22
- 2026-09-23 15:46Подключить реальный анализ OpenAI и формат погоды Open-Meteosmmaff
- 2026-09-23 15:38fix(ui): redraw turbine silhouettes and credit 22Malish2222Malish22
- 2026-09-23 15:23Реализовать расчёт мощности B2 и HTTP API B3 с проверкой историиsmmaff
- 2026-09-23 15:21feat(ui): revise visual design and add loading and site information22Malish22
- 2026-09-23 15:12Зафиксировать контракты расчёта B2 и HTTP API B3smmaff
- 2026-09-23 15:05feat(ui): add turbine and UTC hour selection with saved settings22Malish22
- 2026-09-23 15:03Merge remote-tracking branch 'origin/data' into backendsmmaff
- 2026-09-23 15:03Синхронизировать общие документы backend с опубликованным контрактом Data D1smmaff
- 2026-09-23 14:53feat(data): import organizer turbine history and prepare cutoff-safe hourly dataMcPon4ik
- 2026-09-23 14:49merge: update markdown content from main b804db322Malish22
- 2026-09-23 14:46docs: import module markdown and audit documentation gaps22Malish22
- 2026-09-23 14:46Закрепить регулярную синхронизацию агентов и обновить отчёт UIsmmaff
- 2026-09-23 14:43merge: sync shared main files into ui22Malish22
- 2026-09-23 14:29Синхронизировать общие документы и локальные файлы командыsmmaff
- 2026-09-23 14:16feat(ui): add wind forecast setup screen and real tests22Malish22
- 2026-09-23 14:12Реализовать B1: проверку погодных данных и почасового горизонтаsmmaff
- 2026-09-23 14:09feat(data): audit source CSV quality with real tests and usage guideMcPon4ik
- 2026-09-23 14:08docs(data): record CSV audit ownership and pending source datasetMcPon4ik
- 2026-09-23 14:02Согласовать контракт B1 и зафиксировать начало работы backendsmmaff
- 2026-09-23 13:40Заполнить контрактные файлы под кейс: Agentic AI-систему для прогнозирования почасовой выработки ветроэлектростанции (ВЭС) на горизонте 24–48 часов.smmaff
Структура репозитория (201 файлов)
.claude/.gitattributes .claude/agents/supervisor.md .claude/hooks/check-progress-and-tests.sh .claude/settings.json .dockerignore .env.example .gitignore AGENTS.md CLAUDE.md README.md backend/.gitattributes backend/.gitignore backend/Dockerfile backend/README.md backend/__init__.py backend/agent.py backend/analyze_weather.py backend/audit_low_wind.py backend/b4-verification.md backend/batch_forecast.py backend/config.py backend/contract-proposal.md backend/examples/README.md backend/examples/open_meteo_user_sample.json backend/manual_check.py backend/manual_history.py backend/model.py backend/openai_analysis.py backend/reports/clean-clone.md backend/reports/february/february.csv backend/reports/february/runs/20260131T190000Z.json backend/reports/february/runs/20260201T190000Z.json backend/reports/february/runs/20260202T190000Z.json backend/reports/february/runs/20260203T190000Z.json backend/reports/february/runs/20260204T190000Z.json backend/reports/february/runs/20260205T190000Z.json backend/reports/february/runs/20260206T190000Z.json backend/reports/february/runs/20260207T190000Z.json backend/reports/february/runs/20260208T190000Z.json backend/reports/february/runs/20260209T190000Z.json backend/reports/february/runs/20260210T190000Z.json backend/reports/february/runs/20260211T190000Z.json backend/reports/february/runs/20260212T190000Z.json backend/reports/february/runs/20260213T190000Z.json backend/reports/february/runs/20260214T190000Z.json backend/reports/february/runs/20260215T190000Z.json backend/reports/february/runs/20260216T190000Z.json backend/reports/february/runs/20260217T190000Z.json backend/reports/february/runs/20260218T190000Z.json backend/reports/february/runs/20260219T190000Z.json backend/reports/february/runs/20260220T190000Z.json backend/reports/february/runs/20260221T190000Z.json backend/reports/february/runs/20260222T190000Z.json backend/reports/february/runs/20260223T190000Z.json backend/reports/february/runs/20260224T190000Z.json backend/reports/february/runs/20260225T190000Z.json backend/reports/february/runs/20260226T190000Z.json backend/reports/february/runs/20260227T190000Z.json backend/reports/february/summary.json backend/reports/final-integration.json backend/reports/live-cycle.json backend/reports/low-wind/README.md backend/reports/low-wind/distribution.png backend/reports/low-wind/live-checks.json backend/reports/low-wind/summary.json backend/requirements-audit.txt backend/server.py backend/service.py backend/temperature_correction.py backend/test.sh backend/tests/__init__.py backend/tests/test_analysis.py backend/tests/test_model.py backend/tests/test_server.py backend/tests/test_service.py backend/tests/test_temperature_correction.py backend/tests/test_validation.py backend/tests/test_workflow.py backend/validation.py backend/weather_analysis.py backend/weather_provider.py compose.yaml data/.gitignore data/README.md data/__init__.py data/audit_csv.py data/check_backend.py data/history.py data/prepare_history.py data/prepare_weather.py data/raw/manifest.json data/raw/turbine_1.csv.gz data/raw/turbine_2.csv.gz data/reports/D2-handoff.md data/reports/backend_current_integration.json data/reports/source_audit.json data/reports/weather_coverage.json data/requirements.txt data/test.sh data/tests/test_audit_csv.py data/tests/test_history.py data/tests/test_weather.py data/weather/20260115T190000Z.json data/weather/20260115T190000Z.provenance.json data/weather/20260123T190000Z.json data/weather/20260123T190000Z.provenance.json data/weather/20260131T190000Z.json data/weather/20260131T190000Z.provenance.json data/weather/20260201T190000Z.json data/weather/20260201T190000Z.provenance.json data/weather/20260202T190000Z.json data/weather/20260202T190000Z.provenance.json data/weather/20260203T190000Z.json data/weather/20260203T190000Z.provenance.json data/weather/20260204T190000Z.json data/weather/20260204T190000Z.provenance.json data/weather/20260205T190000Z.json data/weather/20260205T190000Z.provenance.json data/weather/20260206T190000Z.json data/weather/20260206T190000Z.provenance.json data/weather/20260207T190000Z.json data/weather/20260207T190000Z.provenance.json data/weather/20260208T190000Z.json data/weather/20260208T190000Z.provenance.json data/weather/20260209T190000Z.json data/weather/20260209T190000Z.provenance.json data/weather/20260210T190000Z.json data/weather/20260210T190000Z.provenance.json data/weather/20260211T190000Z.json data/weather/20260211T190000Z.provenance.json data/weather/20260212T190000Z.json data/weather/20260212T190000Z.provenance.json data/weather/20260213T190000Z.json data/weather/20260213T190000Z.provenance.json data/weather/20260214T190000Z.json data/weather/20260214T190000Z.provenance.json data/weather/20260215T190000Z.json data/weather/20260215T190000Z.provenance.json data/weather/20260216T190000Z.json data/weather/20260216T190000Z.provenance.json data/weather/20260217T190000Z.json data/weather/20260217T190000Z.provenance.json data/weather/20260218T190000Z.json data/weather/20260218T190000Z.provenance.json data/weather/20260219T190000Z.json data/weather/20260219T190000Z.provenance.json data/weather/20260220T190000Z.json data/weather/20260220T190000Z.provenance.json data/weather/20260221T190000Z.json data/weather/20260221T190000Z.provenance.json data/weather/20260222T190000Z.json data/weather/20260222T190000Z.provenance.json data/weather/20260223T190000Z.json data/weather/20260223T190000Z.provenance.json data/weather/20260224T190000Z.json data/weather/20260224T190000Z.provenance.json data/weather/20260225T190000Z.json data/weather/20260225T190000Z.provenance.json data/weather/20260226T190000Z.json data/weather/20260226T190000Z.provenance.json data/weather/20260227T190000Z.json data/weather/20260227T190000Z.provenance.json data/weather_gfs.py docs/api-contract.md docs/architecture.md docs/archive/README-before-integration.md docs/archive/architecture-before-integration.md docs/archive/plan-before-integration.md docs/db-schema.md docs/generator-prompt.md docs/handover-protocol.md docs/kickoff-prompt-template.md docs/md-audit.md docs/plan.md docs/progress.md docs/repository-sync.md docs/resources-needed.md docs/task-board.md ui/.gitattributes ui/Dockerfile ui/README.md ui/api.mjs ui/app.mjs ui/boot.mjs ui/design-review.md ui/draft.mjs ui/index.html ui/initialization.mjs ui/parameters.mjs ui/privacy.html ui/results.mjs ui/server.mjs ui/smoke-api.mjs ui/styles.css ui/terms.html ui/test.sh ui/tests/api.test.mjs ui/tests/design.test.mjs ui/tests/draft.test.mjs ui/tests/results.test.mjs ui/tests/ui.test.mjs
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.