AAYMA
RU
Войти

Аким на 5 часов

Симулятор «Аким на 5 часов» на чистом Node.js без зависимостей.

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

О проекте

Команда: araraikhan
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Доведённый продукт

Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам

Суть

Симулятор «Аким на 5 часов» на чистом Node.js без зависимостей. Включает детерминированный расчёт Score, объяснения OpenAI, случайные городские события, сравнение команд и генерацию Marp-презентации.

Как устроено

Общий модуль engine.mjs используют браузер, сервер и тесты. В нём данные датасета, валидатор, расчёт эффектов с лагом (8−L)/8, синергии, клиппинг, Score и поиск замен одной меры. Сервер api.mjs заново проверяет сценарий и передаёт в OpenAI Responses API (store:false) только проверенные факты. Модели запрещено придумывать числа. Без ключа работает разбор по правилам. Есть 5 событий (удары по показателям или сокращение бюджета) и таблица сравнения команд.

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

  • детерминированный движок Score
  • перебор замен одной меры
  • городские события EV1–EV5
  • генерация Marp-слайдов
  • WebMCP-инструменты read/set_city_scenario
  • поиск оптимального набора (find-best)

Особенности

Сервер без внешних зависимостей и без npm install. Движок один и тот же в браузере, на сервере и в тестах. Для браузерных AI-агентов выставлены WebMCP-инструменты. Сборка в serverless Worker. В README разобрана неоднозначная формулировка «5 направлений» в ТЗ.

Стек

Node.js 22 · JavaScript (ES modules) · HTML/CSS · OpenAI Responses API · node --test · Prettier

Модели

OpenAI Responses API (gpt-5 по умолчанию)


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

README

README.md

Аким на 5 часов — AI-симулятор управления городом

Симулятор решений для пяти районов Астаны (задача HackAlem AI). Пользователь выбирает ровно пять инициатив в пределах единого бюджета 100. Детерминированная модель считает последствия на горизонте восьми кварталов и итоговый Astana Quality of Life Score. OpenAI объясняет уже проверенные расчёты: сильные стороны, риски и компромиссы. Сам он чисел не считает.

Главный экран: каталог мер, сценарий и бюджет

Соответствие критериям задачи

ТребованиеГде реализовано
Единый бюджет и исходные данные для всехsimulator/public/engine.mjs: константы BUDGET, districts, measures из датасета организаторов
Решения по 5 направлениямКаталог из 14 мер по направлениям транспорт, экология, соцсфера, безопасность, сервисы; счётчик охвата направлений
Автоматический контроль бюджетаvalidate() в интерфейсе (кнопка блокируется с причиной) и повторно на сервере в api.mjs
Решения влияют на показателиЛаги, синергии, клиппинг 0–100, штраф за значения ниже 40; покрыто тестами
AI-анализ и объяснение компромиссовPOST /api/analyze → OpenAI Responses API; без ключа — детерминированный разбор по правилам
Разные решения → разный ScoreДетерминированный пересчёт; порядок решений не важен (тест)
Опц. Сравнение командТаблица «Сравнение команд» с отметкой лидера
Опц. Визуализация изменений районовКарточки районов и матрица 5 × 10 с изменениями к базе
Опц. Рекомендации по улучшениюПроверенные замены одной меры, применяются в один клик
Опц. Неожиданные городские события5 событий: удар по показателям или сокращение бюджета, требующее перераспределения
Опц. Автогенерация краткой презентацииКнопка «Скачать презентацию (.md)» — Marp-совместимые слайды из рассчитанных фактов

Быстрый запуск

Нужен Node.js 22 или новее. Внешних зависимостей нет, npm install не требуется.

cd simulator
npm start          # то же, что: node --env-file-if-exists=.env server.mjs

Откройте http://localhost:5173. Без ключа OpenAI работают расчёты, проверка ограничений, события, сравнение, презентация и автоматический разбор по правилам.

Подключение AI

  1. Скопируйте simulator/.env.example в simulator/.env.
  2. Укажите OPENAI_API_KEY и при необходимости доступную вашему проекту модель в OPENAI_MODEL (по умолчанию gpt-5).
  3. Перезапустите сервер. Кнопка «Получить объяснение AI» станет активной.

