AAYMA
RU
Войти

BesSheshim

Симулятор «Аким на 5 часов» с Go-бэкендом и интерфейсом на Next.js: выбор 5 из 14 инициатив, карта районов, расчёт Score, поиск оптимального набора…

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

О проекте

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

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

Суть

Симулятор «Аким на 5 часов» с Go-бэкендом и интерфейсом на Next.js: выбор 5 из 14 инициатив, карта районов, расчёт Score, поиск оптимального набора, кризисные события и AI-разбор результата.

Как устроено

Данные (districts, initiatives, rules, events) хранятся в JSON и встроены в Go-бинарник. Движок scoring на Go считает Score = 0.7·D_avg + 0.3·min(D_d) − N_crit с учётом лагов и синергий и покрыт golden-тестами; scripts/verify.py независимо воспроизводит числа. Optimizer полным перебором ищет оптимальный сценарий, отдельный модуль выдаёт детерминированные городские события. Go-клиент LLM (OpenAI) объясняет результат, опираясь на проверенные факты; без ключа возвращается кэшированный разбор. Next.js UI показывает карту районов (Three.js) и сравнение сохранённых сценариев.

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

  • deterministic scoring engine
  • golden fixture tests
  • exhaustive optimum search
  • deterministic crisis event deck
  • grounded LLM explanation with cached fallback
  • scenario comparison

Особенности

Сначала зафиксировали контракты (contracts/api.md, scoring.md, примеры JSON), потом параллельно разрабатывали ветками ai/be/fe. Формулу проверяет независимая реализация на Python. Полный перебор для поиска оптимума, Docker в один контейнер, работает без API-ключа.

Стек

Go 1.26 · Gin · Next.js 16 · TypeScript · Three.js · shadcn/ui · Docker · Docker Compose · Makefile · Python

Модели

OpenAI


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

README

README.md

BesSheshim — Five decisions. One city.

Go 1.26 Next.js 16 Demo works without an API key

Пять решений. Один город. Последствия видны сразу.
Примерьте роль акима Астаны: распределите 100 условных единиц между городскими инициативами и увидьте, как ваш выбор влияет на пять районов.

Демо за 60 секунд · Docker · Локальная разработка · Формула · Статус

BesSheshim за 20 секунд

Вы делаетеГород показываетВы понимаете
Выбираете ровно 5 из 14 инициатив в пределах 100 единиц бюджетаКарту районов, показатели качества жизни и итоговый ScoreКакие районы выиграли, какие проблемы остались и за что пришлось заплатить

Задача симулятора — сделать компромиссы городского бюджета понятными. Сильный сценарий поднимает средний результат, но также помогает самому уязвимому району и сокращает число критических показателей.

Главная страница

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

Главная BesSheshim: блок о живой карте, прозрачном результате и сравнении сценариев Ниже на главной: как продукт помогает увидеть эффект и объяснить результат.

Экран симулятора

Экран симулятора BesSheshim: карта районов Астаны, список инициатив и панель «Ваш план» Рабочий экран: слева — каталог из 14 инициатив по категориям, в центре — карта районов с индексом качества жизни, справа — выбранный район и план из пяти решений в пределах бюджета 100 единиц.

Демо за 60 секунд

  1. Откройте http://localhost:3000 и нажмите «Начать игру». На старте у города 2 критических показателя в Нуре; базовый Score по проверенной модели — 52,57.
  2. Добавьте пять мер в указанном порядке: M7 «Школа + детсад» → Нура, M8 «Поликлиника» → Нура, M10 «Освещение и камеры» → Нура, M12 «Платформа обращений» → весь город, M5 «Чистое топливо» → Сарыарка.
  3. В панели «Ваш план» проверьте бюджет: 95 / 100. Нажмите «Посмотреть результат».
  4. Проверенный сценарий даёт 56,49 (+3,92). Оба критических показателя Нуры выходят из красной зоны; сочетание M10 + M12 дополнительно улучшает безопасность Нуры. Разбор объясняет и оставшиеся слабые места: транспорт и экологию Нуры.

