AAYMA
RU
Войти

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

Интерактивный симулятор городского бюджета с анимированной SVG-картой Астаны: выбор 5 мер из 14 по районам, расчёт Score, AI-разбор, предложения…

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

О проекте

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

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

Суть

Интерактивный симулятор городского бюджета с анимированной SVG-картой Астаны: выбор 5 мер из 14 по районам, расчёт Score, AI-разбор, предложения замен, сравнение «План А / План Б», общая таблица команд и автопрезентация.

Как устроено

FastAPI отдаёт статический фронтенд на vanilla JS и API. engine.py считает показатели с лагами, синергиями и несовместимостями по simulation.json, recommender.py проверяет варианты замены одной меры или переноса в другой район. Для AI-разбора сервер строит каталог фактов из расчёта, LLM (OpenAI Chat Completions, gpt-4.1-mini, Structured Outputs) возвращает только ID фактов и их порядок, а формулировки и числа подставляет сервер. Неизвестный ID ведёт к локальному разбору по правилам. Результаты команд хранятся в SQLite с кодом редактирования.

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

  • deterministic scoring model
  • single-swap improvement search
  • fact-ID grounded explanation
  • Structured Outputs
  • SQLite leaderboard

Метрики

Контрольный пример: расход 95, Score 56,54; замена M5 Сарыарка → M3 Нура даёт 57,21 (+0,67)

Особенности

Произвольный текст LLM в интерфейс не попадает: модель выбирает только ID фактов из серверного каталога, у каждого вывода раскрывается «Основание расчёта». Анимированная карта Астаны с объектами всех 14 инициатив и проверкой коллизий, лидерборд команд.

Стек

Python · FastAPI · JavaScript · SVG · SQLite · pytest · Ruff · GitHub Actions

Модели

gpt-4.1-mini


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

README

README.md

«Аким на 5 часов» - симулятор городских решений

Интерактивный AI-симулятор распределения городского бюджета. Выберите пять инициатив для города и его пяти районов, проверьте бюджет и увидьте, как меняются показатели и Astana Quality of Life Score.

Все районы, показатели и эффекты мероприятий в проекте синтетические и используются только для демонстрации модели.

Возможности

  • одинаковые для всех исходные данные и бюджет в 100 условных единиц;
  • обзор города с карточками районов: по клику доступны профиль и все десять исходных показателей;
  • интерактивная схема Астаны: выбор района и мер прямо на карте, появление ЛРТ, деревьев, школ и других проектов;
  • каталог из 14 городских инициатив по пяти направлениям;
  • выбор ровно пяти мер, районов для районных проектов и автоматическая проверка ограничений;
  • учёт стоимости, лага, синергий, несовместимостей и пределов показателей 0–100;
  • раскрываемая формула Score: вклады среднего уровня, слабейшего района и критических показателей до/после;
  • сравнение баллов пяти районов, раскрытие изменений показателей и AI-разбор;
  • проверенные моделью варианты улучшения плана: замена одной меры или перенос её в другой район;
  • личное сравнение «План А / План Б» с бюджетом, решениями, районными баллами и потерянными эффектами;
  • загрузка контрольного примера одной кнопкой с объяснением выбора всех пяти мер;
  • общая таблица результатов нескольких команд с деталями решений и баллами районов;
  • автономная презентация из трёх слайдов с возможностью печати в PDF;
  • список оставшихся показателей ниже критического порога и эффекты мер с учётом лага;
  • локальное объяснение продолжает работать, если AI API-ключ не задан или сервис недоступен;
  • выбранные решения сохраняются в браузере на время следующего визита.

Пользователь выбирает ровно 5 разных мероприятий из каталога пяти направлений. По правилам предоставленного датасета допускается максимум 2 мероприятия одного направления, поэтому план охватывает минимум 3 направления; по одному из каждого выбирать не обязательно.

Запуск

Требуются Python 3.10+ и доступ к установке пакетов из requirements.txt. У фронтенда нет отдельного шага сборки: FastAPI раздаёт клиентские файлы и API с одного адреса.

Linux / macOS

python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python -m uvicorn backend.app.main:app --reload

Windows PowerShell

py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
python -m uvicorn backend.app.main:app --reload

Откройте http://127.0.0.1:8000. Интерактивная документация API доступна по адресу http://127.0.0.1:8000/docs.

AI-разбор для демонстрации

AI-анализ - обязательная часть задания. Для демонстрации живого LLM-анализа нужен действующий серверный ключ OpenAI Chat Completions. Без него приложение рассчитывает тот же Score, но показывает явно помеченное объяснение по правилам (source="rules"); оно не является ответом LLM. Чтобы включить настоящий AI-разбор:

cp .env.example .env