Ключ хранится только на сервере, .env исключён из Git. Запрос использует OpenAI Responses API с store: false.

Как устроен AI-анализ. Сервер заново проверяет присланный набор мер, сам пересчитывает Score (присланные клиентом числа игнорируются) и передаёт модели только проверенные факты:

  • исходные и итоговые показатели;
  • выбранное событие;
  • вклад каждой меры и сработавшие синергии;
  • замены одной меры, которые код уже проверил.

Модели запрещено придумывать числа: рекомендации с цифрами она берёт только из проверенного списка. Живой запрос к OpenAI требует действующего ключа и баланса и поэтому не входит в автономные тесты (там используется подставной провайдер).

Сценарий демонстрации

  1. Нажмите «Попробовать пример». Выбраны M7, M8, M10 в Нуре, M12 по городу и M5 в Сарыарке. Стоимость 95, остаток 5.
  2. Итоговый Score: 56.54307, исходный 52.55768, прирост +3.98539. Показатели Нуры: S1 38 → 48, S2 35 → 43.75, критических значений больше нет.
  3. Нажмите «Разобрать сценарий»: появятся формула Score, компромиссы и проверенные замены одной меры.
  4. Если ключ подключён, нажмите «Получить объяснение AI».
  5. Введите название команды и нажмите «Сохранить для сравнения». Затем «Скачать презентацию (.md)».
  6. Выберите событие EV2. Сокращение городского бюджета. Бюджет станет 85, сценарий (95) заблокируется с объяснением. Замените M5 на M4 (Сарыарка) и сохраните второй сценарий. Таблица сравнения покажет обе команды.
  7. Удалите любую меру: итоговый Score перестаёт рассчитываться до пятого допустимого решения.
Разбор сценарияСобытия и сравнение команд
РазборСобытия
СравнениеМобильная версия

Модель и правила

Исходные документы лежат в instructions/. Данные синтетические и не являются реальным прогнозом. В модели 5 районов, 10 показателей и 14 инициатив. Все показатели направлены одинаково: 0 — плохо, 100 — хорошо.

  • Бюджет 100, ровно 5 разных мер, не более 2 из одного направления.
  • Районная мера требует указать один район. Городская мера района не имеет и применяется ко всем пяти.
  • M1 и M3 несовместимы глобально. M4 и M7, M5 и M13 несовместимы в одном районе.
  • Реализованный эффект меры: полный эффект × (8 − лаг) / 8.
  • Синергии M1+M2, M10+M12, M5+M6 дают +2 к соответствующему показателю в районе первой меры, без уменьшения по лагу.
  • После эффектов и синергий значения ограничиваются диапазоном 0–100.
  • Оценка района — сумма показателей с весами 0.10, 0.10, 0.09, 0.11, 0.11, 0.11, 0.09, 0.09, 0.10, 0.10.
  • Средняя по городу — оценки районов, взвешенные по долям населения 0.27, 0.24, 0.20, 0.13, 0.16.
  • Score = 0.7 × средняя + 0.3 × минимум − число ячеек строго ниже 40.
  • Промежуточные вычисления не округляются, округляется только отображение.

Неожиданные события (необязательный режим). Событие применяется к исходным показателям до мер или сокращает бюджет. Прирост Score считается относительно исходного состояния после события. Одинаковое событие даёт всем командам одинаковый удар, поэтому результаты остаются сопоставимыми.

IDСобытиеЭффект
EV1Авария на теплотрассе в АлматыАлматы: C1 −12, C2 −5
EV2Сокращение городского бюджетаБюджет −15 (доступно 85)
EV3Сильный зимний смогСарыарка: E2 −8; Алматы: E2 −4
EV4Рост числа детей в ЕсилеЕсиль: S1 −10
EV5Весенний паводок в НуреНура: T1 −8, C1 −8; бюджет −10

Трактовка «5 направлений». В кратком брифе сказано о решениях «по пяти направлениям». Подробный датасет разрешает до двух мер из одного направления, то есть охват 3–5 направлений, и пример организаторов охватывает 4. Реализованы подробные правила. Интерфейс показывает, сколько направлений охвачено.

