AAYMA
RU
Войти

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

AI-симулятор управления Астаной: детерминированный движок считает Astana Quality of Life Score, AI-советник разбирает план.

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

О проекте

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

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

Суть

AI-симулятор управления Астаной: детерминированный движок считает Astana Quality of Life Score, AI-советник разбирает план. Есть 3D-карта Астаны на MapLibre/OSM, превью проектов на Unity WebGL и отдельная песочница на Three.js.

Как устроено

Датасет кейса лежит в одном JSON (бюджет, 5 районов, 14 мер, синергии, несовместимости). Backend на FastAPI с чистой архитектурой проверяет план и считает Score = 0.7×среднее по городу + 0.3×худший район − число значений ниже 40, с учётом лага и синергий. Сценарии и история чата хранятся в SQLite/PostgreSQL. Отдельный ai_service получает доверенный контекст расчёта и отвечает через OpenAI Responses API по строгой JSON-схеме со SSE-стримингом; ключ хранится только в этом сервисе.

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

  • детерминированный расчёт Score с лагом и синергиями
  • OpenAI Responses API Structured Outputs
  • SSE-стриминг
  • MapLibre + OSM geojson
  • Unity WebGL
  • Three.js
  • rate limiting AI-запросов

Метрики

Контрольный сценарий: Score 52.56 → 56.54 при бюджете 95/100, критических значений 2 → 0

Особенности

Три отдельных сервиса (движок, AI-советник, фронтенд), ключ модели изолирован в ai_service. Настоящие геоданные Астаны (здания, дороги, парки, реки) и Unity-превью проектов. Таблица, где каждому требованию ТЗ сопоставлена реализация.

Стек

Python · FastAPI · SQLAlchemy · Alembic · SQLite · PostgreSQL · Docker · React · TypeScript · Vite · MapLibre · Unity WebGL · Three.js

Модели

gpt-6-luna (OpenAI)


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

README

README.md

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

Пользователь получает бюджет 100 единиц и данные пяти районов Астаны. Он принимает ровно 5 решений из каталога 14 мероприятий, получает итоговый Astana Quality of Life Score и разбор плана от AI-советника. Все расчёты делает детерминированный Python-движок. AI ничего не считает: он объясняет уже рассчитанный результат.

Данные синтетические, взяты из датасета кейса. Карта и 3D-макет условные, это не цифровой двойник города.

Как закрыты требования кейса

Требование ТЗГде реализовано
Единый виртуальный бюджет и исходные данныеОдин файл backend/src/city_simulator/resources/simulation.v1.json: бюджет 100, 5 районов, 14 мер, синергии, несовместимости
5 решений по 5 направлениямРовно 5 мер, не больше 2 из одного направления, без повторов; для районных мер обязателен район, для городских его быть не должно
Автоматический контроль превышения бюджетаВалидатор backend (budget_exceeded). Кнопка расчёта неактивна, пока план не проходит проверку
Расчёт Astana Quality of Life ScoreScore = 0.7 × среднее по городу (с весами населения) + 0.3 × худший район − 1 × число значений ниже 40, с учётом лага и синергий (backend/.../domain/services.py)
AI-анализ решений: сильные стороны, риски, последствия, рекомендацииai_service: модель получает от backend готовый расчёт и возвращает ответ по строгой JSON-схеме
Изменение набора решений меняет ScoreКаждый расчёт сохраняется. История сценариев доступна на вкладке «Астана»

Контрольная точка из кейса: без решений Score 52.56. Пример M7, M8, M10 в Нуре, M12 и M5 в Сарыарке стоит 95 и даёт 56.54, критических значений 2 → 0.

Как устроено