В .env задайте OPENAI_API_KEY; при необходимости измените OPENAI_MODEL (по умолчанию gpt-4.1-mini). Перезапустите сервер после изменения переменных окружения. Файл .env исключён из Git. Ключ используется только сервером и не передаётся браузеру. На машине демонстрации рассчитайте контрольный сценарий и проверьте метку «ИИ-РАЗБОР» (explanation.source="openai") и содержательность текста. При ошибке провайдера будет метка «ПО ДАННЫМ МОДЕЛИ» (source="rules"); Score не изменится. Проверки с имитацией ответов не заменяют живой вызов.

AI получает каталог утверждений, построенных сервером из рассчитанного сценария: улучшения показателей, слабые места, лаги, синергии и компромиссы допустимых альтернатив. LLM выбирает приоритеты и порядок выводов для резюме, сильных сторон, рисков, последствий и рекомендаций. Формулировки и числа подставляет сервер из этого каталога.

Ответ провайдера содержит только ID фактов. Structured Outputs ограничивает ответ JSON-схемой с разрешёнными ID и количеством пунктов; сервер дополнительно проверяет структуру, допустимость раздела и повторы. Неизвестный факт или произвольный текст приводит к локальному разбору. У каждого вывода можно раскрыть «Основание расчёта»: значения и источники из модели. В /api/analyze эти основания доступны в explanation.evidence, а режим — в explanation.grounding. Это проверяемая сборка анализа: произвольные формулировки LLM не попадают в интерфейс. Релевантность выбранных AI акцентов всё равно оценивает пользователь.

Карта решений

В разделе «Карта решений» выберите район на схеме или в списке, затем направление и проект в палитре. Перед добавлением сервер проверяет бюджет и совместимость, а на карте появляется полупрозрачный эскиз будущего проекта. Кнопка «Добавить за … ед.» включает его в общий план.

ЛРТ и автобусы движутся по протяжённым условным маршрутам внутри района. Парки, школы, медцентры и другие объекты встроены в городскую сцену с кварталами, деревьями, набережными и ориентирами. В районе Алматы размещён Дворец мира и согласия. Все 14 инициатив имеют свою визуализацию.

Для зданий, подписей, ориентиров и транспортных маршрутов предусмотрены отдельные зоны. Транспорт идёт между конечными остановками и возвращается обратно; река и декорации набережной рисуются под проектами. Проверка плотных планов контролирует пересечения площадок, подписей, реки и движущихся машин во всех пяти районах на компьютере и телефоне.

  • Клик по объекту открывает его меру и фактическое место в плане; кнопка «Убрать из плана» удаляет проект. «Показать проект на карте» возвращает к нему. Выбор синхронизирован с каталогом, бюджетом, примером, альтернативами и сохранёнными планами.
  • Городская мера отображается во всех пяти районах, но занимает один слот и оплачивается один раз.
  • Баллы на карте остаются исходными до нажатия «Рассчитать сценарий». После расчёта показаны итоговые районные баллы; при изменении решений карта возвращается к исходным значениям.
  • «Исходный город / Ваш план» позволяет сравнивать состояние без изменения решений. В исходном виде проекты скрыты, а баллы берутся из базового отчёта.
  • «Проекты / Баллы районов» переключает обычную сцену и цветовой слой районных баллов с легендой. Цвет района не равен количеству критических показателей.
  • Кнопки масштаба, перетаскивание мышью, приближение района и «Развернуть карту» помогают рассмотреть город. Развёрнутый режим закрывается по Escape и удерживает клавиатурный фокус внутри карты.
  • «Пауза» останавливает анимации. Системная настройка уменьшения движения также поддерживается.
  • На телефоне панель выбора находится сразу после сцены; добавленный проект автоматически показывается на карте. Список выбранных проектов свёрнут, эффекты и сроки раскрываются отдельно.
  • Несовместимая мера блокируется до добавления с объяснением причины. Бюджетная полоска показывает текущие расходы и стоимость эскиза. Если допустимый промежуточный выбор оставит слишком мало денег даже на самые дешёвые свободные меры, интерфейс пояснит необходимость замены решений.
  • Сброс и загрузка другого плана отменяют ожидающее добавление. Устаревшая проверка не меняет новый выбор; при сетевом сбое предварительную проверку можно повторить.

Это автономная SVG-схема пяти районов синтетического датасета с рекой Есиль и ориентирами Астаны. Границы, маршруты и места строительства условны. Схема загружается с приложением без внешних картографических сервисов; анимация иллюстрирует выбранный проект, а численный эффект рассчитывается сервером с учётом лага.

Сцена и камера находятся в web/city-map.js / web/city-map.css, оформление рабочей панели — в web/map-workspace.css, синхронизация с планом и API — в web/app.js. Компонент сцены получает готовые значения и сообщает о кликах через callbacks; собственных расчётов Score в нём нет.

