AAYMA
RU
Войти

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

Городской симулятор для Астаны: пользователь выбирает 5 инициатив по районам в рамках бюджета 100, получает Astana Quality of Life Score, карту…

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

О проекте

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

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

Суть

Городской симулятор для Астаны: пользователь выбирает 5 инициатив по районам в рамках бюджета 100, получает Astana Quality of Life Score, карту районов, фронт Парето и записку акиму. Есть AI-советник с вызовом инструментов движка.

Как устроено

Данные (data/*.json: районы, меры, конфиг) проверяются Zod и передаются в детерминированный TypeScript-движок. Он считает лаги, синергии, районные баллы и Score = 0.7·D_avg + 0.3·D_min − N_crit. Оптимизатор полным перебором проходит 694 395 допустимых сценариев (лучший и худший набор, Парето, ранг). Три API-маршрута (analyze/advice/agent) используют LLM только для текста и вызова 8 инструментов (evaluate_scenario, find_best, suggest_swaps, pareto, timeline и др.), ограничение 6 раундов. Без ключа работает шаблонный fallback.

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

  • детерминированный симулятор
  • полный перебор сценариев
  • фронт Парето
  • локальный поиск одиночной заменой (swap)
  • function calling
  • Zod-валидация аргументов инструментов
  • квартальный timeline с лагами
  • SVG-карта по GeoJSON OSM

Метрики

Score: baseline 52.56, эталон 56.54 (расход 95), лучший 57.24 (98), худший 52.04 (80); совет 56.54→56.99; перебор 694 395 сценариев

Особенности

Строгое разделение: все числа считает детерминированный движок, LLM только объясняет и вызывает проверяемые инструменты. Числа в тексте LLM заменяются шаблоном, совет перед применением повторно валидируется. Журнал вызовов инструментов виден в UI.

Стек

TypeScript · Next.js · React · Tailwind CSS · Zod · OpenAI SDK · Vitest · Docker

Модели

gpt-5.4-mini · gpt-4o-mini


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

README

README.md

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

AI-симулятор управления городом для HackAlem AI. Задача Astana Innovations: распределить бюджет 100 условных единиц между пятью инициативами и увидеть последствия для районов Астаны.

Пользователь собирает план, получает Astana Quality of Life Score, сравнивает его с другими допустимыми наборами и может применить проверенный совет AI. Все числовые результаты рассчитывает детерминированный TypeScript-движок; языковая модель объясняет результаты и вызывает инструменты анализа.

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

Нужны Node.js 20.9+ и npm. Для локальной разработки проверен Node.js 22.

npm ci
npm run dev

Откройте localhost:3000. Без API-ключа работают расчёты, карта, оптимизатор, Парето и шаблонные объяснения.

Для включения AI скопируйте .env.example в .env.local, если этого файла ещё нет:

# macOS / Linux
cp .env.example .env.local

# Windows PowerShell
Copy-Item .env.example .env.local

Вставьте ключ в LLM_API_KEY и перезапустите приложение. Пример уже связывает OPENAI_API_KEY и OPENAI_MODEL с LLM_*, чтобы включить все три AI-маршрута. Существующий файл с ключами не перезаписывайте.

Docker Compose

Нужны Docker и Docker Compose 2.24+. Запуск без ключа и файла .env:

docker compose up --build

Для AI создайте .env из .env.example, заполните ключ и запустите ту же команду. Compose читает секреты при запуске контейнера; они не включаются в образ. Приложение доступно на localhost:3000.

Dockerfile собирает Next.js standalone и запускает его от непривилегированного пользователя. Контейнерный запуск на машине подготовки проекта не проверен: Docker CLI отсутствовал.

Возможности

ВозможностьЧто доступно в приложении
Пять решенийВыбор инициатив и районов, стоимость, остаток бюджета и ошибки валидации
Расчёт последствийГородской Score, баллы районов до/после, критические показатели и синергии
КартаИнтерактивная SVG-карта пяти районов по локальному GeoJSON; выбор района, слои «D после» и «Изменение D»
Лучший и худший наборЗагрузка сценариев из полного перебора допустимых решений
Сравнение с другими наборамиМесто и процентиль текущего сценария
ПаретоГрафик лучшего Score при разных бюджетах, отметка текущего плана и отставание от оптимума
Записка акимуИтог, сильные стороны, компромиссы, риски и следующий шаг; числовые факты округлены приложением
Применение советаПредпросмотр одиночной замены с ростом Score без увеличения расходов, применение и отмена
AI-советникРазбор сценария, вопросы, три демо-вопроса и раскрываемый журнал вызовов инструментов

Квартальный timeline реализован в движке и доступен AI-агенту через инструмент timeline. Отдельного графика timeline в интерфейсе пока нет.

Демо за несколько минут

  1. Нажмите «Загрузить эталонный сценарий», затем «Рассчитать сценарий».
  2. Проверьте расход 95/100 и Score 56.54 при исходном 52.56.
  3. Посмотрите районные результаты и переключите карту на «Изменение D».
  4. Сравните текущий план с графиком Парето и его местом среди допустимых наборов.
  5. Получите совет об улучшении, посмотрите изменение бюджета и Score, примените его и отмените. Для эталона проверен переход 56.54 → 56.99, расход 95 → 86.
  6. В «AI-советнике» нажмите «Разобрать сценарий» или задайте: «Какой лучший набор, если не трогать Есиль и уложиться в 80?». При настроенной LLM откройте журнал инструментов и проверьте ограничения в вызове find_best.
  7. Для более заметного эффекта загрузите «Худший сценарий», рассчитайте его и запросите улучшение.

Эталон: M7, M8 и M10 в Нуре; M12 для всего города; M5 в Сарыарке. Подробный сценарий проверки совета — AI_ADVICE_DEMO.md.

Скриншоты

Конструктор сценария

Выбор пяти инициатив и районов.

Конструктор городского сценария

Результаты

Эталонный сценарий: Score 56.54 при бюджете 95/100.

Результаты эталонного сценария

Карта районов

Изменение районных баллов после выбранных инициатив.

Изменения на карте районов Астаны

Парето

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

Сравнение сценария с фронтом Парето

AI-советник

Вопросы и резервный разбор сценария по расчётам движка. На этом снимке показан режим fallback.

AI-советник в резервном режиме

Журнал инструментов

Вызовы инструментов с аргументами и результатами расчётов.

Журнал вызовов инструментов AI-советника

AI и проверяемые рекомендации

Три серверных маршрута решают разные задачи:

  • POST /api/analyze составляет записку по результату симуляции. Приложение формирует числовой итог и сведения о слабейшем районе; LLM добавляет текст. Ответы с техническими именами и цифрами в этой текстовой части заменяются шаблонным объяснением.
  • POST /api/advice ищет улучшение одной заменой, проверяет допустимость, рост отображаемого Score и отсутствие роста расходов. Перед применением клиент повторно проверяет предложение и пересчитывает результат. Это локальное улучшение, а не обещание глобального оптимума.
  • POST /api/agent отвечает в режимах explain и chat, вызывает инструменты движка и возвращает { answer, trace, mode }. Панель показывает ответ и фактические вызовы инструментов с их результатами.

Агенту доступны восемь инструментов: evaluate_scenario, validate_scenario, find_best, find_worst, suggest_swaps, pareto, timeline, score_rank. Их аргументы проверяются через Zod. Цикл ограничен шестью раундами инструментов, после которых разрешён один финальный ответ модели без инструментов.

Без LLM или при ошибке провайдера сохраняются расчёты и резервные ответы. В режиме explain агент возвращает шаблонный разбор. В режиме chat резервный ответ явно показывает лучший набор без ограничений из вопроса: полноценное понимание вопроса требует LLM.

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

ПеременнаяГде используется
LLM_API_KEYАгент и карточка совета; в .env.example также передаётся в OPENAI_API_KEY
LLM_MODELАгент и карточка совета; пример конфигурации выбирает gpt-5.4-mini
LLM_BASE_URLOpenAI-совместимый Chat Completions endpoint для агента и карточки совета
OPENAI_API_KEYЗаписка акиму; резервный ключ карточки совета
OPENAI_MODELМодель записки; резервная модель карточки совета
OPENAI_BASE_URLНеобязательный endpoint SDK для записки

Значения по умолчанию в маршрутах — gpt-4o-mini; файл примера переопределяет модель. Для собственного совместимого провайдера задайте и LLM_BASE_URL, и OPENAI_BASE_URL, а также доступную модель. Для записки и совета провайдер должен поддерживать структурированный JSON-ответ, для агента — вызов инструментов.

В Docker entrypoint подставляет LLM_* в отсутствующие OPENAI_*. Секреты хранятся только в локальных env-файлах и исключены из Git.

Модель расчёта

Исходные показатели и каталог инициатив синтетические. Симуляция показывает последствия в рамках заданной модели и не является прогнозом фактического эффекта муниципальной политики.

Эффекты инициатив учитывают лаги, синергии и размещение. Значения показателей ограничиваются диапазоном 0–100. Районный балл — взвешенная сумма показателей; средний городской балл учитывает доли населения.

Score = 0.7 × D_avg + 0.3 × D_min − N_crit
  • D_avg — средневзвешенный балл районов.
  • D_min — балл слабейшего района.
  • N_crit — число показателей ниже 40 во всех районах.

Валидатор требует ровно пять разных мер, бюджет не выше 100, не больше двух мер одного направления, корректное размещение и отсутствие конфликтов. Пустой сценарий используется движком только как baseline.

В квартале q доля эффекта равна max(0, q − L) / 8, где L — лаг меры; синергия включается после лагов обеих мер. Последняя точка timeline совпадает с обычным результатом симуляции. Внутренние расчёты сохраняют точность; отображаемые числа округляются.

Контрольный сценарийРасходScore
Исходное состояние052.56
Эталон9556.54
Лучший найденный9857.24
Худший допустимый8052.04

Оптимизатор перебирает 694 395 допустимых сценариев и сохраняет результаты в памяти серверного процесса. Подробные наборы, параметры и замеры — ENGINE_RESULTS.md; формулы — SIMULATION_SPEC.md.

Архитектура

Next.js App Router, TypeScript, React, Tailwind CSS, Zod, OpenAI SDK и Vitest. Для MVP не нужны база данных, авторизация или GPU.

flowchart LR
  data["data/*.json"] --> loader["Zod: загрузка и проверка"]
  loader --> engine["TypeScript: симуляция и оптимизатор"]
  engine --> ui["React: решения, результаты, карта, Парето"]
  geo["Локальный GeoJSON"] --> ui
  ui --> api["analyze / advice / agent"]
  api --> tools["Проверяемые инструменты"]
  tools --> engine
  api <--> llm["LLM: текст и вызовы инструментов"]
  api --> fallback["Резервный ответ без LLM"]
ПутьНазначение
data/*.jsonЕдинственный каталог числовых данных и параметров
data/geo/Геометрия и сведения об источниках
src/lib/simulation/Расчёты, валидация, оптимизатор, инструменты и проверка советов
src/lib/analysis.tsЧитаемые факты и шаблонная записка
src/app/api/Три серверных AI-маршрута
src/components/Конструктор, результаты, карта, Парето и AI-панель
tests/Проверки движка, API, советов, геоданных и служебных скриптов

Карта использует локальные геометрии OpenStreetMap, включая реальную границу района Алматы. Модель включает пять районов из задания; она не охватывает полное современное административное деление города. Источники и атрибуция: data/geo/README.md, © OpenStreetMap contributors, ODbL 1.0.

Проверка

npm test
npm run lint
npm run build

Последний полный прогон приложения: 127 тестов в 9 файлах, lint и production build прошли. Проверяются baseline, эталон и невалидные сценарии, оптимизатор, ограничения, рейтинг, Парето, timeline, API и fallback, применение советов, форматирование записки, каталог названий для агента и геоданные. Пользовательский путь «эталон → совет → применить → отменить» также проверен вручную.

Границы MVP

  • Отдельный график timeline пока не подключён; расчёт доступен движку и агенту.
  • В интерфейсе местами остаются технические коды инициатив и показателей.
  • Карточка совета проверяет одну замену; глобальный поиск доступен отдельно.
  • События, лидерборд команд, сохранение сценариев и многопользовательский режим не реализованы.
  • Данные и веса модели требуют калибровки для использования в реальном планировании.

Команда: Дамир, Расул и Шахнияр.

Звёзды
⭐ 0
Форки
0
Язык
TypeScript
Последний push
в прошлом месяце
Коммиты
68
Авторы
3
Файлы
100
Ветки
7

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

rasss1k 27ingaralai-wq 21Шахнияр 20

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

  • 2026-09-23 17:51fix(ui): distinguish AI panel keys and restore README screenshotsingaralai-wq
  • 2026-09-23 17:46merge: ui into main (design and screenshots)rasss1k
  • 2026-09-23 17:45merge: main into ui, preserve READMErasss1k
  • 2026-09-23 17:44feat(ui): design fixes and demo screenshotsrasss1k
  • 2026-09-23 17:42fix: harden AI requests and resolve final UI issuesingaralai-wq
  • 2026-09-23 17:37docs: align submission README with implemented featuresingaralai-wq
  • 2026-09-23 17:29docs: READMEШахнияр
  • 2026-09-23 17:25readme: Update README.mdШахнияр
Все коммиты (68)
  • 2026-09-23 17:14ui: add minimal agent panel with explain chat and traceШахнияр
  • 2026-09-23 17:11fix(ai): make mayor brief readable and groundedingaralai-wq
  • 2026-09-23 17:09Merge branch 'main' into codex/damir/simulation-engineHenry Chinaski
  • 2026-09-23 17:08Merge pull request #2 from BAITC-Hacks/codex/damir/apply-adviceingaralai-wq
  • 2026-09-23 17:08Merge branch 'main' into codex/damir/apply-adviceingaralai-wq
  • 2026-09-23 17:06merge: ui в mainrasss1k
  • 2026-09-23 17:03fix(map): реальная граница Алматы из data/geo, убрана схемаrasss1k
  • 2026-09-23 17:00merge: main в ui (реальная граница Алматы)rasss1k
  • 2026-09-23 16:58docs(status): UI по решению PMrasss1k
  • 2026-09-23 16:55feat(ui): график Парето бюджет/Scorerasss1k
  • 2026-09-23 16:51engine: A3 agent routeШахнияр
  • 2026-09-23 16:51feat(ui): лучший/худший сценарий и место в рейтингеrasss1k
  • 2026-09-23 16:49test: verify OSM Almaty boundaryШахнияр
  • 2026-09-23 16:44data: almaty boundaryШахнияр
  • 2026-09-23 16:43engine: A3 agent routeШахнияр
  • 2026-09-23 16:41merge: main в uirasss1k
  • 2026-09-23 16:31chore(config): select GPT-5.4 mini for advice demoingaralai-wq
  • 2026-09-23 16:24feat(layout): две колонки на ≥1200px, результаты справаrasss1k
  • 2026-09-23 16:23docs(demo): record readiness checks and map integration handoffingaralai-wq
  • 2026-09-23 16:17docs(advice): add local key setup and reproducible demo scenarioingaralai-wq
  • 2026-09-23 16:09fix(map): схематичный полигон Алматыrasss1k
  • 2026-09-23 16:08engine: U6 dockerШахнияр
  • 2026-09-23 16:04fix(map): убрать пустое пространство над картойrasss1k
  • 2026-09-23 16:01feat(map): адаптивная шкала и слои D после / изменение Drasss1k
  • 2026-09-23 15:59merge: sync verified advice with latest engine tools and team planingaralai-wq
  • 2026-09-23 15:57feat(advice): preview, apply and undo verified scenario improvementingaralai-wq
  • 2026-09-23 15:56merge: main в ui (E3/E4), карта сохраненаrasss1k
  • 2026-09-23 15:54engine: E3c agent toolsШахнияр
  • 2026-09-23 15:50engine: E3b worst + rankШахнияр
  • 2026-09-23 15:47engine: reassign ticketsШахнияр
  • 2026-09-23 15:44feat(scenario): выбор района на карте и onScenarioChangerasss1k
  • 2026-09-23 15:44feat(map): SVG-карта районов на основе data/georasss1k
  • 2026-09-23 15:40engine: E4 constraints, pareto, timelineШахнияр
  • 2026-09-23 15:34engine: E3 optimizer + swapsШахнияр
  • 2026-09-23 15:32feat(scenario): индикатор бюджета и счётчик решенийrasss1k
  • 2026-09-23 15:32feat(scenario): отображение ошибок валидации и состояние кнопки расчётаrasss1k
  • 2026-09-23 15:32merge: main в ui, конфликты конструктора разрешены в пользу компонентной структурыrasss1k
  • 2026-09-23 15:25engine: compress plan to 18:00Шахнияр
  • 2026-09-23 15:20fix(engine): expose deterministic baseline snapshotingaralai-wq
  • 2026-09-23 15:18engine: switch to TS-only architecture, update planШахнияр
  • 2026-09-23 15:15feat(scenario): отображение ошибок валидации и состояние кнопки расчётаrasss1k
  • 2026-09-23 15:08feat(scenario): индикатор бюджета и счётчик решенийrasss1k
  • 2026-09-23 15:07feat(simulation): русские подписи направлений и показателейrasss1k
  • 2026-09-23 15:07feat(scenario): слоты решений, выбор меры и района, карточка мерыrasss1k
  • 2026-09-23 15:06Merge remote-tracking branch 'origin/main' into uirasss1k
  • 2026-09-23 15:06Add deterministic nearby scenario suggestionsingaralai-wq
  • 2026-09-23 15:03feat(scenario): слоты решений, выбор меры и района, карточка мерыrasss1k
  • 2026-09-23 14:58feat(simulation): русские подписи направлений и показателейrasss1k
  • 2026-09-23 14:55engine: project plan, status boardШахнияр
  • 2026-09-23 14:53Merge teammate map materials without breaking Next runtimeingaralai-wq
  • 2026-09-23 14:47Merge working simulator into mainingaralai-wq
  • 2026-09-23 14:45[shakhniyar] merge Python and Next.js gitignore rulesШахнияр
  • 2026-09-23 14:43Build end-to-end scenario simulation experienceingaralai-wq
  • 2026-09-23 14:41engine: merge local work, remove nested reposШахнияр
  • 2026-09-23 14:41Merge pull request #1 from BAITC-Hacks/codex/damir/simulation-engineingaralai-wq
  • 2026-09-23 14:41Merge branch 'main' into codex/damir/simulation-engineingaralai-wq
  • 2026-09-23 14:33Clarify runtime and teammate workflowingaralai-wq
  • 2026-09-23 14:24Scaffold app and add deterministic simulation engineingaralai-wq
  • 2026-09-23 14:19chore: project skeleton, data, interface contractШахнияр
  • 2026-09-23 14:15Add hackathon product and delivery documentationingaralai-wq
Структура репозитория (100 файлов)
.dockerignore
.env.example
.gitattributes
.githooks/pre-push
.gitignore
AGENTS.md
CLAUDE.md
Dockerfile
README.md
data/config.json
data/districts.json
data/geo/README.md
data/geo/almaty-validation.json
data/geo/astana_districts.geojson
data/measures.json
docker-compose.yml
docs/AGENT_PROMPTS.md
docs/AI_ADVICE_DEMO.md
docs/ARCHITECTURE.md
docs/DELIVERY_PLAN.md
docs/DEMO.md
docs/DEMO_READY.md
docs/ENGINE_RESULTS.md
docs/PLAN.md
docs/PM_HANDOFF.md
docs/PRD.md
docs/SIMULATION_SPEC.md
docs/STATUS.md
docs/TEAM_BRIEFING.md
docs/examples/advice-scenario.json
docs/log/agent-panel.md
docs/log/damir.md
docs/log/rasul.md
docs/log/shakhniyar.md
docs/screenshots/ai-trace.png
docs/screenshots/ai.png
docs/screenshots/main.png
docs/screenshots/map.png
docs/screenshots/pareto.png
docs/screenshots/results.png
docs/status/META.md
docs/status/ai.md
docs/status/engine.md
docs/status/ui.md
eslint.config.mjs
next-env.d.ts
next.config.ts
package-lock.json
package.json
postcss.config.mjs
scripts/docker-entrypoint.mjs
scripts/install-hooks.mjs
scripts/status.mjs
src/app/api/advice/route.ts
src/app/api/agent/route.ts
src/app/api/analyze/route.ts
src/app/globals.css
src/app/layout.tsx
src/app/page.tsx
src/components/agent-panel/AgentPanel.tsx
src/components/map/DistrictMap.tsx
src/components/map/districtGeometry.ts
src/components/map/generateGeometry.mjs
src/components/pareto/ParetoChart.tsx
src/components/pareto/paretoActions.ts
src/components/results/AdviceCard.tsx
src/components/results/MayorBrief.tsx
src/components/results/ResultsDashboard.tsx
src/components/scenario/BudgetMeter.tsx
src/components/scenario/DecisionSlot.tsx
src/components/scenario/MeasureDetails.tsx
src/components/scenario/ScenarioBuilder.tsx
src/components/scenario/ValidationSummary.tsx
src/components/scenario/optimizerActions.ts
src/lib/agent/demo-questions.json
src/lib/analysis.ts
src/lib/simulation/advice.ts
src/lib/simulation/data.ts
src/lib/simulation/index.ts
src/lib/simulation/labels.ts
src/lib/simulation/numeric.ts
src/lib/simulation/optimize.ts
src/lib/simulation/optimizer.ts
src/lib/simulation/scenarios.ts
src/lib/simulation/simulate.ts
src/lib/simulation/timeline.ts
src/lib/simulation/tools.ts
src/lib/simulation/types.ts
src/lib/simulation/validate.ts
tests/advice-route.test.ts
tests/advice.test.ts
tests/agent-route.test.ts
tests/analysis-route.test.ts
tests/analysis.test.ts
tests/geo.test.mjs
tests/optimizer.test.ts
tests/simulation.test.ts
tests/status.test.mjs
tsconfig.json
vitest.config.mjs
Ветки (7)

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

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

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