Архитектура

Браузер (public/)                          Сервер (api.mjs)                   OpenAI
 app.mjs  ── UI, состояние, события,        POST /api/analyze
             сравнение, презентация          ├─ validate() + simulate()  ──►  Responses API
 engine.mjs ◄── общий модуль ──────────────► ├─ improvements(), explain()      (только проверенные
             данные, валидатор, расчёт       └─ ответ: текст + серверный Score  факты, store:false)
  • simulator/public/engine.mjs — данные датасета, события, валидатор, детерминированные расчёты, разбор по правилам, поиск замен одной меры, генерация презентации. Один и тот же модуль используют браузер, сервер и тесты.
  • simulator/public/app.mjs — интерфейс, состояние сценария, режим событий, таблица сравнения, AI-запрос.
  • simulator/public/index.html, style.css — адаптивный интерфейс на русском.
  • simulator/api.mjs — повторная проверка сценария на сервере, защита (same-origin, лимит 16 КБ, только JSON), вызов OpenAI.
  • simulator/server.mjs — локальный HTTP-сервер. Отдаёт только явно разрешённые файлы.
  • simulator/build.mjs — собирает автономный модуль Worker (dist/server/index.js, export default { fetch }) для serverless-хостинга.
  • simulator/tests/ — эталонные значения из датасета, все правила и несовместимости, лаги, синергии, клиппинг, порог 40, события, презентация, AI API с подставным провайдером.

Для AI-агентов в браузерах с поддержкой WebMCP доступны инструменты read_city_scenario и set_city_scenario. Они работают с тем же состоянием и валидатором, что и интерфейс. Поддержка браузером не обязательна.

Проверка и сборка

cd simulator
npm test           # 18 тестов, node --test
npm run lint       # проверка форматирования (Prettier)
npm run build      # dist/server/index.js

Ограничения и развитие

  • Сценарии и таблица сравнения хранятся в памяти браузера до перезагрузки. Следующий шаг — общий рейтинг команд на сервере.
  • Подсказки перебирают замену одной меры и не ищут глобальный оптимум. Полный перебор допустимых наборов — возможное расширение.
  • Перед публичным многопользовательским запуском AI-эндпоинту нужны авторизация и лимиты запросов.
  • simulator/.openai/hosting.json — служебная конфигурация хостинга, использованная при публикации. Для локального запуска не нужна.
Звёзды
⭐ 0
Форки
0
Язык
JavaScript
Последний push
в прошлом месяце
Коммиты
8
Авторы
1
Файлы
29
Ветки
1

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

admin 8

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

  • 2026-09-23 17:30Final commit this timeadmin
  • 2026-09-23 17:17Final commitadmin
  • 2026-09-23 16:58Just some updateadmin
  • 2026-09-23 16:30Update aagainadmin
  • 2026-09-23 15:43Another updateadmin
  • 2026-09-23 15:04Updateadmin
  • 2026-09-23 13:51Merge branch 'main' of https://github.com/BAITC-Hacks/hack-e4c10181-araraikhanadmin
  • 2026-09-23 13:50Initial hackathon setupadmin
Структура репозитория (29 файлов)
.gitignore
README.md
docs/screenshots/simulator-analysis.png
docs/screenshots/simulator-compare.png
docs/screenshots/simulator-desktop.png
docs/screenshots/simulator-events.png
docs/screenshots/simulator-mobile.png
instructions/HackAlem AI_ «Аким на 5 часов» - AI-симулятор управления городом.docx
instructions/Датасет районов.docx
simulator/.env.example
simulator/.gitignore
simulator/.openai/hosting.json
simulator/.prettierrc.json
simulator/api.mjs
simulator/build.mjs
simulator/optimal-scenario.json
simulator/package.json
simulator/public/app.mjs
simulator/public/engine.mjs
simulator/public/icons.mjs
simulator/public/index.html
simulator/public/scenario-worker.mjs
simulator/public/scenarios.mjs
simulator/public/style.css
simulator/scripts/find-best.mjs
simulator/server.mjs
simulator/tests/api.test.mjs
simulator/tests/engine.test.mjs
simulator/tests/scenarios.test.mjs

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

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

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