Быстрый пример и сравнение своих планов

  1. В блоке «Быстрый старт» нажмите «Загрузить пример». Он заменит решения контрольным набором из backend/data/example.json: M7, M8 и M10 в Нуре, M12 для города, M5 в Сарыарке. Загрузка не запускает платный AI-запрос.
  2. Нажмите «Рассчитать сценарий»: расход 95, Score 56,54. В разделе «Как получился Score?» показаны все составляющие формулы и их изменение.
  3. Нажмите «Сохранить для сравнения» — результат станет планом А. Измените решения или выберите предложенную замену и рассчитайте план Б.
  4. В блоке «План А / План Б» сравните бюджет, меры, районные баллы и выигранные/потерянные эффекты. Замена M5 Сарыарка на M3 Нура даёт 57,21 и +0,67 относительно показанного Score плана А, но теряет прирост воздуха и ЖКХ Сарыарки.

План А сохраняется локально, отдельно от таблицы команд. В браузерное хранилище записываются только решения и версия модели; после обновления страницы сервер пересчитывает их через /api/simulate без вызова LLM. План другой версии не сравнивается с текущей моделью. Кнопка «Вернуть решения плана А» загружает его для дальнейшего редактирования; «Убрать сравнение» удаляет локальный снимок.

Сравнение команд и презентация

После расчёта введите название команды и сохраните результат. Сервер заново проверяет пять решений и вычисляет Score, прежде чем добавить запись в общую таблицу. Результаты команд доступны посетителям одного запущенного сервера: можно сравнить Score, расходы и баллы районов или загрузить чужой план для собственного эксперимента.

Первое сохранение выдаёт код команды для редактирования своей записи; браузер сохраняет его автоматически. Чтобы обновить запись с другого устройства, раскройте «Код команды для обновления результата» и вставьте код. Занятое название без верного кода изменить нельзя. Код не показывается в публичной таблице. Локальная база SQLite создаётся автоматически в backend/data/scenarios.sqlite3; путь можно изменить через SIMULATOR_DB_PATH. Файлы базы игнорируются Git. Таблица показывает только результаты текущей версии синтетической модели; это инструмент сравнения сценариев, а не официальный рейтинг хакатона.

Кнопка «Презентация / PDF» скачивает автономный HTML с тремя слайдами по текущему рассчитанному сценарию. Откройте файл в браузере и нажмите «Печать / PDF», чтобы сохранить PDF. Файл не требует доступа к серверу или интернету. На слайд с выводами попадают краткое резюме и до двух сокращённых пунктов каждого раздела, включая последствия. Полный текст остаётся в симуляторе.

Визуальный стиль вдохновлён Astana Innovations: тёмный первый экран, плавающее верхнее меню, мятные акценты, голубо-зелёные градиенты и крупные заголовки. Собственная SVG-иллюстрация Астаны хранится в web/images/astana-future.svg. Светлые рабочие блоки объединяют обзор районов, карту, каталог инициатив и закреплённый бюджет.

На телефоне навигация раскрывается по кнопке меню и закрывается по Escape или выбору раздела; бюджет, число решений и расчёт доступны в нижней панели. Все шрифты — Montserrat, Manrope и JetBrains Mono — хранятся локально в web/fonts/ с лицензиями OFL. Оформление работает без запросов к внешнему сайту. Анимации учитывают системную настройку уменьшения движения.

Проверки

GitHub Actions запускает python -m pytest -q и python -m ruff check backend scripts при каждом push и pull request. Workflow CI использует Python 3.13, устанавливает зависимости из requirements-dev.txt и выполняет проверки без ключа OpenAI. Его также можно запустить вручную во вкладке Actions.

Для линтера и браузерного теста сначала установите инструменты разработчика из requirements-dev.txt (Chromium нужен только для E2E):

python -m pip install -r requirements-dev.txt
python -m pytest -q
ruff check backend scripts
python -m playwright install chromium
python scripts/e2e_smoke.py

Тесты проверяют контрольные примеры датасета, формулу Score, распределение эффектов по районам, бюджет, повторы, направления, ограничения районов, синергии, несовместимости, подбор альтернатив, таблицу команд, AI-факты и API.

Скрипт запускает временный сервер с отключённым платным AI-запросом и отдельной временной SQLite-базой. Он проверяет переключение районов, фильтры, загрузку примера, формулу Score, основания выводов, бюджет, варианты улучшения, сравнение А/Б, восстановление снимка и сравнение команд с двух браузерных сессий. Проверки карты в scripts/map_checks.py охватывают все 14 проектов, движение ЛРТ, отключение анимаций, городские меры, клавиатуру, приближение и синхронизацию с планом. Запоздавшие ответы игнорируются после изменения, сброса и загрузки примера. Экспорт проверяется как трёхстраничный PDF, в том числе с длинным текстом; макет — на ширинах 320–1440 px с заполненными сравнениями.

