О проекте
Команда: NT
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Прототип
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Веб-симулятор, где игрок выбирает 5 городских мер в пределах бюджета 100 единиц и получает оценку по направлениям и районам. Затем моделируется событие (например, отмена стройки школы), игрок выбирает замену и сравнивает результаты.
Как устроено
Детерминированный движок проверяет план (5 уникальных мер, не больше 2 на направление, район для районных мер) и считает эффекты, лаги и синергии на горизонте 8 кварталов по каталогу organizer-v1. Отдельные эндпоинты делают предпросмотр и подтверждение замены после события с ранжированием вариантов. /api/explain объясняет проверенные факты симуляции на RU, KK и EN через OpenAI или по шаблону. UI на Next.js пока работает на mock-сценариях demo-v1 и к новому API не подключён.
Ключевые решения
- deterministic scoring engine
- constraint validation
- event cancellation & ranked replacement
- synergies/lags
- template + LLM explanation
Особенности
Работа разделена на три параллельные части (UI, движок, API/AI) по общему контракту shared-contract-v1, промпты для AI-кодинга лежат в prompts/. Есть golden-фикстуры с эталонным Score 56.54307. README прямо предупреждает, что UI не подключён к настоящему движку и работает только на подготовленных сценариях.
Стек
TypeScript · Next.js · React · Vitest · Playwright · Node.js
Модели
OpenAI (OPENAI_MODEL)
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-14dc5002-nt.
README
README.mdАким на 5 часов
Учебный веб-симулятор команды NT для HackAlem. Пользователь выбирает пять городских мероприятий в пределах бюджета, оценивает результат, а затем пересматривает план после события и сравнивает последствия одной замены.
Сценарий: пять решений → результат → событие → замена → сравнение.
Все показатели и эффекты — синтетические данные учебной модели. Результаты не являются официальной статистикой, методикой акимата или прогнозом реального развития Астаны.
Текущее состояние
Общая версия main объединяет интерфейс Semba, движок Nikita и API/AI Bakytzhan. Интерфейс включает обновлённый дизайн: белую основу, зелёные основные действия, синие информационные акценты и жёлтые уведомления.
Интерфейс пока работает на подготовленных mock-сценариях demo-v1.
Новый расчётный API organizer-v1 также включён в main.
Подключение интерфейса к нему, выбор районов и языка ещё не завершены.
Переключение одного env-флага не выполняет эту интеграцию.
Публичный сайт и доступ браузера к платному AI пока не настроены.
Одного AI_ENABLED=true для такого доступа недостаточно.
Для показа жюри используйте описанный ниже браузерный сценарий. API-ключ, база данных и Docker для него не нужны.
Запуск для жюри
1. Что установить
- Node.js 24.x с npm.
- Git.
- Современный браузер.
- Доступ к приватному репозиторию команды.
Интернет понадобится для скачивания проекта и установки зависимостей. Заранее подготовленное локальное mock-демо не обращается к AI-провайдеру.
2. Скачать проект
Откройте терминал и выполните:
git clone --branch main https://github.com/BAITC-Hacks/hack-14dc5002-nt.git
cd hack-14dc5002-nt
npm ci
Ветка main содержит общую проверенную версию команды.
Если GitHub сообщает Repository not found, проверьте доступ вашего аккаунта
к приватному репозиторию.
Если проект уже скачан, сохраните свои незакоммиченные изменения, затем обновите чистую рабочую копию:
git switch main
git pull --ff-only origin main
npm ci
3. Создать настройки
В корне проекта, рядом с package.json, создайте файл .env.local:
NEXT_PUBLIC_USE_MOCK_API=true
AI_ENABLED=false
Если файл уже существует, измените только эти значения и сохраните остальные
настройки. На Windows убедитесь, что имя файла не стало .env.local.txt.
Для этого показа OPENAI_API_KEY и OPENAI_MODEL не нужны.
Файл .env.local исключён из Git; передавать секреты жюри не требуется.
4. Собрать и запустить
Если сервер проекта уже работает в другом терминале, сначала остановите его сочетанием Ctrl+C.
npm run build
npm run start -- --hostname 127.0.0.1 --port 3000
Откройте http://127.0.0.1:3000.
Оставьте терминал открытым до конца демонстрации. Для остановки приложения нажмите Ctrl+C.
На странице должны быть видны:
- заголовок «Как изменится город от ваших решений?»;
- пометки «Демо-сценарии» и «Модель demo-v1»;
- бюджет 100 условных единиц;
- кнопки «Загрузить демо-план» и «Рассчитать результат».
Альтернатива: запуск для разработки
Вместо production-сборки и start можно выполнить:
npm run dev -- --hostname 127.0.0.1 --port 3000
Адрес остаётся тем же. В этом режиме изменения кода автоматически появляются в браузере. Не запускайте оба режима одновременно на одном порту.
NEXT_PUBLIC_USE_MOCK_API встраивается в браузерный код при сборке.
После изменения значения перезапустите dev-сервер либо заново выполните
npm run build и перезапустите production-сервер. Настройки самого терминала
могут перекрывать значения из .env.local.
Что показать жюри
Для чистого начала удобно открыть приложение в приватном окне: обычный браузер может восстановить ранее сохранённый базовый план.
- Нажмите «Загрузить демо-план». Будут выбраны пять мероприятий: выделенные автобусные полосы, строительство школы, озеленение общественных пространств, освещение безопасных маршрутов и городские услуги онлайн.
- Нажмите «Рассчитать результат». Покажите 5 / 5 выбранных мероприятий, расходы 90 / 100, результат 57, показатели направлений и районов.
- Перейдите к разделу «Что изменилось?». В карточке «Строительство школы отменено: участок недоступен» нажмите «Смоделировать событие».
- Покажите, что в промежуточном плане осталось 4 мероприятия, его результат не рассчитывается, а для замены доступно 40 единиц. Исходный снимок остаётся сохранённым.
- Выберите именно «Модульные учебные корпуса». Нажмите «Подтвердить выбранную замену». Не выбирайте автоматически первую рекомендацию: старый mock поддерживает не все варианты подтверждения.
- Покажите сравнение: исходный результат 57 при расходах 90, новый результат 56,6 при расходах 96, изменение −0,4. Симулятор показывает компромисс: замена не обязана улучшать общий балл.
- В блоке «Объяснение последствий», после подтверждения замены, нажмите «Получить объяснение». Появится текст с пометкой «Шаблонное объяснение».
- При необходимости перезагрузите страницу: исходный план восстановится, ветка события сбросится. Для повторного показа снова загрузите демо-план.
| Этап demo-v1 | Мероприятий | Расходы | Остаток / доступно | Score |
|---|---|---|---|---|
| Исходный план | 5 | 90 | 10 | 57 |
| После отмены школы | 4 | 60 | 40 | Отсутствует |
| После замены на модульные корпуса | 5 | 96 | 4 | 56,6 |
Краткий текст для защиты
«Мы показываем интерфейс на подготовленном демонстрационном сценарии. Пользователь распределяет ограниченный бюджет между пятью городскими мерами. После отмены одного решения исходный план сохраняется, а в отдельном сценарии можно выбрать замену и сравнить последствия. Новый расчётный движок готовится к подключению к этому интерфейсу. Данные учебные и синтетические».
Границы текущего показа
- Числа в браузерном mock-режиме берутся из подготовленных fixtures.
- Произвольные комбинации могут вернуть
MOCK_SCENARIO_NOT_DEFINED. - Объяснение базового плана в старом mock не подготовлено. Показывайте объяснение после подтверждения замены школы.
- Пометка
templateозначает шаблон; этот показ не подтверждает вызов AI. - Сохранённый базовый план и ветка события разделены. Изменение выбора не переписывает исходный снимок до нового расчёта.
- Подсказки несовместимости и зависимостей описывают правила. Жёлтое предупреждение появляется при нарушении в текущем выборе.
Что уже реализовано в интерфейсе
- Каталог мероприятий с фильтрами по направлениям.
- Стоимость, задержка эффекта, эффекты по районам и ограничения.
- Выбор до пяти мероприятий и предварительный бюджет.
- Крупный результат, показатели направлений и районные карточки.
- Отдельный сценарий отмены, предварительные варианты и подтверждение замены.
- Сравнение исходного и нового плана.
- Состояния загрузки, ошибок и шаблонного объяснения.
- Сохранение базового плана с версией модели в localStorage.
- Адаптивная вёрстка и навигация по трём этапам работы.
Новый API: серверная часть в том же приложении
Новая серверная реализация включена в
main.
Она использует organizer-v1, а не модель браузерного демо выше:
| Параметр | demo-v1 в текущем UI | organizer-v1 в новом API |
|---|---|---|
| Районы | Условные D1–D3 | Есиль, Алматы, Сарыарка, Байконур, Нура |
| Каталог | 16 мер, включая меру события | 14 мер |
| Горизонт | 12 месяцев | 8 кварталов |
| Формат плана | actionIds | selections: [{actionId, districtId?}] |
| Эталонный расход | 90 | 95 |
| Эталонный Score | 57 | 56.54307 |
| Языки объяснений | Подготовленный русский mock | ru, kk, en |
Числа этих моделей не взаимозаменяемы. Не переключайте текущий интерфейс на новый API только изменением флага.
Сервер organizer-v1 принимает пять уникальных мер в пределах 100 единиц, не более двух мер одного направления. Для районных мер требуется район; для городских район не передаётся. Сервер проверяет ограничения и рассчитывает эффекты, лаги и синергии. События отмены и обязательной меры — сценарии команды.
Отдельный запуск серверной версии
Этот раздел необязателен для браузерного показа жюри. Все серверные маршруты уже работают в запущенном выше приложении, даже при клиентском mock-флаге true. Для отдельной проверки API можно использовать другую папку:
git clone --branch main https://github.com/BAITC-Hacks/hack-14dc5002-nt.git hackalem-api
cd hackalem-api
npm ci
Создайте в этой папке .env.local:
NEXT_PUBLIC_USE_MOCK_API=false
AI_ENABLED=false
AI_LOCAL_DEMO=false
Запустите API на отдельном порту:
npm run dev:local -- --port 3001
Каталог будет доступен по http://127.0.0.1:3001/api/catalog. Главная страница этой серверной версии ещё не является новым подключённым UI.
Пример получения реального расчёта в PowerShell, во втором терминале:
$apiBase = 'http://127.0.0.1:3001'
$catalog = (Invoke-RestMethod -Uri ($apiBase + '/api/catalog')).data
$body = $catalog.demoPlan | ConvertTo-Json -Depth 20
$result = Invoke-RestMethod -Uri ($apiBase + '/api/simulate') -Method Post `
-ContentType 'application/json; charset=utf-8' `
-Body ([System.Text.Encoding]::UTF8.GetBytes($body))
$result.data | Select-Object valid, totalCost, remainingBudget, officialScore
Для эталонного плана organizer-v1 ожидаются
valid=true, totalCost=95, remainingBudget=5,
officialScore=56.54307.
| Метод | Маршрут нового API | Назначение |
|---|---|---|
| GET | /api/catalog | Каталог и сценарии команды |
| POST | /api/simulate | Проверка и расчёт плана |
| POST | /api/events/preview | Предварительные допустимые замены |
| POST | /api/events/confirm | Подтверждение одной замены |
| POST | /api/explain | Объяснение результата на RU/KK/EN |
Полный контракт, примеры событий и объяснений: инструкция интеграции.
Роль AI
Числа рассчитывает детерминированный движок, а не языковая модель. В новом API сначала создаются проверенные факты. При включённом AI модель выбирает факты и их порядок; текст и числовые значения формирует сервер.
При выключенном AI, отсутствии настроек, ошибке или таймауте возвращается
source=template. Для показа жюри оставьте AI_ENABLED=false.
Опциональное подключение AI описано в инструкции интеграции.
Обычный production-браузер пока получает шаблон; публичный доступ к платным
вызовам требует отдельной настройки и общего серверного лимита.
Ключи хранятся только на сервере. Не добавляйте их в Git или в переменные
с префиксом NEXT_PUBLIC_.
Если приложение не запускается
| Симптом | Решение |
|---|---|
node или npm не найден | Установите Node.js 24.x и заново откройте терминал |
PowerShell блокирует npm.ps1 | Используйте npm.cmd ci, npm.cmd run build, npm.cmd run start -- --hostname 127.0.0.1 --port 3000 |
GitHub сообщает Repository not found | Проверьте адрес и доступ аккаунта к приватному репозиторию |
EADDRINUSE | Остановите предыдущий сервер или используйте npm run start -- --hostname 127.0.0.1 --port 3002 и откройте порт 3002 |
Could not find a production build | Сначала выполните npm run build и дождитесь завершения |
| Сайт не открывается | Проверьте, что сервер работает, терминал открыт и адрес содержит правильный порт |
MOCK_SCENARIO_NOT_DEFINED | Загрузите демо-план и повторите точную последовательность показа |
Неверный каталог или UI_CONTRACT_OUTDATED | Для текущего UI нужен NEXT_PUBLIC_USE_MOCK_API=true; после изменения пересоберите приложение |
| После изменения env режим прежний | Проверьте переменные терминала, остановите сервер, пересоберите и запустите снова |
| Показывается предыдущий план | Откройте приватное окно либо загрузите демо-план и пересчитайте |
| Объяснение помечено как шаблон | Это ожидаемый режим демонстрации без AI |
Технологии и структура интерфейса
Next.js 15, React 19, TypeScript, серверные маршруты в одном приложении, Zod для проверки входных данных. Авторизация пользователей и база данных для локального MVP не требуются.
src/
app/
page.tsx Страница приложения
globals.css Общие стили
api/ Серверные маршруты текущей ветки
features/dashboard/
SimulationDashboard.tsx Интерфейс и состояние сценария
stored-plan.ts Чтение сохранённых ID и версии модели
contracts/ Типы данных
data/ Каталог
lib/client-api.ts Адаптер текущего UI
mocks/ Подготовленные сценарии demo-v1
docs/ Описание проекта и интеграции
localStorage сохраняет план и версию модели. Сохранённые числовые результаты не используются как доверенный источник: план проверяется повторно.
Проверки общей версии
После объединения интерфейса Semba c0bf5d6 и сервера Bakytzhan 710d106
выполнены следующие проверки. Последующие изменения касаются документации.
| Команда | Результат |
|---|---|
npm run typecheck | PASS |
npm run lint | 0 ошибок, 4 предупреждения в старых заглушках движка |
npm test | 132 Vitest + 77 тестов движка — PASS |
node verification/check-fixtures.mjs | 581 проверка — PASS |
npm run test:e2e | 10 проверок mock UI, Edge desktop/mobile — PASS |
npm run test:api:e2e | 2 HTTP-сценария реального API — PASS |
npm run build | PASS, mock UI, без ключа и платного AI |
GitHub Actions отключён по решению команды; это результаты локальных проверок.
Для браузерных тестов установите Chromium (npx playwright install chromium)
или на Windows задайте $env:PLAYWRIGHT_CHANNEL='msedge' для установленного Edge.
Полный отчёт и ограничения — docs/DEMO.md.
Команда и документация
| Ветка | Ответственность |
|---|---|
| Semba | Участник 1: интерфейс и пользовательский сценарий |
| Nikita | Участник 2: данные, формула и движок |
| Bakytzhan | Участник 3: каркас, API, AI и интеграция |
| main | Общая версия: интерфейс, движок и API/AI; ограничения указаны выше |
Работайте в своей ветке. Сембе после сохранения незакоммиченных изменений нужно
подтянуть общую версию, затем подключать src/lib/organizer-api.ts:
git fetch origin
git switch Semba
git merge origin/main
При конфликтах сохраняйте работу обеих сторон и согласуйте правки с владельцем файлов. Не перезаписывайте чужие рабочие ветки.
- docs/DEMO.md — сценарий старого UI и история интеграции.
- docs/BRIEF.md — рамки проекта.
- docs/CONTRACT.md — исходный контракт demo-v1.
- AGENTS.md — правила совместной работы.
- prompts/ — задачи участников.
- Новый HTTP-контракт и передача фронтенду — API, AI и подключение интерфейса.
Авторы коммитов
История коммитов
- 2026-09-23 18:01merge: include Semba jury guide in shared main versionBakytzhan Kassymgali
- 2026-09-23 18:00merge: include Semba jury guide in shared main versionBakytzhan Kassymgali
- 2026-09-23 17:59merge: preserve main imported snapshot ancestryBakytzhan Kassymgali
- 2026-09-23 17:58docs: record verified main integration and remaining UI workBakytzhan Kassymgali
- 2026-09-23 17:58docs: add complete README and jury launch guideSemba667
- 2026-09-23 17:56chore: import Bakytzhan snapshot 710d106 into mainNikita
- 2026-09-23 17:55merge: integrate latest Semba interface for main handoffBakytzhan Kassymgali
- 2026-09-23 17:41chore: finalize organizer API handoff and verification reportBakytzhan Kassymgali
Все коммиты (38)
- 2026-09-23 17:41feat(ui): adopt civic dashboard style and contextual rule hintsSemba667
- 2026-09-23 17:37feat(ai): explain verified simulation facts in Russian Kazakh and EnglishBakytzhan Kassymgali
- 2026-09-23 17:18feat(api): connect event replacement flows and organizer clientBakytzhan Kassymgali
- 2026-09-23 17:17fix(ui): polish demo interactions after integrationSemba667
- 2026-09-23 17:12feat(api): integrate organizer engine and live simulationBakytzhan Kassymgali
- 2026-09-23 17:01chore: remove hosted CI and retain local verificationBakytzhan Kassymgali
- 2026-09-23 17:01Verify mandatory action edge cases and document integrationNikita
- 2026-09-23 16:52Add mandatory catalog action opportunity and replacement rankingNikita
- 2026-09-23 16:46ci: verify integrated mock demo on pushes and pull requestsBakytzhan Kassymgali
- 2026-09-23 16:46docs: document integrated demo setup and verificationBakytzhan Kassymgali
- 2026-09-23 16:26merge: integrate verified bootstrap into SembaBakytzhan Kassymgali
- 2026-09-23 15:59test: add bootstrap browser checks and verification reportBakytzhan Kassymgali
- 2026-09-23 15:59fix(api): isolate mock responses and expand adapter checksBakytzhan Kassymgali
- 2026-09-23 15:55Verify all cancellation targets and document general event handoffNikita
- 2026-09-23 15:49Generalize cancellation to any selected catalog actionNikita
- 2026-09-23 15:43fix(ui): validate stored plans and reset async stateSemba667
- 2026-09-23 15:41Revalidate M7 replacements and document engine integrationNikita
- 2026-09-23 15:36Add isolated M7 cancellation preview and ranked replacementsNikita
- 2026-09-23 15:27Fix engine validation of malformed data and duplicate costsNikita
- 2026-09-23 15:24fix(ui): restore valid UTF-8 dashboard sourceSemba667
- 2026-09-23 15:17feat(ui): event comparisonSemba667
- 2026-09-23 15:16feat(api): add strict JSON request parsingBakytzhan Kassymgali
- 2026-09-23 15:14feat(api): serve catalog and validate requestsBakytzhan Kassymgali
- 2026-09-23 15:10shared-contract-v1Bakytzhan Kassymgali
- 2026-09-23 15:07Document organizer model and update team promptsNikita
- 2026-09-23 15:07Align simulation engine with organizer datasetNikita
- 2026-09-23 15:06feat(ui): base simulationSemba667
- 2026-09-23 14:41Add event previews and deterministic replacementsNikita
- 2026-09-23 14:37Implement deterministic plan validation and scoringNikita
- 2026-09-23 14:29Add Akim project starter packageNikita
Структура репозитория (99 файлов)
.env.example .gitignore AGENTS.md README.md START-PACKAGE-README.md docs/BRIEF.md docs/CONTRACT.md docs/DATASET.md docs/DEMO.md docs/MODEL.md docs/ORGANIZER-INTEGRATION.md docs/TECHNICAL-SOURCES.md eslint.config.mjs next-env.d.ts next.config.ts package-lock.json package.json playwright.api.config.ts playwright.config.ts postcss.config.mjs prompts/00-bootstrap.md prompts/01-ui.md prompts/02-engine.md prompts/03-api-ai-integration.md src/app/api/.gitkeep src/app/api/catalog/route.ts src/app/api/events/confirm/route.ts src/app/api/events/preview/route.ts src/app/api/explain/route.ts src/app/api/simulate/route.ts src/app/globals.css src/app/layout.tsx src/app/page.tsx src/components/.gitkeep src/contracts/index.ts src/contracts/organizer-api.ts src/contracts/organizer.ts src/data/catalog.json src/data/organizer-catalog.json src/data/team-events.ts src/features/.gitkeep src/features/dashboard/SimulationDashboard.tsx src/features/dashboard/stored-plan.ts src/lib/client-api.ts src/lib/organizer-api.ts src/lib/server/.gitkeep src/lib/server/explanation-templates.ts src/lib/server/explanation.ts src/lib/server/organizer-catalog.ts src/lib/server/organizer-events.ts src/lib/server/organizer-http.ts src/lib/server/organizer-request-schemas.ts src/lib/server/read-json-request.ts src/lib/server/request-schemas.ts src/lib/simulation/.gitkeep src/lib/simulation/event-types.ts src/lib/simulation/event-utils.ts src/lib/simulation/events.ts src/lib/simulation/index.ts src/lib/simulation/opportunity.ts src/mocks/catalog.json src/mocks/event.confirm.json src/mocks/event.preview.json src/mocks/explain.template.json src/mocks/opportunity.confirm.json src/mocks/opportunity.preview.json src/mocks/organizer/scenarios.json src/mocks/requests.json src/mocks/simulate.invalid-four.json src/mocks/simulate.invalid-six.json src/mocks/simulate.valid.json tests/api/catalog.test.ts tests/api/client-api-http.test.ts tests/api/client-api.test.ts tests/api/organizer-client.test.ts tests/api/organizer-events.test.ts tests/api/organizer-explain.test.ts tests/api/organizer-fixtures.test.ts tests/api/organizer-simulate.test.ts tests/api/read-json-request.test.ts tests/api/request-schemas.test.ts tests/e2e/.gitkeep tests/e2e/bootstrap.spec.ts tests/e2e/mock-dashboard.spec.ts tests/http/organizer.spec.ts tests/simulation/.gitkeep tests/simulation/cancellation.test.mjs tests/simulation/engine.test.mjs tests/simulation/events.test.mjs tests/simulation/opportunity.test.mjs tests/simulation/validation.test.mjs tsconfig.json verification/RESULTS.txt verification/check-fixtures.mjs verification/fixture-literals.ts verification/generate-organizer-mocks.mjs verification/golden-summary.json verification/typecheck-fixtures.ts vitest.config.ts
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.