О проекте
Команда: Pushin’ Code
Кейс HackAlem.ai: Энергетика — Самрук-Казына
Стадия: Доведённый продукт
Задача кейса: AI-агент, прогнозирующий выработку ветровой электростанции по погодным данным
Суть
Agentic-система для диспетчера ВЭС: сама берёт архивный прогноз погоды, считает почасовую выработку на 48 ч с коридором P10–P90, флагует риски и решает о пересчёте; февраль 2026 воспроизведён 29 выпусками без заглядывания в будущее.
Как устроено
SCADA 10 мин агрегируется в часовой ряд с флагом valid; квантильная модель HistGradientBoosting (P10/P50/P90) обучена на архивных прогнозах погоды против факта. LLM-агент (gpt-5.4-mini, function calling) проходит цикл инструментов fetch_weather (Open-Meteo Previous Runs API/кэш) → check_data → run_model → analyze_forecast (рампы, обледенение, ветер >20 м/с, расхождение моделей) → recalc_forecast → publish_forecast; числа считает только модель, правило «без будущего» — код. В Live-режиме watcher будит агента каждые 30 минут; без ключа работает детерминированный режим.
Ключевые решения
- HistGradientBoosting quantile regression
- P10/P50/P90
- Open-Meteo Previous Runs API
- function calling
- backtest
- persistence/power-curve baselines
- SSE
- scheduled watcher
Метрики
Январь 2026: nMAE 15,8% против 19,4% (кривая мощности) и 36,5% (персистентность); независимая часть 8–31.01 — 16,8%; факт в P10–P90 в 80,5% часов
Особенности
Строгое правило «без будущего» (учёт задержки публикации прогонов) с верификатором scripts/verify.py; агент сам решает, пересчитывать ли при новом прогоне; ленты решений в outputs/traces; развёрнутое демо без регистрации.
Стек
Python · FastAPI · scikit-learn · React · Docker Compose · Caddy
Модели
gpt-5.4-mini
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-07de2d2d-pushin-code.
README
README.mdWindcast — агент прогноза выработки ВЭС на 48 часов
Windcast — Agentic AI-система для диспетчера ветроэлектростанции: к каждому моменту выпуска агент сам берёт архивный прогноз погоды, опубликованный до этого момента, считает выработку на 48 часов с вероятным диапазоном, находит риски, при выходе нового прогона решает, пересчитывать ли, и публикует выпуск. Февраль 2026 воспроизведён день за днём — 29 выпусков с 31.01 по 28.02, без единого взгляда в будущее; в Live агент работает сам.
Демо без регистрации: https://pushin.codes · Трек: «Энергетика», HackAlem AI, 23.09.2026 · Команда: Pushin' Code
Проверяющему: 5 минут
| Что | Где |
|---|---|
| Что это | Агент диспетчера ВЭС: сам берёт архивный прогноз погоды, опубликованный до момента выпуска, считает выработку на 48 ч с вероятным диапазоном, ищет риски, решает о пересчёте при новом прогоне, публикует выпуск |
| Запуск | git clone … && docker compose up --build → http://localhost:8080 (ключ OpenAI не нужен) — § 6 |
| Проверка честности | python3 scripts/verify.py → PASS: 29 выпусков, 4176 строк, каждый прогон погоды опубликован до момента выпуска — § 4 |
| Результаты | outputs/forecasts/{D}.csv (29 выпусков 31.01–28.02) и outputs/evaluation/february_day_ahead.csv — один прогноз на каждый час февраля; оценить одной командой: python3 scripts/score.py --actual <папка с фактом> — § 9 |
| Точность | Январь (факт есть): nMAE 15,8 % против 19,4 % у кривой мощности и 36,5 % у персистентности; независимая часть 8–31.01 — 16,8 %; факт в вероятном диапазоне в 80,5 % часов |
| Где agentic AI | Порядок цикла из ТЗ, решение о пересчёте, реакция на сбой и сводка — LLM (gpt-5.4-mini, function calling); числа — только модель; правило «без будущего» — код; ленты решений — outputs/traces/ — § 3.2 |
| Демо без регистрации | https://pushin.codes — кнопка «Как проверить за 3 минуты» в шапке; чат «Спросить агента» справа внизу |
| Честные ограничения | Факта за февраль нет; в архиве событие «новый прогон» объявляет код; одна погодная модель — § 12 |
Быстрый старт
git clone https://github.com/BAITC-Hacks/hack-07de2d2d-pushin-code && cd hack-07de2d2d-pushin-code
cp .env.example .env # необязательно: без OPENAI_API_KEY агент работает детерминированно
docker compose up --build # первая сборка — несколько минут; затем открыть http://localhost:8080
curl http://localhost:8080/health # в другом терминале: {"ok":true,…,"issues_ready":29,"ports":{…"real"}}
python3 scripts/verify.py # из корня клона, только стандартная библиотека → PASS (14 проверок)
python3 scripts/score.py --actual <папка с turbine_1.csv и turbine_2.csv> # точность февраля, § 9
Как проверить за 3 минуты — § 8. Без Docker —
§ 6.3. Что проверяет verify.py — § 4.
Цифры качества — § 9. Этот путь проверен 23.09 в 16:17 на свежем клоне с GitHub без
.env — § 6.5.
Содержание: 1 Описание · 2 Соответствие задаче · 3 Архитектура · 4 Без будущего · 5 Технологии · 6 Установка и запуск · 7 Окружение · 8 Проверка · 9 Результаты · 10 Структура · 11 Сторонние компоненты · 12 Ограничения · 13 Команда
1. Описание решения и назначение
Для кого. Для диспетчера ВЭС. Каждый день он заявляет почасовой график выработки на следующие сутки, а отклонение факта от графика даёт небаланс: его покрывают резервом и за него платят. Выработка ветровой станции целиком зависит от погоды, поэтому одной цифры диспетчеру мало. Ему нужны ещё оценка неопределённости и предупреждения о рискованных часах.
Что получает диспетчер в каждом выпуске — 48 часов × {турбина 1, турбина 2, ВЭС}:
- P50 — ожидаемая выработка в долях номинала;
- коридор P10–P90: на январском бэктесте факт попал в него в 80,5 % часов (цель ≈ 80 %). P10 — осторожная оценка для заявки, а ширина коридора показывает риск;
- флаги на оси времени: рампа (изменение ≥ 30 % номинала за 2 ч), обледенение (температура от −3 до +1 °C не меньше 2 ч подряд), ветер > 20 м/с, расхождение погодных моделей, скачок против прошлой версии;
- сводку простым языком и строку «что изменилось», если прогноз пересчитан;
- CSV выпуска для своих систем.
Выигрыш на январе 2026: nMAE модели 15,8 % номинала против 19,4 % у лучшей простой базовой линии (кривая мощности по прогнозному ветру) — ошибка меньше на 18,5 %; против персистентности («завтра как сегодня», 36,5 %) — меньше в 2,3 раза. Подробности и остальные метрики — § 9.
Что агент делает сам. Полный цикл из ТЗ, этап за этапом:
- Получение внешних погодных данных —
fetch_weather: Open-Meteo по координатам ВЭС, только прогоны, опубликованные до момента выпуска. Без сети берёт сохранённые ответы изdata/weather_cache/. - Подготовка данных —
check_data: 48 часов без пропусков, значения конечные, все прогоны опубликованы до момента выпуска. - Запуск модели —
run_model. - Формирование почасового прогноза: P10/P50/P90 × 48 ч × {1, 2, ВЭС}.
- Анализ результата —
analyze_forecast: флаги рисков и сводка. - Повторный расчёт при обновлении входных данных —
recalc_forecast. Когда выходит более свежий прогон, агент сравнивает его с прежним и решает сам: пересчитать (версия v2) или объяснить, почему пересчёт не нужен. Если прогон вышел бы после момента выпуска, код откажет в пересчёте.
Цикл завершается действием publish_forecast: агент записывает CSV, запись выпуска и ленту шагов.
Порядок шагов и решения — за LLM (OpenAI, function calling), числа прогноза считает только модель
(кто что решает — § 3.2).
Без человека. В режиме Live бэкенд сам будит агента раз в 30 минут: тот проверяет, вышел ли новый
прогон, решает, выпускать ли новую версию, и записывает итог в журнал (GET /api/live/journal, вкладка
«Live»). Если источник погоды отказал, агент повторяет запрос, а при недоступности API берёт кэш
и предупреждает о сниженном доверии к выпуску.
Демо-доступ (п. 5.6.6). https://pushin.codes открывается без регистрации и личных аккаунтов. Агент там работает в режиме LLM на ключе OpenAI команды (§ 6.6). Для локального запуска ключ не нужен: без него агент проходит тот же цикл в детерминированном режиме и выдаёт те же числа.
Скриншоты с https://pushin.codes — вкладки «Февраль 2026», «Live» и «Качество»:
2. Как это соответствует задаче
Текст ТЗ — docs/hackathon/task.md.
| Требование ТЗ | Как сделано | Где в коде | Как проверить |
|---|---|---|---|
| Модель почасовой выработки на исторических данных | SCADA 10 мин → часовой ряд в UTC с флагом valid (пропуски, простои и отключение Т1 в 05–07.2024 исключены). Квантильная модель P10/P50/P90 (scikit-learn HistGradientBoosting) учится на архивных прогнозах погоды против фактической мощности; для сравнения — три базовые линии | backend/windcast/data.py, model.py, evaluate.py | python -m windcast.data; python -m windcast.evaluate; вкладка «Качество»; outputs/metrics_jan.json |
| Сама получает по координатам ВЭС из открытых источников прогнозы погоды, доступные на момент прогнозирования | fetch_weather сам запрашивает Open-Meteo Previous Runs API по обеим координатам и берёт только прогоны, опубликованные до момента выпуска T (§ 4). Источник api или cache пишется в каждое событие | weather.py, tools.py, timeline.py | кнопка «✓ Без будущего»; meta.source в ленте шагов; weather_runs в GET /api/forecasts/{D} |
| Прогноз на 24–48 ч с почасовой детализацией | 48 часов: h = 1…48, то есть (D+1) 00:00 … (D+2) 23:00 по UTC+5, для турбин 1, 2 и ВЭС, P10/P50/P90 | model.py, outputs/forecasts/{D}.csv (§ 9) | verify.py: 144 строки в каждом выпуске |
| Agentic AI: система сама выполняет полный цикл | Агент с шестью инструментами: LLM выбирает следующий шаг, каждый шаг — событие с этапом meta.stage. В Live агент запускается сам по расписанию и ведёт журнал | agent.py, tools.py, watcher.py | панель агента: этапы загораются по очереди; outputs/traces/{D}.jsonl; GET /api/live/journal |
| → получение внешних погодных данных | fetch_weather | tools.py, weather.py | этап «погода» (meta.stage = weather) |
| → подготовка данных | check_data | tools.py, data.py | этап «подготовка» (prep) |
| → запуск модели | run_model | tools.py, model.py | этап «модель» (model) |
| → формирование почасового прогноза | результат run_model: 144 строки P10/P50/P90 | tools.py, store.py | этап «прогноз» (forecast) |
| → анализ результата | analyze_forecast: флаги рисков и сводка | tools.py | этап «анализ» (analysis) |
| → повторный расчёт при обновлении входных данных | вышел новый прогон → агент решает → recalc_forecast → v2 или «пересчёт не нужен» | agent.py, tools.py | этап «пересчёт» (recalc); «↻ Перевыпустить агентом», «⚡ Новый прогон погоды» |
| Воспроизвести, как в прошлом: 31.01, затем 01.02 и далее последовательно | Выпуск D делается в момент T = (D+1) 00:00 местного. D = 31.01 … 28.02 — 29 выпусков подряд, каждый прошёл полный цикл агента | backtest.py, timeline.issue_dates() | календарь из 29 выпусков; «▶ Воспроизвести февраль»; verify.py |
| Только архивные прогнозы, доступные на тот момент, а не факт | Правило init + 8 ч ≤ T. В признаках нет факта, реанализа и SCADA. В каждой строке CSV есть weather_init_max_utc | timeline.py, weather.py, tools.py, scripts/verify.py | python3 scripts/verify.py → «Без будущего» ✓ 4176/4176 строк, 174/174 прогонов |
Каждый день 01–28.02 покрыт выпуском накануне (h 1–24), а начиная с 02.02 ещё и выпуском за двое суток (h 25–48). Выпуск от 28.02 (на 01–02.03) закрывает второе прочтение ТЗ: «повторить прогнозирование в течение тестового периода».
3. Архитектура
Браузер ──► Caddy (локально http://localhost:8080 · прод https://pushin.codes)
├── / ──► frontend :3000 React + Vite, отдаёт nginx: «Февраль», «Live», «Качество»
└── /api/*, /health ──► backend :8000 FastAPI: REST + SSE-поток шагов агента
│
кнопка или POST /api/runs, ▼
CLI windcast.backtest, ──► агент: ≤ 8 вызовов инструментов ◄──► OpenAI API
Live-наблюдатель раз в 30 мин LLM выбирает шаг; без ключа — те же шаги по правилам
│ вызывает
fetch_weather ─────► Open-Meteo API по координатам ВЭС (без сети — data/weather_cache/)
check_data ────────► 48 ч без пропусков, все прогоны опубликованы до момента выпуска T
run_model ─────────► models/quantile_*.pkl → P10/P50/P90 × 48 ч × {турбина 1, турбина 2, ВЭС}
analyze_forecast ──► рампы, обледенение, ветер > 20 м/с, скачок против прошлой версии
recalc_forecast ───► вышел более свежий прогон → пересчёт → v2; поздний прогон — отказ кода
publish_forecast ──► действие: outputs/forecasts/{D}.csv + {D}.json, outputs/traces/{D}.jsonl
│
API и экран читают только эти файлы ◄──┘ (февраль открывается уже посчитанным)
Подробная схема с диаграммой последовательности — docs/architecture.html
(открыть в браузере). Спецификация API и форматов — docs/CONTRACT.md (v0.5). Swagger —
http://localhost:8080/api/docs.
Модули бэкенда (backend/windcast/): agent.py — цикл агента, tools.py — шесть инструментов и их
JSON-схемы для OpenAI, weather.py — Open-Meteo и кэш, model.py — квантильная модель и базовые линии,
evaluate.py — январский бэктест, data.py — SCADA, store.py — атомарная запись выпусков, ports.py —
подключение погоды, данных и модели к агенту, runs.py — прогоны в фоне, SSE и журнал, watcher.py —
Live-наблюдатель, api.py — REST, backtest.py — CLI, timeline.py — время и правило «без будущего».
3.1 Цикл агента
- Прогон запускают кнопка «↻ Перевыпустить агентом»,
POST /api/runs, CLIwindcast.backtestили Live-наблюдатель. Бэкенд ведёт прогон в фоновом потоке и отдаёт каждое событие на экран по SSE (GET /api/runs/{id}/events). - v1.
fetch_weather(run="previous")берёт прогон на сутки старше. Затемcheck_data,run_model,analyze_forecastиpublish_forecast, который публикует v1. - Вышел новый прогон.
fetch_weather(run="latest")возвращает не решение, а факты: свежее ли прогон, опубликован ли он до T, средний сдвиг ветра за часы 1–24, появились ли новые риски. Решение принимает агент. Политика: пересчитывать, если прогон свежее, опубликован до T и ветер сдвинулся в среднем больше чем на 0,5 м/с или появился новый риск. Тогдаrecalc_forecastсчитает v2 со строкой «что изменилось», аpublish_forecastеё публикует. Иначе агент объясняет, почему v1 остаётся. - «⚡ Новый прогон погоды» (
trigger = new_weather_run): агент ищет прогон свежее текущего и опубликованный до T. На архивном дне, где v2 уже учла последний такой прогон, его нет, и агент объясняет: «Более свежего прогона до момента выпуска нет — v2 остаётся». - Сбой источника (
scenario = weather_outageимитирует отказ Open-Meteo на первом запросе). В ленте: «Источник погоды недоступен» → «Источник погоды не ответил — повторяю запрос» → «Погода получена со второй попытки — доверие к выпуску ниже обычного». Если API недоступен и при повторе,fetch_weatherберёт проверенный кэш (meta.source = "cache"). - Лимит — 8 вызовов инструментов. Последнее событие всегда
verdict. События идут по схемеdocs/references/trace-event-schema.md; у каждого естьmeta.stage,meta.status,meta.source, у решений —meta.by.
Так выглядит лента выпуска 13.02 (outputs/traces/2026-02-13.jsonl), сокращённо:
| # | Тип | Кто | Событие |
|---|---|---|---|
| 1 | thought | код | «Выпуск 13.02: беру только погоду, опубликованную до T … Режим: агент (LLM gpt-5.4-mini)» |
| 3 | tool_result | инструмент | «Погода получена: 48 ч, источник api — часы 1-17 … прогон 12.02 06:00 UTC ✓ опубликован до T; …» |
| 7 | tool_result | модель | «Прогноз готов: 48 ч × Т1, Т2, ВЭС · P10/P50/P90 — ВЭС: пик 98 % номинала — 15.02 18:00, минимум 1 % — 15.02 01:00, в среднем 34 %» |
| 11 | action | агент | «Опубликован выпуск v1» |
| 14 | tool_result | инструмент | «Новый прогон: свежее v1, сдвиг ветра 1,5 м/с … новых рисков: 0» |
| 15 | thought | LLM (meta.by = llm) | «Пересчитываю: прогон свежее текущего, все прогоны опубликованы до T, а средний сдвиг ветра за 1–24 ч = 1.51 м/с > 0,5 м/с» |
| 18 | tool_result | модель | «Пересчитал → v2: ветер +0,7 м/с → пик без изменений, в среднем +2 п.п. против v1» |
| 20 | action | агент, текст LLM | «Опубликован выпуск v2 · По ВЭС ожидается пик 98% номинала 15.02 в 16:00, минимум 1% 14.02 в 15:00, среднее за окно … 36% номинала. Изменение против v1: среднее выросло на 2 п.п., пик без изменений; риски — обледенение 14.02 00:00–09:00 …; держать резерв и учесть в заявке» |
| 21 | verdict | код | «Итог: опубликована v2 — пересчёт на свежем прогоне» |
3.2 Где здесь agentic AI
| Кто | Что делает |
|---|---|
| Модель | Считает все числа: P10/P50/P90 на каждый час и турбину. LLM числа не придумывает и не правит: publish_forecast принимает сводку и номер версии, а не строки прогноза. |
| Код | Гарантирует правило «без будущего» (init + 8 ч ≤ T): fetch_weather и check_data не пропускают прогон, вышедший после T, а recalc_forecast отказывает в пересчёте на позднем прогоне, что бы ни попросила LLM. Публикует сводку LLM, только если каждое число в ней есть в ответах инструментов, иначе берёт шаблонную (в феврале так было дважды: 08.02 и 25.02). Новая версия появляется только после вызова recalc_forecast: если LLM объявляет пересчёт, но не вызывает инструмент, агент напоминает один раз, а затем решает по правилу и пишет by=rule — в итоговых выпусках таких случаев нет. Держит лимит 8 вызовов. Если LLM недоступна, ошиблась или остановилась раньше, доводит цикл по тем же правилам и пишет об этом в ленту. По одной дате одновременно идёт только один прогон. |
LLM (OpenAI Chat Completions, function calling, по умолчанию gpt-5.4-mini) | Ведёт цикл: порядок шагов ограничен предусловиями инструментов (без погоды модель не запустить), поэтому решает LLM не «что за чем», а пересчитывать ли после нового прогона (причина пишется в ленту, meta.by = "llm"), как реагировать на сбой источника и что сказать диспетчеру. Решает, как реагировать на сбой источника погоды: повторить запрос или продолжить с предупреждением. Пишет сводку для диспетчера и строку «что изменилось». |
| Без ключа | Тот же цикл идёт по правилам (meta.by = "rule"): та же политика пересчёта, сводка по шаблону, те же числа. |
Честно про февральский бэктест. В архиве событие «вышел новый прогон» не приходит извне: код объявляет его
после публикации v1 (agent.py, _announce_new_run), и более свежий допустимый прогон в архиве есть всегда. Поэтому во
всех 29 выпусках правило «сдвиг ветра > 0,5 м/с» дало бы то же решение, что и LLM. Вклад LLM в феврале — объяснение
решения, сводка для диспетчера и обработка нештатных ситуаций (сбой источника, scenario = weather_outage); в Live событие
настоящее — новые прогоны выходят четыре раза в сутки, и агент проверяет их сам.
Спросить агента. Кнопка «Спросить агента» в правом нижнем углу открывает чат диспетчера: POST /api/ask
{"question": "Какие риски 14 февраля?", "issue_date": "2026-02-13"} → ответ по-русски не длиннее трёх предложений.
LLM отвечает только по данным инструментов чтения (get_issue_summary, get_hours, get_weather_runs,
get_agent_steps, get_quality, get_live); какие инструменты были вызваны, видно в поле tools и под ответом.
Без ключа OpenAI чат отвечает сводкой выбранного выпуска; кнопка показывается только когда GET /health отдаёт
"ask": true. Готовые вопросы: пик выпуска, риски, почему был пересчёт, насколько точна модель.
Агент работает без человека. Бэкенд раз в LIVE_WATCH_MINUTES минут (по умолчанию 30, 0 —
выключено; первая проверка — через 10 секунд после старта) запускает агента для Live (watcher.py). Если
выпуска ещё нет, агент делает его, а если есть — проверяет новый прогон и сам решает, выпускать ли новую
версию. Каждый запуск Live, свой и по кнопке, пишется в outputs/live/journal.jsonl. Журнал отдаёт
GET /api/live/journal?limit=20 (новые сверху): кто запустил (initiator: agent или user), итог
(outcome: published, kept, refused, error), версия и фраза агента. Реальная запись наблюдателя
через 10 с после старта бэкенда:
{"ts": "2026-09-23T16:12:52+05:00", "initiator": "agent", "trigger": "issue", "run_id": "r_44192848ea97",
"outcome": "published", "version": 1, "title": "Опубликована v1 — более свежего прогона пока нет"}
GET /health показывает live_watch_minutes и live_last_check_local — когда агент проверял погоду
в последний раз.
Детерминированный режим. Без OPENAI_API_KEY (или с "mode": "deterministic") агент идёт по тем же
шагам и выдаёт те же числа. Текущий режим показывает GET /health → mode. Если ключа нет, запрос
с "mode": "agent" тоже выполняется детерминированно, и первое событие в ленте об этом говорит. Проверено
на феврале: детерминированный перевыпуск даёт те же P10/P50/P90 и тот же набор версий, что и записанные
выпуски агента (§ 8.2).
Live. Для issue_date = "live" момент выпуска T — «сейчас». Погода берётся из Open-Meteo Forecast
API (последний вышедший прогон), окно — 48 ч от следующего полного часа.
3.3 Модель и методика
- Данные. SCADA двух турбин, 10-минутные записи с 11.03.2023 по 31.01.2026 23:50, превращаются
в часовые средние в UTC (
python -m windcast.data→data/processed/hourly.parquet). Час помечаетсяvalid = false, если он неполный (меньше 6 записей), если это простой (мощность меньше 0,02 при ветре больше 5 м/с) или если он попал в 18.05–17.07.2024 у турбины 1 (≈ 60 дней пропусков). Валидных часов: турбина 1 — 92,4 %, турбина 2 — 97,0 %. Разбор данных —docs/DATA_NOTES.md. - Часы SCADA. Сдвиг к UTC постоянный, 5 или 6 часов.
evaluateобучает обе версии и выбирает по nMAE на 1–7 января; выбрано 6 (SCADA_UTC_OFFSET_H=6), 8–31 января остались независимой проверкой: nMAE модели там 16,8 % против 36,4 % у персистентности. - Погода для обучения и прогноза — одна и та же: Open-Meteo Previous Runs,
*_previous_day1/2с 01.06.2024 (раньше архива нет), обе координаты турбин усреднены. Признаки только погодные и календарные: ветер на 100 м и 10 м, ветер³, sin/cos направления, температура, местный час и месяц, горизонтh,turbine_id(1, 2, 3 = ВЭС),lag_days(1 или 2; прогоны на 3–4 суток для часов 42–48 используют поведение лага 2). SCADA в признаках нет: в феврале её нет. Так модель заодно учит ошибку погодной модели на той же заблаговременности, с которой работает в прогнозе. - Цель. Часовая нормированная мощность турбины; ВЭС — отдельная цель, среднее двух турбин за часы, когда обе валидны. В обучение попадают только валидные часы, закончившиеся до границы обучения.
- Модель: три
HistGradientBoostingRegressorиз scikit-learn с квантильной функцией потерь (0,1 / 0,5 / 0,9), общие для турбин и ВЭС; 160 итераций, до 15 листьев, минимум 50 наблюдений в листе, L2 = 1, шаг 0,08,random_state = 42— обучение детерминировано. Выход обрезается в [0, 1] и упорядочивается P10 ≤ P50 ≤ P90.MODEL_VERSION = histgb-q-2026-01-31. - Два артефакта (по 0,9 МБ,
pickle):models/quantile_2025-12-31.pkl— граница обучения 31.12.2025 19:00 UTC, 115 005 строк, для январского бэктеста;models/quantile_2026-01-31.pkl— граница 31.01.2026 19:00 UTC, 121 608 строк, для февральских выпусков.predictотказывает, если граница обучения артефакта позже момента выпуска или какой-то прогон погоды позже T. - Базовые линии на тех же данных: кривая мощности — изотоническая регрессия ветер → мощность, своя для лага 1 и 2, применённая к прогнозному ветру; климатология — медиана по турбине, месяцу и часу; персистентность — факт за те же часы предыдущих суток до T (где факта нет — климатология).
- Метрики: nMAE и nRMSE по ВЭС в % номинала по всем 30 выпускам × 48 ч (1417 часов с фактом, 23 часа без валидного факта исключены), по горизонту и по дням, покрытие коридора P10–P90.
- Честное правило стоит точности. Под правилом v0.3 (прогон годен, если стартовал до T) та же
схема давала nMAE 15,0 %. Под правилом v0.4 (
init + 8 ч ≤ T) часы 18–48 берут прогон на 6–24 часа старше, и ошибка выросла до 15,8 %. Мы оставили честный вариант: прогон, стартовавший в D 12:00 или 18:00 UTC, к моменту выпуска ещё не опубликован. - Почему погода, а не история SCADA. Лучшие модели только на истории турбин ошибаются на тех же январских выпусках в 1,7–1,9 раза сильнее (MAE 0,27–0,30 против 0,158) — см. эксперимент в § 9.
4. Правило «без будущего»
Это главная проверка задачи: прогноз на прошлую дату нельзя строить на погоде, которую тогда ещё не знали.
-
Момент выпуска. Выпуск D делается в момент T = D 19:00 UTC = (D+1) 00:00 по UTC+5, прогноз — на 48 часов после T. Внутри системы всё время в UTC, на экране и в файлах — местное UTC+5.
-
Прогон виден не сразу. Глобальные модели стартуют в 00, 06, 12 и 18 UTC; ECMWF публикует открытые данные примерно через 7–8 часов, остальные быстрее. Мы считаем прогон доступным через 8 часов после старта — это наша консервативная оценка по срокам публикации открытых данных, а не документированная гарантия провайдера; для любой модели внутри
best_matchона с запасом:init + 8 ч ≤ T, то есть старт не позже D 11:00 UTC. Самый свежий допустимый прогон — D 06:00 UTC (опубликован ≈ D 14:00 UTC). Архив Open-Meteo хранит прогноз по суткам давности, а не по конкретным прогонам, поэтомуweather_init_max_utc— верхняя граница старта прогона (целевой час минус N суток, округлённые вниз до 00/06/12/18 UTC), а не точное время; в интерфейсе она так и подписана. Модель погоды —best_matchOpen-Meteo (ECMWF / ICON / GFS по выбору сервиса). Прогоны D 12:00 UTC (≈ D 20:00 UTC) и D 18:00 UTC (≈ D+1 02:00 UTC) стартовали раньше T, но вышли уже после него, поэтому запрещены. -
Какой прогон берём на каждый час. Open-Meteo Previous Runs API отдаёт
*_previous_dayN— значение из прогона, стартовавшего не позже чем за N суток до целевого часа. N подбирается по горизонту так, чтобы прогон был опубликован до T:N = timeline.lead_days(h)= ⌈(h − 1 + 8) / 24⌉.Горизонт h Целевые часы (UTC+5) N ( previous_dayN)v1 (прогон на сутки старше) 1–17 (D+1) 00:00 … 16:00 1 2 18–41 (D+1) 17:00 … (D+2) 16:00 2 3 42–48 (D+2) 17:00 … 23:00 3 4 -
Запрещено в признаках:
previous_day0, фактическая погода, реанализ, SCADA. -
У каждой строки CSV есть
weather_init_max_utc— верхняя граница старта прогона: целевой час минус N суток, округлённый вниз до 00/06/12/18 UTC (weather.pyпишет её вinit_time_utcкаждого часа). -
Агент это правило обойти не может. LLM выбирает только
run = "previous"или"latest", а время старта вычисляет код.fetch_weatherотказывает, если хоть один прогон опубликован после T,check_dataпроверяет это ещё раз,recalc_forecastотказывает в пересчёте на таком прогоне.
Пример: выпуск D = 13.02.2026, T = 13.02 19:00 UTC = 14.02 00:00 по UTC+5 (рассчитано timeline.py):
| h | Целевой час (UTC+5) | N | weather_init_max_utc | Опубликован ≈ | ≤ T? |
|---|---|---|---|---|---|
| 1 | 14.02 00:00 | 1 | 2026-02-12T18:00Z | 13.02 02:00 UTC | да |
| 17 | 14.02 16:00 | 1 | 2026-02-13T06:00Z | 13.02 14:00 UTC | да |
| 18 | 14.02 17:00 | 2 | 2026-02-12T12:00Z | 12.02 20:00 UTC | да |
| 41 | 15.02 16:00 | 2 | 2026-02-13T06:00Z | 13.02 14:00 UTC | да |
| 42 | 15.02 17:00 | 3 | 2026-02-12T12:00Z | 12.02 20:00 UTC | да |
| 48 | 15.02 23:00 | 3 | 2026-02-12T18:00Z | 13.02 02:00 UTC | да |
В записи выпуска это видно как weather_runs: часы 1-17 и 18-41 — прогон 2026-02-13T06:00Z, часы 42-48 —
2026-02-12T18:00Z, у каждого before_issue: true.
Как это доказывает scripts/verify.py. Скрипт работает на стандартной библиотеке Python ≥ 3.9 и
намеренно не импортирует timeline.py: правила времени записаны в нём заново, чтобы проверка не
повторяла ошибки проверяемого кода. Для каждого из 29 выпусков он проверяет:
- в каждой строке CSV:
weather_init_max_utc + 8 ч ≤ T— 4176 строк; - в записи
{D}.json: у каждого прогона погоды каждой версии (v1 тоже)init_utc + 8 ч ≤ T— 174 прогона; - формат по § 7 контракта: столбцы по порядку, 144 строки = 48 ч × {1, 2, plant}, время в +05:00, одна версия в файле, P10 ≤ P50 ≤ P90 в [0, 1], ветер в [0, 60) м/с;
- ленту шагов
{D}.jsonl: события по схеме, есть действие (action), последнее событие —verdict; - что нигде нет заглушек (
source = stub), что сводный CSV совпадает с файлами выпусков и что nMAE модели на январе ниже каждой из трёх базовых линий.
Код возврата 0 — PASS, 1 — FAIL. При ошибке печатается файл:строка · что не так. В интерфейсе то же
правило видно по кнопке «✓ Без будущего»: какой прогон взят на какие часы и когда он вышел.
5. Технологии и зависимости
| Слой | Технология | Версия |
|---|---|---|
| Язык бэкенда | Python | 3.12 (образ python:3.12-slim) |
| API | FastAPI, Uvicorn; SSE через Starlette StreamingResponse | 0.115.12, 0.34.0 |
| Данные | pandas, NumPy, PyArrow (Parquet) | 2.2.3, 2.2.2, 19.0.1 |
| Модель | scikit-learn (HistGradientBoostingRegressor, квантильная потеря; IsotonicRegression для базовой линии) | 1.6.1 |
| Агент | openai-python (Chat Completions, function calling), модель gpt-5.4-mini | 1.109.1 |
| HTTP-клиенты | requests (Open-Meteo), HTTPX (тесты API) | 2.32.3, 0.28.1 |
| Тесты и линтер | pytest, Ruff | 8.3.5, 0.16.8 |
| Интерфейс | React + Vite; графики — свой SVG без библиотек; шаги агента — EventSource (SSE); тесты — vitest + jsdom. Собирается в node:20.11.1-alpine3.19, dist/ отдаёт nginx на порту 3000 и проксирует /api/ и /health на backend:8000 | React 18.2.0, Vite 6.4.3, vitest 4.1.11, jsdom 22.1.0, nginx:1.27-alpine |
| Прокси | Caddy (образ caddy:2.11.4-alpine) | 2.11.4 |
| Запуск | Docker, Docker Compose v2 |
Все версии Python-пакетов зафиксированы через == в backend/requirements.txt,
его ставит и Docker-образ. Там же LightGBM 4.6.0, SciPy 1.15.2, joblib 1.4.2, build и setuptools —
установлены, но в итоговом коде не используются.
Внешние сервисы:
| Сервис | Зачем | Ключ | Без него |
|---|---|---|---|
Open-Meteo Previous Runs API (previous-runs-api.open-meteo.com) | архивные прогнозы из прошлых прогонов (*_previous_day1…4, models=best_match) для выпусков и обучения | не нужен, бесплатно | ответы из data/weather_cache/ (meta.source = "cache") |
Open-Meteo Forecast API (api.open-meteo.com) | прогноз для Live | не нужен, бесплатно | Live берёт последний сохранённый снимок, если он покрывает 48 ч; архивный февраль работает |
| OpenAI API | выбор шагов агента, решения о пересчёте, тексты сводок | OPENAI_API_KEY, необязателен | детерминированный режим, те же числа |
Historical Forecast API Open-Meteo в конвейере не используется: в weather.py есть только диагностическая
функция, которую модель и агент не вызывают.
6. Установка и запуск
6.1 Системные требования
- Docker с Compose v2 (команда
docker compose, неdocker-compose), например Docker Desktop. Образ backend проверен на Docker 29.6 / Compose 5.2, arm64 (Apple Silicon); сервер https://pushin.codes — amd64. - Свободные порты 8080 (интерфейс и API через прокси), 8000 (API напрямую), 3000 (фронтенд
напрямую). Если порт занят, поменяйте левую часть в
portsвdocker-compose.yml, например"8081:8080". - Около 2 ГБ на диске под образы (образ backend — 0,72 ГБ; стадия сборки интерфейса на Node.js — ещё несколько сотен МБ, итоговые образы frontend и Caddy — небольшие, на Alpine) и около 1 ГБ свободной памяти.
- Интернет — для сборки образов (PyPI, Docker Hub) и для погоды Open-Meteo. Без сети при работе агент берёт
погоду из
data/weather_cache/(60 файлов, ≈ 3 МБ: окна всех 30 январских и 29 февральских выпусков плюс архив для обучения) — февраль и январь воспроизводятся офлайн. - Для
scripts/verify.pyна хосте нужен толькоpython3≥ 3.9, без пакетов. В Windows вместоpython3используйтеpythonилиpy -3.
6.2 Docker, основной путь
git clone https://github.com/BAITC-Hacks/hack-07de2d2d-pushin-code
cd hack-07de2d2d-pushin-code
cp .env.example .env # необязательно; для режима LLM впишите OPENAI_API_KEY=…
docker compose up --build # или docker compose up --build -d, чтобы вернуть терминал
- Файл
.envможно не создавать: compose читает его только для подстановки переменных и без него тоже стартует. - Поднимаются три контейнера:
backend(FastAPI, собирается из корня репозитория, потому что образу нужныdata/,models/иoutputs/),frontend(сборка React в Node.js, затем nginx сdist/) иproxy(Caddy). - Готовность:
curl http://localhost:8080/health→
{"ok": true, "mode": "deterministic", "model_version": "histgb-q-2026-01-31", "issues_ready": 29,
"ports": {"weather": "real", "data": "real", "model": "real"},
"live_watch_minutes": 30, "live_last_check_local": null}
mode станет agent, если в .env есть ключ; ports подтверждает, что погода, данные и модель настоящие,
а не заглушки; live_last_check_local заполнится через 10 секунд, когда агент сам проверит погоду для Live.
| Адрес | Что |
|---|---|
| http://localhost:8080 | интерфейс, /api/* и /health через один прокси — открывать это |
| http://localhost:8080/api/docs | Swagger UI со всеми эндпоинтами |
| http://localhost:8000 | API напрямую |
| http://localhost:3000 | фронтенд напрямую; его nginx сам проксирует /api/ и /health на backend |
Февраль уже посчитан: 29 выпусков из outputs/ попадают в образ при сборке. Если вы перегенерировали
выпуски на хосте (§ 8.2), пересоберите образ: docker compose up --build.
Остановить: Ctrl+C, затем docker compose down. Удалить и собранные образы:
docker compose down --rmi local.
6.3 Без Docker (для разработчика)
Нужен Python 3.12. Все команды — из корня клона:
python3.12 -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r backend/requirements.txt # LightGBM в списке есть, но кодом не импортируется: libomp не нужен
export PYTHONPATH=backend # Windows PowerShell: $env:PYTHONPATH="backend"
SCADA_UTC_OFFSET_H=6 python -m windcast.data # SCADA → data/processed/hourly.parquet (секунды)
uvicorn windcast.api:app --port 8000 # один воркер; если работает docker compose — сначала docker compose down
python -m windcast.data печатает число часов и долю валидных по каждой турбине:
turbine 1: hours=25392 valid_fraction=0.9242, turbine 2: hours=25392 valid_fraction=0.9704.
Проверка: curl http://localhost:8000/health.
Интерфейс (React + Vite) в режиме разработки — из frontend/, нужен Node.js 20 (в образе — 20.11.1);
API при этом должен работать на :8000:
cd frontend
npm ci # зависимости строго по package-lock.json
npm run dev # http://localhost:3000; /api и /health проксируются на localhost:8000
npm test # vitest: 14 тестов
npm run build # dist/ — то же делает Dockerfile
?fixtures=1 в адресе — работа без бэкенда на синтетических JSON из frontend/fixtures/, только для
разработки. Что где лежит:
| Файл | Что |
|---|---|
src/App.jsx | шапка, вкладки (#february, #live, #quality), режим агента из /health, панель «Как проверить за 3 минуты» |
src/February.jsx, src/Live.jsx, src/Quality.jsx | три экрана |
src/AgentPanel.jsx | шесть этапов по meta.stage, лента событий, сводка для диспетчера |
src/ForecastChart.jsx, src/chart.js | графики на SVG, без библиотек |
src/useAgentRun.js | запуск агента (POST /api/runs + SSE) и проигрывание сохранённых лент |
src/api.js, src/format.js | клиент API § 6 контракта и форматирование |
src/*.test.js | 14 тестов vitest (jsdom) |
nginx.conf | порт 3000; /api/ и /health проксирует на backend:8000 с proxy_buffering off, чтобы шаги агента шли без задержки; resolver 127.0.0.11 — nginx стартует, даже если backend ещё не поднялся |
Dockerfile | две стадии: node:20.11.1-alpine3.19 (npm ci && npm run build) → nginx:1.27-alpine с dist/ |
Проверено 23.09 на выгрузке main: npm ci, npm test (14 passed) и npm run build (dist/ ≈ 1,3 МБ)
проходят. Вкладки открываются и по ссылке: /#february, /#live, /#quality.
Переобучить модели и пересчитать метрики января (≈ 1,5 минуты, погода — из API или кэша):
python -m windcast.evaluate --from 2025-12-31 --to 2026-01-29
Команда обучает модель для обоих сдвигов часов SCADA, выбирает сдвиг по 1–7 января, пишет
models/quantile_2025-12-31.pkl и outputs/metrics_jan.json, затем обучает февральский
models/quantile_2026-01-31.pkl и печатает:
model: nMAE=15.7629% nRMSE=23.4460%
power_curve: nMAE=19.3513% nRMSE=23.9918%
climatology: nMAE=26.8185% nRMSE=37.1858%
persistence: nMAE=36.4548% nRMSE=47.9054%
SCADA_UTC_OFFSET_H=6
Повторный запуск воспроизводит outputs/metrics_jan.json байт в байт (проверено 23.09).
6.4 Тесты
python -m pytest -q backend/tests # агент, инструменты, погода, модель, запись выпусков, API, SCADA, время; без сети
python -m pytest -q scripts/tests # verify.py на синтетических выпусках, запуск как у жюри
cd frontend && npm ci && npm test # интерфейс: vitest + jsdom, нужен Node.js 20
Сейчас это 155, 34 и 14 тестов, все проходят. У эксперимента ml-prognoz-model/ свои тесты и своё
окружение (§ 9).
6.5 Проверено на чистом клоне
23.09 в 16:17 репозиторий склонировали с GitHub в пустой каталог и запустили ровно по
§ 6.2, без .env (без ключа OpenAI, детерминированный режим):
docker compose up --build -d— все три контейнера собрались и поднялись без ручных правок.- http://localhost:8080 ответил
200, интерфейс открылся. /health:ports—weather,data,modelвсеreal;issues_ready: 29./api/issues: все 29 выпусков опубликованы;/api/metricsотдал метрики января.POST /api/runsсtrigger: new_weather_runна 2026-02-13 — агент прошёл цикл в потоке SSE и отказал: «Итог: v2 остаётся — более свежего прогона до момента выпуска нет».python3 scripts/verify.pyна хосте — PASS.
6.6 Развёрнутая версия
https://pushin.codes — тот же код и те же три сервиса, развёрнуты 23.09 в 16:40 через
infra/docker-compose.hosted.yml: backend и frontend слушают только 127.0.0.1 (8100 и 3100), наружу их
выводит системный Caddy хоста с TLS Let's Encrypt (infra/caddy-site.snippet: /api/* и /health →
backend, остальное → frontend, SSE без буферизации). В .env сервера стоит ключ OpenAI команды, поэтому
там агент работает в режиме LLM (/health → "mode": "agent"), и проверка не требует личных аккаунтов
(п. 5.6.6). Проверено снаружи после деплоя: интерфейс отвечает 200, /health показывает все три ports
real и issues_ready: 29, /api/issues отдаёт 29 выпусков (все v2), CSV и метрики скачиваются, шаги
агента идут через Caddy событие за событием, решения LLM видны в ленте.
7. Параметры окружения
Все переменные необязательны, проект запускается и без .env. Секреты хранятся только в .env, он
в .gitignore. Шаблон — .env.example.
| Переменная | По умолчанию | Где читается | Что делает |
|---|---|---|---|
OPENAI_API_KEY | пусто | runs.py, agent.py, backtest.py | есть ключ → режим agent (шаги и решения — LLM); нет → deterministic |
OPENAI_MODEL | gpt-5.4-mini | agent.py | модель OpenAI для агента. Нужна модель с function calling в Chat Completions: семейство gpt-6 инструменты в этом API не поддерживает, поэтому по умолчанию gpt-5.4-mini |
OPENAI_REASONING_EFFORT | пусто | agent.py | reasoning_effort для моделей 2025 года gpt-5, gpt-5-mini, gpt-5-nano; для остальных не передаётся |
OPENAI_TIMEOUT_S | 60 | agent.py | таймаут запроса к OpenAI, секунд |
OPENAI_BASE_URL | API OpenAI | openai-python | другой адрес OpenAI-совместимого API |
LIVE_WATCH_MINUTES | 30 | watcher.py | как часто агент сам проверяет новые прогоны для Live; 0 — выключено; первая проверка через 10 с |
WINDCAST_ROOT | корень репозитория; в образе /app | paths.py | откуда читать и куда писать data/, models/, outputs/ |
SCADA_UTC_OFFSET_H | 5 | config.py | сдвиг часов SCADA к UTC для windcast.data: 5 или 6, другое значение — ошибка. Бэктест выбрал 6, модели обучены при 6 |
WINDCAST_FORCE_STUBS | пусто | ports.py | 1 — синтетические заглушки вместо погоды и модели (только тесты, без сети) |
PYTHONPATH | в образе /app/backend | Python | для запуска без Docker: backend, чтобы работало python -m windcast.… |
Корневой docker-compose.yml передаёт в контейнер OPENAI_API_KEY, OPENAI_MODEL,
OPENAI_REASONING_EFFORT и LIVE_WATCH_MINUTES; .env.example перечисляет первые две и последнюю.
Остальное задано в образе (backend/Dockerfile) и нужно только при запуске без Docker. Переменные
DOMAIN и DEPLOY_COMPOSE_FILE из infra/.env.example нужны только серверу https://pushin.codes,
для проверки они не требуются.
8. Проверка основного сценария
8.1 В интерфейсе, за 3 минуты
Откройте https://pushin.codes (без регистрации) или локально http://localhost:8080. В шапке — три вкладки,
режим агента из /health («агент · LLM» или «агент · без LLM») и кнопка «Как проверить за 3 минуты»: она
открывает список из десяти требований ТЗ, а «Показать» у каждого переключает вкладку и подсвечивает
нужное место на экране.
Вкладка «Февраль 2026 · тест» (/#february):
- Февраль уже посчитан. Календарь «Выпуски 31 января — 28 февраля» — 29 выпусков с днями недели,
у всех метка
v2(был пересчёт), точка — есть риски. Плеер лент агента (без LLM): «▶ Воспроизвести февраль» — с 31.01, «▶ С {дата}» — с выбранного дня; «Пауза» / «▶ Продолжить с {дата}» — возобновляет с того же шага; «■ Стоп» — останавливает и возвращает сохранённую ленту выбранного дня; скорость 0,5×/1×/2×/4×; клик по дню в календаре во время показа переносит его на этот день. - Выпуск за день. Кликните 13.02: карточка «Выпуск на 14–15 февраля» с графиком на 48 часов (P50, коридор P10–P90, прошлая версия), переключатель объекта ВЭС / Т1 / Т2, «Для системы сейчас 14 февраля 2026, 00:00» — всё правее система не видела, «Что изменилось» против v1 и «Риски в окне». В панели «Агент» справа — сводка для диспетчера («По ВЭС ожидается пик 98% номинала 15.02 в 16:00 …»).
- «Без будущего» в блоке «Проверка» — какие прогоны погоды взяты на какие часы и когда они вышли: часы 1-17 и 18-41 — прогон 13.02 06:00 UTC, часы 42-48 — 12.02 18:00 UTC, все опубликованы до T = 13.02 19:00 UTC.
- «Перевыпустить агентом» — агент проходит цикл вживую: в панели «Агент» по очереди загораются шесть этапов ТЗ (погода · подготовка · модель · прогноз · анализ · пересчёт) и идёт лента шагов. Агент публикует v1, получает событие «вышел новый прогон погоды», сравнивает и решает: v2 со строкой «что изменилось» или «пересчёт не нужен». В режиме LLM выпуск занимает 9–16 секунд, в детерминированном — 1–2 секунды.
- «Новый прогон погоды» — агент ищет прогон свежее текущего и опубликованный до T, не находит и отказывает: «Итог: v2 остаётся — более свежего прогона до момента выпуска нет». В пояснении указано, когда стартовал следующий прогон и когда он вышел — позже T. Так правило «без будущего» соблюдается и в решении агента.
- «CSV выпуска» и CSV всех 29 выпусков одним файлом — формат в § 9.
Перевыпуск меняет выпуск только внутри запущенного контейнера; файлы в репозитории, которые проверяет
verify.py, не меняются.
Вкладка «Live · сейчас» (/#live):
- Статус прогонов: текущее время, последний и следующий прогон, как часто агент сам проверяет обновления. «▶ Выпустить прогноз сейчас» — тот же цикл на последнем прогоне Open-Meteo Forecast API (нужен интернет); «Проверить обновления погоды» — агент решает, нужна ли новая версия.
- «Имитировать сбой погоды» — выпуск, в котором первый запрос погоды падает: агент повторяет запрос и предупреждает, что доверие к выпуску ниже обычного.
- «Журнал агента» — что агент сделал сам («агент сам») и по кнопке («по кнопке»). Первая запись наблюдателя появляется через 10 секунд после старта бэкенда, без участия человека.
Вкладка «Качество · январь» (/#quality):
- «Как модель ошибалась в январе»: четыре числа (nMAE модели 15,8 %, выигрыш против лучшей базовой линии, покрытие P10–P90, число выпусков), таблица «Модель против базовых линий», «Ошибка по горизонту» и «Неделя 15–21 января: прогноз против факта, ВЭС». Факта за февраль нет — см. § 9.
8.2 Из командной строки
Перевыпустить февраль с нуля и проверить результат (после § 6.3):
export PYTHONPATH=backend
python -m windcast.backtest --from 2026-01-31 --to 2026-02-28 --mode deterministic # ≈ 35 с; с ключом и --mode agent ≈ 6 мин
python3 scripts/verify.py
backtest печатает, откуда взяты погода, данные и модель, и по строке на выпуск:
backtest 2026-01-31 → 2026-02-28: 29 issues · mode=deterministic · weather=real, data=real, model=real
2026-01-31 v2 source=api mode=deterministic flags=ramp:2,ice:3 (1.2s)
2026-02-01 v2 source=api mode=deterministic flags=ramp:1,ice:2 (1.3s)
…
2026-02-28 v2 source=api mode=deterministic flags=ramp:2,ice:3 (1.0s)
combined: outputs/forecast_feb2026.csv (29 issues)
Что появится:
outputs/forecasts/{D}.csvи{D}.json— 29 выпусков: CSV содержит последнюю версию, JSON — все версии;outputs/traces/{D}.jsonl— 29 лент шагов агента;outputs/forecast_feb2026.csv— сводный файл;- метрики января пишет
python -m windcast.evaluate(§ 6.3).
Выпуски в репозитории перезапишутся; что изменилось, покажет git diff --stat outputs/. Чтобы их не трогать,
задайте другой корень: mkdir -p /tmp/wc && cp -R data models /tmp/wc && WINDCAST_ROOT=/tmp/wc PYTHONPATH=backend python -m windcast.backtest --from 2026-02-12 --to 2026-02-14 --mode deterministic — CSV в /tmp/wc/outputs/forecasts/
совпадут с файлами в outputs/forecasts/ байт в байт. Записанные выпуски
сделаны в режиме LLM (gpt-5.4-mini, 23.09 16:00–16:05; 08, 10, 12 и 14.02 перевыпущены в 16:28–16:29
после исправления агента, § 9). Детерминированный перевыпуск даёт те же числа P10/P50/P90
и тот же набор версий: правило, как и LLM, пересчитало все 29 дней (проверено: все 29 v1 и все 29 v2
совпали строка в строку). Отличаются только тексты сводок — в детерминированном режиме они шаблонные.
То же внутри контейнера, если Python 3.12 ставить не хочется (scripts/ в образ не входит, поэтому
проверка — на хосте):
docker compose exec backend python -m windcast.backtest --from 2026-01-31 --to 2026-02-28 --mode deterministic
mkdir -p /tmp/windcast-check && docker compose cp backend:/app/outputs /tmp/windcast-check/
python3 scripts/verify.py --root /tmp/windcast-check
API читает файлы заново на каждый запрос, поэтому интерфейс сразу покажет перевыпущенные выпуски.
Вывод verify.py на выпусках из репозитория (23.09):
Windcast · проверка выпусков 2026-01-31 … 2026-02-28 (29 выпусков)
Корень: …/hack-07de2d2d-pushin-code
T — момент выпуска D: D 19:00 UTC = (D+1) 00:00 по UTC+5
Прогон погоды доступен к выпуску, если старт + 8 ч ≤ T, т. е. старт не позже D 11:00 UTC (§2)
✓ Выпуски на месте: forecasts/{D}.csv 29/29 выпусков
✓ Без будущего: прогон доступен к T (старт + 8 ч ≤ T, CSV) 4176/4176 строк
✓ Без будущего: прогоны доступны к T (старт + 8 ч ≤ T, {D}.json) 174/174 прогонов
✓ Заголовок CSV: столбцы §7 по порядку 29/29 файлов
✓ 144 строки: турбины 1, 2, plant × h 1…48 29/29 файлов
✓ Дата выпуска, T и целевые часы в +05:00 4176/4176 строк
✓ Одна версия ≥ 1 на файл 29/29 файлов
✓ P10 ≤ P50 ≤ P90, все в [0, 1] 4176/4176 строк
✓ Ветер wind_fc_ms в [0, 60) м/с 4176/4176 строк
✓ Лента шагов агента: traces/{D}.jsonl 29/29 выпусков
✓ Запись {D}.json: latest_version = версия CSV 29/29 выпусков
✓ Без заглушек: нигде нет source = stub 29/29 выпусков
✓ Сводный forecast_feb2026.csv = файлы выпусков 4176/4176 строк
✓ Январь: nMAE модели ниже всех базовых линий nMAE: model 0.1576 · power_curve 0.1935 · climatology 0.2682 · persistence 0.3645
PASS
8.3 API через curl
Работает и с https://pushin.codes вместо http://localhost:8080:
curl -s http://localhost:8080/health
curl -s "http://localhost:8080/api/issues?from=2026-01-31&to=2026-02-28" # календарь: 29 выпусков
curl -s http://localhost:8080/api/forecasts/2026-02-13 # выпуск: 144 строки, weather_runs, сводка
curl -s -o 2026-02-13.csv http://localhost:8080/api/forecasts/2026-02-13.csv # CSV выпуска
curl -s -o feb.csv http://localhost:8080/api/forecasts/february.csv # сводный CSV за февраль
curl -s http://localhost:8080/api/traces/2026-02-13 # записанная лента шагов
curl -s http://localhost:8080/api/metrics # январь: модель и базовые линии
curl -s "http://localhost:8080/api/live/journal?limit=20" # что агент сделал сам (initiator=agent)
curl -s http://localhost:8080/api/forecasts/live # текущий live-выпуск
# запустить агента и смотреть его шаги вживую (SSE); без ключа — детерминированно
ID=$(curl -s -X POST http://localhost:8080/api/runs -H 'content-type: application/json' \
-d '{"issue_date": "2026-02-13", "trigger": "new_weather_run"}' \
| python3 -c 'import json, sys; print(json.load(sys.stdin)["id"])')
curl -N http://localhost:8080/api/runs/$ID/events
# сбой источника погоды: агент повторяет запрос и предупреждает
curl -s -X POST http://localhost:8080/api/runs -H 'content-type: application/json' \
-d '{"issue_date": "2026-02-13", "scenario": "weather_outage"}'
# неверный запрос — 400 с текстом, а не 500
curl -s http://localhost:8080/api/forecasts/2026-13-45
# {"error":"Неверная дата выпуска «2026-13-45»: нужен формат ГГГГ-ММ-ДД"}
9. Результаты
29 выпусков за февраль — в outputs/, они же видны в интерфейсе. Сделаны 23.09.2026
командой PYTHONPATH=backend python -m windcast.backtest --from 2026-01-31 --to 2026-02-28 --mode agent
с OPENAI_MODEL=gpt-5.4-mini, погода — из API (source: api во всех 58 версиях), 9–16 секунд на выпуск.
| Файл | Что |
|---|---|
outputs/forecasts/{D}.csv | выпуск D = 2026-01-31 … 2026-02-28, последняя версия, 144 строки |
outputs/forecasts/{D}.json | запись выпуска со всеми версиями (v1, v2), прогонами погоды, флагами, сводкой — источник для API |
outputs/forecast_feb2026.csv | все 29 выпусков одним файлом, 29 × 144 = 4176 строк |
outputs/traces/{D}.jsonl | лента шагов агента для каждого выпуска — доказательство, что выпуск сделал агент |
outputs/metrics_jan.json | метрики и ряды января для вкладки «Качество» |
Решения агента. 29 из 29 — v2 на более свежем прогоне; решение каждый раз принимал LLM
(meta.by = "llm" в каждой ленте). Если LLM объявляет пересчёт, но не вызывает инструмент, агент
напоминает один раз, а затем решает по правилу и пишет by=rule — в итоговых выпусках таких случаев нет.
Дважды — 08.02 и 25.02 — в сводке LLM для v1 были числа, которых нет ни в одном ответе инструментов, и код
опубликовал шаблонную сводку с пометкой «в сводке числа не из инструментов (…) — взял шаблонную». Всё это
видно в лентах, мы ничего не правили.
Формат CSV (UTF-8, запятая, порядок столбцов фиксирован):
| Столбец | Пример | Смысл |
|---|---|---|
issue_date | 2026-02-13 | дата выпуска D |
issue_time_local | 2026-02-14T00:00+05:00 | момент выпуска T |
target_time_local | 2026-02-14T00:00+05:00 | начало целевого часа; h = 1 → (D+1) 00:00 |
horizon_h | 1 … 48 | горизонт, часов |
turbine | 1, 2, plant | турбина или ВЭС |
p10, p50, p90 | 0.2134 | мощность в долях номинала, [0, 1], 4 знака |
wind_fc_ms | 7.41 | прогноз ветра на высоте 100 м, м/с |
weather_init_max_utc | 2026-02-13T06:00Z | верхняя граница старта прогона погоды (§ 4) |
version | 1 или 2 | версия выпуска: 2 — пересчитан на более свежем прогоне |
Точность на январе 2026. Факта за февраль в выданных данных нет: они заканчиваются 31.01.2026 23:50.
Поэтому точность показываем на бэктесте января, который прошёл тем же путём и по тому же правилу «без
будущего»: 30 выпусков D = 31.12.2025 … 29.01.2026, модель quantile_2025-12-31.pkl обучена на данных
до 31.12.2025. Февральские CSV готовы для сверки организатором. ВЭС, все 48 часов каждого выпуска,
1417 часов с фактом:
| Метод | nMAE, % номинала | nRMSE, % номинала |
|---|---|---|
| Windcast, P50 | 15,76 | 23,45 |
| Кривая мощности по прогнозному ветру | 19,35 | 23,99 |
| Климатология: медиана по турбине, месяцу и часу | 26,82 | 37,19 |
| Персистентность: «завтра как сегодня» | 36,45 | 47,91 |
- Выигрыш против лучшей базовой линии (кривой мощности): 18,5 % по nMAE; по nRMSE выигрыш небольшой (23,45 против 23,99) — крупные промахи у обеих связаны с ошибкой прогноза ветра.
- По горизонту: первые сутки (h 1–24) — nMAE 14,6 %, вторые (h 25–48) — 17,0 %; часы 18–48 считаются по прогонам на 1–2 суток старше (§ 4).
- Покрытие коридора P10–P90: 80,5 % часов при цели ≈ 80 %.
- Ряд «прогноз против факта» (каждый час января по одному разу, 732 часа): факт внутри коридора в 82,8 % часов, ниже P10 — 7,8 %, выше P90 — 9,4 %; средний P50 0,302 против факта 0,317 — смещение −1,5 п.п.; корреляция P50 с фактом 0,77.
- Независимая проверка выбора сдвига часов: 8–31 января (не участвовали в выборе) — nMAE модели 16,8 %, персистентности 36,4 %.
- Под правилом без 8-часовой задержки та же схема давала 15,0 %; честное правило стоит 0,8 п.п. (§ 3.3).
- Ошибка по каждому горизонту и по дням — вкладка «Качество» и
GET /api/metrics(by_horizon,by_day).
Эксперимент: модели только на истории SCADA
Можно ли обойтись без прогноза погоды? Это проверяет отдельный офлайн-эксперимент Кубы
ml-prognoz-model/ (PR #7): только выданные CSV, 48 прямых выходов
из lag- и rolling-признаков SCADA и календаря, 14 кандидатов (персистентность, сезонная медиана, Ridge,
Extra Trees, Random Forest, CatBoost), настройка на октябре–ноябре 2025 и замороженный отложенный период
декабрь–январь. MAE прогноза на 48 ч в долях номинала, по турбинам:
| Метод | Отложенный период эксперимента, 01.12.2025–29.01.2026 | Январь: те же 30 моментов выпуска, что в нашем бэктесте |
|---|---|---|
| Персистентность: последнее значение на все 48 ч | 0,352 | 0,333 |
| CatBoost — лучшая обучаемая модель по валидации | 0,315 | 0,303 |
| Сезонная медиана — лучшая на истории | 0,310 | 0,273 |
| Windcast: прогноз погоды + квантильная модель | — | 0,158 |
Январский столбец мы посчитали сами: по holdout_predictions.parquet эксперимента — на моментах
выпуска 31.12.2025 … 29.01.2026 в 19:00 UTC, а для Windcast — тем же артефактом quantile_2025-12-31.pkl
на тех же выпусках (турбина 1 — 0,1585, турбина 2 — 0,1581). Без прогноза погоды ошибка в 1,7–1,9 раза
больше, по отложенному периоду эксперимента — примерно вдвое, хотя модели на истории знают фактическую
мощность вплоть до момента выпуска. Поэтому продукт строится на прогнозе погоды. Оговорки: модели
эксперимента в отложенном периоде обучены по 01.12.2025, наша январская — по 31.12.2025; часы SCADA
эксперимент принимает за UTC+5, а продукт по бэктесту выбрал +6.
Эксперимент в продукт не входит. Февраль он воспроизвести не может: каждому его прогнозу нужны последние
24 часа наблюдений, а данных за февраль организаторы не дали. Прогноз у него точечный, без P10–P90.
Окружение своё (Python ≥ 3.11, uv); проверено 23.09 на выгрузке main:
uv sync занял около минуты, 80 тестов прошли.
cd ml-prognoz-model && uv sync --extra dev && uv run pytest
Подробности — ml-prognoz-model/README.md и
ml-prognoz-model/docs/results.md.
Как проверить точность февраля
Для жюри с фактом за февраль — три файла в outputs/evaluation/
(доли номинала 0–1, plant = среднее турбин 1 и 2, целевой час начинается в target_time_local, UTC+5):
february_day_ahead.csv — один прогноз на час (выпуск D−1, h 1–24, 2016 строк), february_all_horizons.csv —
все 4176 строк с h 1–48, january_day_ahead.csv — январь с колонкой actual для проверки самого скрипта.
Оценка одной командой, только стандартная библиотека:
python3 scripts/score.py --actual <папка с turbine_1.csv и turbine_2.csv за февраль> # --json для машин
python3 scripts/score.py --actual data/raw --forecast outputs/evaluation/january_day_ahead.csv # самопроверка
Самопроверка на январе: nMAE ВЭС 14,71 % по 732 часам — то же число, что из колонки actual
(15,76 % выше в этом разделе — все 48 горизонтов, 1417 часов). Скачать: https://pushin.codes/api/evaluation/february_day_ahead.csv,
https://pushin.codes/api/evaluation/february_all_horizons.csv. Часы SCADA организатора мы считаем UTC+6
(выбрано на январе); если иначе — --scada-utc-offset 5. Метрики и правила отбора часов —
outputs/evaluation/README.md.
10. Структура репозитория
backend/ FastAPI и пакет windcast; Dockerfile собирается из корня репозитория
windcast/
paths.py timeline.py общие: пути от WINDCAST_ROOT, момент выпуска T, горизонт, лаги
config.py data.py SCADA → почасовой ряд, check_data (Куба)
weather.py model.py evaluate.py Open-Meteo и кэш, квантильная модель и базовые линии, бэктест января (Куба)
agent.py tools.py store.py агент, 6 инструментов, запись выпусков (Абылай)
ports.py _stubs.py подключение функций погоды, данных и модели; заглушки только для тестов
api.py runs.py watcher.py REST + SSE, прогоны в фоне и журнал, Live-наблюдатель (Абылай)
backtest.py CLI: выпуски подряд
tests/ pytest, 155 тестов
frontend/ интерфейс (Сула): React + Vite — src/*.jsx, src/*.test.js, nginx.conf, Dockerfile; fixtures/ — синтетические ответы API для разработки
ml-prognoz-model/ эксперимент Кубы: прогноз только по истории SCADA, своё окружение uv; в продукт не входит
data/raw/ CSV SCADA как выданы организатором, не изменяются
data/processed/ hourly.parquet — генерируется, не в git
data/weather_cache/ 60 ответов Open-Meteo — запасной путь без сети и архив для обучения
models/ quantile_2025-12-31.pkl (январь), quantile_2026-01-31.pkl (февраль)
outputs/ 29 выпусков, ленты шагов, сводный CSV, метрики января — результат для сдачи
scripts/verify.py проверка выпусков для жюри (только стандартная библиотека); scripts/tests/ — её тесты
infra/ Caddy, compose для сервера, деплой push-to-deploy
docs/ контракт, ТЗ, разбор данных, схема архитектуры, макет интерфейса
docker-compose.yml запуск из чистого клона
Свой README.md есть в backend/, frontend/, data/, models/, outputs/, infra/ и ml-prognoz-model/.
11. Сторонние компоненты и заготовки
Полное раскрытие по п. 5.4.4–5.4.4.2 — в docs/THIRD_PARTY.md: открытые библиотеки с версиями и лицензиями, данные (SCADA организатора; погода Open-Meteo, CC BY 4.0), OpenAI API, заготовки до хакатона (инфраструктура, процессные скрипты и шаблоны от 22.09 — без продуктового кода) и использование AI-ассистентов. Вся продуктовая функциональность написана 23.09 в соревновательной части, история — в git. Push от 23.09 12:25, до старта, содержал только эти заготовки — подробно там же.
12. Ограничения и потенциал развития
Ограничения — честно:
- Одна погодная модель — то, что Open-Meteo отдаёт как
best_match. Ансамбля нет, коридор P10–P90 отражает только ошибку, выученную на истории, а флаг «расхождение погодных моделей» не срабатывает: в каждой ленте так и написано — «нет второй модели». - Часы 18–48 считаются по суточным корзинам архива (
previous_day2/3), а не по конкретному прогону, поэтому вторые сутки на 1–2 прогона старше, чем могли бы быть: nMAE 17,0 % против 14,6 % в первые сутки. - Признак
lag_daysв обучении принимает только 1 или 2, а в выпуске часы 42–48 берут прогон давностью 3 суток (в первом расчёте v1 — 4): деревья относят такие часы к той же ветке, что и лаг 2, прямых обучающих примеров с lag 3–4 у модели нет. - Задержка публикации прогона 8 ч — консервативная константа. Архив не хранит реальный момент выхода каждого прогона. Если прогон на деле выходил раньше, мы теряем немного свежести, но утечки нет.
- Факта за февраль нет, поэтому точность февраля не посчитана. Качество показано на январе, февральские CSV готовы для сверки организатором.
- Выигрыш по nRMSE над кривой мощности небольшой (23,45 против 23,99): крупные промахи определяет ошибка прогноза ветра, которую одна модель погоды не снимает. Модель слегка занижает выработку (смещение −1,5 п.п.).
- Прогнозируется выработка исправной турбины. Простои и ограничения (≈ 1 % записей) погодой не
предсказываются; в обучении они исключены через
valid = false. - Архив Previous Runs есть с 06.2024, поэтому модель учится на 20 месяцах (115–122 тысячи часов × турбина × лаг). SCADA с 03.2023 до 06.2024 в обучение модели не входит.
- Ветер > 20 м/с в истории почти не встречался, так что поведение на отсечке модель знает плохо.
Такие часы агент помечает флагом
wind_gt20. - Обледенение — эвристика по температуре (от −3 до +1 °C), без влажности и осадков.
- Прогоны агента хранятся в памяти процесса, поэтому API работает с одним воркером uvicorn. Перезапуск
обрывает незавершённые потоки SSE, а опубликованные выпуски остаются в
outputs/. Live-выпуски и журнал лежат вoutputs/live/внутри контейнера и не переживают его пересборку. - Open-Meteo бесплатен только для некоммерческого использования и с лимитом запросов. Для эксплуатации нужен коммерческий тариф или прямая загрузка открытых данных моделей.
- В Live время прогона погоды — это время загрузки снимка, а не факт запуска модели: Open-Meteo
Forecast API не сообщает момент init, поэтому
init_time_utcтам равен моменту, когда бэкенд забрал ответ.
Что уже сделано нестандартно:
- «Без будущего» встроено в систему, а не только соблюдено: правило в коде (
timeline.py,weather.py, отказrecalc_forecast), независимая проверка (verify.py) и «✓ Без будущего» на экране у каждого выпуска. Мы приняли потерю 0,8 п.п. точности ради честного правила. - Пересчёт запускается событием «вышел новый прогон», а решение принимает агент и объясняет его в ленте; все 29 февральских решений — за LLM.
- Агент работает сам: Live-наблюдатель и журнал решений без участия человека.
- Прогноз вероятностный (P10–P90, покрытие 80,5 %), а не одна цифра, с флагами рисков на оси времени.
- Детерминированный режим даёт те же числа без ключа, так что проверка не требует внешних аккаунтов.
Что можно улучшить (ожидаемый эффект — оценка команды, не измерено):
- Ансамбль погодных моделей (ECMWF IFS, GFS, ICON — все есть в Open-Meteo, в том числе в Previous Runs): ожидаем снижение ошибки на 5–10 % и более узкий, честно откалиброванный коридор; флаг расхождения моделей заработает на реальных данных.
- Архив конкретных прогонов вместо суточных корзин для часов 18–48: вторые сутки считались бы по прогону D 06:00 UTC, ошибка второго дня — с 17 % до примерно 15 %.
- Подмешивание SCADA в первые часы в эксплуатации, когда факт доступен онлайн: текущая выработка и её тренд для h 1–6, где персистентность сильна.
- «Спросить агента»: вопросы диспетчера на естественном языке («почему в 15:00 спад?», «что будет, если ветер усилится?») с ответами через те же инструменты. Пока не реализовано.
- Модель обледенения: температура, влажность и осадки, поправка к P50/P10 в часы риска.
- Несколько ВЭС: новая станция — это координаты и история SCADA. Инструменты и правило «без будущего» от станции не зависят.
- Экспорт графика в формат системного оператора и уведомления диспетчеру о рампах.
- MCP-адаптер над тем же реестром инструментов: агента можно подключить к любому MCP-клиенту (например, к ассистенту диспетчера) без переписывания инструментов.
13. Команда
Pushin' Code
- Абылай — мастер: архитектура, агент и API, инфраструктура.
- Куба — данные, погода, модель.
- Сула — интерфейс.
Авторы коммитов
История коммитов
- 2026-09-23 17:51[T8] README: February player controls, climatology wording, two honest limits; RUBRIC: 29 issuesabekesalimzhanov
- 2026-09-23 17:49[T8] Docs: regulation references in AGENTS, stale pre-start criteria note marked as outdatedabekesalimzhanov
- 2026-09-23 17:49[T7] Risk ranges across midnight show the end date; baseline labels match the code (median, SCADA power curve)abekesalimzhanov
- 2026-09-23 17:45[T7] February playback: pause, resume from the same step, speed 0.5-4x, play from any dayabekesalimzhanov
- 2026-09-23 17:34[T7] Live: plant / turbine 1 / turbine 2 switch, as on the February tababekesalimzhanov
- 2026-09-23 17:30[T9] Chat: plain-language accuracy answersabekesalimzhanov
- 2026-09-23 17:29[T9] Chat: accuracy questions answered from the January metrics in both modesabekesalimzhanov
- 2026-09-23 17:26[T8] README: dispatcher chat; February screenshot with the chat buttonabekesalimzhanov
Все коммиты (114)
- 2026-09-23 17:25[T9] Chat: peak/min/risk questions go through get_issue_summary firstabekesalimzhanov
- 2026-09-23 17:23Merge pull request #9 from BAITC-Hacks/t9-ask-backendAbylay
- 2026-09-23 17:23[T8] Final screenshots and score logabekesalimzhanov
- 2026-09-23 17:23Add dispatcher ask endpointkubevele
- 2026-09-23 17:22[T5] Metrics period validation; cap concurrent runs at 3abekesalimzhanov
- 2026-09-23 17:21[T7] Clearer button labels; run start shown as an upper boundabekesalimzhanov
- 2026-09-23 17:21[T8] Disclose the 12:25 pre-start push: tooling only, per 5.4.4.2abekesalimzhanov
- 2026-09-23 17:20[T8] README: five-minute block for the reviewer at the topabekesalimzhanov
- 2026-09-23 17:19[T8] README: run start is an upper bound, best_match named; rubric counts 29 issuesabekesalimzhanov
- 2026-09-23 17:14[T8] README: shorter opening, honest note on the injected new-run event, third-party list moved to docs/THIRD_PARTY.mdabekesalimzhanov
- 2026-09-23 17:10[T8] Screenshots after the readability and wording passesabekesalimzhanov
- 2026-09-23 17:05Merge Sula's ask-agent chat panel (hidden until /health reports ask: true)abekesalimzhanov
- 2026-09-23 17:04[T8] February evaluation export and one-command scorerabekesalimzhanov
- 2026-09-23 16:59[T7] Plain-language confidence labels instead of P10–P90abekesalimzhanov
- 2026-09-23 16:57[T9] Ask-agent chat panel, shown only when /health reports ask: trueSultan Turan
- 2026-09-23 16:56[T7] Readability pass: big numbers, one-line risks and steps, lighter chartabekesalimzhanov
- 2026-09-23 16:54Merge pull request #8 from BAITC-Hacks/fix-t2/t3-archived-weather-availability-for-contract-v0.5-weather-fetch-must-useAbylay
- 2026-09-23 16:54[T8] Submission texts: 174 weather runs after the regenerationabekesalimzhanov
- 2026-09-23 16:54[T8] Slides, demo script and submission textsabekesalimzhanov
- 2026-09-23 16:54[T8] README: counts after regeneration, no-Docker notes, non-destructive rerun (jury dry-run findings)abekesalimzhanov
- 2026-09-23 16:48[T0] contract v0.6: dispatcher chat POST /api/ask behind a /health flagabekesalimzhanov
- 2026-09-23 16:47[T8] Screenshots of the three tabs from https://pushin.codesabekesalimzhanov
- 2026-09-23 16:47[T8] README: final numbers, agent-mode outputs, watcherabekesalimzhanov
- 2026-09-23 16:47[T8] README: frontend, clean-clone check, CSV-only experimentabekesalimzhanov
- 2026-09-23 16:47[T8] README: all 29 issues v2 after the nudge fixabekesalimzhanov
- 2026-09-23 16:47[T8] README: Sula's React UI, screenshots, deploymentabekesalimzhanov
- 2026-09-23 16:43[T0] Board and score: all tasks done, 83/100 by rubric v3, admission conditions closedabekesalimzhanov
- 2026-09-23 16:40[T2] Live weather passes check_data at fetch time; the 8 h rule stays for archive issuesabekesalimzhanov
- 2026-09-23 16:34Merge Sula's dispatcher UI (React, light theme) into mainabekesalimzhanov
- 2026-09-23 16:31checkpoint(sula): [T6][T7] Light dispatcher UI: February, Live, Quality, agent panel with SSE, 3-minute check; React build served by nginxSultan Turan
- 2026-09-23 16:31[T7] Live label names the weather run generically; frontend README matches the nginx setupabekesalimzhanov
- 2026-09-23 16:29[T4] Regenerate 2026-02-08/10/12/14 after the nudge fix: the LLM's recalc decisions now match its wordsabekesalimzhanov
- 2026-09-23 16:27[T4] Nudge the LLM once when it stops without a tool call; otherwise decide by ruleabekesalimzhanov
- 2026-09-23 16:27Configure ML development test dependencieskubevele
- 2026-09-23 16:26[T7] Fix class collisions, mobile layout, SSE tests, frontend READMESultan Turan
- 2026-09-23 16:26[T6][T7] Light dispatcher UI: February, Live, Quality, agent panel, 3-minute checkSultan Turan
- 2026-09-23 16:25Fix archived weather availability and model lag handlingkubevele
- 2026-09-23 16:25Format weather availability changeskubevele
- 2026-09-23 16:21Merge pull request #7 from BAITC-Hacks/csv-only-forecast-modelsAbylay
- 2026-09-23 16:19[T3] Add CSV-only forecasting pipeline and trained modelskubevele
- 2026-09-23 16:19[T3] Clarify shared raw data and direct forecast outputskubevele
- 2026-09-23 16:16[T7] Frontend on the real API: February, Live, Quality; nginx imageabekesalimzhanov
- 2026-09-23 16:12Merge pull request #6 from BAITC-Hacks/fix-contract-v0.4-no-future-validation-so-weather-runs-are-accepted-only-when-inAbylay
- 2026-09-23 16:07Merge remote-tracking branch 'origin/main' into fix-contract-v0.4-no-future-validation-so-weather-runs-are-accepted-only-when-inkubevele
- 2026-09-23 16:05[T4] February 2026: 29 issues with agent traces (LLM mode), January metrics under v0.4, retrained models, weather cacheabekesalimzhanov
- 2026-09-23 15:59checkpoint(kuba): [T2] enforce publication delay for archive and Live; [T8] add CSV and record boundary regressionskubevele
- 2026-09-23 15:57[T2] Weather and model follow the v0.4 no-future ruleabekesalimzhanov
- 2026-09-23 15:56Enforce weather run publication timeskubevele
- 2026-09-23 15:56Merge Kuba's weather, quantile model and January backtest (T2, T3) into mainabekesalimzhanov
- 2026-09-23 15:52[T8] README draft for the juryabekesalimzhanov
- 2026-09-23 15:52[T4] Default LLM gpt-5.4-mini; 8 h publication delay in the API; stubs visible in /healthabekesalimzhanov
- 2026-09-23 15:50[T6] Live watcher, run journal and weather-outage scenario per contract v0.5abekesalimzhanov
- 2026-09-23 15:50[T6] Pass runner options such as scenario through the default runnerabekesalimzhanov
- 2026-09-23 15:48[T4] Live: v1 on the latest run when no older snapshot existsabekesalimzhanov
- 2026-09-23 15:41Merge pull request #5 from BAITC-Hacks/t3-quantile-modelAbylay
- 2026-09-23 15:39[T0] contract v0.5: autonomous live watcher with a journal; weather outage scenarioabekesalimzhanov
- 2026-09-23 15:37[T4] Agent: six-tool registry, LLM loop with deterministic fallback, store, backtest CLIabekesalimzhanov
- 2026-09-23 15:37[T4] Clearer dispatcher texts: grouped risks, concrete no-future refusal, issue introabekesalimzhanov
- 2026-09-23 15:37[T4] LLM edge cases: premature recalc is not a decision, bad data always ends in error + verdictabekesalimzhanov
- 2026-09-23 15:37Train weather-only quantile forecastskubevele
- 2026-09-23 15:30Merge pull request #4 from BAITC-Hacks/t2-weather-cacheAbylay
- 2026-09-23 15:28Reject future live cache snapshotskubevele
- 2026-09-23 15:27[T8] verify.py: no-future rule counts the run publication delayabekesalimzhanov
- 2026-09-23 15:26Validate live weather before cachingkubevele
- 2026-09-23 15:24Cache live weather failureskubevele
- 2026-09-23 15:23[T0] Stop tracking .DS_Storeabekesalimzhanov
- 2026-09-23 15:23[T0] contract v0.4: no-future rule accounts for run publication delayabekesalimzhanov
- 2026-09-23 15:22Reject incomplete weather responseskubevele
- 2026-09-23 15:22[T8] Build backend from the repo root in the dedicated-host compose tooabekesalimzhanov
- 2026-09-23 15:22[T8] scripts/verify.py: jury check for the 29 issues, no-future rule firstabekesalimzhanov
- 2026-09-23 15:22[T8] root docker-compose for a clean clone; backend builds from the repo rootabekesalimzhanov
- 2026-09-23 15:21Validate weather cache provenancekubevele
- 2026-09-23 15:21[T5] Add FastAPI API per contract §6 with run registry and SSEabekesalimzhanov
- 2026-09-23 15:21[T5] Order events by schema, quiet agent-missing log, clamp Last-Event-IDabekesalimzhanov
- 2026-09-23 15:21[T5] Add backend Dockerfile (root build context) and .dockerignoreabekesalimzhanov
- 2026-09-23 15:21[T5] Make issue-lock test deterministic; keep tests off the real model importabekesalimzhanov
- 2026-09-23 15:19Add API-first weather cachekubevele
- 2026-09-23 15:18Merge pull request #3 from BAITC-Hacks/t1-hourly-scadaAbylay
- 2026-09-23 15:16[T5a] Add API fixtures for all screensabekesalimzhanov
- 2026-09-23 15:16Add red tests for weather cachekubevele
- 2026-09-23 15:13Allow guarded task base fast-forwardkubevele
- 2026-09-23 15:12Exercise nested virtual environment pruningkubevele
- 2026-09-23 15:09checkpoint(kubevele): [T1] Validated UTC hourly SCADA parquet, strict check_data, stacked taskflow base receipts; committed and verified.kubevele
- 2026-09-23 15:08Ignore nested virtual environments during verificationkubevele
- 2026-09-23 15:07Harden SCADA validation and taskflow verificationkubevele
- 2026-09-23 15:06Build validated hourly SCADA datasetkubevele
- 2026-09-23 15:00Add red tests for hourly SCADA pipelinekubevele
- 2026-09-23 14:58[T0] contract v0.3: internal interfaces, record format; shared backend scaffoldabekesalimzhanov
- 2026-09-23 14:43[T0] contract v0.2: UI endpoints, 29 issues, repo tree, UI mockup and architectureabekesalimzhanov
- 2026-09-23 14:42Merge pull request #2 from BAITC-Hacks/taskflow-deliveryAbylay
- 2026-09-23 14:36Make Ruff verification reproduciblekubevele
- 2026-09-23 14:33Merge pull request #1 from BAITC-Hacks/taskflow-deliveryAbylay
- 2026-09-23 14:29Format Python examples for Ruffkubevele
- 2026-09-23 14:20Add Jira-free taskflow workflowkubevele
- 2026-09-23 13:56[T0] first brainstorm: case choice, agent design, user flow (draft, not final)abekesalimzhanov
- 2026-09-23 13:34[T0] task, data notes, contract v1, rubric v3 for the wind power forecasting caseabekesalimzhanov
- 2026-09-23 12:24docs: align with the 22.09 regulation — 13:00–18:00 window, run-or-disqualified, task-specific criteriaabekesalimzhanov
- 2026-09-22 21:38fix(infra): chown the bare repo after git init, not beforeabekesalimzhanov
- 2026-09-22 18:37fix(infra): drop the generic healthcheck that blocked startupabekesalimzhanov
- 2026-09-22 18:35fix(scripts): checkpoint missed secrets in newly added filesabekesalimzhanov
- 2026-09-22 18:26docs: point the board and playbook at the live domainabekesalimzhanov
- 2026-09-22 18:25chore(infra): pushin.codes over the host's existing Caddy, plus a smoke stackabekesalimzhanov
- 2026-09-22 18:02chore(infra): deploy variant for a host that already serves 80/443abekesalimzhanov
- 2026-09-22 17:49chore: add infra/.env.example, previously excluded by the ignore rulesabekesalimzhanov
- 2026-09-22 17:49chore: keep .env.example out of the ignore rulesabekesalimzhanov
- 2026-09-22 17:48chore: agent config, docs and tooling prepared before the hackathonabekesalimzhanov
Структура репозитория (441 файлов)
.agents/roles/delivery-reviewer.md .agents/roles/task-worker.md .agents/skills/worktree-delivery/SKILL.md .agents/skills/worktree-delivery/agents/openai.yaml .codex/agents/delivery-reviewer.toml .codex/agents/task-worker.toml .dockerignore .env.example .gitattributes .gitignore AGENTS.md BOARD.md CLAUDE.md README.md RTK.md backend/Dockerfile backend/README.md backend/pyproject.toml backend/requirements-dev.txt backend/requirements.txt backend/tests/test_agent.py backend/tests/test_api.py backend/tests/test_ask.py backend/tests/test_data.py backend/tests/test_evaluate.py backend/tests/test_model.py backend/tests/test_store.py backend/tests/test_timeline.py backend/tests/test_watcher.py backend/tests/test_weather.py backend/windcast/__init__.py backend/windcast/_stubs.py backend/windcast/agent.py backend/windcast/api.py backend/windcast/ask.py backend/windcast/backtest.py backend/windcast/config.py backend/windcast/data.py backend/windcast/evaluate.py backend/windcast/model.py backend/windcast/paths.py backend/windcast/ports.py backend/windcast/runs.py backend/windcast/store.py backend/windcast/timeline.py backend/windcast/tools.py backend/windcast/watcher.py backend/windcast/weather.py data/README.md data/processed/.gitkeep data/raw/turbine_1.csv data/raw/turbine_2.csv data/weather_cache/.gitkeep data/weather_cache/previous_2025-12-31_2026-01-02.json data/weather_cache/previous_2026-01-01_2026-01-03.json data/weather_cache/previous_2026-01-02_2026-01-04.json data/weather_cache/previous_2026-01-03_2026-01-05.json data/weather_cache/previous_2026-01-04_2026-01-06.json data/weather_cache/previous_2026-01-05_2026-01-07.json data/weather_cache/previous_2026-01-06_2026-01-08.json data/weather_cache/previous_2026-01-07_2026-01-09.json data/weather_cache/previous_2026-01-08_2026-01-10.json data/weather_cache/previous_2026-01-09_2026-01-11.json data/weather_cache/previous_2026-01-10_2026-01-12.json data/weather_cache/previous_2026-01-11_2026-01-13.json data/weather_cache/previous_2026-01-12_2026-01-14.json data/weather_cache/previous_2026-01-13_2026-01-15.json data/weather_cache/previous_2026-01-14_2026-01-16.json data/weather_cache/previous_2026-01-15_2026-01-17.json data/weather_cache/previous_2026-01-16_2026-01-18.json data/weather_cache/previous_2026-01-17_2026-01-19.json data/weather_cache/previous_2026-01-18_2026-01-20.json data/weather_cache/previous_2026-01-19_2026-01-21.json data/weather_cache/previous_2026-01-20_2026-01-22.json data/weather_cache/previous_2026-01-21_2026-01-23.json data/weather_cache/previous_2026-01-22_2026-01-24.json data/weather_cache/previous_2026-01-23_2026-01-25.json data/weather_cache/previous_2026-01-24_2026-01-26.json data/weather_cache/previous_2026-01-25_2026-01-27.json data/weather_cache/previous_2026-01-26_2026-01-28.json data/weather_cache/previous_2026-01-27_2026-01-29.json data/weather_cache/previous_2026-01-28_2026-01-30.json data/weather_cache/previous_2026-01-29_2026-01-31.json data/weather_cache/previous_2026-01-31_2026-02-02.json data/weather_cache/previous_2026-02-01_2026-02-03.json data/weather_cache/previous_2026-02-02_2026-02-04.json data/weather_cache/previous_2026-02-03_2026-02-05.json data/weather_cache/previous_2026-02-04_2026-02-06.json data/weather_cache/previous_2026-02-05_2026-02-07.json data/weather_cache/previous_2026-02-06_2026-02-08.json data/weather_cache/previous_2026-02-07_2026-02-09.json data/weather_cache/previous_2026-02-08_2026-02-10.json data/weather_cache/previous_2026-02-09_2026-02-11.json data/weather_cache/previous_2026-02-10_2026-02-12.json data/weather_cache/previous_2026-02-11_2026-02-13.json data/weather_cache/previous_2026-02-12_2026-02-14.json data/weather_cache/previous_2026-02-13_2026-02-15.json data/weather_cache/previous_2026-02-14_2026-02-16.json data/weather_cache/previous_2026-02-15_2026-02-17.json data/weather_cache/previous_2026-02-16_2026-02-18.json data/weather_cache/previous_2026-02-17_2026-02-19.json data/weather_cache/previous_2026-02-18_2026-02-20.json data/weather_cache/previous_2026-02-19_2026-02-21.json data/weather_cache/previous_2026-02-20_2026-02-22.json data/weather_cache/previous_2026-02-21_2026-02-23.json data/weather_cache/previous_2026-02-22_2026-02-24.json data/weather_cache/previous_2026-02-23_2026-02-25.json data/weather_cache/previous_2026-02-24_2026-02-26.json data/weather_cache/previous_2026-02-25_2026-02-27.json data/weather_cache/previous_2026-02-26_2026-02-28.json data/weather_cache/previous_2026-02-27_2026-03-01.json data/weather_cache/previous_2026-02-28_2026-03-02.json data/weather_cache/previous_runs_bulk.json docker-compose.yml docs/BRAINSTORM.md docs/CONTRACT.md docs/DATA_NOTES.md docs/DEMO.md docs/RUBRIC.md docs/SCORE.md docs/SLIDES.md docs/SUBMISSION.md docs/THIRD_PARTY.md docs/architecture.html docs/change-request-template.md docs/hackathon/cases-forecast.md docs/hackathon/criteria.md docs/hackathon/playbook.md docs/hackathon/regulation.md docs/hackathon/task.md docs/progress/kuba.md docs/progress/kubevele.md docs/progress/sula.md docs/references/README.md docs/references/TracePanel.jsx docs/references/agent_loop.py docs/references/data-quickstart.md docs/references/trace-event-schema.md docs/screenshots/february.png docs/screenshots/live.png docs/screenshots/quality.png docs/task-workflow.md docs/ui-mockup.html frontend/.dockerignore frontend/Dockerfile frontend/README.md frontend/fixtures/.gitkeep frontend/fixtures/README.md frontend/fixtures/_generate.py frontend/fixtures/forecasts_2026-01-31.json frontend/fixtures/forecasts_2026-02-01.json frontend/fixtures/forecasts_2026-02-02.json frontend/fixtures/forecasts_2026-02-03.json frontend/fixtures/forecasts_2026-02-03_v1.json frontend/fixtures/forecasts_2026-02-04.json frontend/fixtures/forecasts_2026-02-05.json frontend/fixtures/forecasts_2026-02-06.json frontend/fixtures/forecasts_2026-02-07.json frontend/fixtures/forecasts_2026-02-08.json frontend/fixtures/forecasts_2026-02-08_v1.json frontend/fixtures/forecasts_2026-02-09.json frontend/fixtures/forecasts_2026-02-10.json frontend/fixtures/forecasts_2026-02-11.json frontend/fixtures/forecasts_2026-02-12.json frontend/fixtures/forecasts_2026-02-13.json frontend/fixtures/forecasts_2026-02-13_v1.json frontend/fixtures/forecasts_2026-02-14.json frontend/fixtures/forecasts_2026-02-15.json frontend/fixtures/forecasts_2026-02-16.json frontend/fixtures/forecasts_2026-02-17.json frontend/fixtures/forecasts_2026-02-18.json frontend/fixtures/forecasts_2026-02-19.json frontend/fixtures/forecasts_2026-02-19_v1.json frontend/fixtures/forecasts_2026-02-20.json frontend/fixtures/forecasts_2026-02-21.json frontend/fixtures/forecasts_2026-02-22.json frontend/fixtures/forecasts_2026-02-23.json frontend/fixtures/forecasts_2026-02-24.json frontend/fixtures/forecasts_2026-02-24_v1.json frontend/fixtures/forecasts_2026-02-25.json frontend/fixtures/forecasts_2026-02-26.json frontend/fixtures/forecasts_2026-02-27.json frontend/fixtures/forecasts_2026-02-27_v1.json frontend/fixtures/forecasts_2026-02-28.json frontend/fixtures/forecasts_live.json frontend/fixtures/health.json frontend/fixtures/issues.json frontend/fixtures/live_status.json frontend/fixtures/metrics.json frontend/fixtures/metrics_series.json frontend/fixtures/run_events_issue.json frontend/fixtures/run_events_new_weather_run.json frontend/fixtures/traces_2026-01-31.json frontend/fixtures/traces_2026-02-01.json frontend/fixtures/traces_2026-02-02.json frontend/fixtures/traces_2026-02-03.json frontend/fixtures/traces_2026-02-04.json frontend/fixtures/traces_2026-02-05.json frontend/fixtures/traces_2026-02-06.json frontend/fixtures/traces_2026-02-07.json frontend/fixtures/traces_2026-02-08.json frontend/fixtures/traces_2026-02-09.json frontend/fixtures/traces_2026-02-10.json frontend/fixtures/traces_2026-02-11.json frontend/fixtures/traces_2026-02-12.json frontend/fixtures/traces_2026-02-13.json frontend/fixtures/traces_2026-02-14.json frontend/fixtures/traces_2026-02-15.json frontend/fixtures/traces_2026-02-16.json frontend/fixtures/traces_2026-02-17.json frontend/fixtures/traces_2026-02-18.json frontend/fixtures/traces_2026-02-19.json frontend/fixtures/traces_2026-02-20.json frontend/fixtures/traces_2026-02-21.json frontend/fixtures/traces_2026-02-22.json frontend/fixtures/traces_2026-02-23.json frontend/fixtures/traces_2026-02-24.json frontend/fixtures/traces_2026-02-25.json frontend/fixtures/traces_2026-02-26.json frontend/fixtures/traces_2026-02-27.json frontend/fixtures/traces_2026-02-28.json frontend/index.html frontend/nginx.conf frontend/package-lock.json frontend/package.json frontend/src/AgentPanel.jsx frontend/src/App.jsx frontend/src/ChatPanel.jsx frontend/src/February.jsx frontend/src/ForecastChart.jsx frontend/src/Live.jsx frontend/src/Quality.jsx frontend/src/agent.test.js frontend/src/api.js frontend/src/api.test.js frontend/src/chart.js frontend/src/chart.test.js frontend/src/chat.css frontend/src/chat.test.js frontend/src/format.js frontend/src/main.jsx frontend/src/stream.test.js frontend/src/styles.css frontend/src/useAgentRun.js frontend/vite.config.js infra/.env.example infra/Caddyfile infra/Caddyfile.local infra/README.md infra/caddy-site.snippet infra/deploy.sh infra/docker-compose.hosted.yml infra/docker-compose.yml infra/server-setup.sh ml-prognoz-model/.gitignore ml-prognoz-model/README.md ml-prognoz-model/artifacts/csv_only/best_ml_model.pkl ml-prognoz-model/artifacts/csv_only/best_ml_model.pkl.manifest.json ml-prognoz-model/artifacts/csv_only/best_model.pkl ml-prognoz-model/artifacts/csv_only/best_model.pkl.manifest.json ml-prognoz-model/artifacts/csv_only/candidate_tuning.csv ml-prognoz-model/artifacts/csv_only/dataset_manifest.json ml-prognoz-model/artifacts/csv_only/evaluation_report.json ml-prognoz-model/artifacts/csv_only/forecast_24h.csv ml-prognoz-model/artifacts/csv_only/forecast_48h.csv ml-prognoz-model/artifacts/csv_only/forecast_ml_24h.csv ml-prognoz-model/artifacts/csv_only/forecast_ml_48h.csv ml-prognoz-model/artifacts/csv_only/holdout_predictions.parquet ml-prognoz-model/artifacts/csv_only/leaderboard.csv ml-prognoz-model/artifacts/csv_only/run_config.json ml-prognoz-model/configs/default.json ml-prognoz-model/configs/history.json ml-prognoz-model/docs/README.md ml-prognoz-model/docs/data.md ml-prognoz-model/docs/delivery.md ml-prognoz-model/docs/evaluation.md ml-prognoz-model/docs/history.md ml-prognoz-model/docs/inference.md ml-prognoz-model/docs/models.md ml-prognoz-model/docs/reproducibility.md ml-prognoz-model/docs/results.md ml-prognoz-model/docs/weather.md ml-prognoz-model/pyproject.toml ml-prognoz-model/reports/generated/data_quality.json ml-prognoz-model/reports/generated/incomplete_hours.csv ml-prognoz-model/reports/generated/inference_verification.json ml-prognoz-model/reports/generated/invalid_rows.csv ml-prognoz-model/src/wind_forecast/__init__.py ml-prognoz-model/src/wind_forecast/__main__.py ml-prognoz-model/src/wind_forecast/artifact.py ml-prognoz-model/src/wind_forecast/cli.py ml-prognoz-model/src/wind_forecast/config.py ml-prognoz-model/src/wind_forecast/data.py ml-prognoz-model/src/wind_forecast/evaluation.py ml-prognoz-model/src/wind_forecast/features.py ml-prognoz-model/src/wind_forecast/history.py ml-prognoz-model/src/wind_forecast/history_artifact.py ml-prognoz-model/src/wind_forecast/history_evaluation.py ml-prognoz-model/src/wind_forecast/history_models.py ml-prognoz-model/src/wind_forecast/io.py ml-prognoz-model/src/wind_forecast/models.py ml-prognoz-model/src/wind_forecast/offline.py ml-prognoz-model/src/wind_forecast/weather.py ml-prognoz-model/tests/test_artifact.py ml-prognoz-model/tests/test_cli.py ml-prognoz-model/tests/test_config.py ml-prognoz-model/tests/test_data.py ml-prognoz-model/tests/test_evaluation.py ml-prognoz-model/tests/test_features.py ml-prognoz-model/tests/test_history.py ml-prognoz-model/tests/test_history_artifact.py ml-prognoz-model/tests/test_history_evaluation.py ml-prognoz-model/tests/test_history_models.py ml-prognoz-model/tests/test_io.py ml-prognoz-model/tests/test_models.py ml-prognoz-model/tests/test_offline.py ml-prognoz-model/tests/test_weather.py ml-prognoz-model/uv.lock models/.gitkeep models/README.md models/quantile_2025-12-31.pkl models/quantile_2026-01-31.pkl outputs/README.md outputs/evaluation/README.md outputs/evaluation/february_all_horizons.csv outputs/evaluation/february_day_ahead.csv outputs/evaluation/january_day_ahead.csv outputs/forecast_feb2026.csv outputs/forecasts/.gitkeep outputs/forecasts/2026-01-31.csv outputs/forecasts/2026-01-31.json outputs/forecasts/2026-02-01.csv outputs/forecasts/2026-02-01.json outputs/forecasts/2026-02-02.csv outputs/forecasts/2026-02-02.json outputs/forecasts/2026-02-03.csv outputs/forecasts/2026-02-03.json outputs/forecasts/2026-02-04.csv outputs/forecasts/2026-02-04.json outputs/forecasts/2026-02-05.csv outputs/forecasts/2026-02-05.json outputs/forecasts/2026-02-06.csv outputs/forecasts/2026-02-06.json outputs/forecasts/2026-02-07.csv outputs/forecasts/2026-02-07.json outputs/forecasts/2026-02-08.csv outputs/forecasts/2026-02-08.json outputs/forecasts/2026-02-09.csv outputs/forecasts/2026-02-09.json outputs/forecasts/2026-02-10.csv outputs/forecasts/2026-02-10.json outputs/forecasts/2026-02-11.csv outputs/forecasts/2026-02-11.json outputs/forecasts/2026-02-12.csv outputs/forecasts/2026-02-12.json outputs/forecasts/2026-02-13.csv outputs/forecasts/2026-02-13.json outputs/forecasts/2026-02-14.csv outputs/forecasts/2026-02-14.json outputs/forecasts/2026-02-15.csv outputs/forecasts/2026-02-15.json outputs/forecasts/2026-02-16.csv outputs/forecasts/2026-02-16.json outputs/forecasts/2026-02-17.csv outputs/forecasts/2026-02-17.json outputs/forecasts/2026-02-18.csv outputs/forecasts/2026-02-18.json outputs/forecasts/2026-02-19.csv outputs/forecasts/2026-02-19.json outputs/forecasts/2026-02-20.csv outputs/forecasts/2026-02-20.json outputs/forecasts/2026-02-21.csv outputs/forecasts/2026-02-21.json outputs/forecasts/2026-02-22.csv outputs/forecasts/2026-02-22.json outputs/forecasts/2026-02-23.csv outputs/forecasts/2026-02-23.json outputs/forecasts/2026-02-24.csv outputs/forecasts/2026-02-24.json outputs/forecasts/2026-02-25.csv outputs/forecasts/2026-02-25.json outputs/forecasts/2026-02-26.csv outputs/forecasts/2026-02-26.json outputs/forecasts/2026-02-27.csv outputs/forecasts/2026-02-27.json outputs/forecasts/2026-02-28.csv outputs/forecasts/2026-02-28.json outputs/metrics_jan.json outputs/traces/.gitkeep outputs/traces/2026-01-31.jsonl outputs/traces/2026-02-01.jsonl outputs/traces/2026-02-02.jsonl outputs/traces/2026-02-03.jsonl outputs/traces/2026-02-04.jsonl outputs/traces/2026-02-05.jsonl outputs/traces/2026-02-06.jsonl outputs/traces/2026-02-07.jsonl outputs/traces/2026-02-08.jsonl outputs/traces/2026-02-09.jsonl outputs/traces/2026-02-10.jsonl outputs/traces/2026-02-11.jsonl outputs/traces/2026-02-12.jsonl outputs/traces/2026-02-13.jsonl outputs/traces/2026-02-14.jsonl outputs/traces/2026-02-15.jsonl outputs/traces/2026-02-16.jsonl outputs/traces/2026-02-17.jsonl outputs/traces/2026-02-18.jsonl outputs/traces/2026-02-19.jsonl outputs/traces/2026-02-20.jsonl outputs/traces/2026-02-21.jsonl outputs/traces/2026-02-22.jsonl outputs/traces/2026-02-23.jsonl outputs/traces/2026-02-24.jsonl outputs/traces/2026-02-25.jsonl outputs/traces/2026-02-26.jsonl outputs/traces/2026-02-27.jsonl outputs/traces/2026-02-28.jsonl scripts/checkpoint.sh scripts/export_evaluation.py scripts/review-prompt.md scripts/review.sh scripts/score.py scripts/taskflow/lib.sh scripts/taskflow/test_tf.sh scripts/taskflow/tf.sh scripts/tests/test_score.py scripts/tests/test_verify.py scripts/verify.py scripts/watch.sh templates/CONTRACT.md templates/README.template.md templates/SLIDES.md templates/SUBMISSION_CHECKLIST.md templates/VIDEO_SCRIPT.md templates/smoke/README.md templates/smoke/backend/Dockerfile templates/smoke/backend/app.py templates/smoke/frontend/Dockerfile templates/smoke/frontend/index.html templates/smoke/frontend/nginx.conf
Ветки (11)
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.


