О проекте
Команда: Back To Future
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Доведённый продукт
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Городской симулятор «Аким на 5 часов» с 3D-картой Астаны. Пользователь выбирает 5 мер из 14 в пределах бюджета 100 и видит, как меняются Score и показатели районов. ИИ-советник объясняет результат.
Как устроено
Клиент (React и MapLibre 3D) отправляет выбранные решения в FastAPI. Ядро city_core на Python Decimal проверяет повторы, бюджет, районы, несовместимости и лимит в две меры на направление. Затем применяет эффекты с лагами на горизонте 8 кварталов, добавляет синергии и ограничивает показатели диапазоном 0–100. Score = 0.7 × средневзвешенный балл районов + 0.3 × балл слабейшего района − число показателей ниже 40. По кнопке каталог и готовый расчёт уходят в OpenAI gpt-6-sol (Flex, structured outputs) за объяснением. Сценарии сохраняются в localStorage.
Ключевые решения
- Decimal arithmetic simulation
- lagged effects
- synergies
- constraint validation
- structured outputs
- 3D map rendering
Метрики
68 тестов ядра и 22 теста API; контрольный пример: Score 52,56 → 56,54 (+3,99), критических значений стало 0 вместо 2
Особенности
Числа считаются точно на Decimal, в JSON передаются строками, а ИИ только объясняет готовый результат. Интерактивная 3D-карта Астаны на MapLibre. Работа разделена по веткам (клиент, API, ядро) и затем объединена через Docker Compose.
Стек
TypeScript · React · Vite · MapLibre GL JS · OpenFreeMap · Python · FastAPI · Pydantic · Docker Compose · Nginx · pytest · Ruff · mypy · ESLint
Модели
gpt-6-sol
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-22fca084-back-to-future.
README
README.mdАким на 5 часов — Astana City Decision Lab
Пять решений. Один бюджет. Будущее Астаны.
Учебный AI-симулятор помогает увидеть, как распределение ограниченного городского бюджета влияет на качество жизни районов. Пользователь выбирает инициативы, сравнивает результат с исходным состоянием и получает объяснение последствий. Проект предназначен для участников кейса и демонстрации жюри хакатона.
Что реализовано
- Интерактивная карта Астаны с объёмными зданиями, переключением 2D/3D, вращением, масштабом и переходами к четырём городским ориентирам.
- Каталог 14 мер в пяти направлениях: транспорт, экология, социальная сфера, безопасность и городские сервисы.
- Выбор ровно пяти мер с бюджетом 100 и назначением района для локальных мер.
- Проверка повторов, бюджета, районов, несовместимости и лимита двух мер одного направления; невалидный набор не получает расчётный результат.
- Точный расчёт Astana Quality of Life Score, изменений пяти районов, показателей, критических значений и синергий.
- ИИ-советник: сильные стороны, риски, последствия и рекомендации.
- Сохранение до 20 именованных сценариев в браузере, повторное открытие, удаление и сравнение баллов в списке.
Как работает решение
- Пользователь открывает карту и выбирает пять инициатив с районами.
- Клиент показывает расходы и отправляет решения в FastAPI.
- Ядро проверяет ограничения и применяет эффекты мер с учётом лагов на горизонте восьми кварталов, затем добавляет синергии и ограничивает значения показателей диапазоном 0–100.
- Интерфейс показывает исходное и итоговое состояние, Score и дельты.
- По отдельной кнопке сервер передаёт каталог и готовый расчёт в OpenAI. Модель объясняет результат; числовой расчёт выполняется только ядром.
- Пользователь сохраняет вариант в браузере и может сравнить его с другими.
Формула ядра: Score = 0.7 × средневзвешенный балл районов + 0.3 × балл слабейшего района − количество показателей ниже 40. Арифметика использует
Decimal; JSON передаёт десятичные значения строками, интерфейс округляет
их только для отображения.
Технологии
| Компонент | Технологии |
|---|---|
| Клиент | TypeScript, React 19.3, Vite 8.3, CSS Modules |
| Карта | MapLibre GL JS 6.11, OpenFreeMap, OpenStreetMap / OpenMapTiles |
| API | Python 3.12, FastAPI 0.141, Pydantic 2.13, Uvicorn, HTTPX |
| Ядро | Python: dataclasses, decimal, без runtime-зависимостей |
| ИИ | OpenAI Chat Completions, gpt-6-sol, service_tier=flex, structured outputs |
| Хранение | Versioned localStorage, без серверной БД |
| Запуск и проверки | Docker Compose, Nginx, pytest, Ruff, mypy, ESLint, TypeScript |
Полные версии зафиксированы в lock-файлах и requirements*.txt.
ИИ настроен на Flex без автоматического перехода на Standard.
Архитектура
flowchart LR
User[Пользователь] --> Client[React + MapLibre]
Client --> Tiles[OpenFreeMap]
Client <--> Storage[localStorage]
Client --> Nginx[Nginx /api]
Nginx --> API[FastAPI]
API --> Core[city_core: валидация и Decimal]
API --> AI[OpenAI: объяснение результата]
client/ отвечает за интерфейс. server/app/api/ — HTTP-контракт,
server/app/agent/ — запрос к модели и проверку ответа. core/city_core/
содержит единственный каталог и формулы. API импортирует ядро как библиотеку;
контейнер core дополнительно предоставляет CLI и после расчёта завершается.
Клиент обращается к API через Nginx в одной origin. Ключ OpenAI передаётся
только серверному контейнеру, не включается в клиентскую сборку.
| Метод | Путь | Назначение |
|---|---|---|
| GET | /api/health | Состояние API |
| GET | /api/catalog | Каталог и исходный результат |
| POST | /api/scenarios/evaluate | Проверка и расчёт набора решений |
| POST | /api/scenarios/analyze | Расчёт и ИИ-объяснение |
Установка и запуск
Нужны Git, Docker с Compose 2.20+ (используется include), Make и интернет
для загрузки зависимостей, карты и OpenAI. Для просмотра 3D нужен браузер с WebGL.
Node.js и Python на хосте для основного запуска не требуются.
git clone [email protected]:BAITC-Hacks/hack-22fca084-back-to-future.git
cd hack-22fca084-back-to-future
cp .env.example .env
Если репозиторий приватный, нужен доступ к нему. При повторном запуске
сохраните существующий .env. Настройки:
| Переменная | Значение примера | Назначение |
|---|---|---|
CLIENT_PORT | 8040 | Локальный порт интерфейса |
API_PORT | 8050 | Локальный порт API и Swagger |
OPENAI_API_KEY | пусто | Необязательный ключ для ИИ-анализа |
OPENAI_MODEL | gpt-6-sol | Модель с доступом у вашего API-проекта |
Добавьте ключ в .env, если нужен ИИ-анализ, и запустите:
make up
Откройте приложение и
документацию API.
При занятых портах измените .env. Сервисы публикуются на 127.0.0.1.
Без ключа работают карта, каталог, расчёт и сохранения; анализ возвращает
явный статус unavailable. При ошибке провайдера расчёт сохраняется.
make ps
make logs
make down
make down останавливает контейнеры. Сохранённые сценарии остаются в браузере.
Для запуска итоговой ветки соседние клоны участников не нужны.
Как проверить решение
Сценарий для жюри
- Откройте приложение и нажмите «Пример из кейса».
- Убедитесь, что выбраны школа с детсадом, поликлиника и Safe City для Нуры, платформа обращений для города и чистое топливо для Сарыарки.
- Нажмите «Рассчитать будущее города».
- Ожидаемый результат: расходы 95, остаток 5, исходный Score 52.55768, итоговый 56.54307, прирост 3.98539. На экране это 52,56 → 56,54, прирост +3,99. Количество критических значений уменьшается с 2 до 0.
- Откройте показатели и синергии; при настроенном ключе нажмите «Проанализировать с ИИ» и дождитесь ответа советника.
- Введите название, сохраните сценарий и откройте вкладку «Сценарии».
- Для негативной проверки сбросьте решения и запустите расчёт: появится ошибка о необходимости выбрать ровно пять мероприятий.
Проверка HTTP без браузера:
curl -fsS http://localhost:8040/api/health
curl -fsS http://localhost:8040/api/scenarios/evaluate \
-H 'Content-Type: application/json' \
--data-binary @core/tests/fixtures/example_95.json
Проверки Python в контейнерах:
make core-check
make api-check
Клиентские проверки (Node.js 22.12+ на хосте):
npm --prefix client ci
npm --prefix client run typecheck
npm --prefix client run lint
npm --prefix client run test
После объединения прошли 68 тестов ядра и 22 теста API с Ruff/mypy;
общий Compose собрался и запустился. До объединения прошли
клиентские типы, линтер и production-сборка. В браузере проверены пример,
невалидный набор, сохранение и живой ответ ИИ. Полный многократный прогон
золотого набора ИИ не проводился; кейсы находятся в server/evals/cases.json.
Данные и интеграции
- География: OpenStreetMap через тайлы OpenFreeMap/OpenMapTiles. Атрибуция сохранена на карте. Ключ картографического сервиса не требуется.
- Расчётные данные: синтетический набор задания — пять условных районов,
десять показателей, четырнадцать мер, веса, лаги и ограничения в
core/city_core/catalog.py. Это не актуальная статистика Астаны. - OpenAI: сервер отправляет каталог и результат симуляции в
https://api.openai.com/v1/chat/completions. Ключ берётся из окружения. Генерация не вызывается для невалидного сценария.
Ограничения текущей версии
- Демонстрационная модель, не прогноз развития реального города.
- 3D-здания — объёмные контуры с доступными или приближёнными высотами, без фотореалистичных фасадов и точных моделей всех достопримечательностей.
- Границы расчётных районов не нанесены на карту; показатели показаны карточками.
- Нет регистрации, общей БД, синхронизации сценариев между устройствами и серверной истории. Очистка данных браузера удаляет сохранения.
- Сравнение сохранений доступно по Score и приросту в списке, без отдельного подробного сравнительного отчёта.
- Карта и ИИ зависят от внешних сервисов. Flex может отвечать дольше или быть временно недоступен; таймаут запроса к модели — 180 секунд.
- Структура ответа ИИ проверяется, но это не гарантия фактической точности каждой рекомендации. Полный золотой прогон и нагрузочные тесты не выполнены.
- Публичный деплой, HTTPS и промышленная эксплуатация не настроены.
Демо и команда
Публичной deployed-версии сейчас нет. Локальное демо после make up:
http://localhost:8040.
| Участник | Часть решения | Ветка |
|---|---|---|
| Ульданов Мансур | Клиент и 3D-карта | mansur |
| Уралбаев Алишер | API и ИИ-советник | alisher |
| Далиев Досжан | Расчётное ядро | doszhan |
Общая сборка — main. Подробности расчётного контракта:
core-contract, карта файлов: CODEMAP.
Авторы коммитов
История коммитов
- 2026-09-23 17:59Объединить клиент API и ядро и описать запуск для жюриУльданов Мансур
- 2026-09-23 17:52Добавить панель городских решений и сквозное демоУльданов Мансур
- 2026-09-23 17:40Настроить gpt-6-sol в режиме FlexУралбаев Алишер
- 2026-09-23 17:34Добавить API сценариев и анализ результатов через OpenAIУралбаев Алишер
- 2026-09-23 17:18Добавить точное расчётное ядро городского симулятораДалиев Досжан
- 2026-09-23 16:59Добавить трёхмерную карту Астаны с управлением камеройУльданов Мансур
- 2026-09-23 16:02aDoszhan
- 2026-09-23 16:01rm: textDoszhan
Все коммиты (12)
- 2026-09-23 16:01textDoszhan
- 2026-09-23 10:17rm text.txtDoszhan
- 2026-09-23 10:14testDoszhan
- 2026-09-23 09:45exmaplem.uldanov
Структура репозитория (91 файлов)
.dockerignore .env.example .githooks/commit-msg .githooks/pre-commit .gitignore CODEMAP.md Makefile README.md STACK.md client/Dockerfile client/eslint.config.js client/index.html client/package-lock.json client/package.json client/src/features/map/mapConfig.ts client/src/features/map/mapStyle.ts client/src/features/map/useCityMap.ts client/src/features/simulator/ResultsPanel.tsx client/src/features/simulator/ScenarioPanel.tsx client/src/features/simulator/Simulator.module.css client/src/features/simulator/storage.ts client/src/features/simulator/useSimulator.ts client/src/main.tsx client/src/pages/MapPage.module.css client/src/pages/MapPage.tsx client/src/shared/api/client.ts client/src/shared/api/types.ts client/src/shared/ui/ErrorBoundary.tsx client/src/shared/ui/Icon.tsx client/src/styles/global.css client/tests/production.test.mjs client/tsconfig.json client/vite.config.ts core/.dockerignore core/Dockerfile core/city_core/__init__.py core/city_core/__main__.py core/city_core/calculator.py core/city_core/catalog.py core/city_core/cli.py core/city_core/models.py core/city_core/serialization.py core/city_core/validation.py core/compose.yml core/pyproject.toml core/requirements-dev.in core/requirements-dev.txt core/tests/conftest.py core/tests/fixtures/cheapest_61.json core/tests/fixtures/example_95.json core/tests/fixtures/over_budget.json core/tests/test_calculator.py core/tests/test_catalog.py core/tests/test_cli.py core/tests/test_validation.py deploy/nginx/nginx.conf docker-compose.preview.yml docker-compose.yml docs/api-plan.md docs/core-contract.md docs/core-plan.md docs/core-usage.md docs/map-prototype-plan.md docs/merge-plan.md docs/simulator-plan.md make/compose.mk server/Dockerfile server/app/__init__.py server/app/agent/__init__.py server/app/agent/prompt.md server/app/agent/provider.py server/app/api/__init__.py server/app/api/analysis.py server/app/api/catalog.py server/app/api/health.py server/app/api/scenarios.py server/app/config.py server/app/core_adapter.py server/app/errors.py server/app/logging.py server/app/main.py server/app/schemas.py server/compose.yml server/evals/cases.json server/pyproject.toml server/requirements-dev.in server/requirements-dev.txt server/requirements.in server/requirements.txt server/tests/conftest.py server/tests/test_api.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.