О проекте
Команда: ProjectZero team
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Рабочий MVP
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Веб-симулятор управления городом: сервер проверяет правила и считает Astana Quality of Life Score, ИИ объясняет результат. Главный экран — 3D-модель центральной Астаны на Three.js с симуляцией трафика и общественного транспорта, плюс карта OSM/Leaflet.
Как устроено
data/scenario.json задаёт правила и меры, server/domain/engine.py (FastAPI) служит единственным источником истины для Score. Фронтенд на React/TS рендерит 3D-город из снимка OpenStreetMap. Детерминированный транспортный движок моделирует очереди, автобусы и ЛРТ для мер M1–M3 и сравнивает поток «без решений» с планом, но на официальный Score не влияет. server/ai/analyst.py отправляет расчётные факты в OpenAI-совместимый /v1/chat/completions (vLLM на NVIDIA Brev с Qwen3-4B-Instruct-2507). Модель возвращает JSON со ссылками на факты, сервер проверяет ссылки и отсутствие цифр в тексте, при сбое показывается объяснение по правилам.
Ключевые решения
- детерминированный движок Score
- 3D-рендер Three.js
- агентная симуляция трафика
- OpenStreetMap импорт
- JSON mode с проверкой ссылок на факты
- vLLM
Особенности
Выделяется 3D-город Астаны с узнаваемыми ориентирами и живой симуляцией трафика и ОТ, которая работает в браузере без ключей карт. LLM запрещено генерировать цифры: числа подставляются из расчёта отдельно. Модель можно развернуть на своём GPU через vLLM. Команда сначала выбрала энергетический трек, потом сменила его.
Стек
React · TypeScript · Vite · Three.js · Leaflet · FastAPI · Python · Nginx · Docker Compose · vLLM · NVIDIA Brev · Playwright · GitHub Actions
Модели
Qwen3-4B-Instruct-2507 (vLLM)
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-55232f74-projectzero-team.
README
README.mdАким на 5 часов — ProjectZero
HackAlem AI · Astana Innovations · Астана, EXPO · 23.09.2026
Веб-симулятор управления городом: 100 единиц бюджета, 5 решений, 14 мероприятий и 5 условных районов Астаны. Сервер проверяет правила и рассчитывает Astana Quality of Life Score. ИИ объясняет рассчитанные факты и компромиссы.
Главный экран — интерактивная 3D-модель центральной Астаны на Three.js: дороги и русло Есиля из OpenStreetMap, мосты, движущиеся машины, очереди, автобусы и спрос на общественный транспорт. Байтерек, Хан Шатыр, Акорда, Дворец мира и согласия, EXPO «Нур Алем», Астана Опера и Хазрет Султан представлены узнаваемыми стилизованными моделями. Обычная застройка процедурная, трафик учебный; это не цифровой двойник города.
Снимок геоданных включён в репозиторий: для 3D-экрана не нужны ключ карт, GPU-сервер или запросы к OpenStreetMap при запуске. Источники, ограничения и обновление геометрии: модель Астаны.
В разделе «Аналитика и карта» доступна реальная карта OpenStreetMap + Leaflet. Показатели синтетические, метки районов приблизительные и не обозначают административные границы.
Запуск после клонирования
Нужны Git, Docker Engine или Docker Desktop и Docker Compose v2. Docker должен быть запущен. Локальные Python, Node.js и GPU для обычного запуска не нужны. Первая сборка скачивает зависимости из интернета.
git clone https://github.com/BAITC-Hacks/hack-55232f74-projectzero-team.git
cd hack-55232f74-projectzero-team
cp .env.example .env
docker compose up --build -d --wait
В PowerShell вместо cp .env.example .env можно использовать Copy-Item .env.example .env.
- Приложение: http://localhost:8080
- Документация API (Swagger): http://localhost:8080/api/docs
- Проверка API: http://localhost:8080/api/health
Откройте «Мой план» → «Загрузить контрольный пример» или «Аналитика и карта» → «Загрузить пример сценария»: бюджет — 95, Score — 56,54, прирост — 3,99. Кнопка «Разобрать с ИИ» покажет объяснение. Без настройки LLM результат явно отмечен «Без LLM»: это объяснение по правилам, не ответ нейросети. Для выполнения критерия AI-анализа подключите модель по инструкции ниже.
Как играть в 3D-городе
- ЛКМ и движение мыши вращают камеру, ПКМ перемещает её, колесо приближает. Метка района выбирает его для следующей инициативы; кнопка прицела в панели приближает район. На телефоне работают жесты камеры.
- Переключатель «Ориентиры Астаны» приближает семь достопримечательностей, возвращает к бульвару Нуржол или показывает всю сцену. Можно нажимать и на подписи достопримечательностей непосредственно в городе.
- Нижняя панель открывает инициативы пяти направлений. Выберите район и нажмите «Включить в план». Бюджет, повторы и несовместимости проверяются сразу.
- «Трафик» окрашивает дороги по очередям и плотности. Нажмите на дорогу и закройте панель инициатив, чтобы увидеть её показатели.
- «Транспорт» показывает маршруты ОТ и очереди на остановках. Высота столбика отражает число ожидающих; автобусы забирают людей с учётом вместимости.
- M1 добавляет выделенные полосы и автобусы, M2 меняет циклы светофоров, M3 запускает районный ЛРТ. Изменение плана перезапускает оба сравниваемых потока с одинакового начального состояния.
- Пауза, скорости 1× / 3× / 6×, утренний/вечерний час пик и спокойный день находятся над панелью строительства. Освещение переключается отдельно от спроса.
- «Сравнить: без решений» показывает базовый трафик на том же модельном времени. «Аналитика и карта» открывает официальный Score и объяснение ИИ; выбранный план общий для обоих экранов.
Трафик работает в браузере, GPU на Brev нужен только для необязательной LLM. При недоступном WebGL отображается схема 2D с той же симуляцией. Дорожные потоки не меняют формулу официального Score. Подробности и ограничения: модель транспорта.
docker compose ps # состояние контейнеров
docker compose logs -f api web # логи; Ctrl+C завершает просмотр
docker compose down # остановить проект
Если порт занят, поменяйте APP_PORT=8081 в .env и повторите запуск. Все сервисы проекта доступны через один порт; API и ключ модели не публикуются отдельно.
Разработка вместе с командой
Режим разработки обновляет React при сохранении файлов и перезапускает FastAPI при изменении Python или датасета:
docker compose down
docker compose -f compose.yaml -f compose.dev.yaml up --build
Откройте http://localhost:8080. Редактируйте файлы на компьютере в IDE. Контейнеры получают изменения через bind mounts. Остановка: Ctrl+C, затем docker compose -f compose.yaml -f compose.dev.yaml down.
После изменения зависимостей в app/package.json, app/package-lock.json или server/requirements*.txt повторите команду с --build. Не добавляйте .env, ключи, .venv, node_modules и файлы IDE в Git.
| Часть | Где работать | Договорённость |
|---|---|---|
| Интерфейс и карта | app/src/ | React + TypeScript, запросы через api.ts |
| 3D-город и трафик | app/src/traffic/ | Детерминированный движок отдельно от Three.js и Score |
| Расчёт и валидация | server/domain/engine.py | Единственный источник истины для Score |
| API | server/main.py | Контракт смотрите в /api/docs |
| ИИ | server/ai/analyst.py | Объясняет факты сервера; не рассчитывает числа |
| Датасет | data/scenario.json | Меняя правила, повышайте version и обновляйте тесты |
| Деплой | compose*.yaml, Dockerfile, app/nginx.conf | Nginx раздаёт интерфейс и проксирует /api |
Каждую задачу делайте в отдельной ветке. Перед объединением запускайте тесты и сборку. Зависимости фронтенда фиксируются в package-lock.json; его нужно коммитить вместе с package.json. Выбор мер живёт в текущей вкладке: для сохранения результата используйте «Скачать расчёт» (JSON). Авторизация и общая таблица команд в эту версию не входят.
Локальный фронтенд + сервер в Docker
Для удобной работы с Node.js нужен Node 24:
# В корне — сервер и веб-прокси
docker compose up --build -d --wait
# Во втором терминале — фронтенд с HMR на другом порту
cd app
npm ci
Linux / macOS / WSL:
API_PROXY_TARGET=http://127.0.0.1:8080 npm run dev -- --port 5173
PowerShell:
$env:API_PROXY_TARGET="http://127.0.0.1:8080"
npm run dev -- --port 5173
Открывайте http://localhost:5173. Для чисто Docker-разработки эта настройка не нужна.
Тесты
Расчёт, правила, API и проверка ответов ИИ проверяются без облачных ключей:
docker compose --profile test run --build --rm test
Проверка TypeScript и сборки фронтенда (Node 24):
cd app
npm ci
npm run test:traffic
npm run build
Форматирование исходников фронтенда: npm run format, проверка: npm run format:check (из app/).
При запущенном Docker-режиме разработки транспортные тесты доступны без локального Node: docker compose -f compose.yaml -f compose.dev.yaml exec web npm run test:traffic.
Браузерные тесты запускаются на работающем приложении с пустыми LLM_BASE_URL и LLM_MODEL:
# Из app/
npx playwright install chromium
npm run test:e2e
Если в Linux не хватает системных библиотек Chromium: npx playwright install --with-deps chromium. Другой адрес приложения задаётся переменной TEST_BASE_URL. Автотесты блокируют загрузку тайлов OSM, чтобы не создавать автоматизированный трафик к публичному сервису.
Подключение ИИ
Backend работает с совместимым /v1/chat/completions API, включая vLLM на NVIDIA Brev и NVIDIA NIM. Обучать модель на датасете не требуется.
Если endpoint уже запущен, заполните .env:
LLM_BASE_URL=https://your-inference-host.example/v1
LLM_MODEL=your-served-model-name
LLM_API_KEY=your-key-if-required
LLM_JSON_MODE=true
docker compose up -d --force-recreate api
URL должен быть доступен из контейнера API. localhost внутри него — сам контейнер. Для сервиса на хосте используйте host.docker.internal и обеспечьте доступность порта из Docker. URL веб-интерфейса модели не заменяет API endpoint. Если сервер не поддерживает response_format, установите LLM_JSON_MODE=false; строгая проверка полученного JSON сохранится.
ИИ получает рассчитанные факты и готовые допустимые замены мер. Возвращает структурированный JSON со ссылками на эти факты. Сервер проверяет структуру, ссылки и отсутствие цифр в сгенерированном тексте; точные числа выводятся из расчётных фактов отдельно. Это ограничивает числовые ошибки, но не гарантирует истинность каждой фразы модели. При таймауте или некорректном ответе интерфейс явно переключается на объяснение по правилам.
NVIDIA Brev: всё приложение и модель на одной GPU-машине
Есть отдельный compose.brev.yaml с vLLM и Qwen3-4B-Instruct-2507. Это конфигурация для проверки на GPU; обычный CPU-запуск её не использует. Начните с GPU с 24 ГБ VRAM, например L4; доступность и стоимость проверяйте в своём аккаунте. Фактическая совместимость зависит от драйвера и свободной памяти.
- В Brev создайте GPU VM с Docker и NVIDIA Container Toolkit. Проверьте GPU командой
nvidia-smi. - Клонируйте репозиторий на VM и создайте
.envиз.env.example. - Для доступа к приложению через порт VM задайте
APP_BIND=0.0.0.0. Модель остаётся во внутренней Docker-сети. - Запустите:
docker compose -f compose.yaml -f compose.brev.yaml up --build -d
docker compose -f compose.yaml -f compose.brev.yaml logs -f llm
Первый запуск скачивает веса и прогревает модель; веб-приложение может открыться раньше готовности ИИ. После сообщения о готовности inference-сервера нажмите «Разобрать с ИИ» и проверьте отметку «ИИ подключён». Имя модели для загрузки меняется через BREV_MODEL; внутреннее имя API — city-analyst.
Для демонстрации используйте доступ к порту 8080, настроенный в Brev, либо SSH port-forward. Secure Link / веб-туннель Brev может требовать авторизацию в браузере — заранее проверьте доступ для жюри. Наш API обращается к модели по внутреннему адресу http://llm:8000/v1, поэтому браузерная авторизация Brev не мешает запросам к LLM.
Кредит $50 расходуется на работающую GPU-машину. docker compose down останавливает контейнеры, но не саму VM: завершив работу, остановите инстанс в Brev. При остановленной VM возможна плата за хранение. Тариф и остаток часов смотрите в Billing.
Источники: Brev GPU instances, доступ к портам Brev, vLLM Docker, карточка Qwen.
Правила и контрольный результат
- Ровно 5 разных мер, не более 2 из одного направления, сумма стоимости ≤ 100.
- Требовать по одной мере из каждого направления нельзя: подробные правила задания допускают минимум три направления.
- Для районной меры район обязателен; для городской используется
district_id: null. - M1 / M3 несовместимы во всём городе. M4 / M7 и M5 / M13 — в одном районе.
- Эффект:
full_effect × (8 − lag) / 8; синергии не масштабируются лагом. - Все эффекты суммируются, потом показатели ограничиваются диапазоном 0–100.
Dрайона — сумма показателей с весами;D_avg— среднее с долями населения.Score = 0.7 × D_avg + 0.3 × min(D) − N_crit, гдеN_crit— число пар район × показатель строго ниже 40.- Невалидный набор возвращает
score: nullи причины. До пяти решений интерфейс показывает подписанную базовую оценку. - Остаток бюджета не добавляет баллов; порядок выбора не влияет на результат.
| Сценарий | Стоимость | Score без округления | На экране |
|---|---|---|---|
| База | 0 | 52.55768 | 52,56 |
| M7 Нура + M8 Нура + M10 Нура + M12 город + M5 Сарыарка | 95 | 56.54307 | 56,54 |
Вклад мер в Score рассчитан методом Шепли по всем подмножествам выбранных пяти мер: вклады учитывают синергии, штрафы и изменение слабейшего района и складываются в общий прирост. Рекомендации перебирают замену одной меры (включая смену её района), проверяют бюджет и конфликты и показывают до трёх улучшений. Это локальный поиск, не доказательство глобального оптимума.
Структура и API
app/ React, TypeScript, Vite, Leaflet, Three.js; Dockerfile + Nginx
src/traffic/ дорожный граф, транспортный движок, 3D-рендерер, игровой интерфейс
data/astana.json готовый снимок геометрии OpenStreetMap (ODbL)
scripts/ импорт геоданных из сохранённого ответа Overpass
server/
main.py FastAPI, входные схемы, API
domain/engine.py валидатор, Score, вклады и рекомендации
ai/analyst.py факты, prompt, LLM-клиент и расчётное объяснение
data/scenario.json общий версионированный датасет и все правила
tests/ тесты расчёта, API и поведения при сбоях LLM
app/e2e/ браузерные тесты
docs/ архитектурные решения
compose.yaml обычный запуск
compose.dev.yaml режим разработки
compose.brev.yaml необязательный inference на GPU
| Метод | Путь | Назначение |
|---|---|---|
| GET | /api/health | Проверка сервера |
| GET | /api/config | Датасет, правила, базовый расчёт, пример |
| POST | /api/simulate | Проверить пять решений и рассчитать результат |
| POST | /api/analyze | Пересчитать решения на сервере и получить объяснение |
POST-запрос содержит только decisions, например {"decisions":[{"measure_id":"M7","district_id":"nura"}, ...]}. Клиент не передаёт стоимость или Score. Полный допустимый пример хранится в поле example датасета. Бизнес-ошибки /simulate возвращаются с HTTP 200 и valid: false; повреждённая структура запроса и невалидный набор для /analyze — HTTP 422.
Карта и деплой
Сборка раздаётся Nginx, API работает отдельным непривилегированным Python-процессом. Для развёртывания на сервере достаточно Docker и тех же команд запуска. По умолчанию порт доступен только локально. Для удалённого доступа измените APP_BIND; для публичного сервиса настройте HTTPS на внешнем reverse proxy. Авторизация и ограничение публичных запросов к GPU остаются задачами следующего этапа.
Подложка OpenStreetMap загружается через интернет. Атрибуция © OpenStreetMap contributors видима на карте; тайлы не скачиваются пакетно для офлайн-работы. При отсутствии подложки выбор районов и расчёт доступны через интерфейс. Соблюдайте Tile Usage Policy OSM и лицензию данных OSM. Для большой публичной нагрузки выберите провайдера тайлов с подходящими условиями.
Команда
| Участник | Роль | GitHub |
|---|---|---|
| Ернур Канат (leader) | LLM / AI-агенты / research / full-stack + devops | @yrn-dev |
| Alizhan Sharipzhan | Robotics / AI-dev | @Alizhan-cmd |
| Ғалымжан Қажымұрат | Full-stack backend | @GitHubGM |
Разработка с использованием Codex. © 2026 ProjectZero team. All rights reserved. Сторонние библиотеки, модель и картографические данные распространяются по собственным лицензиям.
Авторы коммитов
История коммитов
- 2026-09-23 17:55Merge pull request #1 from BAITC-Hacks/claude/beautiful-johnson-uq3k6vGalymzhan
- 2026-09-23 17:52added logo and fixed APIYernur
- 2026-09-23 17:24Fixed city map, added better simulation and added famous places from the citycovek6212
- 2026-09-23 16:49Added API key for Open AI LLMcovek6212
- 2026-09-23 16:16Added 3D mapcovek6212
- 2026-09-23 15:00added map of Astana using OpenStreetMap and also idk most of the parts of this project is ready imocovek6212
- 2026-09-23 14:35FastApi backend requirements and data with scenarioscovek6212
- 2026-09-23 13:49Change project track to Astana InnovationsYernur
Все коммиты (11)
- 2026-09-23 17:42Clean up Mangilik El × Arkhar interchange and add real traffic lightsClaude
- 2026-09-22 18:57chore: add .gitignoreYernur
- 2026-09-22 18:57docs: project skeleton for Energy trackYernur
Структура репозитория (56 файлов)
.dockerignore .env.example .github/workflows/ci.yml .gitignore README.md app/.prettierignore app/.prettierrc.json app/Dockerfile app/e2e/city.spec.ts app/e2e/simulator.spec.ts app/index.html app/nginx.conf app/package-lock.json app/package.json app/playwright.config.ts app/public/logo.png app/src/App.tsx app/src/CityMap.tsx app/src/api.ts app/src/main.tsx app/src/rules.ts app/src/styles.css app/src/traffic/CityRenderer.ts app/src/traffic/GameView.tsx app/src/traffic/cityScene.ts app/src/traffic/data/NOTICE.md app/src/traffic/data/astana.json app/src/traffic/data/landmark-sources.json app/src/traffic/engine.test.ts app/src/traffic/engine.ts app/src/traffic/game.css app/src/traffic/geography.ts app/src/traffic/landmarks.ts app/src/traffic/network.ts app/src/types.ts app/tsconfig.json app/vite.config.ts compose.brev.yaml compose.dev.yaml compose.yaml data/scenario.json docs/architecture.md docs/astana.md docs/traffic.md scripts/import-astana.mjs server/Dockerfile server/__init__.py server/ai/__init__.py server/ai/analyst.py server/domain/__init__.py server/domain/engine.py server/main.py server/requirements-dev.txt server/requirements.txt tests/test_api.py tests/test_engine.py
Ветки (2)
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.