О проекте
Команда: CAPEXNULL
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Рабочий MVP
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Городской симулятор для Астаны: пользователь выбирает 5 мероприятий из каталога M1–M14 в пределах бюджета 100 и назначает районы. Детерминированный движок считает Astana Quality of Life Score, AI объясняет результат, а стратегию можно проверить на неожиданное событие.
Как устроено
Validator проверяет бюджет, число мер, лимит направлений и несовместимости. Математический движок применяет эффекты мер с лагом (множитель (8−L)/8 на горизонте 8 кварталов), синергии и clip(0,100) к 10 показателям пяти районов. Score = 0.7·D_avg (с весами по доле населения) + 0.3·min(D_d) − N_crit. Затем сервер передаёт рассчитанные факты в OpenAI Responses API и проверяет структурированный ответ из пяти полей: summary, strengths, risks, tradeoffs, recommendations. Слой событий меняет показатели района и пересчитывает оценку.
Ключевые решения
- детерминированная модель эффектов с лагами и синергиями
- взвешенный индекс качества жизни со штрафом за критические показатели
- валидация ограничений
- stress-test событиями
- structured outputs
Особенности
Расчёт чётко отделён от интерпретации: LLM не считает цифры и получает только результат движка, а при сбое AI числовой результат сохраняется. Формула оценки учитывает территориальный дисбаланс (30% веса у самого слабого района) и штрафует за критические показатели. Есть режим устойчивости к городским событиям.
Стек
JavaScript · Node.js · HTML · CSS
Модели
OpenAI Responses API
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-1fc1b22e-capexnull.
README
README.mdАким на 5 часов
AI-симулятор городских управленческих решений для Астаны. Пользователь получает бюджет 100 условных единиц, выбирает ровно пять мероприятий и назначает районы для районных мер. Приложение рассчитывает последствия, показывает Astana Quality of Life Score и объясняет результат с помощью AI. После расчёта можно проверить устойчивость стратегии к неожиданному городскому событию.
Городское управление требует одновременно учитывать транспорт, экологию, социальную инфраструктуру, безопасность и городские сервисы при ограниченном бюджете. Симулятор помогает сравнивать последствия распределения ресурсов, замечать слабые места районов и понимать компромиссы выбранной стратегии.
Как работает приложение
- На главной странице показаны исходные показатели пяти районов: Есиль, Алматы, Сарыарка, Байконур и Нура.
- Пользователь выбирает пять мероприятий из каталога M1–M14. Для каждой районной меры назначает район; городская действует на все районы.
- Панель «Выбранные мероприятия» показывает состав набора, использованный бюджет и остаток.
- По кнопке «Рассчитать сценарий» система проверяет допустимость набора и рассчитывает последствия на горизонте восьми кварталов.
- Результат содержит итоговую оценку, изменение относительно исходных 52.56, сравнение районов и изменения показателей.
- AI-анализ объясняет сильные стороны, риски, компромиссы и рекомендации.
- В блоке «Неожиданное городское событие» можно выбрать событие и район, получить новое состояние города и объяснение последствий.
Архитектура
Интерфейс собирает решения, сервер проверяет их и выполняет математический расчёт. Только после этого рассчитанные факты передаются AI для объяснения. Отображение результата отделено от вычислений.
UI → Validator → Mathematical Engine → Agentic AI → UI
↓
структурированный результат
| Компонент | Ответственность | Реализация |
|---|---|---|
| Пользовательский интерфейс и ввод | Каталог, выбор районов, бюджет, состояния загрузки и ошибок | index.html, src/ui/, src/scenario/ |
| Validator | Проверка структуры, бюджета, количества мер и несовместимостей до расчёта | src/validator/index.js |
| Mathematical Engine | Исходные данные, эффекты, лаги, синергии, ограничения и детерминированная оценка | src/engine/ |
| Agentic AI backend | Вызов OpenAI API, получение и проверка объяснения | src/ai/backend.js |
| Слой событий | Каталог воздействий, независимое состояние после события и пересчёт оценки | src/dynamic/ |
| Представление результата | Оценки, сравнение районов и AI-анализ | src/result/index.js |
| Сервер и API | Статические файлы, последовательность обработки запросов, хранение рассчитанных сценариев в памяти | server.js |
Сервер управляет последовательностью действий. LLM интерпретирует результат и не запускает математический расчёт самостоятельно. При ошибке AI численный результат остаётся доступен, а интерфейс сообщает о недоступности объяснения.
API
| Запрос | Назначение |
|---|---|
GET /api/scenario | Исходные показатели, каталог, районы и бюджет. Повторяемый параметр measure, например ?measure=M7&measure=M8, возвращает предварительную стоимость набора |
POST /api/scenario | Проверка и анализ набора: объект с mode: "MATAN-only" и массивом decisions; каждое решение содержит measureId, а районное — также districtId |
POST /api/event | Анализ события: объект с scenarioId, eventType и district; идентификатор сценария берётся из заголовка X-Scenario-Id базового ответа, тип события — из src/dynamic/catalog.js |
Базовый ответ содержит calculation — математический результат, aiAnalysis — объяснение и aiError — сообщение при недоступности AI. Ответ события содержит event — рассчитанные последствия, а также aiAnalysis и aiError.
Недопустимый набор получает HTTP 422 с причиной: его оценка не рассчитывается, AI не вызывается. Запрос события без сохранённого базового сценария получает HTTP 409. Сценарии хранятся в памяти процесса; после перезапуска сервера нужно выполнить базовый расчёт снова.
Показатели и математическая модель
Каждый район описан десятью показателями по шкале 0–100: больше — лучше. Исходные значения, веса, доли населения и каталог мероприятий находятся в src/engine/data.js.
| Код | Показатель | Вес |
|---|---|---|
| T1 | Разгрузка дорог | 0.10 |
| T2 | Доступность общественного транспорта | 0.10 |
| E1 | Озеленение | 0.09 |
| E2 | Качество воздуха | 0.11 |
| S1 | Школы и детсады | 0.11 |
| S2 | Поликлиники и первичная медпомощь | 0.11 |
| B1 | Безопасность улиц | 0.09 |
| B2 | Безопасность дорожного движения | 0.09 |
| C1 | Надёжность ЖКХ | 0.10 |
| C2 | Скорость решения обращений жителей | 0.10 |
Доли населения в модели: Есиль — 0.27, Алматы — 0.24, Сарыарка — 0.20, Байконур — 0.13, Нура — 0.16.
Эффекты мероприятий
Лаг — задержка реализации мероприятия в кварталах. На горизонте восьми кварталов эффект учитывается с множителем (8 − лаг) / 8. Районный эффект применяется только к назначенному району, городской — к каждому из пяти районов.
Для формул: d — район, k — показатель, m — мероприятие; I_dk — исходное значение, I'_dk — итоговое значение, effect_m,k — эффект мероприятия, L_m — его лаг. Сумма включает только меры, действующие на данный район. Дополнительные совместные эффекты обозначены synergies_dk. Функция clip(x, 0, 100) ограничивает значение диапазоном от 0 до 100.
I'_dk = clip(I_dk + Σ_m(effect_m,k × (8 − L_m) / 8) + synergies_dk, 0, 100)
Совместные эффекты применяются после обычных эффектов, без масштабирования по лагу:
- M1 + M2: разгрузка дорог +2 в районе M1.
- M5 + M6: качество воздуха +2 в районе M5.
- M10 + M12: безопасность улиц +2 в районе M10.
Ограничение диапазона применяется после суммирования всех эффектов и синергий. Промежуточного округления нет.
Итоговая оценка
Оценка района (D_d) — взвешенная сумма его итоговых показателей; w_k — вес показателя. Средневзвешенная оценка города (D_avg) учитывает долю населения каждого района (pop_d). Количество критических показателей (N_crit) — число пар «район × показатель» со значением строго ниже 40; значение 40 критическим не считается.
D_d = Σ_k(w_k × I'_dk)
D_avg = Σ_d(pop_d × D_d)
N_crit = число пар (d, k), для которых I'_dk < 40
Score = 0.7 × D_avg + 0.3 × min(D_d) − N_crit
Последняя формула задаёт Astana Quality of Life Score: 70% приходится на среднюю оценку города, 30% — на оценку самого слабого района, затем вычитается по одному баллу за каждый критический показатель. Такой расчёт учитывает и общий уровень, и территориальный дисбаланс.
Все численные результаты рассчитываются детерминированным Mathematical Engine и модулем пересчёта состояния события по той же формуле. Agentic AI самостоятельно оценку, бюджет и другие математические показатели не рассчитывает. Для отображения оценки округляются до двух знаков; изменение вычисляется из неокруглённых значений.
Правила сценария
- Общий бюджет — 100 условных единиц; стоимость набора не должна его превышать.
- Требуется ровно пять разных мероприятий. Каждое используется максимум один раз.
- Для районной меры выбирается один из пяти районов. Для городской район не назначается.
- Допускается максимум два мероприятия одного направления.
- M1 «Районная мобильность» и M3 «Транспортная инфраструктура» несовместимы независимо от района.
- M4 «Экология района» и M7 «Социальная инфраструктура» несовместимы в одном районе.
- M5 «Экологическая инфраструктура» и M13 «Районные сервисы» несовместимы в одном районе.
Validator отклоняет нарушения до расчёта последствий. Порядок выбора не влияет на результат: решения приводятся к фиксированному порядку по номерам мероприятий. Предварительное обновление бюджета при выборе мер не является расчётом итоговой оценки сценария.
Agentic AI и внешний сервис
OpenAI API используется сервером для интерпретации рассчитанных результатов. В основном сценарии AI получает проверенный набор решений и рассчитанные численные факты. При событии передаётся только рассчитанный результат события, включая показатели и оценки до и после воздействия.
Объяснение состоит из итога, сильных сторон, рисков, компромиссов и рекомендаций. Сервер запрашивает структурированный ответ через Responses API (https://api.openai.com/v1/responses) и проверяет пять обязательных полей: summary, strengths, risks, tradeoffs, recommendations. Дополнительно проверяются пользовательские формулировки и формат чисел. Эти проверки не доказывают смысловую точность каждого утверждения AI.
Вызов ограничен 30 секундами. Ошибка сервиса, отказ или некорректный ответ приводят к сообщению о недоступности AI без потери математического результата. Формулировки объяснений могут различаться; численные результаты одинакового сценария воспроизводимы.
Проверка устойчивости к событиям
Динамический режим позволяет проверить выбранную стратегию при неожиданном изменении городской ситуации:
Рассчитанная стратегия → выбранное событие и район → изменение показателей
→ новое состояние города → повторный расчёт оценки → AI-анализ последствий
| Демонстрационное событие | Воздействие на выбранный район |
|---|---|
| Перекрытие крупной магистрали | Разгрузка дорог −20 |
| Сильное загрязнение воздуха | Качество воздуха −20 |
| Рост нагрузки на социальную инфраструктуру | Школы и детсады −10; поликлиники и первичная медпомощь −10 |
| Ухудшение дорожной безопасности | Безопасность дорожного движения −20 |
| Авария теплосети | Надёжность ЖКХ −20 |
Эти воздействия — синтетические демонстрационные параметры проекта, отсутствующие в исходном датасете организаторов.
Событие запускается пользователем. Оно изменяет только предусмотренные каталогом показатели выбранного района, с ограничением значений до 0–100. Остальные показатели сохраняются. Оценка пересчитывается по приведённой выше формуле, включая строгий критический порог ниже 40.
Каждый запуск начинается с неизменяемого результата выбранных пяти мероприятий. Воздействие применяется один раз; повторный запуск не накапливает ущерб, а мероприятия M1–M14 заново не применяются. После проверки допустимости сохраняется результат события и запрашивается AI-анализ. AI может объяснить необходимость пересмотра стратегии, но автоматического перераспределения бюджета или второго инвестиционного цикла приложение не выполняет.
Технологии и системные требования
- Node.js ≥22 — сервер, встроенные HTTP, файловые операции,
fetchи средство тестированияnode:test. Это минимальная версия, указанная вpackage.json. - JavaScript с ES-модулями, HTML и CSS — приложение без frontend-фреймворка и этапа сборки.
- OpenAI Responses API — объяснение результатов; запросы выполняются встроенным
fetch, без отдельного SDK. - Git — получение официального репозитория.
- Современный браузер — работа с интерфейсом. Полный набор тестов запускает Microsoft Edge в фоновом режиме без окна.
Внешних npm-зависимостей нет. База данных и отдельный сервер статических файлов не требуются. Приведённые команды рассчитаны на Windows 11 / PowerShell. Для реального AI-анализа нужны HTTPS-доступ к api.openai.com, действующий API-ключ и доступ к выбранной модели. Для получения репозитория нужен доступ к GitHub.
Установка
Установите Git и Node.js с npm, затем откройте PowerShell в каталоге, где будет размещён проект:
git clone https://github.com/BAITC-Hacks/hack-1fc1b22e-capexnull.git
Set-Location hack-1fc1b22e-capexnull
node --version
npm --version
Команда node --version должна показать версию не ниже 22. Отдельный npm install не нужен: в package.json нет внешних зависимостей. Сборка также не требуется.
Настройка окружения
| Переменная | Назначение |
|---|---|
OPENAI_API_KEY | Серверный ключ для реального AI-анализа. Без него математический расчёт работает, а вместо объяснения отображается сообщение о недоступности AI |
OPENAI_MODEL | Необязательное имя модели; при отсутствии или пустом значении используется gpt-5.6-luna, заданная в backend |
PORT | Необязательный порт сервера, по умолчанию 3000 |
EDGE_PATH | Необязательный путь к исполняемому файлу Microsoft Edge для браузерного теста; на работу приложения не влияет |
В той же сессии PowerShell, из которой будет запущен сервер, введите ключ через скрытый запрос. Команды не записывают его в файл или историю команд:
$secureKey = Read-Host 'OPENAI_API_KEY' -AsSecureString
$env:OPENAI_API_KEY = [System.Net.NetworkCredential]::new('', $secureKey).Password
Remove-Variable secureKey
Если OPENAI_MODEL уже настроена для доступной модели, сохраните это значение. При отсутствии переменной сервер использует указанную выше модель по умолчанию. Для явной настройки и стандартного порта:
$env:OPENAI_MODEL = 'gpt-5.6-luna'
$env:PORT = '3000'
Приложение читает переменные окружения процесса. Файлы .env автоматически не загружаются.
Проверка AI-функций
Основная математическая логика приложения, проверка сценариев и моделирование городских событий выполняются локально и детерминированно.
Для генерации AI-анализа приложение использует OpenAI API на стороне сервера. Для запуска реального AI-анализа требуется переменная окружения OPENAI_API_KEY. API-ключ хранится только в окружении процесса и не передаётся в браузер.
Автоматические тесты проверяют контракт AI-интеграции с помощью mock-ответа и не требуют доступа к OpenAI API. Для проверки реального AI-анализа необходимо перед запуском сервера задать действующий OPENAI_API_KEY в окружении.
$secureKey = Read-Host 'OPENAI_API_KEY' -AsSecureString
$env:OPENAI_API_KEY = [System.Net.NetworkCredential]::new('', $secureKey).Password
Remove-Variable secureKey
Затем в той же сессии PowerShell запустите npm start из корня проекта.
Запуск
Из корня проекта, в подготовленной сессии PowerShell:
npm start
Откройте http://127.0.0.1:3000. Сервер слушает только локальный адрес 127.0.0.1; при изменении PORT используйте соответствующий порт в URL. Остановка — Ctrl+C.
Скрипт start запускает node --watch server.js, поэтому изменения отслеживаемого кода перезапускают сервер. Для запуска без наблюдения за файлами можно использовать node server.js вместо npm start.
Развёртывание
Приложение рассчитано на воспроизводимый локальный запуск из репозитория. Публичная размещённая версия не предоставлена.
На чистой машине выполните раздел «Установка»; внешние зависимости устанавливать не требуется. Настройте окружение, запустите сервер, откройте локальный URL и выполните оба контрольных сценария ниже. Дополнительная инфраструктура не нужна.
Для самостоятельной проверки вычислений и интеграционных контрактов без личных аккаунтов команды предусмотрены автоматические тесты с подменой ответа OpenAI. Проверка реального AI-объяснения выполняется с разрешённым эксперту API-ключом и доступной моделью; тестовая подмена не является реальным вызовом сервиса.
Контрольный основной сценарий
- Откройте приложение и убедитесь, что показаны пять районов, каталог M1–M14 и исходная оценка 52.56.
- Выберите следующие мероприятия, указав районы в карточках:
| Мероприятие | Район | Стоимость |
|---|---|---|
| M7 — Социальная инфраструктура | Нура | 24 |
| M8 — Социальная доступность | Нура | 20 |
| M10 — Безопасность района | Нура | 12 |
| M12 — Цифровые услуги | Весь город; район не выбирается | 14 |
| M5 — Экологическая инфраструктура | Сарыарка | 25 |
- Проверьте панель «Выбранные мероприятия»: выбрано 5/5, использовано 95, осталось 5.
- Нажмите «Рассчитать сценарий» и дождитесь результата.
- Ожидаемый Astana Quality of Life Score — 56.54, изменение относительно исходного — +3.99, критических показателей — 0. Ниже доступны сравнение районов и AI-анализ при настроенном OpenAI API.
Контрольный динамический сценарий
После предыдущего расчёта:
- Найдите блок «Неожиданное городское событие».
- Выберите «Авария теплосети» и район «Нура».
- Нажмите «Смоделировать событие».
- Ожидаемый Astana Quality of Life Score после события — 55.72, до события — 56.54, изменение — −0.82. Надёжность ЖКХ Нуры меняется с 60 на 40; критических показателей остаётся 0. При доступном OpenAI API отображается «AI-анализ события».
Этот пример служит воспроизводимой проверкой динамического режима. Повторное нажатие с теми же параметрами должно дать тот же численный результат, сохраняя исходный результат стратегии.
Пересмотр стратегии после события
После расчёта события нажмите «Пересмотреть стратегию». В существующем каталоге измените состав пяти мероприятий или их районы, затем нажмите «Рассчитать адаптированную стратегию». Новый набор проходит те же проверки и независимо укладывается в бюджет 100.
Приложение сравнивает две альтернативные стратегии при одном событии в одном районе. Каждый план рассчитывается от исходного состояния города, после чего событие применяется ровно один раз. Первоначальный результат сохраняется. Это контрфактическое сравнение: расходы планов не суммируются, дополнительный бюджет не выделяется, а новый план не считается физически реализованным после события.
В сравнении показаны оценки обеих стратегий после события, их разница, критические показатели, районные оценки, изменения показателей, расходы и остаток каждого плана. AI объясняет рассчитанные различия, риски и компромиссы. Для контрольной проверки оставьте первоначальный набор без изменений: обе оценки после аварии теплосети в Нуре должны быть 55.72, разница — 0.00.
Расчёт сравнения реализован в src/adaptation/index.js, отображение — в src/adaptation/ui.js. Серверный POST /api/adaptation принимает eventId из заголовка X-Event-Id ответа события и альтернативный scenario; событие и район берутся из сохранённого сервером результата.
Тесты
Из корня репозитория выполните полный набор тестов:
node --test tests/engine-validator.test.js tests/ai.test.js tests/dynamic.test.js tests/ui.test.js tests/adaptation.test.js tests/adaptation-ui.test.js
Последний подтверждённый результат: 55/55 PASS. Проверяются ограничения набора, эффекты и синергии, независимость от порядка выбора, контрольные оценки, неизменность состояния при событиях, обработка ошибок AI, контракты API и пользовательский путь в реальном браузере.
Дополнительно проверки адаптации прошли 6/6 PASS: изоляция состояний, идентичность результата при одинаковом плане, детерминизм, валидация, AI-контракт и браузерный путь сравнения. AI-интеграция в этих тестах использует mock-ответы.
Тесты используют подмену OpenAI и не требуют действующего API-ключа или платных запросов. Для браузерного теста нужен установленный Microsoft Edge. По умолчанию используется путь C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe. Если Edge установлен в другом месте, перед запуском тестов задайте EDGE_PATH равной фактическому полному пути к msedge.exe.
Структура репозитория
README.md Описание, запуск и проверка
package.json Требование к Node.js и команда запуска
server.js HTTP-сервер, API и серверный рабочий поток
index.html Основная страница
.gitignore Исключение файлов окружения
src/
scenario/index.js Формирование входного набора решений
validator/index.js Проверка допустимости сценария
engine/
data.js Показатели, каталог, веса и синергии
index.js Базовый и сценарный расчёты
preview.js Предварительная стоимость выбора
ai/backend.js Реальная интеграция с OpenAI и проверка ответа
dynamic/
catalog.js Пять демонстрационных событий
engine.js Применение события и пересчёт состояния
ui.js Выбор события и отображение последствий
result/index.js Отображение базового результата
ui/
app.js Управление пользовательским сценарием
styles.css Оформление и адаптивная компоновка
tests/
engine-validator.test.js Математика и правила набора
ai.test.js AI-контракт и обработка ошибок
dynamic.test.js События и сохранность исходного состояния
ui.test.js API и браузерный пользовательский путь
hackalem-checkpoints.ps1 Служебная фиксация проекта в GitHub
Дерево показывает ключевые файлы действующего приложения. Почасовой скрипт фиксации не требуется для запуска или проверки продукта.
Безопасность
API-ключ хранится в окружении серверного процесса. Frontend его не получает; сервер передаёт ключ OpenAI в заголовке авторизации и не включает его в пользовательский ответ. Тексты ошибок провайдера пользователю не передаются.
.gitignore исключает .env и .env.*. В отслеживаемых файлах проверенной версии секретного файла окружения и значения API-ключа нет. Реальные ключи и токены не следует добавлять в исходный код или документацию.
Данные и ограничения
Данные районов синтетические. Числовая модель и каталог мероприятий зафиксированы в коде; внешние городские источники в реальном времени не подключаются. События и их эффекты служат демонстрации устойчивости стратегии.
Приложение является симулятором для анализа управленческих сценариев. Его результаты отражают заданную модель и не являются откалиброванным прогнозом реальных последствий городских решений.
Авторы коммитов
История коммитов
- 2026-09-23 17:47HackAlem final adaptationTern
- 2026-09-23 17:33HackAlem final submissionTern
- 2026-09-23 17:00HackAlem hourly checkpoint 17:00Tern
- 2026-09-23 16:00HackAlem hourly checkpoint 16:00Tern
- 2026-09-23 15:00HackAlem hourly checkpoint 15:00Tern
- 2026-09-23 14:01HackAlem hourly checkpoint 14:00Tern
- 2026-09-14 09:01Test Codex GitHub connectionTern
Структура репозитория (29 файлов)
.gitignore ARCHITECTURE.md CODEX_GIT_TEST.txt README.md hackalem-checkpoints.ps1 index.html package.json server.js src/adaptation/index.js src/adaptation/ui.js src/ai/backend.js src/ai/index.js src/dynamic/catalog.js src/dynamic/engine.js src/dynamic/ui.js src/engine/data.js src/engine/index.js src/engine/preview.js src/result/index.js src/scenario/index.js src/ui/app.js src/ui/styles.css src/validator/index.js tests/adaptation-ui.test.js tests/adaptation.test.js tests/ai.test.js tests/dynamic.test.js tests/engine-validator.test.js tests/ui.test.js
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.