О проекте
Команда: MindForge
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Рабочий MVP
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Симулятор управления условной Астаной: игрок с бюджетом 100 выбирает ровно 5 из 14 инициатив по 5 направлениям для 5 районов. Детерминированный движок на Go считает Astana Quality of Life Score, а LLM объясняет уже рассчитанный результат.
Как устроено
Go HTTP API проверяет сценарий: бюджет, ровно 5 решений, не больше 2 в одном направлении, несовместимые пары. Движок применяет эффекты с поправкой на лаг (effect × (8−L)/8) и фиксированные синергии, ограничивает 10 метрик районов диапазоном 0–100 и считает взвешенный District Score. Итог: Score = 0.7×D_avg (по доле населения) + 0.3×min(D_d) − N_crit (метрики ниже 40). Необязательный слой OpenAI поверх результата объясняет сильные решения и риски, но на числа не влияет.
Ключевые решения
- детерминированный simulation engine
- лаг эффекта (8−L)/8
- синергии инициатив
- взвешенный score со штрафом за худший район
- валидация правил совместимости
Метрики
Baseline Score ≈ 52.56 → ≈ 56.5 на эталонном сценарии (подтверждено go test)
Особенности
Жёсткое разделение: числа считает только детерминированная формула, LLM их не пересчитывает и эффектов не придумывает. В формулу Score входит минимум по районам, поэтому выиграть за счёт одного сильного центра нельзя. Эталонный сценарий закреплён в Go-тесте.
Стек
Go · статический HTML/JS · Python (ai-заготовка)
Модели
OpenAI
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-052c857d-mindforge.
README
README.mdАким на 5 часов
By Assem Kumarbekova and Alisher Bugubay.
AI-симулятор управления городом.
Представьте: у вас есть 100 условных единиц бюджета, ровно 5 решений, 5 районов города и десятки дискуссионных приоритетов. Где лучше вложить деньги, чтобы не развалить одну часть города ради другой? Этот проект помогает ответить на этот вопрос в формализованной, быстро повторяемой модели условной Астаны.
Пользователь выбирает инициативы, сервер валидирует сценарий, а детерминированный engine рассчитывает эффект на инфраструктуру, экологию, городские сервисы, безопасность и социальную сферу. Результат превращается в Astana Quality of Life Score — число, которое отражает итоговое состояние города, а не просто сумму красивых обещаний.
AI в проекте не заменяет математику. Он — слой анализа поверх уже рассчитанного сценария: объясняет, куда уходит бюджет, какие решения сильные, где скрытые риски и что можно улучшить дальше.
Проблема
Городскому управленцу приходится распределять ограниченный бюджет между несколькими направлениями развития:
- транспорт;
- экология и озеленение;
- социальная инфраструктура;
- безопасность;
- городские сервисы.
Инвестиции в одно направление сокращают возможности для другого. Синтетическая модель города позволяет экспериментировать с разными стратегиями и видеть последствия решений без необходимости опираться на реальные данные о текущем состоянии столицы.
Продукт не претендует на прогноз реального развития Астаны. Он моделирует условную городскую систему с заданными эффектами и синергиями.
Решение
Основной цикл выглядит так:
Исходное состояние города → бюджет 100 → выбор 5 инициатив → валидация → симуляция → применение эффектов и синергий → пересчёт проблемных показателей → Astana Quality of Life Score → результат и объяснение.
Именно здесь проект раскрывает свою идею: он не обещает “магический” прогноз, а показывает, как небольшая серия управленческих решений меняет состояние города в заданной синтетической модели. Это и есть основной продукт — безопасный, быстрый, прозрачный способ тестировать стратегии без риска для реальной городской политики.
Ключевое правило проекта: численные показатели рассчитываются детерминированным алгоритмом. LLM не считается источником баллов, не придумывает эффекты инициатив и не пересчитывает Score.
Как работает симулятор
- Пользователь видит исходное состояние пяти районов города.
- Пользователь получает бюджет в 100 условных единиц.
- Пользователь выбирает ровно 5 мероприятий.
- Для районных мер выбирается район, в котором эффект применяется.
- Система валидирует сценарий по правилам бюджета и совместимости.
- Если стратегия невалидна, возвращается понятная причина ошибки.
- Если стратегия валидна, запускается simulation engine.
- Эффекты корректируются с учётом лага мероприятия.
- При наличии соответствующих пар инициатив активируются синергии.
- Показатели районов пересчитываются и ограничиваются диапазоном 0–100.
- Считается районный Score по весам показателей.
- Считается Astana Quality of Life Score по итоговой формуле.
- Результат отображается в интерфейсе с изменением относительно базового состояния.
- Если AI доступен, LLM объясняет уже рассчитанный результат, но не влияет на числа.
Модель города
В текущей реализации используются пять районов условной Астаны:
| ID | Район | Доля населения | Краткое описание |
|---|---|---|---|
| esil | Есиль | 0.27 | Пробки на мостах и перегруженные школы |
| almaty | Алматы | 0.24 | Старый ЖКХ и пробки |
| saryarka | Сарыарка | 0.20 | Смог от частного сектора, слабое озеленение |
| baikonyur | Байконур | 0.13 | Сбалансированный район без выраженных перекосов |
| nura | Нура | 0.16 | Главный аутсайдер по социальной инфраструктуре и транспорту |
Показатели города представлены в форме десяти метрик на шкале 0–100. Чем выше значение, тем лучше состояние показателя.
Транспорт
- T1 — разгрузка дорог
- T2 — доступность общественного транспорта
Экология
- E1 — озеленение
- E2 — качество воздуха
Социальная инфраструктура
- S1 — школы и детские сады
- S2 — поликлиники и первичная медицинская помощь
Безопасность
- B1 — безопасность улиц
- B2 — безопасность дорожного движения
Городские сервисы
- C1 — надежность ЖКХ
- C2 — скорость решения обращений жителей
Данные о районах и инициативах находятся в backend/internal/simulation/data/city.json.
Управленческие инициативы
В проекте реализованы 14 инициатив. Ниже — фактическая таблица из embedded dataset.
| ID | Направление | Мероприятие | Тип | Стоимость | Лаг |
|---|---|---|---|---|---|
| M1 | transport | Выделенные полосы для автобусов | district | 18 | 2 |
| M2 | transport | Умные светофоры | city | 22 | 2 |
| M3 | transport | Линия ЛРТ / расширение | district | 30 | 4 |
| M4 | green | Парк / сквер | district | 15 | 2 |
| M5 | green | Чистое топливо для частного сектора | district | 25 | 3 |
| M6 | green | Озеленение и ветрозащитные полосы | city | 20 | 4 |
| M7 | social | Школа + детсад | district | 24 | 3 |
| M8 | social | Центр семейного здоровья / поликлиника | district | 20 | 3 |
| M9 | social | Дворовые спорт-хабы | district | 10 | 1 |
| M10 | safety | Освещение и камеры Safe City | district | 12 | 1 |
| M11 | safety | Безопасные переходы и школьные зоны | district | 10 | 1 |
| M12 | services | Цифровая платформа обращений | city | 14 | 1 |
| M13 | services | Модернизация тепло- и водосетей | district | 28 | 4 |
| M14 | services | Аварийные бригады ЖКХ | city | 16 | 1 |
Пояснение к типу:
- district — эффект применяется к конкретному району выбранной инициативы;
- city — эффект применяется ко всем районам.
Правила
Фактически реализованные правила в backend/internal/simulation/simulation.go:
- бюджет равен 100;
- пользователь выбирает ровно 5 решений;
- повтор одной и той же инициативы запрещён;
- для районной инициативы район обязателен;
- для городской инициативы район не указывается;
- не более 2 мероприятий в одном направлении;
- M1 и M3 несовместимы;
- M4 и M7 нельзя размещать в одном районе;
- M5 и M13 нельзя размещать в одном районе;
- порядок выбора решений не влияет на итоговый расчёт, поскольку эффекты суммируются и синергии проверяются по набору выбранных инициатив.
Если стратегия невалидна, сервер возвращает читаемое сообщение и не считает Score.
Математическая модель
Горизонт симуляции
Горизонт симуляции равен 8 кварталам.
Лаг и реализация эффекта
Для мероприятия с лагом L фактическая доля эффекта рассчитывается так:
realized_fraction = (8 - L) / 8
Реализованный эффект от инициативы:
realized_effect = full_effect × (8 - L) / 8
Пример из реализации: для M7 эффект по S1 равен +16, а лаг равен 3.
16 × (8 - 3) / 8 = 16 × 5/8 = 10
То есть при выборе M7 к соответствующему показателю добавляется +10 в текущем расчёте, а не полный +16.
Синергии
В проекте реализованы три фиксированные синергии:
| Синергия | Эффект | Где активируется |
|---|---|---|
| M1 + M2 | T1 +2 | в районе M1 |
| M10 + M12 | B1 +2 | в районе M10 |
| M5 + M6 | E2 +2 | в районе M5 |
Эти бонусы фиксированные, применяются после индивидуальных эффектов и не масштабируются лагом.
District Score
Для района рассчитывается агрегированный Score по формуле:
D_d = Σ w_k × I_dk
Где веса текущей реализации такие:
- T1 = 0.10
- T2 = 0.10
- E1 = 0.09
- E2 = 0.11
- S1 = 0.11
- S2 = 0.11
- B1 = 0.09
- B2 = 0.09
- C1 = 0.10
- C2 = 0.10
То есть формула в коде выглядит так:
districtScore(m Metrics) float64 {
return .10*m.T1 + .10*m.T2 + .09*m.E1 + .11*m.E2 + .11*m.S1 + .11*m.S2 + .09*m.B1 + .09*m.B2 + .10*m.C1 + .10*m.C2
}
Это означает, что итоговый районный балл — это взвешенная сумма показателей. Слабые показатели в одном районе влияют на итог сильнее, поскольку в формуле общего Score учтён минимум по районам.
Astana Quality of Life Score
Итоговый городской балл рассчитывается следующим образом:
Score = 0.7 × D_avg + 0.3 × min(D_d) − N_crit
Где:
D_avg— средневзвешенный районный Score с учётом доли населения;min(D_d)— наихудший район по Score;N_crit— число паррайон × показатель, где итоговое значение строго меньше 40.
В текущей реализации это соответствует коду:
func cityScore(ms []Metrics) (float64, int) {
avg, minScore := 0.0, math.Inf(1)
crit := 0
for i, m := range ms {
s := districtScore(m)
avg += Districts[i].Population * s
if s < minScore {
minScore = s
}
crit += critical(m)
}
return .7*avg + .3*minScore - float64(crit), crit
}
Смысл формулы:
- 70% веса у среднего по городу состояния;
- 30% веса у самого слабого района;
- штраф за критические показатели ниже 40;
- формула не позволяет «победить» только за счёт сильного центра, если в другом районе показатели падают слишком низко.
Проверка корректности
В текущем репозитории есть проверка эталонного сценария в backend/internal/simulation/simulation_test.go.
Фактически проверяются следующие значения:
- Baseline Score ≈ 52.56;
- сценарий:
M7→nuraM8→nuraM10→nuraM12→ cityM5→saryarka
- стоимость сценария = 95;
- активируется синергия
M10 + M12; - итоговый Score ≈ 56.5.
Эти цифры подтверждены тестом в коде. Для фактической проверки проекта используется команда:
cd backend
go test ./...
На текущем состоянии репозитория эта команда проходит успешно.
Архитектура
Frontend (static HTML/JS)
|
v
Go HTTP API
|
v
Validator
|
v
Simulation Engine
|
+--> Budget checks
+--> Effects / Lag
+--> Synergies
+--> District Score
+--> Astana Quality of Life Score
|
v
Simulation Result
|
+--> API response to UI
|
+--> Optional AI layer
|
v
OpenAI API
|
v
AI Analysis
В текущем коде AI является необязательным слой поверх уже рассчитанного результата. Если OPENAI_API_KEY отсутствует, сервер логирует предупреждение и продолжает работать в обычном режиме.
Структура проекта
.
├── .env # локальный .env, не коммитится
├── .gitignore # игнорирует .env
├── README.md # документация проекта
├── ai/
│ └── pyproject.toml # минимальная конфигурация Python-модуля
├── backend/
│ ├── cmd/
│ │ └── api/
│ │ ├── env.go # загрузка переменных окружения
│ │ ├── main.go # HTTP API и точка входа
│ │ └── web/
│ │ └── index.html # UI
│ ├── go.mod
│ └── internal/
│ ├── domain/ # доменные сущности и связанные тесты
│ ├── openai/ # клиент OpenAI для AI-анализа
│ └── simulation/ # данные, валидатор и математическая модель
├── config/
├── data/
├── tests/
└── .gitignore
Технологии
- Go — backend и simulation engine;
- embedded static HTML/JS — пользовательский интерфейс, отдаваемый сервером;
- JSON — данные по районам и мерам;
- OpenAI API — дополнительный AI-слой для объяснения результата;
- Git — контроль версий.
В проекте нет отдельного React/Vue/Next.js frontend. Веб-интерфейс сервится статическим файлом через Go.
Запуск проекта
Требования
- Go 1.22 или выше;
- доступ к Интернету только при использовании AI-анализа;
- локальный файл
.envв корне проекта дляOPENAI_API_KEYпри необходимости.
Клонирование
git clone <repo-url>
cd hack-052c857d-mindforge
Переменные окружения
Для AI-режима создайте файл .env в корне проекта:
OPENAI_API_KEY=your_openai_key_here
OPENAI_MODEL=gpt-4.1-mini
Файл .env уже добавлен в .gitignore, поэтому он не попадёт в Git.
Запуск
cd backend
go run ./cmd/api
После запуска приложение будет доступно по адресу:
http://localhost:8080
AI-анализ
AI-слой в проекте реализован как анализ уже рассчитанной симуляции. Он не должен:
- рассчитывать Score;
- менять бюджет;
- пересчитывать эффекты мер;
- генерировать численные показатели.
Он делает следующее:
- объясняет итоговые результаты;
- выделяет сильные стороны сценария;
- перечисляет ключевые риски;
- описывает компромиссы;
- предлагает следующие шаги и альтернативные направления.
Примечание: в текущем коде AI-путь зависит от корректной настройки OPENAI_API_KEY и доступности OpenAI API. Если ключ отсутствует, функция анализа считается недоступной, а основной симулятор продолжает работать без её участия.
Отказоустойчивость AI
Даже если OpenAI недоступен, пользователь должен иметь возможность:
- выбрать инициативы;
- получить валидный сценарий;
- увидеть Budget, Score, изменения по районам и синергии;
- пользоваться детерминированной моделью без внешнего сервиса.
Это реализовано в текущем проекте: AI не является обязательным компонентом.
Тестирование
В проекте есть unit-тесты на симуляцию и AI-клиент. Проверка выполняется командой:
cd backend
go test ./...
На текущем состоянии репозитория тесты проходят успешно.
Демо за 5 минут
Вот короткий, эффектный сценарий, который удобно показать на живой демонстрации:
- Запустите backend через
go run ./cmd/api. - Откройте
http://localhost:8080. - Убедитесь, что интерфейс показывает бюджет в 100 единиц.
- Выберите 5 инициатив, например:
M7→nuraM8→nuraM10→nuraM12→ cityM5→saryarka
- Нажмите кнопку расчёта.
- Посмотрите, как базовый и итоговый Score расходятся: это уже заметное “до/после”.
- Проверьте, что синергия
M10 + M12активируется — на глаз это очень видно в итоговом результате. - Если
OPENAI_API_KEYнастроен, запустите AI-анализ и покажите, как модель объясняет логику решения без вмешательства в сами числа.
Эта демонстрация хорошо работает для жюри: она показывает не только цифры, но и управленческую логику проекта.
Ограничения
- Данные синтетические и служат для демонстрации логики;
- эффекты инициатив заданы заранее;
- модель не является прогнозом реальной городской политики;
- результат описывает игру внутри симулятора, а не реальное состояние Астаны;
- AI-объяснение не влияет на математические расчёты и не заменяет модель;
- проект соответствует хакатонной реализации и пока не является полноценной системой городского планирования.
Возможное развитие
Не реализовано в текущем коде, но может быть логичным продолжением проекта:
- сравнение нескольких сценариев и стратегий;
- сохранение сценариев и история решений;
- более развитая визуализация по районам;
- карта города с выделением проблемных зон;
- учебные рекомендации по улучшению стратегии;
- более сложная модель городских зависимостей;
- расширенный AI-советник на основе уже рассчитанных результатов;
- экспорт результатов в PDF/CSV.
Финальная проверка
README подготовлен на основе реального состояния репозитория. В нём отражены только те компоненты, которые реально существуют в проекте:
- детерминированная симуляция;
- валидаторы и ограничения;
- city dataset;
- Go backend;
- API и UI;
- опциональная OpenAI интеграция;
- тестовый сценарий с проверяемыми числами.
README не утверждает, что AI работает без настройки ключа, и не описывает несуществующие API-эндпоинты или технологии.
Авторы коммитов
История коммитов
- 2026-09-23 17:57Update .env.example to use placeholder API keyAssem Kumarbekova
- 2026-09-23 17:56Change OPENAI_API_KEY to a placeholderAssem Kumarbekova
- 2026-09-23 17:42Add READMEassrakhimova
- 2026-09-23 17:35style: refresh simulator design and remove empty placeholdersakumarbe
- 2026-09-23 17:30fix: complete OpenAI scenario analysisakumarbe
- 2026-09-23 16:59feat: load OpenAI credentials from local env fileakumarbe
- 2026-09-23 16:25Add OpenAI analysis integrationassrakhimova
- 2026-09-23 15:30fix: load dataset and handle simulator errorsakumarbe
Все коммиты (12)
- 2026-09-23 15:07docs: add simulator usage guideakumarbe
- 2026-09-23 15:04feat: add city management simulator MVPakumarbe
- 2026-09-23 14:42Add Go city simulator domainassrakhimova
- 2026-09-23 13:49Create Akim 5 Hours simulator structureakumarbe
Структура репозитория (20 файлов)
.env.example .gitignore README.md ai/pyproject.toml ai/src/akim_ai/__init__.py backend/.env.example backend/cmd/api/env.go backend/cmd/api/main.go backend/cmd/api/web/index.html backend/go.mod backend/internal/domain/budget.go backend/internal/domain/budget_test.go backend/internal/domain/city.go backend/internal/domain/initiative.go backend/internal/openai/client.go backend/internal/openai/client_test.go backend/internal/simulation/data/city.json backend/internal/simulation/simulation.go backend/internal/simulation/simulation_test.go config/app.example.yaml
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.