AAYMA
RU
Войти

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

Симулятор городского бюджета на FastAPI.

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

О проекте

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

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

Суть

Симулятор городского бюджета на FastAPI. Проверяет правила, считает Astana QoL Score, предлагает замены мер, сравнивает план с полным перебором всех допустимых планов и объясняет результат через LLM с проверкой фактов.

Как устроено

Пакет engine/ на Python включает модель данных, валидатор с кодами ошибок и scoring (лаг, синергии, клиппинг, веса показателей и долей населения). Советник ищет до трёх лучших замен одной меры. Полным перебором построен индекс top_sets.json: 694 395 допустимых планов, топ-100, квантили и Парето-граница. По нему считается ранг сценария. Объяснение LLM проверяется по JSON-схеме, ссылкам на факты и допустимым числам, при ошибке включается шаблонный разбор. Фронт на чистом JS раздаётся тем же FastAPI.

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

  • полный перебор 694 395 планов
  • Парето-граница стоимость/Score
  • квантильный перцентиль
  • замена одной меры
  • валидация LLM-ответа по JSON-схеме и числам
  • шаблонный fallback
  • humanizer-стиль промптов

Метрики

Полный перебор: 694 395 допустимых планов, глобальный максимум Score 57.236735 при стоимости 98; лучшая замена в эталоне даёт 57.20556; ~60 тестов

Особенности

Команда полностью перебрала пространство решений и показывает, где план пользователя стоит относительно глобального оптимума и Парето-границы. Объяснения LLM проверяются на соответствие фактам и числам. В коммитах видно, как два агента-«консилиума» (Claude/Codex) выбирали трек. Патруль Go2 и дрон сделаны как визуальная демонстрация.

Стек

Python · FastAPI · Uvicorn · Pydantic · openai · HTTPX · pytest · HTML/CSS/JavaScript

Модели

gpt-4o-mini · OpenAI-совместимый API · Nano Banana Pro (генерация иллюстраций)


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

README

README.md

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

AI-симулятор управления городом команды AVM AI. HackAlem AI, задача 12, спец-трек Astana Innovations. Условия задачи и данные организатора: техническое задание и дополнительный документ с датасетом, каталогом мер и формулой.

Пользователь получает бюджет 100 условных единиц и выбирает ровно пять мер из 14 по пяти направлениям: транспорт, экология, социальная инфраструктура, безопасность и городской сервис. Приложение проверяет ограничения, рассчитывает Astana Quality of Life Score, показывает изменения в пяти районах и предлагает улучшения заменой одной меры.

Модель учебная: показатели и эффекты заданы организатором, а не описывают реальный городской бюджет. Решение предназначено для обучения и обсуждения компромиссов городского управления.

Требования и зависимости

  • Python 3.11+ с pip и модулем venv; Windows, Linux или macOS.
  • Современный браузер с JavaScript. Фронтенд — HTML, CSS и JavaScript без сборки; Node.js, Docker и база данных не требуются.
  • Интернет нужен для установки зависимостей и внешней AI-модели. Основной сценарий работает без ключей и аккаунтов.

Прямые зависимости зафиксированы в requirements.txt: FastAPI 0.115.0, Uvicorn 0.30.6, Pydantic 2.9.2, openai 1.51.0, python-dotenv 1.0.1, HTTPX 0.27.2, pytest 8.3.3. Полного lock-файла транзитивных зависимостей пока нет.

Установка и запуск

Получите репозиторий и выполняйте команды из его корня, где находится app.py.

Windows PowerShell:

python -m venv .venv
.\.venv\Scripts\python.exe -m pip install --no-cache-dir -r requirements.txt
.\.venv\Scripts\python.exe -m uvicorn app:app --host 127.0.0.1 --port 8000

Linux / macOS:

python3 -m venv .venv
./.venv/bin/python -m pip install --no-cache-dir -r requirements.txt
./.venv/bin/python -m uvicorn app:app --host 127.0.0.1 --port 8000

В Debian/Ubuntu при отсутствии venv установите системный пакет python3-venv. Активация виртуального окружения не требуется. После установки для каждого запуска достаточно последней команды.

Открывайте страницу через сервер, а не как файл static/index.html. Для режима без ключа не создавайте .env и не задавайте OPENAI_API_KEY в окружении. При наличии ключа приложение использует внешнюю модель.

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

Настройки перечислены в .env.example. Для живого AI скопируйте его в .env (Copy-Item .env.example .env в PowerShell, cp .env.example .env в Bash), заполните поля и перезапустите сервер. Переменные процесса имеют приоритет над .env; файл с ключами исключён из Git.

