Нейросеть вместо решателя в игре (air-onnx) — план

Ветка feature/air-onnx, копия ~/deltaplan-air-onnx, код задач ON, журнал docs/plan/air-onnx/. Контракты — docs/contracts/air-onnx.md (O1–O6). Основа — план air-nn §3.2–3.4, §6 и задача NN-4.

Решения пользователя (не обсуждаются)

  • Стратегически решатель в игре заменяется ONNX-сетью; решатель остаётся офлайн как источник данных. Скорость важнее точности: ошибка порядка 10–15 % ветра допустима. Вместо эмпирического запасного кода — страж области применимости входа (журнал air-nn, решение 03.10).
  • В настройках «Ветер над рельефом: расчёт / упрощённый» добавить третий вариант «нейросеть». Автоотключения нет.
  • Цель модуля — работающий сквозной путь, а не качество: после П-2 пользователь кладёт .onnx в ветку, собирает игру и летает. Не шлифовать (мелкие визуальные детали — ≤ 2 попыток); совместимость со старыми форматами не нужна.
  • Готовность: игра в этой ветке грузит .onnx из файла и строит поле сетью вместо решателя; переключение в настройках; Linux и Windows (macOS — по возможности, не блокер).

Решения координатора

  • Сеть первого пилота (~/air_nn_data/pilot/runs/2026-10-02_pilot/main/model.onnx, П2 v2: maps [1, 4, 96, 96]) — совместима: её 4 карты — первые 4 карты П2 v4 с теми же формулами, числа FiLM, выход и to_physical не менялись (сверено по коду bb63480). Игра принимает C = 4 и C = 9 → сквозная проверка на обученной сети до конца П-2.
  • Экспорт ONNX у пилота есть (evaluate.py:export_onnx, имена maps/nums/out, opset 17) — П-2 сам выдаст main/model.onnx; скрипт экспорта этой ветки нужен для метаданных, малой тестовой сети и экспорта из чекпойнта.
  • engine — отдельный ключ конфига (solver по умолчанию); окна 100/50 м у сети нет — один уровень 400 м.
  • Сеть в git не кладём до П-2 (13 МБ на версию); тесты — малая случайная сеть (≤ 200 КБ), первый пилот — локально.

Границы (что модуль не делает)

  • Качество сети, переобучение, окна 100/50 м, проекция на бездивергентное поле, удаление решателя — не здесь.
  • macOS-сборка, CI, подпись — по возможности. Запуск на Windows здесь проверить нечем (wine нет) — проверяет пользователь.

Задачи

Python — venv пилота только на чтение: CUDA_VISIBLE_DEVICES= ~/deltaplan-air-nn/tools/research/air_nn_pilot/.venv/bin/python (ничего не ставить; GPU не трогать). Тяжёлые CPU-прогоны (сборка ORT/godot-cpp, пачки тестов) — dp lock cpu.

ON-1. Экспорт и малая тестовая сеть (dp-researcher, Sonnet, ~1 ч)

tools/air_onnx/export_onnx.py: из прогона пилота (<прогон>/main: ckpt/best.pt + task.json, как evaluate.load_net) или --random --channels 8,16 --maps 9 → .onnx по O1 через evaluate.export_onnx/те же параметры + метаданные O1; --info файл — входы, выходы, метаданные. Фикстура tests/air_onnx/fixtures/tiny_p2v4.onnx (≤ 200 КБ) + эталон ORT Python на детерминированном входе (tiny_p2v4_ref.*). Замер ORT CPU 1/4 потока для полной сети первого пилота (числа в README tools/air_onnx/README.md). Приёмка: O1-тест на свой экспорт и на tiny; --info на сети первого пилота; эталон воспроизводим.

ON-2. Расширение AirOnnx (dp-engineer, Opus, ~3–4 ч)

Из пробника native/air_nn_probe/ (П5 v2) — native/air_onnx/ + каталог аддона: API O2 (несколько входов по именам, формы и метаданные из модели), сборка Linux и Windows из Linux, включение в экспорт (зависимость ORT в .gdextension, tools/build.sh собирает/проверяет расширение), игра без бинарников не падает. Пробник удалить после переноса. Приёмка: build.sh all (под dp lock cpu), test_air_onnx ≤ 1e-5, артефакты Windows (импорты — только KERNEL32/UCRT + ORT/VC-redist), README сборки.

ON-3. AirNnPrep = prep.py (dp-engineer, Sonnet, ~2–3 ч)

O3: перенос rotation_of, case_meta, maps, film, to_physical; tools/air_onnx/make_prep_fixture.py (фикстура из prep.py, реальный рельеф — образец набора ~/air_nn_data/pilot/datasets/… только чтение); тест N1/N2. Приёмка: test_air_nn_prep (≤ 1e-4), фикстуры ≤ 3 МБ, скрипт воспроизводит фикстуру побитно.

ON-4. AirNnInput: вход из игры и страж (dp-engineer, Sonnet, ~2–3 ч)