Проверенное окружение: Linux, Python 3.13.12; FastAPI 0.141.1, Pydantic 2.13.5, Uvicorn 0.53.0, HTTPX 0.28.1, pytest 9.1.1, Playwright 1.63.0, pypdf 6.19.0, Ruff 0.16.8. Диапазоны версий заданы в requirements.txt и requirements-dev.txt.

Модель

  • Горизонт симуляции — 8 кварталов. Для меры с лагом L применяется доля эффекта (8 − L) / 8.
  • Городская мера действует во всех пяти районах; районная — только в выбранном.
  • Синергия добавляется один раз без масштабирования лагом.
  • После применения эффектов показатели ограничиваются диапазоном 0–100.
  • Районный балл — взвешенная сумма десяти показателей; сумма их весов равна 1.
  • Городской средний балл взвешивается по доле населения районов.
  • К итоговому Score применяется формула 0.7 × D_avg + 0.3 × min(D_d) − N_crit, где N_crit — количество значений строго ниже 40.
  • Для симуляции требуются ровно пять уникальных мер, не более двух на направление, без превышения бюджета и нарушений несовместимости.
  • Остаток бюджета не увеличивает Score. Порядок выбора мер не влияет на расчёт.

Подробности формулы и контрольные примеры: docs/model.md. Демонстрационный сценарий: docs/demo.md.

Путь данных: браузер отправляет ID мер и районов → сервер проверяет правила → движок считает показатели и Score → рекомендатель проверяет одиночные замены → сервер составляет каталог фактов → LLM выбирает акценты → сервер проверяет выбранные ID и собирает текст с основаниями. Синтетический датасет перенесён из предоставленного «Датасета районов» в backend/data/simulation.json; он един для всех запросов.

Структура

backend/app/       API, валидатор, scoring engine и AI-разбор
backend/data/      общая версия синтетических данных и правил
backend/tests/     проверки движка и API
scripts/           E2E-проверка в Chromium
web/               интерфейс симулятора
docs/              заметки о модели и сценарий демонстрации

Соответствие пунктам задания и границы дополнительных функций: docs/requirements.md. Итоги повторной сверки и исправления: docs/review.md.

Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
11
Авторы
3
Файлы
45
Ветки
1

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

Alimzhan 6sonidev7 4sonidev7 1

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

  • 2026-09-23 17:39ci: add pytest and Ruff checks and fix web serving testsonidev7
  • 2026-09-23 17:28feat: refresh Astana design and fix map object collisionssonidev7
  • 2026-09-23 17:18Update README.mdsonidev7
  • 2026-09-23 16:28feat: add interactive Astana map with animated projects and planning controlssonidev7
  • 2026-09-23 15:33feat: redesign simulator and add verified AI analysis and plan comparisonsonidev7
  • 2026-09-23 14:24fix: expose baseline districts and address compliance reviewAlimzhan
  • 2026-09-23 14:16style: refine simulator layout and mobile planning flowAlimzhan
  • 2026-09-23 14:04feat: compare teams and export scenario presentationsAlimzhan
Все коммиты (11)
  • 2026-09-23 13:47test: add chromium end-to-end smoke coverageAlimzhan
  • 2026-09-23 13:42feat: suggest validated scenario improvementsAlimzhan
  • 2026-09-23 13:39feat: add city budget simulator MVPAlimzhan
Структура репозитория (45 файлов)
.env.example
.github/workflows/ci.yml
.gitignore
README.md
backend/__init__.py
backend/app/__init__.py
backend/app/ai.py
backend/app/engine.py
backend/app/evidence.py
backend/app/leaderboard.py
backend/app/main.py
backend/app/recommender.py
backend/data/example.json
backend/data/simulation.json
backend/tests/test_ai.py
backend/tests/test_api.py
backend/tests/test_engine.py
backend/tests/test_leaderboard.py
backend/tests/test_recommender.py
backend/tests/test_score_breakdown.py
docs/demo.md
docs/model.md
docs/requirements.md
docs/review.md
requirements-dev.txt
requirements.txt
scripts/e2e_smoke.py
scripts/map_checks.py
web/app.js
web/city-map.css
web/city-map.js
web/fonts/JETBRAINS-MONO-OFL.txt
web/fonts/MANROPE-OFL.txt
web/fonts/jetbrains-cyrillic.woff2
web/fonts/jetbrains-latin.woff2
web/fonts/manrope-cyrillic.woff2
web/fonts/manrope-latin.woff2
web/fonts/montserrat-OFL.txt
web/fonts/montserrat-cyrillic.woff2
web/fonts/montserrat-latin.woff2
web/images/astana-future.svg
web/index.html
web/map-workspace.css
web/presentation.js
web/styles.css

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

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

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