ПеременнаяНазначение
OPENAI_API_KEYКлюч модели; без ключа объяснение строится из фактов расчёта
OPENAI_MODELИмя модели, по умолчанию gpt-4o-mini
LLM_TIMEOUT_SТаймаут запроса, по умолчанию 15 секунд
OPENAI_BASE_URLНеобязательный адрес OpenAI-совместимого API; модель и ключ должны соответствовать провайдеру

Ключ можно ввести в настройках интерфейса. Он хранится в sessionStorage вкладки, передаётся серверу заголовком X-OpenAI-Key и имеет приоритет над серверным. Приложение не записывает его в файлы или журналы. «Убрать ключ» очищает значение в браузере. Совместимость всех функций с каждым сторонним провайдером отдельно не проверена.

Проверка основного сценария

  1. Откройте приложение и нажмите «Сброс», если восстановился прежний план. Бюджет — 100, Score без решений — 52.55768, критических показателей в Нуре — 2.
  2. Нажмите «Готовый сценарий». Набор: M7 Нура, M8 Нура, M10 Нура, M12 город, M5 Сарыарка. Стоимость — 95, Score — 56.54307, критических показателей — 0. Интерфейс округляет Score до двух знаков.
  3. Нажмите «Объяснить». Без ключа появится «автоматический разбор» с сильными сторонами, рисками и рекомендациями. С ключом приложение попробует получить ответ модели.
  4. Нажмите «Найти улучшение». Лучший вариант меняет M5 Сарыарка на M3 Нура: стоимость 100, Score 57.20556. «Применить» пересчитывает план.
  5. Удалите одну меру: результат неполного набора не рассчитывается. Каталог запрещает шестую меру, превышение бюджета, повторы и несовместимые решения.
  6. В Swagger вызовите POST /api/evaluate с набором M3 Есиль, M13 Алматы, M5 Сарыарка, M7 Нура, M12 город. Стоимость 121: ожидаются valid: false, score: null и ошибка budget_exceeded.

План сохраняется в localStorage браузера. Для одинаковых стартовых условий используйте «Сброс».

Пример расчёта через PowerShell:

$city = Invoke-RestMethod http://127.0.0.1:8000/api/city
$body = @{ decisions = $city.example_scenario } | ConvertTo-Json -Depth 10
Invoke-RestMethod http://127.0.0.1:8000/api/evaluate -Method Post -ContentType 'application/json' -Body $body

Пример через Bash:

curl -sS http://127.0.0.1:8000/api/evaluate \
  -H 'Content-Type: application/json' \
  -d '{"decisions":[{"measure_id":"M7","district_id":"nura"},{"measure_id":"M8","district_id":"nura"},{"measure_id":"M10","district_id":"nura"},{"measure_id":"M12"},{"measure_id":"M5","district_id":"saryarka"}]}'

Тесты и терминальное демо

Windows PowerShell:

.\.venv\Scripts\python.exe -m pytest -q
.\.venv\Scripts\python.exe -m pip check
.\.venv\Scripts\python.exe scripts/demo.py

Linux / macOS:

./.venv/bin/python -m pytest -q
./.venv/bin/python -m pip check
./.venv/bin/python scripts/demo.py

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

Архитектура и структура

Браузер: static/ → HTTP JSON → FastAPI: app.py → engine/ → data/
                                                   ↓
                                       внешняя AI-модель (опционально)
Файл или каталогНазначение
app.pyAPI и раздача интерфейса с одного сервера
engine/model.py, validator.py, scoring.pyЗагрузка данных, проверка решений, расчёт Score
engine/advisor.py, ranking.pyЗамены одной меры, полный перебор и ранжирование
engine/facts.py, templates.py, explain.pyФакты, локальное объяснение и работа с моделью
data/city.jsonДанные, меры и правила из ТЗ
data/top_sets.jsonГотовый индекс: топ-100, квантили и Парето-граница
static/Интерфейс, изображения и демонстрация патруля
scripts/demo.py, scripts/rank_all.pyТерминальное демо и пересчёт индекса
tests/Тесты расчёта, правил, советника, API и демо

API: GET /api/health, /api/city, /api/top; POST /api/evaluate, /api/improvements, /api/explain, /api/chat, /api/ai/check. Схемы доступны в /docs.

Правила расчёта и роль AI

По подробным правилам ТЗ принимаются ровно пять разных мер, не более двух из одного направления. Для районной меры обязателен район; городская действует на все районы. Бюджет, направления и несовместимости проверяет сервер. Невалидный набор не получает Score.

