О проекте
Команда: ILON
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Доведённый продукт
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Городской симулятор: 5 мер из 14 распределяются по районам в рамках бюджета 100, детерминированный движок считает Astana Quality of Life Score. Есть поиск одной замены, разложение Шепли и потоковый AI-совет экспертов.
Как устроено
FastAPI-движок (engine.py) загружает city.v2.json и считает лаги, охват, синергии и несовместимости, итог: Score = 0,7·среднее + 0,3·слабейший район − число критических показателей. Невалидный сценарий отклоняется до расчёта и до вызова AI. challenge.py ищет лучшую одиночную замену, shapley.py считает вклады мер. Совет (council) стримится в NDJSON: 6 запросов к OpenAI со structured output или демо-шаблоны. Frontend на Next.js проксирует API и показывает экраны Город → Решения → Результат с картой районов.
Ключевые решения
- детерминированный scoring
- значения Шепли
- локальный поиск одной замены
- NDJSON streaming
- OpenAI structured output
Метрики
Контрольный расчёт: база 52,55768 → 56,54307 (расход 95); challenge 57,20556
Особенности
Score считает только движок, AI лишь объясняет. Python-зависимости закреплены SHA-256-хешами, в CI есть e2e на Playwright поверх Compose, Шепли-вклады мер показаны в UI.
Стек
Python 3.12 · FastAPI · Next.js · TypeScript · Docker Compose · Playwright · GitHub Actions · ruff · pytest
Модели
gpt-4o-mini
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-91b593d7-ilon.
README
README.mdAQYL — «Аким на 5 часов»
Учебный AI-симулятор управления городом команды ILON. Пользователь распределяет общий бюджет 100 условных единиц, выбирает ровно пять инициатив и видит, как они меняют показатели пяти условных районов Астаны.
Для жюри (3–5 мин): docs/DEMO_SCRIPT.md — город → пример → Score → оспорить → слушать совет. Score считает движок; AI только объясняет.
Основной backend работает с синтетическим датасетом организаторов: 5 районов, 10 показателей, 14 мероприятий. Цена каждой меры фиксирована. Движок учитывает лаг, районный или городской охват, синергии и несовместимости. Сервер отклоняет превышение бюджета и другие недопустимые сценарии до расчёта и до вызова AI.
Astana Quality of Life Score вычисляет детерминированный движок:
Score = 0,7 × средняя оценка по населению + 0,3 × оценка слабейшего района − число показателей ниже 40
AI и демонстрационный совет получают готовый расчёт и объясняют компромиссы. Их текст не влияет на Score. Данные относятся к учебной модели и не являются прогнозом реальных городских последствий. В интерфейсе говорим о «состоянии модельного города» и «правилах синтетической модели».
| Контрольный расчёт | База | Пример пяти решений | Одна замена (challenge) |
|---|---|---|---|
| Расходы | 0 | 95 | 100 |
| Средняя оценка по населению | 56,8624 | 58,0776 | — |
| Оценка слабейшего района | 49,18 | 52,9625 | 54,9825 |
| Критических показателей | 2 | 0 | 0 |
| Score | 52,55768 | 56,54307 | 57,20556 |
Контрольный набор: M7/M8/M10 Нура, M12 город, M5 Сарыарка. Challenge находит замену M5 Сарыарка → M3 Нура (поиск ограничен одной заменой, не глобальный оптимум).
Что уже работает в интерфейсе
После запуска открывается игровой сценарий из трёх экранов:
- Город — базовый Score, критические пары, карта слева / выбранный район справа.
- Решения — компактные карточки мер (до 5 в ряд), выбор района, счётчик
N/5, бюджет,preview. - Результат — Score и KPI сразу на виду; карта и районы; объяснение; «Слушать совет»; вкладки challenge / Шепли / события / история.
Слот AI-совета (стрим council) на экране результата. Дополнительно: сравнение, Шепли, экспорт Markdown/PDF, учебные события и история — без изменения формулы Score.
Скрипт защиты: docs/DEMO_SCRIPT.md. Бонусы: docs/STAGE9_BONUS.md. UX: docs/UX_V2_NOTES.md. План: docs/ROADMAP.md.
Запуск приложения
Нужны Git и запущенный Docker с Compose v2. Из корня репозитория:
docker compose up --build
Для демонстрационного режима .env и API-ключ не нужны. После запуска:
- приложение: http://localhost:3000
- документация API: http://localhost:8000/docs
- проверки: http://localhost:3000/health и http://localhost:8000/health
На главной странице должны появиться статус подключения, режим AI, версия датасета, пять районов, четырнадцать мероприятий и экран «Город».
Фоновый запуск с ожиданием готовности:
docker compose up --build --detach --wait --wait-timeout 120
docker compose ps
docker compose down
Если порты заняты: WEB_PORT=3100 API_PORT=8100 docker compose up --build. Внутреннее соединение контейнеров остаётся на backend:8000.
Локальная разработка
Для backend нужен Python 3.12; для frontend — Node.js 24. Версии указаны в .python-version и .nvmrc. Backend, первый терминал:
cd backend
python3.12 -m venv .venv
.venv/bin/python -m pip install --require-hashes -r requirements-dev.txt
AI_PROVIDER=demo .venv/bin/python -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
Frontend, второй терминал:
cd frontend
npm ci
npm run dev
Next.js по умолчанию обращается к http://127.0.0.1:8000. Если адрес backend другой, укажите API_INTERNAL_URL в окружении команды npm run dev или в frontend/.env.local. Корневой .env используется backend и Compose, но не загружается Next.js автоматически.
Реальный AI-совет (OpenAI / ключ Codex Platform)
Сейчас по умолчанию AI_PROVIDER=demo — программные шаблоны, не модель. Для живого совета:
- Скопируйте
.env.example→.envв корне репозитория. - Вставьте ключ с platform.openai.com/api-keys в
OPENAI_API_KEY. - Укажите
AI_PROVIDER=openaiи модель с structured output, напримерOPENAI_MODEL=gpt-4o-mini(coding-only Codex-модели для совета не рекомендуются). - Перезапустите backend без префикса
AI_PROVIDER=demoизbackend/:
.venv/bin/python -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
- В UI на результате: «Слушать совет» → режим должен стать «AI-модель OpenAI». Один полный совет = 6 запросов к модели. Ключ остаётся только на сервере.
API и прокси frontend
Backend (подробности в backend/README.md):
| Метод | Путь | Назначение |
|---|---|---|
GET | /api/v1/catalog | Каталог, baseline, default_scenario |
POST | /api/v1/preview | 0…5 решений, бюджет и ошибки |
POST | /api/v1/simulate | Ровно 5 допустимых → полный Score |
POST | /api/v1/challenge | Одна лучшая допустимая замена |
POST | /api/v1/shapley | Вклады пяти мер (Шепли) |
POST | /api/v1/council/stream | NDJSON-совет экспертов |
Frontend ходит на backend только через same-origin proxies: /api/health, /api/catalog, /api/preview, /api/simulate, /api/challenge, /api/shapley, /api/council/stream. Произвольный upstream URL не допускается. Если backend недоступен, страница показывает ошибку и кнопку повтора; Score не подменяется моками.
Пример контрольного расчёта:
curl -s -X POST http://127.0.0.1:8000/api/v1/simulate \
-H 'Content-Type: application/json' \
--data-binary @backend/examples/scenario.json
Проверки
Из backend/: .venv/bin/python -m pytest -q, .venv/bin/ruff check app tests, .venv/bin/ruff format --check app tests.
Из frontend/: npm run lint, npm run typecheck, npm run build, npm run test:e2e (нужен поднятый стек или CI).
GitHub Actions запускает backend, frontend и интеграционные проверки (Compose + Playwright) при push в main.
Зависимости Python закреплены с SHA-256 хешами в backend/requirements.txt и backend/requirements-dev.txt; frontend использует package-lock.json и npm ci. Исходные данные и формула: docs/DATASET_AND_RULES.md. Прежняя модель сохранена в docs/BACKEND_PROTOTYPE.md.
Авторы коммитов
История коммитов
- 2026-09-23 17:51chore: drop phase/stage labels from UI copyYerbol Klimov
- 2026-09-23 17:45feat: split decisions map layout and add bottom calculate CTAYerbol Klimov
- 2026-09-23 17:37chore: harden defense demo path for jury reviewYerbol Klimov
- 2026-09-23 17:09feat: add stage 9 bonuses and UX/UI V2 presentation layerYerbol Klimov
- 2026-09-23 17:52docs: clarify UX screen navigation wordingAssima
- 2026-09-23 16:47feat: add challenge, AI council UI, and defense docsYerbol Klimov
- 2026-09-23 17:15docs: add stage nine bonus and UX V2 notesAssima
- 2026-09-23 16:12feat: add Phase 4 simulator UI with district mapYerbol Klimov
Все коммиты (17)
- 2026-09-23 16:10feat: add partial scenario preview APIYerbol Klimov
- 2026-09-23 15:38chore: ignore local Playwright browser cache directoryYerbol Klimov
- 2026-09-23 15:19feat: add phase 1 application infrastructureYerbol Klimov
- 2026-09-23 15:58docs: add final developer handoff for AQYL demoAssima
- 2026-09-23 15:30Implement official city dataset and scoring engineAssima
- 2026-09-23 14:21docs: preserve supplied dataset rules and verified score examplesYerbol Klimov
- 2026-09-23 14:08Merge simulator foundation into main and preserve team requirementsYerbol Klimov
- 2026-09-23 14:00chore: checkpoint simulator backend and project documentationYerbol Klimov
- 2026-09-23 13:57docs: describe AQYL city simulator concept and rulesAssima
Структура репозитория (102 файлов)
.dockerignore .editorconfig .env.example .github/workflows/ci.yml .gitignore .nvmrc .python-version README.md backend/Dockerfile backend/README.md backend/app/__init__.py backend/app/challenge.py backend/app/config.py backend/app/council.py backend/app/engine.py backend/app/main.py backend/app/preview.py backend/app/schemas.py backend/app/shapley.py backend/app/validation.py backend/data/city.v2.json backend/examples/scenario.json backend/pyproject.toml backend/requirements-dev.in backend/requirements-dev.txt backend/requirements.in backend/requirements.txt backend/tests/conftest.py backend/tests/test_api.py backend/tests/test_challenge.py backend/tests/test_council.py backend/tests/test_dataset.py backend/tests/test_preview.py backend/tests/test_shapley.py backend/tests/test_simulation.py compose.yaml docs/Akim_Project_Brief.docx docs/Astana_Dataset_and_Rules.docx docs/BACKEND_PROTOTYPE.md docs/CODEX_STAGE6_HANDOFF.md docs/DATASET_AND_RULES.md docs/DEMO_SCRIPT.md docs/DEVELOPER_HANDOFF.md docs/PROJECT_BRIEF.md docs/ROADMAP.md docs/STAGE8_PART_B_CHECKLIST.md docs/STAGE9_BONUS.md docs/UX_V2_NOTES.md frontend/.dockerignore frontend/Dockerfile frontend/e2e/challenge.spec.ts frontend/e2e/connection.spec.ts frontend/e2e/council.spec.ts frontend/e2e/map.spec.ts frontend/e2e/simulator.spec.ts frontend/e2e/stage9.spec.ts frontend/eslint.config.mjs frontend/next-env.d.ts frontend/next.config.ts frontend/package-lock.json frontend/package.json frontend/playwright.config.ts frontend/postcss.config.mjs frontend/public/icon.svg frontend/scripts/check-report.mts frontend/src/app/api/catalog/route.ts frontend/src/app/api/challenge/route.ts frontend/src/app/api/council/stream/route.ts frontend/src/app/api/health/route.ts frontend/src/app/api/preview/route.ts frontend/src/app/api/shapley/route.ts frontend/src/app/api/simulate/route.ts frontend/src/app/globals.css frontend/src/app/health/route.ts frontend/src/app/layout.tsx frontend/src/app/page.tsx frontend/src/components/AppChrome.tsx frontend/src/components/ChallengePanel.tsx frontend/src/components/CityScreen.tsx frontend/src/components/ComparePanel.tsx frontend/src/components/CouncilPanel.tsx frontend/src/components/DecisionsScreen.tsx frontend/src/components/DistrictMap.tsx frontend/src/components/DistrictMapDynamic.tsx frontend/src/components/EventsPanel.tsx frontend/src/components/ExplainPanel.tsx frontend/src/components/ExportBar.tsx frontend/src/components/HistoryPanel.tsx frontend/src/components/ResultScreen.tsx frontend/src/components/ShapleyPanel.tsx frontend/src/components/SimulatorProvider.tsx frontend/src/data/astana-districts.json frontend/src/data/district-profiles.json frontend/src/data/events.json frontend/src/lib/backend.ts frontend/src/lib/contracts.ts frontend/src/lib/history.ts frontend/src/lib/labels.ts frontend/src/lib/mapStyles.ts frontend/src/lib/reportMarkdown.ts frontend/src/lib/scenario.ts frontend/tsconfig.json
Ветки (2)
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.