О проекте
Команда: SE G_YS
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Рабочий MVP
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Городской симулятор распределения бюджета: 5 решений по районам с лимитом 100, детерминированный результат на конец Q8, SVG-карта районов, календарь лагов, история попыток и сравнение с глобальным оптимумом («What If»).
Как устроено
Черновик сценария проверяется FastAPI-пакетом draft-api без раскрытия Score. POST /api/scenarios/accept на Node-сервере считает детерминированный результат Q8. «What If» полным перебором проходит все 694 395 допустимых сценариев тем же валидатором и калькулятором и показывает глобальный максимум, результат кэшируется по версии модели. Есть подбор лучшей одиночной замены и разбор вклада решений по Шэпли. AI-разбор идёт через OpenAI Responses API со строгой JSON-схемой, каждый факт проверяется, при ошибке остаётся базовый разбор. Интерфейс на ru/kk, история попыток в localStorage.
Ключевые решения
- полный перебор сценариев
- Shapley value (вклад решений)
- best single replacement
- OpenAI Responses API strict JSON schema
- проверка фактов AI-ответа
- кэширование по версии модели
Метрики
Холодный полный перебор 694 395 допустимых сценариев ≈ 11.2 с
Особенности
Сравнение с глобальным оптимумом полным перебором и разложение вклада решений по Шэпли дают игроку объективную точку отсчёта. Разработка шла по PRD и issue-driven TDD с Codex-скиллами в .agents/skills.
Стек
Node.js · FastAPI · Python · uv · Vite · React · TypeScript · Docker
Модели
OpenAI gpt-4o-mini
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-78afec8c-se-g-ys.
README
README.mdhack-78afec8c-se-g-ys
Hackathon team repository for SE G_YS
Локальный запуск
QALA показывает исходные данные пяти районов и каталог 14 мероприятий, а затем позволяет собрать и проверить черновик сценария. Проверка не раскрывает Score.
Один раз установите зависимости:
uv sync --locked --project packages/draft-api
npm ci --prefix packages/draft-ui
npm start
Откройте http://127.0.0.1:5173. Скрипт запускает FastAPI на порту 8013 и Vite на порту 5173; Vite передаёт запросы /api в FastAPI.
Запуск в Docker
Для запуска полной текущей веб-версии достаточно Docker: образ содержит Node-сервер QALA и не требует установки зависимостей на хосте.
docker build --tag qala .
docker run --rm --name qala --publish 3000:3000 qala
Откройте http://127.0.0.1:3000. Чтобы использовать AI-разбор, передайте серверный ключ при запуске, например docker run --rm --publish 3000:3000 --env OPENAI_API_KEY qala. Не добавляйте ключ в образ или репозиторий.
Проверка:
npm test
uv run --project packages/draft-api pytest
npm test --prefix packages/draft-ui
Каталог доступен через GET /api/catalog, а проверка черновика — через POST /api/drafts/validate.
Принятие сценария выполняется через POST /api/scenarios/accept с JSON-полем decisions из пяти решений. Сервер возвращает причины отказа без результата для невалидного набора и детерминированный результат Q8 для принятого сценария.
После принятия браузер сохраняет неизменяемую попытку в localStorage. История доступна только в том же браузере: из неё можно открыть результат, создать редактируемую копию с тем же лимитом 100 или сравнить любые две попытки по решениям, расходам, Score и изменениям показателей районов.
Phase 2: карта, лаги и What If
После принятия сценария доступна проектная SVG-карта пяти синтетических районов. Район можно выбрать мышью или клавиатурой; рядом всегда есть текстовое описание исходных и, после принятия, Q8-показателей и решений. Цвет и геометрия не являются единственным источником данных. В черновике карта показывает только выбранные решения, их охват и стоимость — Score и значения Q8 не раскрываются.
Принятый сценарий также получает календарь лагов с пятью строками и Q1–Q8. Маркер обозначает начало действия мероприятия; это не поквартальная симуляция и не содержит промежуточных Score или показателей. Весь результат относится к концу Q8.
Кнопка What If: сравнить с глобальным максимумом появляется только у принятой попытки и запускается только по явному нажатию. Сервер перебирает все допустимые неупорядоченные сценарии активного каталога, используя тот же валидатор и калькулятор, затем показывает Score, разницу, пять решений и стоимость стабильного представителя. Равные максимумы выбираются по меньшей стоимости, затем по каноническому порядку идентификаторов и районов. Результат сохраняется с попыткой; он относится исключительно к синтетическому каталогу и формуле и не является рекомендацией реальной городской политики.
Измерение глобального поиска
Повторяемая команда для холодного измерения:
time node --test test/issue12-global-comparison.test.js
На текущем ноутбуке разработки (2026-09-23) сам холодный перебор 694 395 допустимых сценариев занял примерно 11.2 с. Сервер кеширует результат только пока его вычисленная версия модели совпадает с текущими данными и формулой; изменение каталога или модели создаёт другую версию и не использует старый ответ. Для демонстрационного ноутбука измерение следует повторить этой же командой.
AI-разбор OpenAI
После принятия браузер сразу сохраняет расчётный результат и базовый разбор, затем асинхронно вызывает POST /api/scenarios/analyze с теми же решениями. Сервер пересчитывает результат, отправляет только рассчитанные факты в OpenAI Responses API со строгой JSON-схемой и проверяет каждый факт, обоснование и точечную замену перед показом AI-разбора. Ошибка сети, тайм-аут или некорректный ответ оставляют доступным явно обозначенный базовый разбор; кнопка «Повторить AI-разбор» не создаёт новую попытку.
Для живого вызова задайте серверные переменные окружения перед запуском:
export OPENAI_API_KEY='...'
export OPENAI_MODEL='gpt-4o-mini' # необязательно
export OPENAI_TIMEOUT_MS=12000 # необязательно
npm start
Ключ не передаётся браузеру и не сохраняется в localStorage. Без OPENAI_API_KEY приложение продолжает работать с базовым разбором.
The cached API at POST /api/attempts/:attemptId/analysis returns one verified AI version per attempt and locale (ru or kk), deduplicating concurrent requests and falling back to the localized basic analysis on failure.
Авторы коммитов
История коммитов
- 2026-09-23 18:02chore: add Docker run workflowsulte4
- 2026-09-23 17:46feat: add Shapley impact breakdownsulte4
- 2026-09-23 17:44merge: block invalid scenario acceptanceYertugan06
- 2026-09-23 17:41feat: retain global comparison historysulte4
- 2026-09-23 17:38feat: add global What If comparisonsulte4
- 2026-09-23 17:35feat: block invalid scenario acceptanceTamerlan Yessimov
- 2026-09-23 17:33feat: add Q1-Q8 lag calendar for accepted scenariosTamerlan Yessimov
- 2026-09-23 17:33feat: persist global comparisons in historysulte4
Все коммиты (38)
- 2026-09-23 17:31Implement Phase 2 visual analysis and global comparisonYertugan06
- 2026-09-23 17:21chore: prdYertugan06
- 2026-09-23 17:20Merge remote-tracking branch 'origin/feature/issue-10-bilingual-ai'Yertugan06
- 2026-09-23 17:14merge: prevent budget overspending in draftsYertugan06
- 2026-09-23 17:12merge: add verified OpenAI scenario analysisYertugan06
- 2026-09-23 17:08chore: ignore local OpenAI environmentYertugan06
- 2026-09-23 17:06feat: add bilingual cached AI analysesTamerlan Yessimov
- 2026-09-23 17:04feat: add verified OpenAI scenario analysisYertugan06
- 2026-09-23 17:02Merge branch 'feat/issue-9'Yertugan06
- 2026-09-23 17:00feat: prevent budget overspending in draftsTamerlan Yessimov
- 2026-09-23 16:55feat: add accessible district scenario mapsulte4
- 2026-09-23 16:55include calculated facts and basic analysisTamerlan Yessimov
- 2026-09-23 16:54merge history into localized issue baseTamerlan Yessimov
- 2026-09-23 16:54Merge branch 'feat/issue-8-history'Yertugan06
- 2026-09-23 16:52feat: localize QALA in Kazakhsulte4
- 2026-09-23 16:49feat: save and compare accepted attemptssulte4
- 2026-09-23 16:47feat: add calculated facts and basic analysisYertugan06
- 2026-09-23 16:44Merge remote-tracking branch 'origin/main' into feat/issue-9sulte4
- 2026-09-23 16:39Merge branch 'feature/issue-4-5-merged'Yertugan06
- 2026-09-23 16:32Merge branch 'feature/issue-4-q8-result' into feature/issue-5-recommendationTamerlan Yessimov
- 2026-09-23 16:30feat: combine catalog and draft editorsulte4
- 2026-09-23 16:28chore: localize recommendation fallbackTamerlan Yessimov
- 2026-09-23 16:27feat: recommend best single replacementTamerlan Yessimov
- 2026-09-23 16:25feat: accept scenarios and calculate deterministic Q8 resultTamerlan Yessimov
- 2026-09-23 16:24feat: accept scenarios and calculate deterministic Q8 resultTamerlan Yessimov
- 2026-09-23 16:01feat: add initial city and event catalog pathTamerlan Yessimov
- 2026-09-23 15:28chore : prdYertugan06
- 2026-09-23 14:45chore: context processYertugan06
- 2026-09-23 11:35Make skill cross-references portable to CodexYertugan06
- 2026-09-23 11:29Add Codex-compatible skills under .agents/skillsYertugan06
Структура репозитория (84 файлов)
.agents/skills/grill-with-docs/ADR-FORMAT.md .agents/skills/grill-with-docs/CONTEXT-FORMAT.md .agents/skills/grill-with-docs/SKILL.md .agents/skills/implement/SKILL.md .agents/skills/implement/agents/openai.yaml .agents/skills/improve-codebase-architecture/DEEPENING.md .agents/skills/improve-codebase-architecture/HTML-REPORT.md .agents/skills/improve-codebase-architecture/INTERFACE-DESIGN.md .agents/skills/improve-codebase-architecture/LANGUAGE.md .agents/skills/improve-codebase-architecture/SKILL.md .agents/skills/prototype/LOGIC.md .agents/skills/prototype/SKILL.md .agents/skills/prototype/UI.md .agents/skills/resolving-merge-conflicts/SKILL.md .agents/skills/tdd/SKILL.md .agents/skills/tdd/deep-modules.md .agents/skills/tdd/interface-design.md .agents/skills/tdd/mocking.md .agents/skills/tdd/refactoring.md .agents/skills/tdd/tests.md .agents/skills/to-issues/SKILL.md .agents/skills/to-prd/SKILL.md .dockerignore .gitignore CONTEXT.md Dockerfile README.md Requirements.md dataset.md docs/PLAN.md docs/PRD-phase-2.md docs/PRD-phase-3.md docs/PRD.md package.json packages/draft-api/.gitignore packages/draft-api/README.md packages/draft-api/examples/app.py packages/draft-api/examples/catalog.json packages/draft-api/pyproject.toml packages/draft-api/src/qala_draft/__init__.py packages/draft-api/src/qala_draft/api.py packages/draft-api/src/qala_draft/core.py packages/draft-api/tests/conftest.py packages/draft-api/tests/test_contract.py packages/draft-api/tests/test_draft_api.py packages/draft-api/uv.lock packages/draft-ui/.gitignore packages/draft-ui/README.md packages/draft-ui/demo/main.tsx packages/draft-ui/demo/style.css packages/draft-ui/index.html packages/draft-ui/package-lock.json packages/draft-ui/package.json packages/draft-ui/src/DraftEditor.tsx packages/draft-ui/src/client.ts packages/draft-ui/src/index.ts packages/draft-ui/src/messages.ts packages/draft-ui/src/style.css packages/draft-ui/src/types.ts packages/draft-ui/tests/DraftEditor.test.tsx packages/draft-ui/tests/server.ts packages/draft-ui/tests/setup.ts packages/draft-ui/tsconfig.json packages/draft-ui/vite.config.ts public/client.js public/history.js run.sh server.js src/ai-analysis.js src/data.js src/i18n.js src/openai-analysis-provider.js src/simulation.js src/view.js test/catalog.test.js test/issue10-ai-analysis.test.js test/issue11-budget.test.js test/issue12-global-comparison.test.js test/issue19-acceptance.test.js test/issue4-scenario.test.js test/issue6-basic-analysis.test.js test/issue7-ai-analysis.test.js test/issue8-history.test.js test/issue9-localization.test.js
Ветки (15)
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.