Эффект каждой меры умножается на (8 − лаг) / 8; синергии добавляются без масштабирования. Значения показателей ограничиваются диапазоном 0–100. Далее:

D_района = сумма(вес показателя × новое значение)
D_города = сумма(доля населения × D_района)
Score = 0.7 × D_города + 0.3 × min(D_района) − N_crit

N_crit — число пар «район × показатель» со значением строго ниже 40. Все числа считает Python; модель получает готовые факты. Ответ объяснения проверяется по JSON-схеме, ссылкам на факты и допустимым числам. При ошибке или отсутствии ключа используется локальное объяснение template. Проверка не гарантирует смысловую точность каждой фразы. Свободный чат доступен только с ключом.

Поиск улучшений возвращает до трёх лучших замен одной меры. Полный перебор содержит 694 395 допустимых планов, лучший Score — 57.236735 за 98 единиц. Процентиль приближённый, по квантилям; Парето-граница показывает лучшие соотношения стоимости и Score. Индекс включён в проект, пересчитывать его для запуска не нужно.

Демонстрационные возможности и ограничения

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

Рейтинг сравнивает математические сценарии, а не зарегистрированные команды.

Сторонние материалы

  • FastAPI — MIT; Uvicorn — BSD-3-Clause; Pydantic — MIT; openai-python — Apache-2.0; python-dotenv и HTTPX — BSD-3-Clause; pytest — MIT. Зависимости устанавливаются штатными пакетами с их лицензиями.
  • Данные, каталог мероприятий, формула, синергии и ограничения предоставлены организатором в условиях трека 12 (ссылки в начале README) и перенесены в data/city.json без изменений.
  • Карта, иконки и изображения персонажей и камеры сгенерированы командой с помощью Nano Banana Pro. Это иллюстрации, а не съёмка реального патруля.
  • Код разработан с использованием Claude Code и Codex. Правила стиля AI-ответов адаптированы из skill humanizer, основанного на материале Wikipedia «Signs of AI writing»; источник отмечен в engine/explain.py. Co-Authored-By сохранён в истории коммитов.

Команда

  • Алибек Абдеков — капитан, фронтенд.
  • Канат Тәжіхан — бэкенд.
Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
69
Авторы
2
Файлы
61
Ветки
1

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

Alibek Abdekov 40Kanat06 29

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

  • 2026-09-23 17:54ui: убрана пометка о демонстрации в окне камерыAlibek Abdekov
  • 2026-09-23 17:53fix: кадр камеры соответствует событию ленты у собаки и дронаAlibek Abdekov
  • 2026-09-23 17:52ui: кнопка «Правила» вместо «Как считается»Alibek Abdekov
  • 2026-09-23 17:51docs: README упоминает дрон в блоке демонстрации патруляAlibek Abdekov
  • 2026-09-23 17:51feat: сменяемые кадры камер робота-собаки и дрона, по пять на каждого (демо-данные)Alibek Abdekov
  • 2026-09-23 17:49feat: отдельный кадр камеры дрона с высоты (демо-данные)Alibek Abdekov
  • 2026-09-23 17:48feat: дрон в блоке патруля — экология, транспорт, ЖКХ (демо-данные)Alibek Abdekov
  • 2026-09-23 17:42chore: убраны правила для агентов (AGENTS.md, CLAUDE.md) и упорядочен .gitignoreAlibek Abdekov