Это эталонный сценарий из контракта. Числа независимо воспроизводит scripts/verify.py и проверяют golden-фикстуры.

Как запустить

Для запуска без Docker нужны Go 1.26+ и Node.js 20.9+. Ключ LLM для демонстрации не нужен.

git clone https://github.com/BAITC-Hacks/hack-2e79658e-nosleep-dev.git
cd hack-2e79658e-nosleep-dev
cp .env.example .env
npm --prefix apps/web ci
make dev

Затем откройте http://localhost:3000. API доступен на http://localhost:8000/api/v1; проверка работоспособности — /health.

[!IMPORTANT] Фронтенд по умолчанию обращается к Go API через /api/v1/*. Для локальной разработки запустите API вместе с Next.js через make dev. Локальные демоданные включаются только при NEXT_PUBLIC_USE_MOCKS=true.

Docker: один контейнер

Docker-образ собирает Go API и production-сборку Next.js. Внутри контейнера работают оба процесса; наружу открыт только порт веб-приложения 3000. Запросы браузера к /api/v1/* Next.js перенаправляет на API внутри контейнера. Поэтому один и тот же образ работает на localhost, IP-адресе сервера или за обратным прокси без пересборки с новым адресом API.

Быстрый запуск

Установите Docker с плагином Compose и запустите из корня репозитория:

docker compose up -d --build --wait
docker compose ps
curl -f http://localhost:3000/api/v1/health

Откройте http://localhost:3000 и перейдите в симулятор. Ответ проверки API — {"status":"ok"}. --wait дожидается статуса healthy; запуск API может занять около полуминуты. Compose поднимает один сервис app; файл .env и ключ LLM для этого демо не требуются. При первом запуске Docker загрузит базовые образы, установит Go и npm-зависимости и соберёт оба приложения.

Без Compose

docker build -t bessheshim:local .
docker run --name bessheshim -d --restart unless-stopped -p 3000:3000 bessheshim:local
docker ps --filter name=bessheshim
curl -f http://localhost:3000/api/v1/health

Если проверка сразу после docker run ещё не отвечает, подождите, пока docker ps покажет (healthy), и повторите curl. Для остановки и удаления контейнера: docker rm -f bessheshim. Контейнер не использует базу данных или постоянный том; данные приложения и контрактные примеры входят в образ.

Порт и размещение на сервере

В Compose APP_PORT меняет порт хоста, а APP_BIND — адрес привязки. Например, если HTTPS завершает обратный прокси на том же сервере:

APP_BIND=127.0.0.1 APP_PORT=8080 docker compose up -d --build --wait
curl -f http://127.0.0.1:8080/api/v1/health

Направьте обратный прокси на http://127.0.0.1:8080 и передавайте через него все пути, включая /api/v1/* и /_next/*. Для прямого доступа с другой машины оставьте APP_BIND=0.0.0.0 (значение по умолчанию), откройте выбранный APP_PORT в сетевом экране и перейдите на http://IP_СЕРВЕРА:APP_PORT. Для публичного сайта настройте домен и HTTPS на обратном прокси. В варианте docker run порт меняется флагом -p 8080:3000.

Не меняйте внутренний API_PORT=8000 или PORT=3000: собранный Next.js направляет API-запросы на 127.0.0.1:8000, а Docker публикует порт 3000.

Обновление и диагностика

git pull
docker compose up -d --build --wait
docker compose logs -f app
docker compose ps
docker compose down

docker compose up -d --build --wait пересобирает образ из текущего кода, заменяет контейнер и дожидается его здоровья. В docker compose ps статус должен быть healthy: встроенная проверка обращается к /api/v1/health через Next.js и тем самым проверяет оба процесса. Если порт занят, задайте APP_PORT=8080. Если проверка не проходит, посмотрите docker compose logs app и убедитесь, что оба процесса запустились. При завершении одного из процессов контейнер останавливает второй, после чего Compose перезапускает сервис.

Образ собирается с NEXT_PUBLIC_USE_MOCKS=false и относительным адресом API /api/v1; эти значения в браузерном коде фиксируются во время сборки. Без ключа LLM API возвращает разбор с пометкой cached; при настроенном OPENAI_API_KEY он может вернуть живой AI-разбор.

Настройка AI-разбора (необязательно)

Для живого AI-разбора скопируйте .env.example в .env, заполните OPENAI_API_KEY, затем запустите docker compose up -d --build --wait. Compose передаёт в контейнер LLM_BASE_URL, OPENAI_API_KEY, LLM_MODEL, LLM_TIMEOUT_SECONDS и DEMO_MODE; .env не включается в образ и игнорируется Git. При DEMO_MODE=true, пустом ключе или ошибке провайдера API отдаёт разбор с пометкой cached. Для docker run добавьте --env-file .env перед именем образа. Внутренние API_PORT и PORT оставьте как в образе.

Как считается результат

Результат рассчитывается по открытой спецификации, на одинаковых исходных данных для каждого игрока:

$$ \text{Score} = 0.7 \cdot D_{avg} + 0.3 \cdot \min_d(D_d) - N_{crit} $$

Здесь D_avg — средний балл районов с учётом населения, min D_d — балл самого уязвимого района, N_crit — число показателей ниже 40. Формула учитывает не только общий рост, но и состояние самого слабого района. Эффекты инициатив учитывают задержку, а некоторые сочетания дают синергию.

Проверенный примерДоПосле
Итоговый Score52,5756,49
Критические показатели20
Бюджет0 / 10095 / 100

Данные и правила лежат в data/, а независимая реализация формулы — в scripts/verify.py. Реализованный AI-клиент получает готовые результаты для объяснения; он не рассчитывает Score.

Что реализовано

ЧастьТекущий статус
Главная и интерфейс симулятораЗапускаются локально; выбор инициатив, районов, бюджет и результат доступны в UI
Детерминированный движок ScoreРеализован на Go в internal/scoring и покрыт golden-тестами
HTTP APIМаршруты подключены к движку Score; каждый набор решений рассчитывается по данным модели
AI-разборGo-клиент подключён к HTTP API; без ключа или при сбое LLM возвращается разбор с пометкой cached
Оптимум, сравнение, городские событияOptimizer и события подключены к API; сохранение и сравнение сценариев доступны в UI, события остаются в API

Это hackathon demo build. Для точной проверки модели используйте тесты и эталонный сценарий выше.

Архитектура

flowchart LR
    Player[Игрок] --> Web[Next.js интерфейс]
    Web --> API[Go + Gin API]
    API --> Engine[Движок Score на Go]
    API --> LLM[Go AI-клиент и cached fallback]
    Data[data/*.json] --> Engine
    Engine --> Tests[Golden-тесты и независимая проверка]

Контракт API — в contracts/api.md. Основные маршруты: GET /catalog, POST /simulate, POST /explain, GET /optimum, POST /compare, POST /events/draw.

Проверка

python3 scripts/verify.py
cd apps/api && go test ./...

Ожидаемые опорные значения: BASE Score=52.5650, EX Score=56.4851. Go-тесты проверяют scoring, HTTP, LLM-клиент, optimizer и события. Проверены npm run lint и npm run build для веб-приложения.

Команда

NoSleep.dev · BAITC Hackathon 2026 · Астана, Казахстан

BesSheshim — «Аким на 5 часов». Пять решений. Один город.

Звёзды
⭐ 0
Форки
0
Язык
HTML
Последний push
в прошлом месяце
Коммиты
64
Авторы
5
Файлы
133
Ветки
4

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

Alikhan Sailybaev 26d.shaimurat 16Alibek 14Danial 7alibektasten 1

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

  • 2026-09-23 17:54fixAlikhan Sailybaev
  • 2026-09-23 17:52Update README.mdalibektasten
  • 2026-09-23 17:52fixAlikhan Sailybaev
  • 2026-09-23 17:43feat sim screen readmeAlikhan Sailybaev
  • 2026-09-23 17:31docs(ai): record merged implementation statusAlibek
  • 2026-09-23 17:31fix(api): retry invalid structured LLM outputAlibek
  • 2026-09-23 17:24Merge pull request #20 from BAITC-Hacks/codex/openai-api-defaultAlikhan Sailybaev
  • 2026-09-23 17:23docs: refresh graph after OpenAI branch mergeAlikhan Sailybaev
Все коммиты (64)
  • 2026-09-23 17:22Merge remote-tracking branch 'origin/main' into HEADAlikhan Sailybaev
  • 2026-09-23 17:19Merge pull request #19 from BAITC-Hacks/codex/play-cockpit-redesignAlikhan Sailybaev
  • 2026-09-23 17:18Merge main into play cockpit and integrate scenario comparisonAlikhan Sailybaev
  • 2026-09-23 17:05feat: ship BesSheshim demo in one Docker containerAlikhan Sailybaev
  • 2026-09-23 16:51docs(handoff): note current API process predates summary changed.shaimurat
  • 2026-09-23 16:50refactor(llm): clarify scenario summary wordingd.shaimurat
  • 2026-09-23 16:45Merge pull request #18 from BAITC-Hacks/codex/openai-api-defaultDanial
  • 2026-09-23 16:45Merge pull request #15 from BAITC-Hacks/ai/demo-qaDanial
  • 2026-09-23 16:44docs: refresh judge README and homepage screenshotsAlikhan Sailybaev
  • 2026-09-23 16:41Merge main into ai/demo-qa and resolve handoffAlibek
  • 2026-09-23 16:36docs(handoff): link backend pull requestd.shaimurat
  • 2026-09-23 16:33Merge pull request #17 from BAITC-Hacks/codex/openai-verificationAlikhan Sailybaev
  • 2026-09-23 16:25docs(handoff): record local API smoke checkd.shaimurat
  • 2026-09-23 16:23fix(llm): ground scenario explanation and constrain recommendationsd.shaimurat
  • 2026-09-23 16:05docs(backend): record live LLM fixture evaluationd.shaimurat
  • 2026-09-23 16:00test(ai): verify OpenAI defaults and live integrationAlibek
  • 2026-09-23 15:53docs: add spoken timing check to demo preparationAlibek
  • 2026-09-23 15:52Merge pull request #16 from BAITC-Hacks/codex/openai-api-defaultDanial
  • 2026-09-23 15:51docs: flag live comparison timeout riskAlibek
  • 2026-09-23 15:50docs: add rehearsed 60-second demo and QA statusAlibek
  • 2026-09-23 15:48fix(llm): accept grounded OpenAI explanationsd.shaimurat
  • 2026-09-23 15:35docs(backend): record OpenAI review handoffd.shaimurat
  • 2026-09-23 15:34feat(llm): default to OpenAI API key and modeld.shaimurat
  • 2026-09-23 15:17Merge pull request #14 from BAITC-Hacks/ai/llm-groundingDanial
  • 2026-09-23 15:15fix(ai): ground live explanations in verified scoring factsAlibek
  • 2026-09-23 15:14Merge pull request #13 from BAITC-Hacks/be/demo-readinessDanial
  • 2026-09-23 15:12docs: record demo readiness PR handoffd.shaimurat
  • 2026-09-23 15:10fix(demo): make clean startup and Compose web runnabled.shaimurat
  • 2026-09-23 14:58docs: define BesSheshim brand and graph workflowAlikhan Sailybaev
  • 2026-09-23 14:53feat(web): build editorial landing pageAlikhan Sailybaev
  • 2026-09-23 14:51Merge pull request #10 from BAITC-Hacks/be/startup-integrationDanial
  • 2026-09-23 14:50Merge main and resolve frontend API client conflictd.shaimurat
  • 2026-09-23 14:48Merge pull request #9 from BAITC-Hacks/fe/scenario-compareAlikhan Sailybaev
  • 2026-09-23 14:45docs: record backend review handoffd.shaimurat
  • 2026-09-23 14:44feat(web): add saved scenario comparisonAlikhan Sailybaev
  • 2026-09-23 14:44fix(api): restore demo startup and explanation datad.shaimurat
  • 2026-09-23 14:32Merge pull request #8 from BAITC-Hacks/fe/real-api-integrationAlikhan Sailybaev
  • 2026-09-23 14:29feat(web): integrate live API and optimum gaugeAlikhan Sailybaev
  • 2026-09-23 14:27Merge pull request #7 from BAITC-Hacks/ai/real-servicesAlikhan Sailybaev
  • 2026-09-23 14:25feat(api): wire real AI data services into serverAlibek
  • 2026-09-23 14:21Merge pull request #6 from BAITC-Hacks/be/api-bootstrapDanial
  • 2026-09-23 14:15docs(backend): record API bootstrap handoffd.shaimurat
  • 2026-09-23 14:14feat(api): scaffold fixture-backed simulator endpointsd.shaimurat
  • 2026-09-23 14:12Merge pull request #5 from BAITC-Hacks/fe/live-simulatorAlikhan Sailybaev
  • 2026-09-23 14:11Merge pull request #4 from BAITC-Hacks/docs/readme-designAlikhan Sailybaev
  • 2026-09-23 14:09feat(web): build live city budget simulatorAlikhan Sailybaev
  • 2026-09-23 14:04docs: redesign README as product page with SVG bannerAlikhan Sailybaev
  • 2026-09-23 14:03Merge pull request #3 from BAITC-Hacks/ai/scoringAlikhan Sailybaev
  • 2026-09-23 14:01fix(api): embed scoring datasets for standalone runtimeAlibek
  • 2026-09-23 13:57Merge pull request #2 from BAITC-Hacks/ai/scoringAlikhan Sailybaev
  • 2026-09-23 13:55feat(api): add deterministic crisis event deckAlibek
  • 2026-09-23 13:55feat(api): add exhaustive optimum scenario searchAlibek
  • 2026-09-23 13:53Merge pull request #1 from BAITC-Hacks/ai/scoringAlikhan Sailybaev
  • 2026-09-23 13:50feat(api): add resilient structured LLM explainerAlibek
  • 2026-09-23 13:49feat(api): implement scoring engine per contractsAlibek
  • 2026-09-23 13:40docs: add team plan, API contract, dataset and workflow for Akim-5h simulatorAlikhan Sailybaev
Структура репозитория (133 файлов)
.ai/DECISIONS.md
.ai/JUDGING.md
.ai/PLAN.md
.ai/STATUS.md
.ai/WORKFLOW.md
.ai/handoffs/ai-data.md
.ai/handoffs/backend.md
.ai/handoffs/frontend.md
.ai/prompts/ai-data.md
.ai/prompts/backend.md
.ai/prompts/frontend.md
.dockerignore
.env.example
.gitignore
AGENTS.md
DEMO.md
Dockerfile
Makefile
README.md
apps/api/cmd/server/main.go
apps/api/go.mod
apps/api/go.sum
apps/api/internal/events/events.go
apps/api/internal/events/events_test.go
apps/api/internal/http/fixtures.go
apps/api/internal/http/live.go
apps/api/internal/http/live_test.go
apps/api/internal/http/router.go
apps/api/internal/http/router_test.go
apps/api/internal/http/types.go
apps/api/internal/llm/llm.go
apps/api/internal/llm/llm_test.go
apps/api/internal/llm/prompt.go
apps/api/internal/optimizer/optimizer.go
apps/api/internal/optimizer/optimizer_test.go
apps/api/internal/real/services.go
apps/api/internal/real/services_test.go
apps/api/internal/scoring/data/districts.json
apps/api/internal/scoring/data/initiatives.json
apps/api/internal/scoring/data/rules.json
apps/api/internal/scoring/scoring.go
apps/api/internal/scoring/scoring_test.go
apps/web/.gitignore
apps/web/AGENTS.md
apps/web/CLAUDE.md
apps/web/DESIGN.md
apps/web/README.md
apps/web/components.json
apps/web/docs/frontend-plan.md
apps/web/docs/landing-prompt.md
apps/web/eslint.config.mjs
apps/web/next.config.ts
apps/web/package-lock.json
apps/web/package.json
apps/web/postcss.config.mjs
apps/web/public/brand/hero-amber.png
apps/web/public/brand/hero-emerald-poster.jpg
apps/web/public/brand/hero-emerald.mp4
apps/web/public/brand/tools/ai-analysis.png
apps/web/public/brand/tools/budget.png
apps/web/public/brand/tools/city-impact.png
apps/web/public/brand/tools/district-map.png
apps/web/public/brand/tools/scenario.png
apps/web/public/brand/tools/transparent-score.png
apps/web/public/file.svg
apps/web/public/globe.svg
apps/web/public/next.svg
apps/web/public/vercel.svg
apps/web/public/window.svg
apps/web/src/app/globals.css
apps/web/src/app/icon.svg
apps/web/src/app/layout.tsx
apps/web/src/app/page.tsx
apps/web/src/app/play/page.tsx
apps/web/src/components/astana-three-map.tsx
apps/web/src/components/block/arrow-fill-button.css
apps/web/src/components/block/arrow-fill-button.tsx
apps/web/src/components/block/dotted-grid.jsx
apps/web/src/components/block/rectangular-text-reveal.jsx
apps/web/src/components/city-map.tsx
apps/web/src/components/landing-page.tsx
apps/web/src/components/scenario-compare.tsx
apps/web/src/components/score-counter.tsx
apps/web/src/components/scroll-reveal.tsx
apps/web/src/components/simulator.tsx
apps/web/src/components/simulator/district-picker.tsx
apps/web/src/components/simulator/initiative-catalog.tsx
apps/web/src/components/simulator/live-impact-panel.tsx
apps/web/src/components/simulator/plan-tray.tsx
apps/web/src/components/simulator/scenario-header.tsx
apps/web/src/components/simulator/scenario-result.tsx
apps/web/src/components/site-header.tsx
apps/web/src/components/ui/badge.tsx
apps/web/src/components/ui/button.tsx
apps/web/src/components/ui/card.tsx
apps/web/src/components/ui/progress.tsx
apps/web/src/components/ui/select.tsx
apps/web/src/components/ui/separator.tsx
apps/web/src/components/ui/skeleton.tsx
apps/web/src/data/README.md
apps/web/src/data/astana-districts.json
apps/web/src/lib/api-client.ts
apps/web/src/lib/types.ts
apps/web/src/lib/utils.ts
apps/web/tsconfig.json
contracts/api.md
contracts/examples/catalog.json
contracts/examples/compare.json
contracts/examples/error.json
contracts/examples/event.json
contracts/examples/explain-cached.json
contracts/examples/optimum.json
contracts/examples/simulate-invalid-budget.json
contracts/examples/simulate-partial.json
contracts/examples/simulate-valid.json
contracts/go-interfaces.md
contracts/scoring.md
data/districts.json
data/events.json
data/fixtures/golden.json
data/initiatives.json
data/rules.json
docker-compose.yml
docs/BRAND.md
docs/assets/banner.svg
docs/assets/homepage-features.png
docs/assets/homepage-hero.png
docs/assets/simulator-screen.png
graphify-out/GRAPH_REPORT.md
graphify-out/graph.html
graphify-out/graph.json
scripts/start-container.mjs
scripts/verify.py
Ветки (4)

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

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

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