О проекте
Команда: ABZHURD
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Рабочий MVP
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Streamlit-симулятор: 5 мер из 14 с назначением районов, проверка ограничений, расчёт Score города и районов до и после, объяснение результата (OpenAI, NVIDIA или автономная сводка).
Как устроено
Данные о районах и мерах лежат в локальных JSON. engine/simulator.py (validate_scenario, simulate) считает эффект с лагом (8−L)/8 и бонусами сочетаний, затем Score = 0.7·D_avg + 0.3·min(D) − N_crit. generate_debrief не генерирует свободный текст: модель выбирает по одной формулировке из вариантов, заранее собранных Python. Ответ проверяется по структуре и списку допустимых фраз, есть кеш сессии и автономный фолбэк.
Ключевые решения
- deterministic scoring engine
- constrained choice from pre-generated options
- OpenAI Responses API structured output
- session cache
Метрики
Пример жюри: Score 52.56 → 56.54 (+3.99), 95/100 бюджета, критических показателей 0
Особенности
Предельно строгая защита от галлюцинаций: LLM только выбирает одну из формулировок, собранных Python, и ответ проверяется на совпадение с допустимыми вариантами.
Стек
Python · Streamlit · Pydantic · pandas · python-dotenv · unittest
Модели
OpenAI gpt-6-sol · NVIDIA mistralai/mistral-nemotron
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-b5cf38a5-abzhurd.
README
README.mdQalaMind
Симулятор городских решений для Астаны. Проект для кейса «Аким на 5 часов» на HackAlem AI 2026.
Краткое описание
Как распределить ограниченный бюджет между школами, транспортом, экологией, безопасностью и городскими сервисами? QalaMind позволяет выбрать пять мер и увидеть, как по модели кейса изменятся показатели качества жизни в районах Астаны.
Приложение рассчитано на участников и жюри хакатона, а также на тех, кто хочет разобраться в компромиссах городского управления. Это учебный симулятор на фиксированных данных: его результаты описывают заданную модель, а не прогнозируют реальные изменения в городе.
Для демонстрации не нужны API-ключи: расчёты и автономное объяснение работают локально после установки зависимостей.
Что реализовано
- Выбор пяти решений из каталога 14 мер по пяти направлениям. Для районной меры пользователь выбирает район; общегородская применяется ко всем районам.
- Проверка ограничений при каждом изменении: бюджет до 100 условных единиц, ровно пять решений, отсутствие повторов, не более двух мер одного направления, корректное назначение районов и совместимость мер. Ошибки показаны рядом с выбором; неверный сценарий нельзя рассчитать.
- Закреплённая панель с количеством решений, стоимостью и остатком бюджета. При прокрутке она плавно становится компактнее.
- Расчёт результата: итоговая оценка города и её изменение, самый слабый район, количество критических показателей, таблица районных оценок до и после решений.
- Понятные названия показателей в предупреждениях и объяснениях, справочник показателей и группировка выбранных мер по направлениям.
- Три источника объяснения: OpenAI, NVIDIA или автономная сводка. Ответ содержит разделы «Почему изменился балл», «Главный риск» и «Рекомендация».
- Готовый пример и очистка выбора. Любое изменение решения убирает прежний результат и объяснение, чтобы они не относились к устаревшему сценарию.
- Защита от лишних API-запросов: объяснение запрашивается отдельной кнопкой, повторный запрос с теми же фактами, провайдером и моделью использует кэш текущей сессии. При сбое выбранного провайдера приложение выдаёт автономную сводку и показывает фактический источник.
Как работает решение
- При открытии приложение загружает локальные данные и подставляет пример из пяти мер. Автоматического расчёта или обращения к AI нет.
- Пользователь меняет меры и районы. Стоимость, остаток бюджета и сообщения об ошибках обновляются сразу.
- Кнопка «Рассчитать сценарий» запускает проверку и расчёт на Python.
- Пользователь изучает итоговую оценку, слабый район, критические показатели и сравнение районов.
- В разделе «Объяснение результата» выбирает источник и нажимает «Получить объяснение».
Расчёт охватывает 8 кварталов. Эффект меры с задержкой L умножается на (8 − L) / 8. Затем прибавляются предусмотренные сочетаниями мер бонусы, а значения показателей ограничиваются диапазоном от 0 до 100. Бонусы сочетаний мер не уменьшаются из-за задержки.
Районная оценка — взвешенная сумма десяти показателей. Итоговая оценка города учитывает среднюю оценку с весами населения, самый слабый район и число критических показателей:
Score = 0.7 × D_avg + 0.3 × min(D_d) − N_crit
Здесь D_avg — средняя районная оценка с весами населения, D_d — оценка отдельного района, а N_crit — количество пар «район — показатель» со значением строго ниже 40. Неиспользованный бюджет не даёт дополнительных баллов. Полные формулы и ограничения описаны в AGENTS.md.
Все числа рассчитывает Python. В текущей версии модель выбирает по одной готовой формулировке для каждого раздела объяснения из вариантов, сформированных Python по результату. Ответ проходит проверку структуры и совпадения с допустимыми формулировками. Свободная генерация советов не реализована.
Технологии
| Компонент | Технология и назначение |
|---|---|
| Язык | Python 3.10+ — проверка сценариев, вычисления и логика приложения |
| Интерфейс | Streamlit 1.52+ — формы, таблицы, показатели и состояние сессии |
| Валидация | Pydantic 2 — модели данных, сценариев, результатов и объяснений |
| Табличные данные | pandas — зависимость для работы с таблицами, в том числе в тестах интерфейса |
| Настройки | python-dotenv — чтение локального файла .env |
| OpenAI | SDK openai>=3.17,<4, Responses API со структурированным ответом; модель по умолчанию — gpt-6-sol, уровень reasoning — low |
| NVIDIA | Chat Completions API через тот же SDK; модель по умолчанию — mistralai/mistral-nemotron |
| Оформление | Локальные HTML, CSS и JavaScript — закрепление и анимация панели бюджета |
| Проверки | unittest, unittest.mock, Streamlit AppTest |
Модели задаются в конфигурации. Наличие модели в настройках не гарантирует доступ к ней в конкретном аккаунте провайдера.
Архитектура проекта
app.py Интерфейс, состояние выбора и кнопки действий
engine/
├── models.py Контракты данных и числовые правила модели
├── simulator.py Загрузка данных, проверка сценария и расчёты
├── advisor.py OpenAI, NVIDIA, кэш и автономное объяснение
└── labels.py Русские названия показателей
data/
├── districts.json Исходные показатели и веса населения районов
├── measures.json Меры, стоимость, задержки, эффекты и совместимость
└── mock_debrief.json Шаблоны автономной сводки
assets/
└── summary_header.html Оформление и анимация закреплённой панели
.streamlit/config.toml Светлая тема приложения
tests/
├── test_simulator.py Формулы, данные и ограничения
├── test_advisor.py Провайдеры, проверка ответов и резервный режим
└── test_app.py Пользовательский сценарий и границы API-вызовов
.env.example Шаблон настроек без ключей
requirements.txt Зависимости
AGENTS.md Спецификация и правила проекта
Поток данных: локальные JSON → проверка выбора → расчёт на Python → результат в интерфейсе → объяснение по запросу.
Модуль расчётов не зависит от Streamlit или клиентов AI. Его основные функции — load_dataset, validate_scenario и simulate. Результат содержит изменения показателей, прямой вклад каждой меры, отдельные бонусы сочетаний мер и поправки из-за границ шкалы. Функция generate_debrief получает готовый результат и не изменяет его числа.
JavaScript управляет только видом панели бюджета. Вычислений городской модели в браузере нет.
Установка и запуск
Нужны Python 3.10+ и Git. Интернет требуется для первоначальной установки зависимостей и, при использовании AI-провайдера, для запроса объяснения.
1. Скачайте проект
git clone https://github.com/BAITC-Hacks/hack-b5cf38a5-abzhurd qalamind
cd qalamind
2. Создайте окружение и установите зависимости
Для macOS и Linux:
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
cp .env.example .env
Для Windows PowerShell:
py -3 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
Copy-Item .env.example .env
Копировать шаблон нужно только при первом запуске. Если .env уже есть, отредактируйте его, сохранив свои настройки.
3. Выберите режим
Для демонстрации без API-ключей установите в .env:
ADVISOR_PROVIDER=offline
MOCK_MODE=true
Для объяснения через OpenAI или NVIDIA установите MOCK_MODE=false, выберите провайдера и добавьте его ключ. Доступные настройки:
| Переменная | Назначение |
|---|---|
ADVISOR_PROVIDER | openai, nvidia или offline; в шаблоне — openai |
MOCK_MODE | true принудительно включает автономный режим и блокирует переключение провайдера в интерфейсе |
OPENAI_API_KEY | Ключ для OpenAI |
NVIDIA_API_KEY | Ключ для NVIDIA |
OPENAI_MODEL | Модель OpenAI, по умолчанию gpt-6-sol |
OPENAI_REASONING_EFFORT | Уровень reasoning OpenAI, по умолчанию low |
NVIDIA_MODEL | Модель NVIDIA, по умолчанию mistralai/mistral-nemotron |
ADVISOR_MAX_OUTPUT_TOKENS | Лимит ответа, по умолчанию и максимум — 700 |
Файл .env исключён из Git. Полей для ввода ключей в интерфейсе нет.
4. Запустите приложение
Для macOS и Linux:
.venv/bin/python -m streamlit run app.py
Для Windows PowerShell:
.\.venv\Scripts\python.exe -m streamlit run app.py
Откройте адрес, который напечатает Streamlit, обычно http://localhost:8501.
После изменения .env остановите сервер сочетанием Ctrl+C, запустите его снова и обновите страницу. Переменные окружения процесса имеют приоритет над .env: запуск с MOCK_MODE=true в командной строке принудительно оставит приложение в автономном режиме.
Как проверить решение
Сценарий для жюри
Запустите приложение в автономном режиме и нажмите «Загрузить пример». В строках будут выбраны:
| Мера | Район или территория | Стоимость |
|---|---|---|
| M7 — Школа + детсад | Нура | 24 |
| M8 — Центр семейного здоровья / поликлиника | Нура | 20 |
| M10 — Освещение и камеры | Нура | 12 |
| M12 — Единая цифровая платформа обращений | Весь город | 14 |
| M5 — Перевод частного сектора на чистое топливо | Сарыарка | 25 |
Нажмите «Рассчитать сценарий» и сравните результат:
| Показатель | Ожидаемое значение |
|---|---|
| Исходная оценка города | 52.56 |
| Итоговая оценка города | 56.54 |
| Изменение | +3.99 |
| Потрачено | 95 из 100 |
| Остаток бюджета | 5 |
| Самый слабый район | Нура |
| Оценка Нуры после решений | 52.96 |
| Критических показателей после решений | 0 |
Числа округляются только при отображении. Например, изменение считается из исходных значений 56.54307 − 52.55768, поэтому отображается как +3.99.
Затем проверьте поведение приложения:
- Нажмите «Получить объяснение». Появятся три раздела и отметка «Автономная сводка».
- Измените район у любой районной меры. Предыдущие результат и объяснение исчезнут; для нового выбора потребуется новый расчёт.
- Загрузите пример снова и во второй строке выберите M7 вместо M8. Повтор меры будет отмечен ошибкой, кнопка расчёта станет недоступной. Стоимость при этом будет 99: повторная строка учитывается в бюджете, хотя сценарий недопустим.
- Нажмите «Очистить выбор». Останутся пять пустых строк; рассчитать их нельзя.
При настроенном API повторите запрос объяснения с OpenAI или NVIDIA. Если запрос не удался, интерфейс должен показать автономную сводку с соответствующей отметкой.
Автоматические проверки
На macOS и Linux:
.venv/bin/python -m unittest discover -s tests -v
На Windows:
.\.venv\Scripts\python.exe -m unittest discover -s tests -v
Тесты проверяют исходные данные, формулы, задержки эффектов, границы шкалы, порог критических показателей, совместимость мер, бюджет, кэш и работу интерфейса. Для математики используется независимый расчёт с точными дробями. Вызовы провайдеров подменены тестовыми ответами: реальные ключи и платные запросы не нужны.
Полный перебор районных назначений включается отдельно; по умолчанию этот длительный тест пропускается:
SIMULATOR_EXHAUSTIVE=1 .venv/bin/python -m unittest tests.test_simulator -v
Данные и интеграции
Числовая основа проекта — данные конкурсного кейса, сохранённые в репозитории:
- data/districts.json: пять районов — Есиль, Алматы, Сарыарка, Байконур и Нура; по десять исходных показателей и вес населения для каждого.
- data/measures.json: 14 мер по направлениям «Транспорт», «Экология», «Соцсфера», «Безопасность» и «Сервисы», их стоимость, задержки, эффекты, бонусы сочетаний и несовместимости.
- data/mock_debrief.json: шаблоны автономного объяснения, которые заполняются результатами расчёта.
- engine/models.py: веса показателей и числовые ограничения, перенесённые из спецификации в AGENTS.md.
Актуальные городские данные из внешних систем не загружаются. Все материалы, необходимые для запуска и проверки модели, находятся в репозитории.
Внешние API используются только для объяснения по отдельному действию пользователя:
| Провайдер | Интеграция |
|---|---|
| OpenAI | Responses API со структурированным ответом |
| NVIDIA | Chat Completions API: https://integrate.api.nvidia.com/v1 |
Провайдер получает рассчитанные факты сценария и допустимые формулировки объяснения. Для одного запроса установлен тайм-аут до 20 секунд, автоматические повторы отключены. Ошибка, отсутствие ключа или неподходящий ответ приводят к автономной сводке; другой платный провайдер автоматически не вызывается.
Ограничения
- Модель использует фиксированные данные и правила кейса. Стоимость задана в условных единицах; реальная оценка бюджета и прогноз городской ситуации не выполняются.
- AI выбирает готовые подтверждённые формулировки. Свободный диалог, поиск оптимального набора мер и генерация новых мер не реализованы.
- Доступность моделей и условия оплаты зависят от аккаунта провайдера. Автоматические тесты не проверяют реальную авторизацию или доступ к платному API.
- Нет графиков, тепловой карты показателей, отдельного сравнения нескольких сценариев и дополнительных пресетов.
- Нет кнопки экспорта полного расчёта в JSON, сохранения истории сценариев или запуска через
run.sh. - Выбор, результат и кэш хранятся в сессии Streamlit. Постоянное хранилище сценариев не реализовано.
- Закрепление панели использует структуру страницы Streamlit; после обновления фреймворка оформление стоит перепроверить.
Развёрнутая версия
Публичная ссылка на развёрнутое приложение в репозитории не указана. Для проверки используйте локальный запуск из раздела «Установка и запуск».
Адрес http://localhost:8501 доступен на компьютере, где запущен Streamlit, и не является публичной демоверсией.
Авторы коммитов
История коммитов
- 2026-09-23 17:41Rewrite README in Russian for hackathon judgespoizon
- 2026-09-23 17:34Add compact sticky summary and QalaMind brandingpoizon
- 2026-09-23 17:18Improve user-facing indicator explanationspoizon
- 2026-09-23 16:53Build Streamlit scenario flowpoizon
- 2026-09-23 16:12Add dual-provider advisor with grounded offline fallbackpoizon
- 2026-09-23 15:36Implement deterministic city simulator and testspoizon
- 2026-09-23 14:58docs: make repository sources authoritativepoizon
- 2026-09-23 14:41feat(iteration-2): add deterministic data and typed contractspoizon
Все коммиты (10)
- 2026-09-23 14:25docs: align project governance with hackathon specificationpoizon
- 2026-09-23 13:52feat(hour-1): project scaffolding, data models, and simulator architecturepoizon
Структура репозитория (20 файлов)
.env.example .gitignore .streamlit/config.toml AGENTS.md README.md app.py assets/summary_header.html data/districts.json data/measures.json data/mock_debrief.json engine/__init__.py engine/advisor.py engine/labels.py engine/models.py engine/simulator.py requirements.txt tests/__init__.py tests/test_advisor.py tests/test_app.py tests/test_simulator.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.