Все коммиты (69)
  • 2026-09-23 17:42docs: тексты правил игры простым языкомAlibek Abdekov
  • 2026-09-23 17:41docs: README без фразы о непригодности для реальных бюджетных решенийAlibek Abdekov
  • 2026-09-23 17:38Merge branch 'main' of https://github.com/BAITC-Hacks/hack-e4dfa205-avm-aiAlibek Abdekov
  • 2026-09-23 17:38chore: чистка репозитория перед сдачей — убраны рабочие документы процесса и планы агентов, README сокращён под эксперта, .env.example только OpenAI, AGENTS.md без инструментов команды; правки интерфеAlibek Abdekov
  • 2026-09-23 17:38docs: дорожная карта после хакатона — ИИ-агенты города (дрон, транспорт, ЖКХ) как источник данных симулятораKanat06
  • 2026-09-23 17:36fix: восстановлены изменения Каната, потерянные при слиянии 78a7ae9: валидатор и регрессионные тестыAlibek Abdekov
  • 2026-09-23 17:33Merge origin/main: правки Каната по ревью и финальные проверки; в чате оставлен явный ответ об ошибке моделиAlibek Abdekov
  • 2026-09-23 17:32fix: по ревью 23.09: лимит тела по чанкам, пустой ответ модели в чате, советник не смешивает сценарии и не открывается сам, ошибка поиска замен с повтором, прокрутка окон, шапка на телефоне, патруль пAlibek Abdekov
  • 2026-09-23 17:32docs: финальный статус — чистый запуск 17:32 (65 тестов), итог в документе анализаKanat06
  • 2026-09-23 17:31fix: по ревью Codex — пустой ответ модели даёт mode=error, ядро терпит нестроковый measure_id; отчёт Codex и статус находокKanat06
  • 2026-09-23 17:29docs: README — финальный чистый запуск 17:29, 63 тестаKanat06
  • 2026-09-23 17:28fix: closeModal сбрасывает advisorOpen — после Escape советник снова открывается; прогон интерфейса проходит полностьюKanat06
  • 2026-09-23 17:26docs: результаты прогона интерфейса — Escape ломает открытие советника (advisorOpen), косметика шапкиKanat06
  • 2026-09-23 17:24feat: новые таблички районов с единицами и шкалой, «до решений» вместо «база», без слова «лаг»; патруль Go2, аватар советника, README и ревьюAlibek Abdekov
  • 2026-09-23 17:21feat: единицы «балла/баллов» у всех чисел оценки в интерфейсе, автоматическом разборе и промпте; правки стилей и разметкиAlibek Abdekov
  • 2026-09-23 17:16Merge origin/main: Парето в индексе и README после слиянияAlibek Abdekov
  • 2026-09-23 17:16fix: советник без меток фактов, нейтральная фраза про автоматический разбор, чат шлёт только role и contentAlibek Abdekov
  • 2026-09-23 17:15docs: README после слияния (демо в терминале, OPENAI_BASE_URL, установка по ОС); тест demo не зависит от локального .envAlibek Abdekov
  • 2026-09-23 17:14Merge remote-tracking branch 'origin/main'Kanat06
  • 2026-09-23 17:14feat: Парето «стоимость → лучший Score» из полного перебора в индексе, /api/top и демоKanat06
  • 2026-09-23 17:12Merge branch 'main' of https://github.com/BAITC-Hacks/hack-e4dfa205-avm-aiAlibek Abdekov
  • 2026-09-23 17:12feat: живой стиль советника по правилам humanizer в промптах и банке ответов, доработки интерфейса, район без кодов показателейAlibek Abdekov
  • 2026-09-23 17:10feat: демонстрация сценария в терминале (scripts/demo.py), OPENAI_BASE_URL для OpenAI-совместимых провайдеров, тестыKanat06
  • 2026-09-23 17:01docs: концепт патруля Go2 — задача для фронтенда и раздел README о пути к реальным даннымKanat06
  • 2026-09-23 16:56docs: README, чистый запуск зафиксированной версии проверен на Windows 11Alibek Abdekov
  • 2026-09-23 16:53docs: README — 60 тестов и чистый запуск с фронтендом; статус фронтенда в планеKanat06
  • 2026-09-23 16:50feat: интерфейс подключён к API (расчёт, объяснение, замены, ключ, диалог), раздача static, строгая сверка чисел AI, защита от M²Alibek Abdekov
  • 2026-09-23 16:38docs: оценка опыта патрульного робота для раздела безопасности, два варианта без изменения формулыKanat06
  • 2026-09-23 16:35docs: бриф для Codex — контекст, правила, зоны, ожидаемые предложенияKanat06
  • 2026-09-23 16:33docs: анализ, концепция и план до сдачи; README: ссылка на SwaggerKanat06
  • 2026-09-23 16:29feat: ключ OpenAI из интерфейса, топ планов, ранг сценария, диалог с советникомKanat06
  • 2026-09-23 16:24feat: полный перебор планов, глобальный топ, банк ответов советникаKanat06
  • 2026-09-23 16:22docs: план, часть 2 по ревью Codex: совместимость explain с app, кэш по ключу и провайдеру, схема чата, текстыAlibek Abdekov
  • 2026-09-23 16:16fix: ядро устойчиво к null и не-объектам, повторы считаются до фильтрацииKanat06
  • 2026-09-23 16:11docs: часть 2 плана бэкенда с понятными текстами советника, склонениями и ключом из интерфейсаAlibek Abdekov
  • 2026-09-23 15:58Merge remote-tracking branch 'origin/main'Kanat06
  • 2026-09-23 15:58docs: README, соответствие ТЗ, лицензии, проверка чистого запускаKanat06
  • 2026-09-23 15:58docs: часть 2 плана бэкенда (перебор, банк ответов, ключ из интерфейса, диалог) и ревью задач 1–6Alibek Abdekov
  • 2026-09-23 15:53feat: FastAPI сервер, маршруты city, evaluate, improvements, explain, лимит телаKanat06
  • 2026-09-23 15:50feat: AI-объяснение с проверкой схемы, ссылок и чисел, резервKanat06
  • 2026-09-23 15:43feat: факты для объяснения и резервное объяснение по правиламKanat06
  • 2026-09-23 15:38feat: советник, улучшение заменой одной мерыKanat06
  • 2026-09-23 15:36feat: формула Score, вклад мер, разложение прироста, контрольные тестыKanat06
  • 2026-09-23 15:30feat: валидатор набора решений с кодами ошибок и индексамиKanat06
  • 2026-09-23 15:26feat: модель данных с проверкой датасета, gitignore для кэша PythonKanat06
  • 2026-09-23 15:24feat: данные задачи и зависимостиKanat06
  • 2026-09-23 15:11docs: план реализации бэкенда для агента, спека и ревью Codex, gitignore для venvAlibek Abdekov
  • 2026-09-23 14:46docs: согласованная спецификация симулятора, основа README, структура папок, переменные окруженияAlibek Abdekov
  • 2026-09-23 14:41chore: убрать из отслеживания лишние материалы и устаревшие черновикиAlibek Abdekov
  • 2026-09-23 14:29Merge remote-tracking branch 'origin/main'Alibek Abdekov
  • 2026-09-23 14:29docs: выбрана задача 12 «Аким на 5 часов», условия ТЗ в AGENTS.md и docs/tracksAlibek Abdekov
  • 2026-09-23 14:28docs: выбран трек «Аким на 5 часов» (Astana Innovations) — разбор задачи и черновая архитектураKanat06
  • 2026-09-23 13:48Merge branch 'main' of https://github.com/BAITC-Hacks/hack-e4dfa205-avm-aiAlibek Abdekov
  • 2026-09-23 13:48chore: консилиум убран из репозитория, правило коммитов в AGENTS.mdAlibek Abdekov
  • 2026-09-23 13:46docs: разбор трека — прогноз выработки ВЭС (Самрук-Казына)Kanat06
  • 2026-09-23 13:39docs: консилиум — слепой первый раунд, позиция Клода вынесена в отдельный файлAlibek Abdekov
  • 2026-09-23 13:39docs: консилиум — сводная таблица по 12 задачам, анкеты ТЗ, позиция КлодаAlibek Abdekov
  • 2026-09-23 13:36docs: общий файл консилиума двух агентов для выбора задачиAlibek Abdekov
  • 2026-09-23 13:31docs: все 12 задач хакатона с полными ТЗ и ссылками на материалыAlibek Abdekov
  • 2026-09-23 13:25setup: правила хакатона для агентов, .env.example, .gitignoreAlibek Abdekov
  • 2026-09-23 12:09check: проверка доступа на запись перед хакатономAlibek Abdekov
