О проекте
Команда: Beknur
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Доведённый продукт
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Браузерная стратегия управления городом на Go: 100 единиц бюджета, 5 решений по направлениям и районам, расчёт AQLS на сервере, мультиплеерные комнаты, AI-разбор партии и экспорт отчётов.
Как устроено
Go-сервер с PostgreSQL проверяет бюджет и ограничения (5 решений, не больше 2 мер одного направления, минимум 3 направления, несовместимости, районы) и считает эффект как effect×(8−lag)/8 с синергиями и ограничением 0–100. Затем взвешенный AQLS. После сдачи сценариев OpenAI делает анализ по grounded-промпту с серверной валидацией. Есть комнаты до 8 игроков через WebSocket, детерминированный бот-оппонент (поиск, не LLM), админка, AI-генерация учебного города и отчёты PDF/DOCX/JSON.
Ключевые решения
- lagged effect model
- synergies
- constraint validation
- deterministic bot search
- WebSocket multiplayer
- PBKDF2-SHA256 auth
- grounded prompt
Особенности
Единственное в батче мультиплеерное решение кейса: комнаты, лобби, одинаковые замороженные условия, рейтинг побед, режим hot-seat. Подробная инструкция деплоя на VPS с HTTPS через Caddy и бэкапами PostgreSQL.
Стек
Go 1.25 · PostgreSQL · WebSocket · Docker Compose · Caddy
Модели
OpenAI
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-eb3ba7a0-beknur.
README
README.mdАким на 5 часов
Браузерная стратегия управления городом: 100 условных единиц, ровно 5 решений, 5 направлений, 8 кварталов последствий. Сервер считает Astana Quality of Life Score, а OpenAI объясняет результат. Учебные показатели не являются официальной статистикой или прогнозом.
Возможности
- Регистрация, вход, профиль, смена пароля; серверные роли пользователя и администратора.
- Одиночная игра, комнаты до 8 игроков, локальная игра на одном компьютере, детерминированный AI-бот.
- Лобби и готовность, обновления WebSocket, одинаковые замороженные условия для участников комнаты.
- Проверка бюджета, пяти решений, максимум двух мер одного направления, минимум трёх направлений, несовместимостей и районов.
- Автоматический AI-анализ после сдачи сценариев: достижения, улучшения, риски, компромиссы, районы и рекомендации.
- Сохранённая история, рейтинг по победам, PDF с кириллицей, DOCX и JSON без повторного вызова AI.
- Админ: шаблоны, районы, объекты, мероприятия, пользователи, комнаты, результаты и статистика.
- Ручной город и генерация учебного города через AI с серверной проверкой.
Быстрый запуск
Нужны Go 1.25+ и Docker Desktop с Linux containers.
- Скопируйте
.env.exampleв.env, если.envещё нет. Не перезаписывайте существующий ключ. - Задайте
OPENAI_API_KEY, доступную вашему аккаунту модель вOPENAI_MODEL, случайныйPOSTGRES_PASSWORDи такой же пароль вDATABASE_URL. - Выполните из папки с
go.mod:
docker compose up -d db
go run .
Откройте http://127.0.0.1:3000. Если порт занят, задайте другой PORT в .env и перезапустите сервер. При текущей локальной проверке новая версия запущена на http://127.0.0.1:3001.
Полностью через Docker:
docker compose --profile full up --build -d
Не запускайте одновременно два сервера на одном порту. В контейнере используется та же PostgreSQL; данные хранятся в именованных volumes.
Настройки и администратор
Инструкция публикации на сервере: Деплой на VPS.
DATABASE_URL: PostgreSQL; локальная БД доступна на127.0.0.1:54329.POSTGRES_PASSWORD: пароль пользователя БД для Compose. Для примера URL удобно использовать случайные буквы и цифры.OPENAI_API_KEY,OPENAI_MODEL: только сервер. Доступность модели зависит от аккаунта.PORT: по умолчанию 3000;HOST: по умолчанию 127.0.0.1.ADMIN_PASSWORD: минимум 12 символов, задаёт пароль при первом создании администратора.- Если
ADMIN_PASSWORDпуст, случайный пароль сохраняется в.local/admin-password.txt. Логин:admin. В Docker файл находится в/app/.local/admin-password.txt. PUBLIC_ORIGIN: точный HTTPS origin за reverse proxy.
.env, .local, база, логи и ключи игнорируются Git. Пароли пользователей хешируются PBKDF2-SHA256. Сессии сохраняются на сервере, cookie — HttpOnly и SameSite=Strict. Для публичного развёртывания необходим HTTPS.
Деплой на VPS: Linux + Docker Compose + HTTPS
Приложению нужны постоянно работающие Go-сервер и PostgreSQL. Публикация только папки dist на GitHub Pages не запустит игру: авторизация, расчёты, комнаты и отчёты работают через backend.
1. Подготовьте сервер и домен
На Linux-сервере установите Git, Docker Engine с Compose plugin и Caddy. Инструкции: Docker Engine, Caddy. Go и Node.js на сервере не нужны: Go-приложение собирается внутри Docker.
Настройте DNS-запись A домена, например akim.example.com, на IP сервера. Если используете AAAA, она также должна вести на этот сервер. Откройте входящие TCP-порты 80 и 443 и сохраните доступ по SSH. Приложение и БД в текущем Compose опубликованы только на localhost: 127.0.0.1:3000 и 127.0.0.1:54329.
2. Скачайте код и настройте окружение
Команды далее выполняются в терминале Linux, в папке с compose.yaml:
git clone https://github.com/BAITC-Hacks/hack-eb3ba7a0-beknur.git
cd hack-eb3ba7a0-beknur
git switch main
cp -n .env.example .env
chmod 600 .env
nano .env
Заполните .env реальными значениями вместо примеров:
POSTGRES_PASSWORD=REPLACE_WITH_RANDOM_HEX_PASSWORD
ADMIN_PASSWORD=REPLACE_WITH_ANOTHER_STRONG_PASSWORD
OPENAI_API_KEY=REPLACE_WITH_YOUR_API_KEY
OPENAI_MODEL=REPLACE_WITH_MODEL_AVAILABLE_TO_YOUR_ACCOUNT
PUBLIC_ORIGIN=https://akim.example.com
Для генерации каждого пароля можно отдельно выполнить openssl rand -hex 32. PUBLIC_ORIGIN укажите без завершающего слеша. Он нужен для проверки Origin и Secure-cookie за HTTPS-прокси. Ключ и .env не добавляйте в Git.
При запуске через Compose адрес DATABASE_URL формируется автоматически: PostgreSQL доступен приложению как db:5432. Значение DATABASE_URL из .env.example используется для запуска Go непосредственно на хосте. Порт приложения внутри контейнера фиксирован — 3000; локальный порт 3001 из примера разработки здесь не используется.
3. Добавьте настройки серверного запуска
Создайте рядом с compose.yaml файл compose.production.yaml:
services:
db:
restart: unless-stopped
app:
restart: unless-stopped
environment:
PUBLIC_ORIGIN: ${PUBLIC_ORIGIN:?Set PUBLIC_ORIGIN in .env}
Этот override передаёт PUBLIC_ORIGIN внутрь приложения — одного добавления переменной в .env недостаточно, поскольку базовый Compose её не передаёт. Файл также задаёт политику перезапуска контейнеров. Подход соответствует документации Compose для production.
Запустите сервисы:
docker compose -f compose.yaml -f compose.production.yaml --profile full up -d --build
docker compose -f compose.yaml -f compose.production.yaml --profile full ps
curl --fail http://127.0.0.1:3000/ > /dev/null
При первом запуске сервер создаст таблицы, начальный датасет и аккаунт admin. Его пароль берётся из ADMIN_PASSWORD. Изменение переменной после создания аккаунта не меняет существующий пароль: используйте смену пароля в профиле.
4. Подключите HTTPS через Caddy
При установке Caddy как системной службы добавьте в /etc/caddy/Caddyfile блок, заменив домен на свой. Существующие блоки других сайтов сохраните:
akim.example.com {
reverse_proxy 127.0.0.1:3000
}
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl enable --now caddy
sudo systemctl reload caddy
curl --fail https://akim.example.com/ > /dev/null
Caddy получает и обновляет HTTPS-сертификат для домена при корректном DNS и доступных портах 80/443. См. официальную инструкцию reverse proxy. Прокси должен сохранять исходный заголовок Host; приведённая конфигурация подходит и для WebSocket-комнат.
5. Проверьте опубликованную игру
Откройте https://akim.example.com, зарегистрируйте двух игроков в разных браузерах, создайте комнату и проверьте присоединение, готовность и сдачу пяти решений. Дождитесь AI-анализа, скачайте PDF/DOCX/JSON и проверьте историю. Для AI нужен исходящий HTTPS-доступ сервера к OpenAI и рабочие ключ, модель и квота.
Логи для диагностики:
docker compose -f compose.yaml -f compose.production.yaml --profile full logs --tail=100 app db
sudo journalctl -u caddy -n 100 --no-pager
502 обычно требует проверки контейнера app; ошибка входа по Origin — проверки PUBLIC_ORIGIN и домена; ошибка AI — проверки настроек API в логах и интерфейсе. Главная страница сама по себе не подтверждает работу БД или AI: выполните игровой сценарий выше.
Обновление и сохранность данных
Перед обновлением сделайте дамп БД. Команда ниже рассчитана на Linux shell:
mkdir -p backups
chmod 700 backups
umask 077
docker compose exec -T db pg_dump -U akim -d akim -Fc > "backups/akim-$(date +%Y%m%d-%H%M%S).dump"
Храните копии отдельно от сервера и проверяйте восстановление на тестовой БД. PostgreSQL хранит игры и AI-анализ в volume postgres_data; локальные файлы приложения — в app_data. Не выполняйте docker compose down -v для рабочей установки: флаг -v удаляет volumes. Пароль PostgreSQL в .env относится к первичной инициализации; его замена не меняет пароль уже существующей БД.
Обновление кода с пересборкой только приложения:
git pull --ff-only origin main
docker compose -f compose.yaml -f compose.production.yaml --profile full up -d --build --no-deps app
docker compose -f compose.yaml -f compose.production.yaml --profile full logs --tail=50 app
Пересоздание контейнера даёт короткий перерыв в работе. Незавершённый AI-анализ после перезапуска можно повторить из сохранённого результата. Сейчас запускайте один экземпляр приложения: встроенные AI-задачи и восстановление их состояния не рассчитаны на несколько реплик.
Как играть
Зарегистрируйтесь → Комнаты → Создать комнату → выберите режим и город. В сетевой комнате отправьте код второму игроку; все нажимают «Я готов», создатель запускает игру. Каждый выбирает пять мероприятий и районы. После сдачи изменить решения нельзя. Когда сдали все, сервер фиксирует места и автоматически запускает AI-анализ. При ошибке ключа/модели/сети расчёт сохраняется, а анализ можно повторить.
В локальной игре экран передачи хода скрывает предыдущий сценарий. Бот использует только исходные данные и те же ограничения; это детерминированный поиск, не LLM-соперник. Отдельная LLM отвечает за разбор игры.
Равные Score делят место. В глобальном рейтинге учитываются завершённые результаты зарегистрированных игроков; одиночные игры не дают побед, локальные гости и боты не ранжируются.
Формула и данные
Seed: data/seed.json, перенесён из исходного main. Исходные PDF из master prompt пока не приложены, поэтому полная сверка с ними не выполнена. Контрольные значения совпадают с ТЗ.
- Реализованный эффект:
effect × (8 − lag) / 8. - Синергии: M1+M2 → T1 +2; M10+M12 → B1 +2; M5+M6 → E2 +2, в районе первой меры.
- Индикаторы ограничиваются 0–100 после эффектов и синергий.
- Веса:
[.10,.10,.09,.11,.11,.11,.09,.09,.10,.10]. D_d = Σ(w_k × I'_dk);D_avg = Σ(pop_d × D_d).Score = .7 × D_avg + .3 × min(D_d) − N_crit, где критический индикатор строго меньше 40.- Остаток бюджета не даёт бонуса. Итоговый Score не обрезается; применяется точная формула.
Baseline: 52.5577. Пример M7/Нура, M8/Нура, M10/Нура, M12/город, M5/Сарыарка: стоимость 95, Score 56.5431.
Рекомендации получают только проверенные сервером замены одного решения. Это не поиск глобального оптимума. Предельные вклады решений учитывают потерю синергий при исключении меры и не складываются в общую дельту.
Технологии и хранение
Go net/http, PostgreSQL через pgx, WebSocket Gorilla, HTML/CSS/JavaScript без сборщика. PDF: gofpdf и встроенный PT Sans (OFL). DOCX: настоящий пакет Office Open XML.
PostgreSQL хранит аккаунты, сессии, шаблоны, комнаты и результаты как JSONB-агрегаты с первичными ключами и индексами результатов. Снимки сценариев сохраняются в результатах, поэтому изменение шаблона не меняет старые отчёты. Подробнее: архитектура и ограничения.
API
Все пути имеют префикс /api:
POST /register,/login,/logout;GET /me.GET/PATCH /profile,POST /profile/password.GET /cities,POST /cities/generate.GET/POST /rooms,GET /rooms/{id}.POST /rooms/{id}/join|leave|ready|start|decisions|submit.- WebSocket
/rooms/{id}/ws,GET /room-ranking/{id}. GET /results/{id},POST /results/{id}/retry.GET /results/{id}/pdf|docx|json,/history,/leaderboard.- Админ:
GET /admin/overview,POST/PUT /admin/cities,DELETE /admin/cities/{id},GET/PUT /scenario.
Текст AI-промпта открыт в prompts/post-game.txt. Используется Responses API со Structured Outputs, затем сервер проверяет обязательные разделы.
Проверки
go test ./...
go vet ./...
Для интеграционного теста задайте TEST_DATABASE_URL на тестовый PostgreSQL и запустите go test -v ./.... Тест создаёт и удаляет отдельную случайную схему, проверяет регистрацию двух игроков, RBAC, комнату, готовность, сдачу, приватность, AI-контракт, экспорты и рейтинг. AI заменён локальным HTTP mock: тест не расходует API-квоту.
Отдельные тесты проверяют baseline, пример датасета, бюджет, количество решений, повторы, направления, несовместимости, типы мер, лаги, синергии, clipping, штрафы, бота и структуру PDF/DOCX.
Статус и дальнейшее развитие
Это работающий MVP для проверки хакатонного сценария, а не заверенная production-ready система. Для публичного запуска нужны мониторинг, резервные копии, усиленные лимиты нагрузки, пагинация, внешний worker AI и ручная проверка DOCX в Microsoft Word. Живой вызов OpenAI зависит от действительного ключа и доступной модели. Исходные требования сохранены в docs/master-prompt.txt и docs/post-game-requirements.txt.
Авторы коммитов
История коммитов
- 2026-09-23 17:50Document VPS deployment with Docker Compose and HTTPSerdem
- 2026-09-23 17:45Build Go city simulation game with multiplayer and AI reportserdem
- 2026-09-23 16:01Add configurable city scenarios, district presets and plan optimizationerdem
- 2026-09-23 15:14Verify concise AI instructions reach the modelmr_realist
- 2026-09-23 15:13Use concise grounded prompt for AI analysismr_realist
- 2026-09-23 15:11Merge remote-tracking branch 'origin/codex/akim-simulator' into codex/akim-simulatorerdem
- 2026-09-23 15:11Add server-side AI scenario analysis and validation testserdem
- 2026-09-23 15:10Add concise grounded scenario analysis promptmr_realist
Все коммиты (13)
- 2026-09-23 14:15Document local API key setup and current integration statusmr_realist
- 2026-09-23 14:15Add safe example for OpenAI API configurationmr_realist
- 2026-09-23 14:14Ignore local API credentialsmr_realist
- 2026-09-23 13:49Document simulator rules and reproducible score calculationmr_realist
- 2026-09-23 13:49Add city budget simulatormr_realist
Структура репозитория (27 файлов)
.dockerignore .env.example .gitignore Dockerfile README.md ai.go assets/OFL.txt assets/PT_Sans-Web-Regular.ttf city_ai.go compose.yaml data/seed.json database.go docs/architecture.md docs/master-prompt.txt docs/post-game-requirements.txt features.go game.go go.mod go.sum integration_test.go main.go model.go model_test.go prompts/post-game.txt report.go server.go store.go
Ветки (2)
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.