O4: row_from_case (величины profile/day из погоды и AirCase игры — аналоги real.case/weather.Day.summary), guard; эталон Python — скрипт tools/air_onnx/make_input_fixture.py (tools/research/air3d, CPU, как airlite_gen.solve_case до решения); тест N3. Расхождения игры и Python — в контракт O4 «Расхождения». Приёмка: test_air_nn_input, список расхождений.

ON-5. AirRuntime engine = “nn” (dp-engineer, Sonnet, ~4 ч; после ON-2, ON-3, ON-4)

O5, O6: ветка сети в AirRuntime (загрузка, два прохода k, полёт), сборка WindField из 13 AGL, термики, конфиг, пункт настроек «нейросеть», поиск файла сети, отказы, строки журнала, include_filter *.onnx. Приёмка: test_air_nn_runtime, затронутые тесты (air_runtime, settings, c3, c4, air_thermals), прогон загрузки места с сетью первого пилота (tools/loading/load_probe.gd или аналог) — строка «поле (нейросеть …)» и время этапа, без GPU-решателя.

ON-6. README «как вставить сеть П-2 и полетать» + guide (dp-writer, Sonnet, ~1 ч; после ON-5)

data/air_nn/README.md (точные команды: проверка файла O1, куда положить, сборка расширения и игры, user://-подмена, что видно в журнале), раздел в docs/guide/air-model.md, CHANGELOG «В работе».

ON-7. Сквозная проверка сборок (dp-mechanic, ~1 ч; после ON-5)

tools/build.sh Linux и Windows с расширением; экспортированная Linux-сборка с сетью первого пилота в user:// — headless-загрузка места, строка журнала сети; состав Windows-каталога (DLL рядом).

Волны

  1. ON-1, ON-2, ON-3 (параллельно); ON-4 — когда освободится место (≤ 3 исполнителей).
  2. ON-5 (после слияния ON-2, ON-3, ON-4).
  3. ON-6, ON-7.

Шлюзы

  • Вливание нового air-nn ломает тест O1 (формат П2 сменился) — остановиться, версия контрактов, вопрос.
  • Этап «Рассчитываем ветер» с сетью на этой машине > 5 с — отчёт с разбивкой времени (бюджет плана air-nn ≤ 1,5 с на Ryzen 5 4500 — цель, не блокер модуля).
  • ORT под Windows не собирается/не линкуется из Linux — варианты (CI, готовая сборка) и рекомендация.

Приёмка сети: заменимость решателя (пользователь, 03.10.2026)

Вопрос приёмки — не «точна ли сеть», а заменима ли она решателем: воспроизводит ли сеть решатель в пределах собственной погрешности решателя.

  • Мерка — погрешность самого решателя, а не заранее выбранный порог:
    • решатель против реального ветра: Askervein — разгон на вершине занижен на 15–20 % на 20–50 м (≈ −11 % полной скорости на вершине), за холмом дефицит сильнее данных (−0,58 против −0,19), ТКЭ над склоном ×2–3 (tools/research/air3d/reference.md → «4. Askervein», реестр выводов air-model); в основном потоке ≈ ±10–20 %;
    • решатель против самого себя: разброс поздних состояний у несошедшихся (медиана p90 ≈ 0,5 м/с на пробе П-2), остаточные колебания у сошедшихся, расхождение между версиями и численными настройками.
  • Критерий: отличие сети от решателя заметно меньше отличия решателя от реальности → взаимозаменяемы (порог ШП-2 max(0,3 м/с; 10 %) стоит на этом уровне). Сводка — по группам: сошедшиеся, несошедшиеся, отложенные горные системы; отношение «ошибка сети / погрешность решателя» по точкам.
  • Слабый ветер — сеть даёт среднее, и это правильно. Решатель там не имеет установившегося решения (течение от плавучести нестационарно), колеблется и упирается в предел итераций; его мгновенное поле — случайный снимок. Сеть учится на среднем поздних состояний (Q2) и выдаёт гладкое среднее поле — ровно то, что игра читает как среднее; изменчивость дают термики (масштаб 2) и болтанка. Ошибку сети на слабом ветре мерить против среднего и разброса решателя, не против мгновенного снимка.
  • Скорость и стабильность важнее точности: лучше гладкое не совсем точное поле, чем точное, но в ~20 раз дольше и нестабильное. Сеть — фиксированное время (≈ 60 мс CPU, без GPU), детерминированный ответ (сетевая игра), нет «расчёт не успел» и вылетов решателя; ошибка ≈ 10–15 % ветра на месте допустима (пилот примет за порыв).

Риски

  • Windows не запускается здесь (wine нет): VC++ redist, путь загрузки DLL — проверяет пользователь на Windows.
  • Конфликт libstdc++ с addons/debug_draw_3d на Linux — пробник решил dlmopen; сохраняется.
  • Расхождение входа игры и набора пилота (O4) — сеть видит «чужой» мир: тест N3 и список расхождений.