О проекте
Команда: Zerohub
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Доведённый продукт
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Городской симулятор: 100 единиц бюджета распределяются по 5 направлениям и районам, считаются изменения индикаторов и индекс качества жизни, AI объясняет плюсы, риски и компромиссы, сценарии сохраняются и сравниваются.
Как устроено
Фронтенд на React/TS отправляет 5 решений в FastAPI. Бэкенд проверяет бюджет (превышение отклоняется с HTTP 422), применяет эффекты инициатив к индикаторам районов (0–100) и считает City Score как 0,8 × средневзвешенный по населению балл районов + 0,2 × минимальный балл района. Сценарии сохраняются в SQLite. AI-адаптер (OpenAI Responses API, gpt-4.1-mini) получает рассчитанные факты и проверенные альтернативы, возвращает структурированный JSON, числа в него подставляются из бэкенда. Без ключа работает явно помеченный rule-based режим.
Ключевые решения
- rule-based simulation
- population-weighted scoring with equity term
- structured JSON output
- grounding against fact catalog
- scenario comparison
Особенности
В формулу индекса добавлен член справедливости: 20% веса у самого слабого района. Ответ LLM проверяется по схеме и каталогу рассчитанных фактов, а числовые плейсхолдеры заполняются значениями бэкенда, поэтому модель не может исказить цифры. Проект с CI, e2e-тестами на Playwright и воспроизводимым тест-планом (итоговый балл 49.819).
Стек
React · TypeScript · Vite · Zod · Pydantic · Python 3.12 · FastAPI · SQLite · Docker Compose · Nginx · Pytest · Vitest · Playwright · GitHub Actions
Модели
gpt-4.1-mini-2025-04-14
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-d875bcea-zerohub.
README
README.mdAkim for 5 Hours
An AI-assisted city-management simulator for the Astana Innovations hackathon.
1. What the product does
Akim for 5 Hours helps users explore how a limited city budget can affect quality of life across hypothetical districts.
Every user starts with the same 100 virtual budget units and the same synthetic dataset. The user chooses a district and one initiative in each of five areas:
- Transport
- Greening
- Social infrastructure
- Safety
- City services
The application checks the budget, calculates changes in district indicators, and produces an Astana Quality of Life Score. With live AI enabled, it explains the strengths, risks, trade-offs and possible consequences of the plan. Users can refine decisions and compare saved scenarios.
Who it is for: city managers, analysts, students and anyone learning about urban budget decisions.
Important: the districts, costs and effects are synthetic educational assumptions. The score is not an official Astana index or a forecast of real city outcomes. The dataset contains no personal data.
Main features
- A shared starting budget and dataset.
- Exactly five decisions: one per development area.
- Budget-overrun prevention in the interface and backend.
- Immediate previews of modeled effects.
- Before-and-after district indicators and city scores.
- Saved scenarios that can be reopened and compared.
- AI explanations and explanations of tested, affordable alternatives.
- An explicitly labeled rule-based mode for running without an API key.
2. Technologies used
| Part | Technologies | Purpose |
|---|---|---|
| Frontend | React, TypeScript, Vite | Simulator interface and results pages |
| API validation | Zod, Pydantic | Validate data exchanged between frontend and backend |
| Backend | Python 3.12, FastAPI, Uvicorn | Budget rules, simulation, API and analysis coordination |
| Database | SQLite | Store scenarios and cached analysis |
| AI | OpenAI Responses API, HTTPX, structured JSON output | Generate explanations from calculated results |
| Deployment | Docker Compose, Nginx | Run both services and forward API requests |
| Testing | Pytest, Vitest, Playwright | Backend, frontend and browser integration checks |
| Automation | GitHub Actions | Run verification and build a source release |
The configured AI model is gpt-4.1-mini-2025-04-14. Provider access must be checked with the team's API key.
How the parts work together
The browser sends the five decisions to FastAPI. The backend validates them, calculates the cost and score, and saves the scenario in SQLite. The AI adapter receives the calculated facts and tested alternatives, then returns explanatory text. The frontend displays the results.
The language model does not calculate or change the official simulation score.
3. Project structure
Use the complete Person 4 package. It already includes the code from Persons 1–3; no additional merging is needed.
| Path | Contents |
|---|---|
frontend/ | React application |
backend/ | API, simulation, database, AI adapter and backend tests |
data/v1/ | Shared synthetic districts, initiatives and scoring configuration |
data/person3/ | Assumption ledger and repeatable evaluation cases |
scripts/ | Development launcher, verification and release tools |
docs/ | Deployment guide, demo script and verification record |
.github/workflows/ci.yml | Continuous integration workflow |
compose.yaml | Configuration for starting the complete application |
.env.example | Example Docker deployment settings |
4. Run the whole project in VS Code
Use this method first. It starts both the frontend and backend.
Before starting
You need:
- The extracted complete project folder, named
akim-teamin the supplied ZIP. - VS Code.
- Docker Desktop installed and running, with Docker Compose available.
- Internet access for the first build to download dependencies and images.
You do not need to install Python or Node.js on your computer for this Docker method.
Step 1 — Open the correct folder
In VS Code, select File → Open Folder → akim-team.
You are in the correct folder if the VS Code file panel shows compose.yaml, frontend and backend together.
Step 2 — Open the terminal
Select Terminal → New Terminal.
Run all commands in this section from the project root—the folder containing compose.yaml.
Step 3 — Check Docker
Run these commands one at a time:
docker --version
docker compose version
Both commands should print a version. If Docker is not recognized, install Docker Desktop and reopen VS Code. If Docker cannot connect to its engine, open Docker Desktop and wait until it is running.
Step 4 — Create the settings file
Windows PowerShell:
Copy-Item .env.example .env
macOS/Linux:
cp .env.example .env
Do this only during the first setup. If .env already exists, keep it and edit its values as needed.
The initial settings use rule-based explanations. This lets you check the simulator before adding an AI key.
Step 5 — Start everything
docker compose up --build
The first build may take several minutes. Leave this terminal open while using the application. You do not need to start either service separately.
Step 6 — Open the application
Open this address in your browser:
Choose your initiatives and districts, then select “See my city’s future.”
Stop and start again
To stop the application, press Ctrl+C in the running terminal.
To start it again:
docker compose up
Saved scenarios remain in the named database volume. Do not run docker compose down -v unless you intentionally want to delete that stored data.
5. Enable real AI analysis
The default rule-based mode does not demonstrate the hackathon's live AI requirement. Complete this section before presenting the AI feature.
- Open the root
.envfile, next tocompose.yaml. - Set these values, replacing the API-key placeholder with your own key:
AKIM_ADVISOR_FACTORY=app.services.team_advisor:TeamAdvisor
AKIM_ANALYSIS_VERSION=person4-ai-v1
OPENAI_API_KEY=your_actual_api_key
OPENAI_MODEL=gpt-4.1-mini-2025-04-14
- Save the file. Open a second VS Code terminal in the project root and run:
docker compose up -d --force-recreate backend frontend
- Open the application and submit a scenario. A successful live assessment displays “Your AI city advisor.”
AI requests require working provider credentials and model access and may incur charges. Keep the key server-side; never put it in frontend code or commit .env.
If AI fails, the calculated score and saved scenario remain available. The backend does not silently replace a failed live AI response with a rule-based one.
After changing the model or prompt, change AKIM_ANALYSIS_VERSION so cached explanations from the earlier configuration are not reused.
6. How the score is calculated
Each district has five quality indicators on a 0–100 scale, with higher values meaning better quality. Initiatives change the targeted district's indicators according to the shared dataset. Resulting indicators are constrained to the 0–100 range.
The five indicators have equal weight in each district's score.
City Quality of Life Score =
0.80 × population-weighted average district score
+ 0.20 × lowest district score
The lowest-district component makes the distribution of benefits matter, alongside the citywide average. These weights are explicit model design choices.
The starting city score is 47.1375, shown as 47.1 when rounded to one decimal. All users use the same starting configuration.
AI output is checked against a structured schema and a catalog of calculated facts. Numeric placeholders are filled with backend values. These checks constrain numeric errors, but they cannot prove every free-form statement is correct; explanations still need human review.
7. Verify the solution manually
Start the application, then perform the checks below.
| Check | What to do | Expected result |
|---|---|---|
| Same starting conditions | Start a new plan in two separate browser sessions | Both receive 100 budget units and the same district data |
| All five areas | Inspect the decision tabs and build a complete plan | Transport, greening, social infrastructure, safety and city services are available |
| Five-decision rule | Try submitting before completing every area | An incomplete plan cannot be submitted successfully |
| Budget control | Build a 100-unit plan, then try a more expensive replacement | An over-budget selection/submission is prevented; backend tests also check HTTP 422 rejection |
| Indicator changes | Submit the repeatable plan below | The district indicators and final city score change |
| Saved results | Reload the results page | The saved scenario remains available |
| Different choices | Refine the plan with a cheaper transport measure and submit again | The resulting score changes |
| Comparison | Open “Compare plans” after saving two plans | Both plans can be compared |
| Live AI | Enable AI, submit a plan and inspect its report | A successful AI report includes a summary, strengths, risks and trade-offs grounded in the scenario |
Repeatable test plan
Select these exact initiatives and districts:
| Area | Initiative | District | Cost |
|---|---|---|---|
| Transport | Bus-priority lanes | District A | 20 |
| Greening | Neighborhood pocket park | District B | 20 |
| Social infrastructure | Local clinic upgrade | District C | 20 |
| Safety | Safer pedestrian crossings | District D | 20 |
| City services | District maintenance crew | District E | 20 |
Expected results for the unchanged v1 dataset:
- Total cost: 100.
- Remaining budget: 0.
- Final score: 49.819, displayed as 49.8.
This plan is stored as distributed_medium_cost in data/person3/evaluation_cases.json.
8. Automated verification
A. Check a running Docker deployment
In a second terminal at the project root:
docker compose ps
Both services should be running and become healthy. In your browser, open:
http://127.0.0.1:8080/api/health
The JSON response should include "status": "ok". Its analysis_source should be rules or ai, matching your configuration. Health alone does not confirm that a real provider request succeeds.
B. Run the full automated test suite
This method requires Python 3.12 and Node.js 24, in addition to the source files. Run from the project root.
Create a Python environment:
python -m venv .venv
If your system uses python3, use python3 -m venv .venv instead.
Activate it on Windows PowerShell:
.\.venv\Scripts\Activate.ps1
Or activate it on macOS/Linux:
source .venv/bin/activate
Install the locked dependencies:
python -m pip install -r backend/requirements.lock
npm --prefix frontend ci
Run backend tests, dataset validation, frontend tests and the production build:
python scripts/verify.py
For browser integration, first install Chromium:
cd frontend
npx playwright install chromium
cd ..
Then run:
python scripts/verify.py --browser
Keep ports 8001 and 5182 free for the browser checks. These checks launch their own temporary services and database. They exercise rule-based analysis and the AI adapter with mocked provider responses; they make no live AI calls.
C. Run a deployment smoke check
With Python available and the Docker application running:
python scripts/smoke.py
This checks health and shared configuration without creating scenarios.
For scenario creation, persistence, overrun rejection, changed scores and analysis:
python scripts/smoke.py --write --analysis
This creates two scenarios and requests one assessment. In AI mode, the assessment may incur provider charges. The scenario checks use the included v1 dataset.
Recorded verification status
| Check | Recorded result |
|---|---|
| Backend tests | 60 passed |
| Frontend model tests | 8 passed |
| TypeScript and production build | Passed |
| Browser integration with rule-based analysis | Passed |
| Browser integration with mocked AI provider | Passed |
| Direct API smoke check | Passed |
| Local development launcher on Linux | Passed |
| Docker/Nginx execution and GitHub Actions | Supplied but not executed in the development environment |
| Real provider call | Not executed; requires the team's API key |
These are results from the assembled project's prior verification, not a guarantee that a new machine is configured correctly. Run the checks again on the machine used for submission. See docs/VERIFICATION.md for details.
9. Optional: run without Docker
Follow the Python/Node dependency setup in Section 8B, then run from the project root with the virtual environment activated:
python scripts/dev.py
Open http://127.0.0.1:5173. The launcher starts the frontend and backend together. Press Ctrl+C to stop them.
For this local method, AI settings belong in backend/.env, following backend/person3.env.example. Docker uses the root .env instead. Do not confuse these two settings locations.
10. Common problems
| Problem | What to check |
|---|---|
docker is not recognized | Install Docker Desktop, then reopen VS Code |
| Cannot connect to Docker engine | Open Docker Desktop and wait for it to start |
| No Compose configuration file found | Open the terminal in the folder containing compose.yaml |
.env.example cannot be found | You are probably in the wrong folder; open akim-team |
| Port 8080 is occupied | Set AKIM_PORT=8081 in root .env, rerun Compose and open port 8081 |
| Frontend opens but API fails | Check that both services are healthy and inspect their logs |
| AI report fails | Check the server-side key, model access, provider quota and backend logs |
| An old AI explanation appears | Change AKIM_ANALYSIS_VERSION and recreate the services |
Inspect logs with:
docker compose logs --tail=100 backend frontend
Do not share your API key when reporting an error.
11. Scope and further documentation
The simulator models immediate indicator effects; it does not model implementation timelines or recurring operating costs. Comparison is between saved plans available in the current browser, not an authenticated cross-team leaderboard. Random city events and automatic slide export are not implemented.
The Docker configuration binds to localhost by default. Public hosting requires additional access control and operational safeguards; this prototype does not implement user accounts or per-user authorization.
Further guides:
docs/DEMO.md— presentation walkthrough.docs/DEPLOYMENT.md— persistence, backup and operations.docs/RUBRIC.md— mapping to hackathon criteria.docs/VERIFICATION.md— detailed test record.
To create a source release with checksums, run:
python scripts/package_release.py
The ZIP is written to artifacts/akim-team-release.zip.
Авторы коммитов
История коммитов
- 2026-09-23 17:42Remove root environment examplerobot
- 2026-09-23 17:41Update project READMErobot
- 2026-09-23 17:37Sync local project staterobot
- 2026-09-23 16:59Merge remote project historyrobot
- 2026-09-23 16:58Initial project importrobot
- 2026-09-23 16:40Merge remote project historyrobot
- 2026-09-23 16:39Initial commitrobot
- 2026-09-23 16:09Merge remote-tracking branch 'origin/main'asim
Все коммиты (10)
- 2026-09-23 16:07Initial backend projectasim
- 2026-09-23 15:41feat(frontend): finalize simulator changes and test suiteJamshid
Структура репозитория (194 файлов)
.dockerignore .github/workflows/ci.yml .gitignore .nvmrc Dockerfile README.md SHA256SUMS backend/.env.example backend/.gitignore backend/Dockerfile backend/app/__init__.py backend/app/ai/__init__.py backend/app/ai/evaluation.py backend/app/ai/evidence.py backend/app/ai/grounding.py backend/app/ai/provider.py backend/app/ai/schemas.py backend/app/ai/settings.py backend/app/config.py backend/app/database.py backend/app/errors.py backend/app/main.py backend/app/models.py backend/app/prompts/person3_advisor.txt backend/app/repositories/__init__.py backend/app/repositories/scenarios.py backend/app/routes/__init__.py backend/app/routes/analysis.py backend/app/routes/configuration.py backend/app/routes/health.py backend/app/routes/scenarios.py backend/app/schemas.py backend/app/services/__init__.py backend/app/services/ai_advisor.py backend/app/services/analysis.py backend/app/services/dataset.py backend/app/services/recommendations.py backend/app/services/scenarios.py backend/app/services/scoring.py backend/app/services/simulation.py backend/app/services/team_advisor.py backend/app/services/validation.py backend/docs/API-CONTRACT.md backend/docs/ARCHITECTURE.md backend/docs/PERSON-3-HANDOFF.md backend/docs/VERIFICATION.md backend/docs/openapi.json backend/person3.env.example backend/pyproject.toml backend/requirements-person3.txt backend/requirements.lock backend/scripts/check-frontend.mjs backend/scripts/export_openapi.py backend/scripts/person3_evaluate.py backend/scripts/person3_live_check.py backend/scripts/person3_validate_data.py backend/scripts/person4_backup.py backend/tests/__init__.py backend/tests/conftest.py backend/tests/fixtures/analysis-response.json backend/tests/fixtures/completed-scenario.json backend/tests/fixtures/config-response.json backend/tests/fixtures/preview-request.json backend/tests/fixtures/preview-response.json backend/tests/fixtures/scenario-request.json backend/tests/person3/__init__.py backend/tests/person3/test_advisor.py backend/tests/person4/__init__.py backend/tests/person4/mock_app.py backend/tests/person4/test_backup.py backend/tests/test_analysis.py backend/tests/test_api.py backend/tests/test_boundaries.py backend/tests/test_simulation.py backend/tests/test_storage.py compose.yaml contracts/analysis-response.json contracts/completed-scenario.json contracts/config-response.json contracts/preview-request.json contracts/preview-response.json contracts/scenario-request.json data/person3/evaluation_cases.json data/person3/initiative_assumptions.json data/v1/data_dictionary.md data/v1/districts.json data/v1/initiatives.json data/v1/model_config.json docs/API-CONTRACT.md docs/DEMO.md docs/DEPLOYMENT.md docs/RUBRIC.md docs/VERIFICATION.md docs/team/PERSON-1.md docs/team/PERSON-2.md docs/team/PERSON-3.md frontend/.dockerignore frontend/.env.example frontend/.gitignore frontend/Dockerfile frontend/contracts/analysis-response.json frontend/contracts/completed-scenario.json frontend/contracts/config-response.json frontend/contracts/preview-request.json frontend/contracts/preview-response.json frontend/contracts/scenario-request.json frontend/docs/API-CONTRACT.md frontend/docs/VERIFICATION.md frontend/index.html frontend/nginx.conf frontend/package-lock.json frontend/package.json frontend/playwright.config.ts frontend/public/favicon.svg frontend/scripts/generate-fixtures.mjs frontend/src/App.tsx frontend/src/api/client.ts frontend/src/api/scenarios.ts frontend/src/api/storage.ts frontend/src/components/AIReport.tsx frontend/src/components/BudgetBar.tsx frontend/src/components/CategoryTabs.tsx frontend/src/components/DecisionSummary.tsx frontend/src/components/DistrictCard.tsx frontend/src/components/DistrictSelector.tsx frontend/src/components/Icon.tsx frontend/src/components/IndicatorComparison.tsx frontend/src/components/InitiativeCard.tsx frontend/src/components/RecommendationCard.tsx frontend/src/components/ScoreCard.tsx frontend/src/demo/config.json frontend/src/demo/engine.test.ts frontend/src/demo/engine.ts frontend/src/hooks/useRoute.ts frontend/src/hooks/useScenario.ts frontend/src/main.tsx frontend/src/pages/ComparePage.tsx frontend/src/pages/ResultsPage.tsx frontend/src/pages/SimulatorPage.tsx frontend/src/pages/WelcomePage.tsx frontend/src/styles/global.css frontend/src/types/simulation.ts frontend/src/utils/decisions.ts frontend/src/utils/format.ts frontend/tests/e2e/api.spec.ts frontend/tests/e2e/simulator.spec.ts frontend/tsconfig.json frontend/vite.config.ts index.html install_person3.py nginx.conf package-lock.json package.json playwright.config.ts public/favicon.svg scripts/check-stack.mjs scripts/dev.py scripts/generate-fixtures.mjs scripts/package_release.py scripts/smoke.py scripts/verify.py src/App.tsx src/api/client.ts src/api/scenarios.ts src/api/storage.ts src/components/AIReport.tsx src/components/BudgetBar.tsx src/components/CategoryTabs.tsx src/components/DecisionSummary.tsx src/components/DistrictCard.tsx src/components/DistrictSelector.tsx src/components/Icon.tsx src/components/IndicatorComparison.tsx src/components/InitiativeCard.tsx src/components/RecommendationCard.tsx src/components/ScoreCard.tsx src/demo/config.json src/demo/engine.test.ts src/demo/engine.ts src/hooks/useRoute.ts src/hooks/useScenario.ts src/main.tsx src/pages/ComparePage.tsx src/pages/ResultsPage.tsx src/pages/SimulatorPage.tsx src/pages/WelcomePage.tsx src/styles/global.css src/types/simulation.ts src/utils/decisions.ts src/utils/format.ts tests/e2e/api.spec.ts tests/e2e/simulator.spec.ts tsconfig.json vite.config.ts
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.