Структура репозитория (61 файлов)
.env.example
.gitignore
README.md
app.py
data/city.json
data/top_sets.json
docs/roadmap-ai-city-agents.md
engine/__init__.py
engine/advisor.py
engine/explain.py
engine/facts.py
engine/model.py
engine/ranking.py
engine/scoring.py
engine/templates.py
engine/validator.py
pytest.ini
requirements.txt
scripts/demo.py
scripts/rank_all.py
static/app.css
static/app.js
static/img/advisor.png
static/img/ic-brigade.png
static/img/ic-buslane.png
static/img/ic-cleanfuel.png
static/img/ic-clinic.png
static/img/ic-crossing.png
static/img/ic-digital.png
static/img/ic-greening.png
static/img/ic-lights.png
static/img/ic-lrt.png
static/img/ic-park.png
static/img/ic-pipes.png
static/img/ic-safecity.png
static/img/ic-school.png
static/img/ic-sport.png
static/img/map.jpg
static/img/patrol-cam.jpg
static/img/patrol-dog.png
static/img/patrol-dog2.jpg
static/img/patrol-dog3.jpg
static/img/patrol-dog4.jpg
static/img/patrol-dog5.jpg
static/img/patrol-drone.png
static/img/patrol-drone2.jpg
static/img/patrol-drone3.jpg
static/img/patrol-drone4.jpg
static/img/patrol-drone5.jpg
static/img/patrol-dronecam.jpg
static/index.html
static/patrol.js
tests/test_advisor.py
tests/test_api.py
tests/test_bank.py
tests/test_codex_regressions.py
tests/test_demo.py
tests/test_explain.py
tests/test_facts.py
tests/test_scoring.py
tests/test_validator.py

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

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

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