Браузер ──► frontend (React + Vite, :5173)
              │  /api/v1/*  (прокси, без ключей)
              ▼
           backend (FastAPI, :8000) ── правила, расчёт Score, сценарии, история чата
              │  доверенный контекст расчёта             │
              ▼                                          ▼
           ai_service (FastAPI, :8001)              SQLite / PostgreSQL
              │  ключ OpenAI хранится только здесь
              ▼
           OpenAI Responses API (структурированный JSON, стриминг)

Единственная точка входа для браузера — backend. Ключ модели лежит только в ai_service/.env и не попадает во frontend.

Что установить

ЧтоВерсияПроверка
Python3.12 (backend требует ≥ 3.11)py -3.12 --version / python3.12 --version
Node.js22.16+ (проверено на 24)node --version
Ключ OpenAI APIс доступом к модели из ai_service/.env.example—
PostgreSQLне обязателен: для локальной проверки хватает SQLite—

Быстрый запуск: 3 терминала, из корня репозитория

Команды даны для Windows PowerShell. Для Linux и macOS отличия указаны под каждым шагом.

Терминал 1 — backend (расчёт и правила)

cd backend
py -3.12 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -e ".[dev]"
$env:DATABASE_URL = "sqlite+aiosqlite:///./.venv/sandbox-local.db"
.\.venv\Scripts\python.exe -m city_simulator.init_local_db
.\.venv\Scripts\python.exe -m uvicorn city_simulator.main:app --host 127.0.0.1 --port 8000

Linux/macOS: python3.12 -m venv .venv, .venv/bin/python вместо .\.venv\Scripts\python.exe, export DATABASE_URL=... вместо $env:DATABASE_URL = ....

Проверка: http://127.0.0.1:8000/api/v1/health отвечает {"status":"ok"}, Swagger — http://127.0.0.1:8000/docs. Для этого режима файл backend/.env не нужен. Запуск с PostgreSQL и Docker описан в backend/README.md.

Терминал 2 — AI-советник

py -3.12 -m venv ai_service/.venv
& ./ai_service/.venv/Scripts/python.exe -m pip install -r ai_service/requirements-lock.txt
Copy-Item ai_service/.env.example ai_service/.env
notepad ai_service/.env
& ./ai_service/.venv/Scripts/python.exe -m uvicorn ai_service.app:app --host 127.0.0.1 --port 8001

В ai_service/.env вставьте ключ в OPENAI_API_KEY=. Модель уже указана (OPENAI_MODEL=gpt-6-luna), с ней команда проверяла сервис. Если у вашего API-проекта нет доступа к этой модели, укажите любую, которая поддерживает Responses API, Structured Outputs и стриминг. После изменения .env сервис нужно перезапустить.

Linux/macOS: python3.12 -m venv ai_service/.venv, ai_service/.venv/bin/python ..., cp ai_service/.env.example ai_service/.env.

Проверка: http://127.0.0.1:8001/health отвечает {"status":"ok"}. Этот эндпоинт не проверяет ключ: ключ проверяется первым вопросом советнику.

Терминал 3 — интерфейс

cd frontend
Copy-Item .env.example .env
npm ci
npm run dev

Linux/macOS: cp .env.example .env, дальше те же команды.

Откройте http://127.0.0.1:5173/.

Основной сценарий проверки

Приложение открывается на вкладке «Песочница». Это дополнительная учебная игра «Новый Берег» со своими правилами (бюджет 200, 12 кварталов), она не входит в расчёт кейса. Сценарий из ТЗ находится на вкладке «Астана» (или сразу по адресу http://127.0.0.1:5173/#astana):

  1. Вкладка «Астана» → «Попробовать пример». В план попадут 5 мер из примера кейса.
  2. «Сохранить план» → «Рассчитать результат».
  3. На экране результатов должно быть: Astana Quality of Life Score 56.54 (до плана 52.56), бюджет 95 / 100, критических показателей 2 → 0, изменения по пяти районам и вклад каждой меры.
  4. Откройте AI-советника. В ответе есть блоки «Сильные стороны», «Риски», «Возможные последствия», «Рекомендации». Числа в ответе совпадают с расчётом.
  5. Замените любую меру и пересчитайте: Score изменится. Попробуйте нарушить правило, например добавить M4 и M7 в один район. Расчёт не запустится, валидатор назовёт причину.

Если backend не запущен, вкладка «Астана» работает в явно отмеченном деморежиме. Там показан только пример из кейса (52.56 → 56.54), произвольные планы не считаются.

Переменные окружения

Все секреты и адреса берутся из .env рядом с сервисом. Шаблоны лежат в *.env.example, сами .env в Git не попадают. Переменные процесса имеют приоритет над файлом.

ФайлПеременнаяПо умолчаниюЗачем
ai_service/.envOPENAI_API_KEY— (обязательно)Ключ модели; без него советник отвечает 503 ai_not_configured
OPENAI_MODELgpt-6-lunaМодель с Responses API + Structured Outputs + стримингом
OPENAI_TIMEOUT_SECONDS45Предел ожидания ответа модели, до 120
OPENAI_MAX_OUTPUT_TOKENS4000Лимит длины ответа, 256–16000
backend/.env (необязателен)DATABASE_URLsqlite+aiosqlite:///./xarabis.dbБаза сценариев; для PostgreSQL см. шаблон
AI_SERVICE_URLhttp://127.0.0.1:8001Адрес AI-советника
CORS_ALLOWED_ORIGINShttp://localhost:5173,http://127.0.0.1:5173Откуда разрешены запросы браузера
AI_CHAT_REQUESTS_PER_MINUTE10Лимит вопросов советнику
frontend/.envPYTHON_API_URLhttp://127.0.0.1:8000Куда проксировать /api/v1
PORT, HOST3000, 127.0.0.1Только для npm run serve (production-сборка)

backend/.env читается из текущей папки, поэтому backend запускайте из backend/.

Тесты

Ключ и сеть для тестов не нужны.

# backend: правила, формула, API, сценарии
cd backend; .\.venv\Scripts\python.exe -m pytest -q; cd ..
# AI-советник: контракт, схемы, стриминг, ошибки (модель подменяется)
& ./ai_service/.venv/Scripts/python.exe -m pytest ai_service/tests -q
# frontend: SSE, прокси, размещение проектов; затем проверка типов и сборка
cd frontend; npm test; npm run build; cd ..

Если не работает

СимптомПричина и решение
ImportError: cannot import name 'StrEnum'Python старше 3.11. Пересоздайте .venv через py -3.12
На вкладке «Астана» деморежимНе запущен backend на :8000 или в frontend/.env неверный PYTHON_API_URL
Советник: ai_not_configuredВ ai_service/.env пустой ключ или модель. Заполните и перезапустите сервис
Советник: ai_unavailableНет доступа к модели, закончились кредиты или нет сети до api.openai.com. Проверьте OPENAI_MODEL
Советник: ai_timeoutМодель не ответила вовремя. Увеличьте OPENAI_TIMEOUT_SECONDS (до 120)
Ключ в .env «не применяется»В терминале осталась переменная $env:OPENAI_API_KEY или $env:OPENAI_MODEL. Откройте новый терминал
address already in useПорт 8000, 8001 или 5173 занят. Остановите старый процесс или смените порт
Пустая 3D-сценаНужен браузер с WebGL. Открывайте через http://, а не file://; вид «Список районов» работает без WebGL

Структура

ПапкаЧто внутриПодробно
backend/FastAPI: датасет, валидатор, расчёт Score, сценарии, чат с советником, песочницаbackend/README.md, контракт — frontend-integration.md
ai_service/FastAPI: промпты, строгие схемы ответа, OpenAI Responses API, SSEai_service/README.md
frontend/React + TypeScript: карта Астаны (MapLibre + OSM), Unity WebGL, песочница на Three.jsfrontend/README.md

Ограничения

  • Данные районов и эффекты мер синтетические, из датасета кейса. Это не прогноз для реальной Астаны.
  • AI не считает и не меняет Score. Цифры в его ответе берутся из расчёта backend. Проверку допустимости плана делает только валидатор.
  • Карта, объекты проектов и движение машин — условная визуализация, а не транспортная или градостроительная модель.
  • Локальный запуск рассчитан на 127.0.0.1. Для публичного сервера нужны аутентификация и лимиты на платные AI-запросы.
Звёзды
⭐ 1
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
38
Авторы
3
Файлы
186
Ветки
3

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

shutovBro 14Sultan 13Xarabis 12

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

  • 2026-09-23 18:01docs: describe local startup and city visualizationshutovBro
  • 2026-09-23 18:01Merge remote-tracking branch 'origin/main' into codex/astana-city-mapshutovBro
  • 2026-09-23 18:00feat: Implement automatic analysis feature in the city simulatorXarabis
  • 2026-09-23 18:00fix: update label for score layer to improve clarityXarabis
  • 2026-09-23 17:58feat(frontend): animate Astana streets and refine map UXshutovBro
  • 2026-09-23 17:42Update README.mdOlzhas Azirali
  • 2026-09-23 17:41Merge branch 'main' of https://github.com/BAITC-Hacks/hack-6744d423-xarabisSultan
  • 2026-09-23 17:40delete readme from backendSultan
Все коммиты (38)
  • 2026-09-23 17:34feat: implement ScenarioConsultantPanel component and update test scriptXarabis
  • 2026-09-23 17:33Merge branch 'main' of https://github.com/BAITC-Hacks/hack-6744d423-xarabisXarabis
  • 2026-09-23 17:28Merge branch 'main' of https://github.com/BAITC-Hacks/hack-6744d423-xarabisXarabis
  • 2026-09-23 17:28Merge remote-tracking branch 'origin/main' into codex/astana-city-mapshutovBro
  • 2026-09-23 17:28feat: add report streaming functionality and validationXarabis
  • 2026-09-23 17:25feat(frontend): build city-first decision workspace and Unity project previewsshutovBro
  • 2026-09-23 17:04fixSultan
  • 2026-09-23 16:59fixSultan
  • 2026-09-23 16:46feat(frontend): integrate Astana geodata and AI consultant APIshutovBro
  • 2026-09-23 16:37Merge remote-tracking branch 'origin/main' into codex/astana-city-mapshutovBro
  • 2026-09-23 16:35Add interactive Astana 3D map and district boundariesshutovBro
  • 2026-09-23 16:32feat(backend): integrate external AI consultantSultan
  • 2026-09-23 16:16Merge frontend into mainshutovBro
  • 2026-09-23 16:15feat(backend): complete frontend integration APISultan
  • 2026-09-23 16:13Merge branch 'main' of https://github.com/BAITC-Hacks/hack-6744d423-xarabisXarabis
  • 2026-09-23 16:13feat: Implement streaming response for chat endpoint with incremental answer deliveryXarabis
  • 2026-09-23 16:12Add standalone frontend with Unity and city API integrationshutovBro
  • 2026-09-23 15:55docs(backend): add frontend integration guideSultan
  • 2026-09-23 15:40Merge branch 'main' of https://github.com/BAITC-Hacks/hack-6744d423-xarabisSultan
  • 2026-09-23 15:40feat(backend): persist scenarios in PostgreSQLSultan
  • 2026-09-23 15:24Merge branch 'main' of https://github.com/BAITC-Hacks/hack-6744d423-xarabisXarabis
  • 2026-09-23 15:23feat: Add chat request and response examples, evaluation questions, and knowledge loading functionalityXarabis
  • 2026-09-23 15:18feat(backend): complete deterministic simulation coreSultan
  • 2026-09-23 14:45Add Unity Blender to project descriptionOlzhas Azirali
  • 2026-09-23 14:44chore: update .gitignore to include additional file types and create ai_service/.gitignoreXarabis
  • 2026-09-23 14:39feat(backend): add FastAPI city simulator foundationSultan
  • 2026-09-23 14:03Add Akim to the README.mdOlzhas Azirali
  • 2026-09-23 13:59Add location information to READMEOlzhas Azirali
  • 2026-09-23 13:53impove gitignoreSultan
  • 2026-09-23 13:44Первый гитигнорXarabis
Структура репозитория (186 файлов)
.gitignore
README.md
ai_service/.env.example
ai_service/.gitignore
ai_service/README.md
ai_service/__init__.py
ai_service/app.py
ai_service/config.py
ai_service/data/city.json
ai_service/district_schemas.py
ai_service/districts.py
ai_service/errors.py
ai_service/examples/chat-request.json
ai_service/examples/chat-with-result.json
ai_service/examples/evaluation.json
ai_service/knowledge.py
ai_service/prompts/consultant.txt
ai_service/prompts/districts.txt
ai_service/prompts/sandbox.txt
ai_service/provider.py
ai_service/requirements-dev.txt
ai_service/requirements-lock.txt
ai_service/requirements.txt
ai_service/sandbox.py
ai_service/schemas.py
ai_service/streaming.py
ai_service/tests/__init__.py
ai_service/tests/test_api.py
ai_service/tests/test_config.py
ai_service/tests/test_contract.py
ai_service/tests/test_districts.py
ai_service/tests/test_examples.py
ai_service/tests/test_knowledge.py
ai_service/tests/test_sandbox.py
ai_service/tests/test_streaming.py
backend/.dockerignore
backend/.env.example
backend/.gitignore
backend/Dockerfile
backend/README.md
backend/alembic.ini
backend/compose.yml
backend/docs/frontend-integration.md
backend/migrations/env.py
backend/migrations/script.py.mako
backend/migrations/versions/20260923_0001_scenarios.py
backend/migrations/versions/20260923_0002_chat.py
backend/pyproject.toml
backend/scripts/smoke.py
backend/src/city_simulator/__init__.py
backend/src/city_simulator/application/__init__.py
backend/src/city_simulator/application/chat.py
backend/src/city_simulator/application/scenarios.py
backend/src/city_simulator/application/use_cases.py
backend/src/city_simulator/core/__init__.py
backend/src/city_simulator/core/config.py
backend/src/city_simulator/core/logging.py
backend/src/city_simulator/domain/__init__.py
backend/src/city_simulator/domain/chat.py
backend/src/city_simulator/domain/chat_ports.py
backend/src/city_simulator/domain/district_sandbox.py
backend/src/city_simulator/domain/entities.py
backend/src/city_simulator/domain/enums.py
backend/src/city_simulator/domain/exceptions.py
backend/src/city_simulator/domain/ports.py
backend/src/city_simulator/domain/sandbox.py
backend/src/city_simulator/domain/scenario_ports.py
backend/src/city_simulator/domain/scenarios.py
backend/src/city_simulator/domain/services.py
backend/src/city_simulator/infrastructure/__init__.py
backend/src/city_simulator/infrastructure/chat_repository.py
backend/src/city_simulator/infrastructure/consultant_client.py
backend/src/city_simulator/infrastructure/database.py
backend/src/city_simulator/infrastructure/models.py
backend/src/city_simulator/infrastructure/repositories.py
backend/src/city_simulator/infrastructure/result_codec.py
backend/src/city_simulator/infrastructure/scenario_repository.py
backend/src/city_simulator/init_local_db.py
backend/src/city_simulator/main.py
backend/src/city_simulator/presentation/__init__.py
backend/src/city_simulator/presentation/chat_routes.py
backend/src/city_simulator/presentation/chat_schemas.py
backend/src/city_simulator/presentation/dependencies.py
backend/src/city_simulator/presentation/district_sandbox_routes.py
backend/src/city_simulator/presentation/middleware.py
backend/src/city_simulator/presentation/routes.py
backend/src/city_simulator/presentation/sandbox_chat_routes.py
backend/src/city_simulator/presentation/sandbox_routes.py
backend/src/city_simulator/presentation/scenario_routes.py
backend/src/city_simulator/presentation/scenario_schemas.py
backend/src/city_simulator/presentation/schemas.py
backend/src/city_simulator/resources/__init__.py
backend/src/city_simulator/resources/simulation.v1.json
backend/tests/conftest.py
backend/tests/fixtures/invalid_dataset.json
backend/tests/test_api.py
backend/tests/test_chat_api.py
backend/tests/test_config.py
backend/tests/test_consultant_client.py
backend/tests/test_dataset.py
backend/tests/test_district_sandbox_api.py
backend/tests/test_init_local_db.py
backend/tests/test_logging.py
backend/tests/test_middleware.py
backend/tests/test_persistence_api.py
backend/tests/test_sandbox_api.py
backend/tests/test_sandbox_chat_stream.py
backend/tests/test_scenario_repository.py
backend/tests/test_score_calculator.py
backend/tests/test_validation.py
frontend/.env.example
frontend/.gitignore
frontend/README.md
frontend/docs/UNITY-BRIDGE.md
frontend/docs/WORKSPACE-QA.md
frontend/index.html
frontend/package-lock.json
frontend/package.json
frontend/proxy.mjs
frontend/public/data/astana/README.md
frontend/public/data/astana/buildings.geojson
frontend/public/data/astana/buildings.geojson.gz
frontend/public/data/astana/districts.geojson
frontend/public/data/astana/districts.geojson.gz
frontend/public/data/astana/manifest.json
frontend/public/data/astana/parks.geojson
frontend/public/data/astana/parks.geojson.gz
frontend/public/data/astana/qa.json
frontend/public/data/astana/rivers.geojson
frontend/public/data/astana/rivers.geojson.gz
frontend/public/data/astana/roads.geojson
frontend/public/data/astana/roads.geojson.gz
frontend/public/unity/Build/unity.data.gz
frontend/public/unity/Build/unity.framework.js.gz
frontend/public/unity/Build/unity.loader.js
frontend/public/unity/Build/unity.loader.js.gz
frontend/public/unity/Build/unity.wasm.gz
frontend/public/unity/FONT-LICENSE.txt
frontend/public/unity/build-manifest.json
frontend/public/unity/geo/astana-atlas.json
frontend/public/unity/geo/astana-atlas.json.gz
frontend/public/unity/geo/atlas-report.json
frontend/scripts/check-project-previews.mjs
frontend/serve.mjs
frontend/src/App.tsx
frontend/src/AstanaMap.tsx
frontend/src/CityLifeLayer.ts
frontend/src/ConsultantPanel.tsx
frontend/src/DistrictCity.tsx
frontend/src/DistrictSandbox.tsx
frontend/src/Sandbox.tsx
frontend/src/SandboxCity.tsx
frontend/src/ScenarioConsultantPanel.tsx
frontend/src/ScenarioLibrary.tsx
frontend/src/UnityCityView.tsx
frontend/src/Workspace.tsx
frontend/src/automaticAnalysis.ts
frontend/src/caseData.ts
frontend/src/cityApi.ts
frontend/src/cityLifeData.ts
frontend/src/cityNetwork.ts
frontend/src/cityNetworkDemo.ts
frontend/src/consultant.ts
frontend/src/districtApi.ts
frontend/src/districts.css
frontend/src/main.tsx
frontend/src/offlineAstanaStyle.ts
frontend/src/projectPreview.ts
frontend/src/reportStream.ts
frontend/src/roadGeometry.ts
frontend/src/sandbox.css
frontend/src/sandboxApi.ts
frontend/src/styles.css
frontend/src/traffic.ts
frontend/src/useCitySimulator.ts
frontend/src/vite-env.d.ts
frontend/src/workspace.css
frontend/tests/automaticAnalysis.test.mjs
frontend/tests/proxy.test.mjs
frontend/tests/roadGeometry.test.mjs
frontend/tests/sandbox.test.mjs
frontend/tests/stream.test.mjs
frontend/tests/traffic.test.mjs
frontend/tsconfig.json
frontend/vite.config.ts
patches/claude-readme-env-score.patch
Ветки (3)

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

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

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