О проекте
Команда: Название команды
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Доведённый продукт
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Симулятор управления Астаной с интерактивной картой: пользователь выбирает 5 из 14 мер и районы в пределах бюджета 100 и получает Score, изменения показателей и объяснение от AI. Есть советник, который подбирает лучший допустимый план.
Как устроено
FastAPI-эндпоинт /api/sim/evaluate проверяет правила (бюджет, ровно 5 мер, конфликты, лимиты направлений) и считает Score = 0.7·D_avg + 0.3·min(D) − N_crit с лагом (8−lag)/8 и синергиями. /api/sim/analyze передаёт проверенные факты LLM через OpenAI-совместимый API и стримит объяснение по SSE; числа подставляет сервер. Вклад мер оценивается по Шепли, optimizer.py ищет точный оптимум. Без ключа работает mock-режим.
Ключевые решения
- Shapley values
- exact plan optimizer
- SSE streaming
- deterministic scoring engine
- MapLibre geo layers
Метрики
Пример жюри: Score 52.56 → 56.54 (+3.99), 95/100 бюджета, критические показатели 2 → 0; советник находит 57.24
Особенности
Вклад мер разложен по Шепли, модель только выбирает проверенные факты, а тексты и числа подставляет сервер. Настроен деплой с health-check и автооткатом.
Стек
Python · FastAPI · Pydantic · React 19 · TypeScript · Vite · Tailwind CSS 4 · MapLibre GL JS · nginx · systemd
Модели
OpenAI-compatible API (OPENAI_MODEL)
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-b4eba7e5-team.
README
README.md
AI-симулятор управления Астаной · кейс Astana Innovations
Изучите районы, распределите бюджет и узнайте последствия своих решений.
Открыть живое демо ↗ · Запустить локально · Сценарий для жюри · Техническое задание
Город оживает в ваших решениях
Школа в Нуре, чистое топливо в Сарыарке или цифровые сервисы для всего города? Ресурсов на всё не хватит. Соберите план, проверьте компромиссы и получите объяснение результата.
https://github.com/user-attachments/assets/c1c7dbb5-a679-4dec-a570-22b67309e630
25 секунд · 1080p · карта → план → результат. Ролик со звуком.
Приложение уже развёрнуто: hackalem.oqcrm.kz. Можно сразу открыть симулятор в браузере, без локальной установки.
Управлять — значит выбирать
| 01 · Изучите город | 02 · Соберите план | 03 · Поймите результат |
|---|---|---|
| Интерактивная карта, пять направлений и десять показателей каждого района. Найдите, где изменения нужнее всего. | Выберите ровно пять разных мероприятий и назначьте районы. Проверяются бюджет, лимиты и несовместимости. | Получите Score и изменения показателей. Аналитик объяснит сильные стороны, риски и компромиссы. |
В рабочей версии:
- Карта Астаны: выбор района, карточка показателей, плавная камера и пять независимых геослоёв.
- План из 14 мероприятий: стоимость, лаг и ожидаемые эффекты видны до выбора; не больше двух мер из одного направления.
- Проверка правил: бюджет до 100, ровно пять разных решений, обязательные районы и контроль конфликтов.
- Прозрачный расчёт: показатели до и после, итоговый Score, критические значения и автоматически применённые синергии.
- Аналитик: потоковое объяснение через OpenAI-совместимый API либо воспроизводимый отчёт без ключа.
- Советник: точный подбор лучшего допустимого плана, сравнение с вашим сценарием и применение по кнопке.
- Подсказки: пять шагов знакомства с интерфейсом по кнопке «Запустить подсказки».
Математика считает. ИИ объясняет
Все числа вычисляет детерминированный движок. Модель выбирает проверенные факты для объяснения; текст и числовые значения этих фактов подставляет сервер. Вклад отдельных мер оценивается по Шепли.
Score = 0.7 × D_avg + 0.3 × min(D) − N_crit
- 70% веса — средний результат города с учётом долей населения.
- 30% веса — самый слабый район: нельзя улучшить один район и забыть об остальных.
- −1 балл за каждый показатель района строго ниже 40.
Эффект мероприятия учитывает срок начала действия: (8 − lag) / 8.
Синергии добавляются отдельно, а итоговые показатели ограничиваются диапазоном 0–100.
Остаток бюджета не даёт бонуса; порядок решений не влияет на результат.
Данные организаторов синтетические. Это учебная модель, а не актуальная статистика Астаны. Исходные значения и формула сохранены без изменений.
Формула и правила · Исходный датасет · Подробности расчёта
Проверить результат
Воспроизводимый сценарий для жюри — соберите эти пять мероприятий:
| Мера | Решение | Где применяется |
|---|---|---|
| M7 | Школа + детсад | Нура |
| M8 | Центр семейного здоровья / поликлиника | Нура |
| M10 | Освещение и камеры | Нура |
| M12 | Единая цифровая платформа обращений | Весь город |
| M5 | Перевод частного сектора на чистое топливо | Сарыарка |
Нажмите «Рассчитать результат», затем «Объяснить результат».
| Итоговый Score | Потрачено | Остаток | Критические показатели |
|---|---|---|---|
| 52,56 → 56,54 | 95 / 100 | 5 | 2 → 0 |
Прирост по точным значениям — +3,99. В Нуре обеспеченность школами меняется с 38 до 48, доступность медицины — с 35 до 43,75. Срабатывает синергия M10 + M12. Без ключа объяснение помечено как шаблонное и строится из рассчитанных данных.
Точные значения и полный сценарий проверки
Исходный Score: 52.55768. Итоговый: 56.54307.
D_avg = 58.0776, min(D) = 52.9625, N_crit = 0.
Округление применяется только для отображения.
Полный чеклист включает конфликты, смену района, геослои, карточки, подсказки, работу клавиатурой и экран шириной 360 px.
Проверить советника
- После расчёта примера выше со Score 56,54 нажмите «Подобрать лучший план».
- Сравните предложение с текущим планом: M2 и M14 — весь город; M3, M8 и M9 — Нура.
Ожидаемый Score — 57,24 (точно
57.236735), стоимость — 98, критических показателей — 0. - Нажмите «Применить предложенный план». Проверьте пять предложенных мер, остаток бюджета 2 и пересчитанный Score 57,24. До нажатия кнопки текущий план сохраняется.
Быстрый старт
Без API-ключа и без Docker. Нужны Git, uv,
pnpm и Node.js.
Проверено на Node.js 24; Vite требует ^20.19.0 || >=22.12.0.
Python 3.13 и зависимости устанавливаются через uv при первом запуске.
Скрипт запускается в Bash на macOS / Linux. На Windows используйте WSL. Первый запуск требует сети для загрузки зависимостей; порты 8000 и 5173 должны быть свободны.
git clone https://github.com/BAITC-Hacks/hack-b4eba7e5-team.git
cd hack-b4eba7e5-team
LLM_MOCK=true ./dev.sh
| Адрес | Что открывает |
|---|---|
| 127.0.0.1:5173 | Симулятор |
| 127.0.0.1:8000/api/docs | Swagger API |
| 127.0.0.1:8000/api/health | Состояние бэкенда и режим аналитика |
Для этого режима файл .env не нужен. Без заданного ключа mock включается автоматически:
расчёт остаётся полноценным, аналитик выдаёт шаблонный отчёт. Остановка — Ctrl+C.
Подключить настоящего ИИ
Из корня репозитория создайте локальный файл настроек:
cp backend/.env.example backend/.env
Откройте backend/.env и заполните OPENAI_API_KEY=ваш-ключ.
| Переменная | Назначение |
|---|---|
OPENAI_API_KEY | Ключ выбранного провайдера |
OPENAI_MODEL | Доступная вашему ключу модель |
OPENAI_FALLBACK_MODEL | Запасная модель при временных ошибках |
OPENAI_BASE_URL | Адрес OpenAI-совместимого API; для обычного OpenAI не задаётся |
LLM_MOCK | true принудительно включает шаблонный режим даже при наличии ключа |
LLM_CACHE | true включает кеш ответов |
AI_REQUESTS_PER_MINUTE | Общий лимит анализа и советника на процесс; по умолчанию 10 |
Выберите доступные вашему провайдеру модели. Убедитесь, что LLM_MOCK не включён
в .env или окружении, и перезапустите приложение:
./dev.sh
Проверка подключения и доступных моделей:
cd backend
uv run python -m app.llm_check
Ключ хранится только на бэкенде. Файл .env исключён из Git;
frontend .env для запуска не требуется.
Если приложение не запускается
uvилиpnpmне найдены — установите инструменты по ссылкам выше и откройте новый терминал.- Vite сообщает об ошибке Node — проверьте
node --version; используйте поддерживаемую версию, например 24. - Порт занят — остановите прежний локальный экземпляр и повторите запуск; frontend должен обращаться к тому же backend на 8000.
- Аналитик пишет «Шаблон» после добавления ключа — проверьте
LLM_MOCKи перезапустите./dev.sh. - Нет подложки карты — внешние тайлы, шрифты и спрайты требуют сети. Выбор района остаётся доступен при отказе WebGL.
Под капотом
| Интерфейс | Расчётный движок | Объяснение |
|---|---|---|
| React 19 + TypeScript | FastAPI + Python 3.13 | OpenAI-совместимый API |
| Vite · Tailwind CSS v4 · MapLibre GL JS 5 | Pydantic v2 · проверка правил · фиксированный датасет | OpenAI SDK · SSE · кеш · mock-режим |
frontend → /api/sim/evaluate → проверка правил → расчёт → Score и показатели
frontend → /api/sim/analyze → готовый результат → LLM / mock → объяснение SSE
backend/
app/sim.py Формулы, типы данных и валидатор
app/optimizer.py Точный подбор допустимого плана
app/analysis.py Проверенные факты и объяснение
app/routes/sim.py Конфигурация, расчёт и анализ
app/llm.py Единая обёртка вызовов модели
.env.example Шаблон настроек
tests/ Тесты без сети и API-ключа
uv.lock Зафиксированные Python-зависимости
frontend/
src/pages/ Экран симулятора
src/components/ Карта, каталог и панели
src/lib/api.ts Запросы к бэкенду
public/map/ Локальные геослои
pnpm-lock.yaml Зафиксированные frontend-зависимости
docs/ Задание, датасет и документация
deploy/ Скрипты выкатки и отката
dev.sh Локальный запуск backend + frontend
Продакшен работает на nginx + uvicorn под systemd. Docker и внешняя база данных для локального запуска не требуются.
API и эксплуатация · Данные карты · Правила разработки
Проверки
Из корня репозитория после установки зависимостей:
(cd backend && uv run pytest -q && uv run ruff check app tests && uv run ruff format --check app tests)
(cd frontend && node --test tests/*.test.mjs && pnpm lint && pnpm build)
Тесты проверяют формулу, бюджет, несовместимости, синергии, критический порог, API, потоковое объяснение и геоданные. Настоящий ключ и сеть для тестов не нужны. После изменений пройдите сценарий в браузере.
Что дальше
В планах — сохранение истории сценариев и агент с инструментами. Эти возможности пока не реализованы.
Продуктовые задачи · Архитектура
Какой будет ваша Астана? Откройте симулятор и примите пять решений →
HackAlem · кейс Astana Innovations. Карта: © OpenStreetMap contributors / ODbL; OpenFreeMap / OpenMapTiles. Источники геоданных.
Авторы коммитов
История коммитов
- 2026-09-23 17:56Merge pull request #25 from BAITC-Hacks/codex/readme-advisor-checkOryntai
- 2026-09-23 17:54Добавить сценарий проверки советника в READMEOryntai
- 2026-09-23 17:43Оформить README в стиле приложения и добавить видеоOryntai
- 2026-09-23 17:42Merge pull request #23 from BAITC-Hacks/codex/review-fixesOryntai
- 2026-09-23 17:41Merge pull request #22 from BAITC-Hacks/gdlllm/collapsed-measuresOryntai
- 2026-09-23 17:40Merge pull request #24 from BAITC-Hacks/codex/header-actionsOryntai
- 2026-09-23 17:40Сворачивать мероприятия и переходить к итоговому ScoreGdlllm
- 2026-09-23 17:39Убрать пустую строку над панелямиOryntai
Все коммиты (57)
- 2026-09-23 17:39Добавить сравнение планов и защитить интерфейс от обрыва ответаOryntai
- 2026-09-23 17:39Добавить точный оптимизатор и проверенный анализ сценариевOryntai
- 2026-09-23 17:35Merge pull request #21 from BAITC-Hacks/codex/remove-simulator-labelOryntai
- 2026-09-23 17:35Убрать подпись и выделить доступный сброс планаOryntai
- 2026-09-23 17:31Merge pull request #19 from BAITC-Hacks/codex/panel-spacingOryntai
- 2026-09-23 17:30Сбалансировать отступы вокруг панелейOryntai
- 2026-09-23 17:25Merge pull request #18 from BAITC-Hacks/codex/compact-simulator-headerOryntai
- 2026-09-23 17:24Убрать лишний текст над картойOryntai
- 2026-09-23 17:21Merge pull request #17 from BAITC-Hacks/gdlllm/scroll-panelsOryntai
- 2026-09-23 17:21Разделить карту и план на панели с независимой прокруткойGdlllm
- 2026-09-23 17:09Герб Астаны в шапке сайта (#16)Tokpoki
- 2026-09-23 17:07Герб Астаны в шапке сайтаTokpoki
- 2026-09-23 17:05Merge pull request #15 from BAITC-Hacks/codex/tour-help-buttonOryntai
- 2026-09-23 17:04Круглая кнопка запуска подсказокOryntai
- 2026-09-23 16:57Merge pull request #13 from BAITC-Hacks/gdlllm/map-popup-cornerOryntai
- 2026-09-23 16:54Merge pull request #14 from BAITC-Hacks/codex/tour-finish-scrollOryntai
- 2026-09-23 16:53Плавный возврат после завершения подсказокOryntai
- 2026-09-23 16:49Закрепить попап района в углу карты и уменьшить на 15%Gdlllm
- 2026-09-23 16:37Изменяемая ширина каталога и плавная карта (#12)Tokpoki
- 2026-09-23 16:36Изменяемая ширина каталога и плавное масштабирование картыTokpoki
- 2026-09-23 16:25Вернуть плавный скролл в подсказках (#11)Oryntai
- 2026-09-23 16:24Вернуть плавную прокрутку между подсказкамиOryntai
- 2026-09-23 16:20Стабильные подсказки и собственный выбор районов (#10)Oryntai
- 2026-09-23 16:20Добавить единый список выбора районовOryntai
- 2026-09-23 16:14Зафиксировать подсказки и убрать рывки между шагамиOryntai
- 2026-09-23 16:12Добавить попапы районов и подробную статистику (#9)Gdlllm
- 2026-09-23 16:10Карточки районов на карте и пояснения мероприятийGdlllm
- 2026-09-23 16:10Уточнить проверки diff и конфликтов перед отправкойGdlllm
- 2026-09-23 16:09Предпросмотр эффектов мероприятий с учётом лаговGdlllm
- 2026-09-23 16:04Добавить подсказки с ручным запуском (#8)Oryntai
- 2026-09-23 16:03Добавить плавные подсказки с ручным запускомOryntai
- 2026-09-23 15:56Убрать кнопку готового примера (#7)Tokpoki
- 2026-09-23 15:55Убрать кнопку готового примера из симулятораTokpoki
- 2026-09-23 15:50Улучшить карту и убрать лишние надписиOryntai
- 2026-09-23 15:49Увеличить карту и добавить независимые цветные слоиOryntai
- 2026-09-23 15:47Добавить анимацию оценки города и искры (#5)Tokpoki
- 2026-09-23 15:45Анимация оценки города с цветной шкалой и искрамиTokpoki
- 2026-09-23 15:29Объединить Алматы и Сарайшык и упростить оформлениеOryntai
- 2026-09-23 15:28Объединить Алматы и Сарайшык и упростить оформлениеOryntai
- 2026-09-23 15:14Добавлена карта Астаны с плавным выбором районаOryntai
- 2026-09-23 15:13Карта Астаны с точным выбором района и плавным интерфейсомOryntai
- 2026-09-23 14:49Добавлен рабочий MVP симулятора городаOryntai
- 2026-09-23 14:48Рабочий прототип симулятора города с расчётом и аналитикомOryntai
- 2026-09-23 14:15Каркас приложения: FastAPI + React, обёртка над OpenAI со стримингомOryntai
- 2026-09-23 14:15Деплой: релизы с health-check и автооткатом, секреты и логиOryntai
- 2026-09-23 14:15ТЗ кейса, данные в JSON и согласованная архитектураOryntai
- 2026-09-23 14:15README проекта и правила разработки командыOryntai
- 2026-09-23 13:53Merge pull request #1 from BAITC-Hacks/tokpoki/add-case-docsTokpoki
- 2026-09-23 13:50Добавить материалы кейсаTokpoki
Структура репозитория (103 файлов)
.gitignore AGENTS.md CLAUDE.md HackAlem AI_ «Аким на 5 часов» - AI-симулятор управления городом.docx README.md backend/.env.example backend/.python-version backend/app/__init__.py backend/app/analysis.py backend/app/config.py backend/app/data/dataset.json backend/app/llm.py backend/app/llm_check.py backend/app/logs.py backend/app/main.py backend/app/optimizer.py backend/app/rate_limit.py backend/app/routes/__init__.py backend/app/routes/chat.py backend/app/routes/sim.py backend/app/sim.py backend/app/sse.py backend/pyproject.toml backend/tests/conftest.py backend/tests/test_advisor.py backend/tests/test_llm.py backend/tests/test_map_data.py backend/tests/test_sim.py backend/tests/test_smoke.py backend/uv.lock deploy/config.sh deploy/deploy.sh deploy/hackalem.service deploy/llm-check.sh deploy/logs.sh deploy/nginx.conf deploy/rollback.sh deploy/server-bootstrap.sh deploy/set-secret.sh dev.sh docs/ARCHITECTURE.md docs/DEMO.md docs/MAP_DATA.md docs/REFERENCE.md docs/TASKS.md docs/TZ.md docs/assets/README.md docs/assets/cover.svg docs/assets/rules.svg docs/dataset.json frontend/.gitignore frontend/.oxlintrc.json frontend/index.html frontend/package.json frontend/pnpm-lock.yaml frontend/public/astana-emblem.LICENSE.txt frontend/public/astana-emblem.svg frontend/public/favicon.svg frontend/public/map/green_spaces.json frontend/public/map/healthcare.json frontend/public/map/road_safety_objects.json frontend/public/map/schools_kindergartens.json frontend/public/map/transport_stops.json frontend/src/App.tsx frontend/src/ErrorBoundary.tsx frontend/src/components/.gitkeep frontend/src/components/AstanaMap.tsx frontend/src/components/DistrictPopup.tsx frontend/src/components/DistrictSelect.tsx frontend/src/components/GuidedTour.tsx frontend/src/components/MeasureDetails.tsx frontend/src/components/PlanAdvisor.tsx frontend/src/components/ResizableColumns.tsx frontend/src/components/ScoreGauge.tsx frontend/src/data/basemap.json frontend/src/data/districts.json frontend/src/index.css frontend/src/lib/api.ts frontend/src/lib/districtSelectLayout.ts frontend/src/lib/districts.ts frontend/src/lib/mapViewport.ts frontend/src/lib/panelClasses.ts frontend/src/lib/panels.ts frontend/src/lib/score.ts frontend/src/lib/scrollPanel.ts frontend/src/lib/tour.ts frontend/src/main.tsx frontend/src/pages/ChatPage.tsx frontend/src/pages/SimulatorPage.tsx frontend/tests/districtSelectLayout.test.mjs frontend/tests/mapViewport.test.mjs frontend/tests/panels.test.mjs frontend/tests/score-preview.html frontend/tests/score-preview.tsx frontend/tests/score.test.mjs frontend/tests/scrollPanel.test.mjs frontend/tests/streamIntegrity.test.mjs frontend/tests/tour.test.mjs frontend/tsconfig.app.json frontend/tsconfig.json frontend/tsconfig.node.json frontend/vite.config.ts Датасет районов.docx
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.