О проекте
Команда: id
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Доведённый продукт
Задача кейса: «Граф денег»: восстановить структуру организованной группы по сети транзакций (AML)
Суть
Локальное приложение для AML-аналитика: по транзакционной сети (nodes/edges/transactions.parquet) присваивает узлам роли и score, строит кластеры, приоритеты и выгружает три CSV задания; React-интерфейс с графом и AI-объяснениями.
Как устроено
Пайплайн run_pipeline читает Parquet, делает аудит схемы, считает базовые метрики (включая взвешенный направленный PageRank), временные признаки по датам, роли по правилам с численными основаниями из config/rules.json, кластеры Louvain и приоритеты; результат сохраняется как неизменяемый артефакт real-<hash> с manifest и SHA256. Python-бэкенд (uvicorn, Swagger) отдаёт API карточек узлов/кластеров, подграфа с усечением и выгрузок; React/TS-фронтенд показывает граф и таблицу приоритетов. AI (OpenAI, опционально) объясняет выбранный узел/кластер по ограниченному контексту, без ключа работают локальные объяснения.
Ключевые решения
- Louvain
- weighted directed PageRank
- rule-based role scoring
- temporal activity features
- OpenAPI contract
- artifact manifest + SHA256
Особенности
Сильная инженерная дисциплина: неизменяемые артефакты расчёта с manifest и контрольными суммами, контракт API v1 с run_id в заголовках, строгое разделение реальных данных, фикстур и синтетического демо-редактора; параллельная работа по планам A/B/C/D (аналитика/бэкенд/фронтенд/AI). Роли явно подаются как гипотезы, а не вероятности виновности.
Стек
Python · FastAPI · Uvicorn · React · TypeScript · Vite · Docker · Docker Compose · GitHub Actions
Модели
OpenAI (модель задаётся AI_MODEL)
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-686ce1df-id.
README
README.mdГраф денег
Локальное приложение для исследования транзакционной сети: роли, кластеры, приоритеты, граф и объяснения. Роли и score — аналитические гипотезы, не вероятности виновности; ограничения исходной выборки сохраняются в карточках.
Полный расчёт обрабатывает предоставленные 2248 узлов, 3119 направленных связей и 4840 переводов. Реализованы правила с численными основаниями, кластеры Louvain, признаки активности по датам, API, React-интерфейс и три CSV задания. AI необязателен: пользователь подтвердил работу с API-ключами; автоматические проверки провайдера используют имитацию. Методология, проверки реального расчёта.
Основной запуск: реальные данные в Docker
Нужен Docker Desktop с Linux-контейнерами на Windows/macOS либо Docker Engine с Compose v2+ на Linux. Отдельная установка Python и Node.js не нужна. Все команды выполнять из корня этого репозитория, где находятся compose.yaml и Dockerfile.
Разместить три предоставленных файла в data/raw/:
data/raw/nodes.parquet
data/raw/edges.parquet
data/raw/transactions.parquet
Запустить:
docker compose -f compose.yaml -f compose.real.yaml up --build --wait --wait-timeout 90
Открыть http://localhost:8000. Если в локальном .env задан APP_PORT, использовать этот порт. При каждом старте выполняется полный расчёт из Parquet; ошибка входа останавливает запуск. Каталог входа подключён только для чтения, результаты сохраняются в Docker volume. Редактор искусственных записей в этом режиме отключён. Health возвращает data_ready=true и run_id с префиксом real-.
# Проверка интерфейса, API и всех выгрузок; внешние AI-вызовы не выполняются
docker compose -f compose.yaml -f compose.real.yaml exec -T app python scripts/real_smoke.py --base-url http://127.0.0.1:8000
# Логи и остановка; данные и volume сохраняются
docker compose -f compose.yaml -f compose.real.yaml logs --tail=100 app
docker compose -f compose.yaml -f compose.real.yaml down
Для другого каталога исходных данных задать AML_DATA_DIR в локальном .env. Настройка AI также находится только в .env: OPENAI_API_KEY, AI_MODEL и при необходимости AI_PROVIDER=openai. Создать .env по образцу .env.example, если его ещё нет; существующий файл не перезаписывать. Реальные ключи не вписывать в .env.example, Compose или Dockerfile. Без ключа и модели работают локальные объяснения. Подробности: Docker, AI.
Явные -f выбирают нужный режим даже при старом COMPOSE_FILE в окружении. Одиночный compose.yaml по-прежнему служит для искусственных фикстур; для реальных данных нужен compose.real.yaml или готовые артефакты через compose.artifacts.yaml.
Запуск без Docker
Нужны Python 3.12+ и Node.js 22.12+. Исходные три Parquet находятся в data/raw/. На macOS/Linux:
python3.12 -m venv .venv
.venv/bin/python -m pip install -r requirements.lock
npm --prefix frontend ci
npm --prefix frontend run build
.venv/bin/python -m backend --data-dir data/raw
Windows PowerShell:
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.lock
npm --prefix frontend ci
npm --prefix frontend run build
.\.venv\Scripts\python.exe -m backend --data-dir data/raw
Открыть http://127.0.0.1:8000. При занятом порте добавить, например, --port 8003 и открыть именно этот адрес. Процесс загружает один снимок расчёта; для другого набора нужно перезапустить сервер. API: health, Swagger. Оформление Swagger использует CDN; само приложение и contracts/openapi.json доступны локально.
Полный расчёт и CSV задания
Только расчёт, без веб-сервера (в Windows заменить .venv/bin/python на .venv\Scripts\python.exe):
.venv/bin/python -m backend.app.analytics.cli run --input-dir data/raw --output-dir artifacts --rules config/rules.json --mapping config/input_mapping.json
Команда сообщает run_dir, фактические количества и полное время. Результат — artifacts/real-<hash>/. Повторный расчёт выполняется заново, проверяет содержимое существующего неизменяемого результата и не переписывает его. В Docker тот же каталог находится внутри volume по пути /app/artifacts/real-<hash>/.
В меню «Выгрузки» доступны:
| Файл | Содержимое |
|---|---|
nodes_roles.csv | Все 2248 узлов, роль и score, кластер, приоритет, численные основания и все базовые метрики starter, включая взвешенный направленный PageRank |
clusters.csv | Строка на кластер: число узлов и seed, внутренняя сумма, ведущие gid и гипотеза назначения |
top_nodes.csv | Первые 20 узлов по убыванию приоритета с объяснениями |
Обязательные столбцы starter сохранены. CSV использует score 0–1; интерфейс и внутренние файлы roles.csv/priorities.csv — 0–100. NA явно обозначает неопределённое отношение при нулевом входе или неизвестный признак и не заменяет их нулём. Все строковые gid, включая ведущие нули, сохраняются. Правила и формулы описаны в методологии.
Проверка готового каталога (подставить run_dir из вывода расчёта):
.venv/bin/python scripts/verify_task_exports.py "artifacts/real-<hash>"
Артефакты также содержат temporal.json, diagnostics.json, точные правила и mapping, контрольные суммы и паспорт запуска. Исходные Parquet не переписываются; data/ и artifacts/ исключены из Git. Расчёт выполняется локально без внешнего AI. Все 19 изолятов сохранены: полный граф имеет 35 компонент, из них 16 с рёбрами.
Загрузка уже рассчитанного набора
Для Python-сервера:
.venv/bin/python -m backend --run-dir "artifacts/real-<hash>"
Для Docker задать в локальном .env путь AML_ARTIFACTS_DIR=./artifacts/real-<hash>, затем:
docker compose -f compose.yaml -f compose.artifacts.yaml up --build --wait --wait-timeout 90
В этом режиме готовый каталог подключён только для чтения; новый расчёт не запускается. Сервер проверяет manifest.json со статусом complete, все перечисленные файлы, модели, ссылки, количества, SHA256 правил и соответствие CSV карточкам. Ошибка загрузки даёт data_ready=false и HTTP 503; фикстуры не подставляются. Для другого результата указать новый каталог и повторить up.
Можно задать AML_RUN_DIR в .env и запустить python -m uvicorn backend.app.main:app. Существующее окружение имеет приоритет над .env. Источники AML_FIXTURE_MODE, AML_DEMO_MODE и AML_RUN_DIR взаимно исключаются; явный CLI-флаг источника заменяет настройки источника из окружения.
Отдельные искусственные режимы
Для разработки без предоставленных данных:
# Четыре контрактных примера
.venv/bin/python -m backend --fixtures
# Редактируемое синтетическое демо
.venv/bin/python -m backend --demo
# Контрактные фикстуры в Docker
docker compose -f compose.yaml up --build --wait --wait-timeout 90
Набор fixture-contract-v1 содержит 0007, 7, 0012 и isolated; интерфейс явно отмечает искусственные данные. Эти роли и приоритеты — примеры формата. Отдельные frontend-команды npm run fixtures и npm run dev:fixtures также запускают только искусственные данные. Обычный npm run dev работает с backend по адресу, заданному в API_PROXY_TARGET; по умолчанию это порт 8000.
API и интеграция команды
| Маршрут | Назначение |
|---|---|
GET /api/health | Готовность текущего запуска |
GET /api/node?gid=… | Точная карточка строкового gid, включая /, Unicode и сегменты ./.. |
GET /api/nodes/{gid} | Совместимый маршрут карточки через путь; поддерживает / внутри gid |
GET /api/priorities?limit=20&offset=0 | Приоритеты, score убывает, затем gid возрастает |
GET /api/clusters | Список кластеров |
GET /api/cluster?cluster_id=… | Точная карточка кластера с непрозрачным строковым ID |
GET /api/clusters/{cluster_id} | Совместимый маршрут карточки через путь; поддерживает / внутри ID |
GET /api/graph | Ровно один gid, cluster_id или component_id; radius 1/2, limit 1–300 |
GET /api/exports/{name} | Логическое имя обязательного файла, manifest или точный ключ из manifest.files |
POST /api/ai/explain | Объяснение выбранного узла/кластера или локальная справка |
POST /api/ai/investigate | Вопрос по выбранному узлу/кластеру или локальная справка |
Граф обходит связи в обе стороны, сохраняет направление рёбер и выбранный узел. Усечение сортируется по расстоянию, priority_score и gid; ответ сообщает truncated, total_nodes, shown_nodes. Метрики карточек берутся из полного расчёта. null сохраняется отдельно от нуля, денежные строки не преобразуются в float.
Для точного поиска frontend использует query-маршруты /api/node и /api/cluster с кодированием параметров через URLSearchParams. Это сохраняет ID с точками и разделителями без нормализации сегментов пути браузером. Пустой или отсутствующий ID даёт 422, неизвестный — 404.
Все результаты связаны contract_version="1.0" и run_id. Ошибки имеют форму {error:{code,message},run_id}: 404 — неизвестный id/экспорт, 422 — параметры, 503 — нет завершённого расчёта, 409 — устаревший run_id AI-запроса. Заголовки API: X-Data-Source, X-Contract-Version, X-Run-Id, Cache-Control: no-store. В X-Run-Id специальные и Unicode-символы percent-encoded; JSON содержит исходную строку без изменения.
- A:
backend.app.analytics.pipeline.run_pipelineвыполняет чтение, аудит, роли, кластеры, приоритеты и экспорт. Модели — вbackend/app/contracts.py;ResultStore.check_concentrationвызывает общий расчёт концентрации. Зависимости аналитики включены вrequirements.lockи обычную установку проекта. - C: снимок OpenAPI, общие примеры и запуск frontend. Dev proxy:
/api→http://127.0.0.1:8000. Разрешён CORS-originhttp://localhost:5173; другой локальный origin задаётсяAML_FRONTEND_ORIGIN. После сборкиfrontend/dist/index.htmlFastAPI автоматически раздаёт этот каталог и SPA-маршруты. Для отдельного frontend dev-сервера адрес backend задаётся черезAPI_PROXY_TARGETвfrontend/.env.local; при реальном backend на порту 8003 нужноAPI_PROXY_TARGET=http://127.0.0.1:8003. Собранный интерфейс Docker обращается к API того же приложения. - D: подключены async-функции
backend.app.ai.service.explain(request, store)иinvestigate(request, store), возвращающиеAIResponse. HTTP-слой проверяет существование target и текущий run_id до вызова и сохраняет коды ошибок запросов D. Доступны методы Store из контракта, типыExplainRequest,InvestigateRequest,AIResponse.
Без OPENAI_API_KEY и AI_MODEL оба AI-маршрута возвращают status="fallback" с локальными объяснениями, основаниями и ограничениями. Для провайдера используется AI_PROVIDER=openai; модель задаётся конфигурацией. Общий срок — AI_TIMEOUT_SECONDS (не более 30), бюджет функций — AI_MAX_TOOL_CALLS (0–3). D проверяет ссылки на основания и числовые утверждения; сбой даёт локальную справку с уже завершёнными проверками. Неверный формат ответа или несовместимый run_id на границе HTTP дают локальную справку B. Вопрос длиннее 4000 символов получает 422. Подробности и пределы проверки достоверности — в инструкции AI. Работу живого AI через свои API-ключи пользователь подтвердил 23.09.2026. Автоматические проверки используют имитацию провайдера; отдельный протокол с моделью и задержкой живого API не записывался.
Проверки
Из корня проекта на macOS/Linux:
.venv/bin/python -m pytest -q
.venv/bin/python -m pip check
.venv/bin/python -m backend.app.openapi --check
npm --prefix frontend test
npm --prefix frontend run build
В Windows использовать .venv\Scripts\python.exe. API/AI-тесты используют искусственные данные и имитацию провайдера, без платных запросов. Полные команды Docker и проверки реального HTTP-сервера — в инструкции Docker. Независимый verify_task_exports.py проверяет CSV, числовые значения, кластеры, порядок топа и стационарное уравнение PageRank.
Фактические прогоны, время полного расчёта и границы проверки записаны в отчёте реального запуска. Ранние отчёты на 102/206/233 теста относятся к этапам разработки API; их нельзя принимать за количество проверок текущей версии. Есть предупреждение устаревшего alias Starlette/AnyIO, не влияющее на выполненные проверки.
Рост до порядка миллиона узлов требует пакетной агрегации, компактного хранилища графа и выдачи подграфов по запросу. Сейчас Store держит этот небольшой набор и выгрузки в памяти; масштаб до миллиона узлов не измерялся.
Контекст и планы
Комплект сдачи и его проверка описаны в инструкции комплекта. Для защиты подготовлен сценарий демо на 3–5 минут. Архив собирается локально из чистой закоммиченной версии:
.venv/bin/python scripts/package_submission.py --run-dir "artifacts/<run_id>" --verification data/audit/submission-csv-verification.json
Протокол --verification должен относиться к этому же расчёту и содержать
результат verify_task_exports.py; в текущем комплекте он дополнен итогами
Docker и воспроизводимости. Архивы находятся в игнорируемом submission/.
Авторы коммитов
История коммитов
- 2026-09-23 17:28Merge pull request #3 from BAITC-Hacks/codex/submission-readyMubtal Dinmukhamed
- 2026-09-23 17:26docs: complete submission verification and real-data demo guideDimash
- 2026-09-23 17:20feat: package verified submission without credentials or raw inputDimash
- 2026-09-23 17:13feat: provide verified Docker startup from real Parquet inputDimash
- 2026-09-23 17:06fix: load AI credentials from local environment onlyDimash
- 2026-09-23 17:04fix: preserve full starter CSV schema and weighted PageRankDimash
- 2026-09-23 16:46Merge pull request #2 from BAITC-Hacks/codex/real-data-pipelineMubtal Dinmukhamed
- 2026-09-23 16:39merge: integrate main review fixes with real-data workflowsDimash
Все коммиты (29)
- 2026-09-23 16:36feat: complete real-data analysis and editable demo workflowsDimash
- 2026-09-23 15:56docs: plan persistent named demo editor implementationDimash
- 2026-09-23 15:48first code reviewBirzhan
- 2026-09-23 15:46docs: add human names to synthetic demo designDimash
- 2026-09-23 15:43Code review frontendleonalib
- 2026-09-23 15:41docs: specify editable synthetic demoDimash
- 2026-09-23 15:39first code reviewBirzhan
- 2026-09-23 15:37Setting up dockerDimash
- 2026-09-23 15:36Setting up dockerDimash
- 2026-09-23 14:58Merge latest main while preserving backend integrationBirzhan
- 2026-09-23 14:55Merge codex/backend into main and integrate AI serviceBirzhan
- 2026-09-23 14:53Merge remote-tracking branch 'origin/main'leonalib
- 2026-09-23 14:51Merge remote-tracking branch 'origin/main' into codex/frontendleonalib
- 2026-09-23 14:51Merge pull request #1 from BAITC-Hacks/codex/analyticsMubtal Dinmukhamed
- 2026-09-23 14:50Merge main into analytics and remove generated filesDimash
- 2026-09-23 14:48feat(frontend): implement money graph analyst workspaceleonalib
- 2026-09-23 14:45feat: implement plan B backend and contract APIBirzhan
- 2026-09-23 14:44feat(ai): implement grounded explanations and bounded investigationsDimash
- 2026-09-23 14:40finishing first version of plan ADimash
- 2026-09-23 14:38feat(analytics): add schema audit and exact graph metricsDimash
- 2026-09-23 14:16docs: define architecture and parallel hackathon work plansDimash
Структура репозитория (180 файлов)
.dockerignore .env.example .gitattributes .github/workflows/docker.yml .gitignore Dockerfile README.md backend/__init__.py backend/__main__.py backend/app/__init__.py backend/app/ai/.gitignore backend/app/ai/__init__.py backend/app/ai/context.py backend/app/ai/prompts.py backend/app/ai/provider.py backend/app/ai/service.py backend/app/ai/smoke.py backend/app/ai/tools.py backend/app/ai/validation.py backend/app/analytics/__init__.py backend/app/analytics/audit.py backend/app/analytics/checks.py backend/app/analytics/cli.py backend/app/analytics/clusters.py backend/app/analytics/derive.py backend/app/analytics/exports.py backend/app/analytics/features.py backend/app/analytics/io.py backend/app/analytics/pipeline.py backend/app/analytics/priority.py backend/app/analytics/requirements-tested.txt backend/app/analytics/roles.py backend/app/analytics/temporal.py backend/app/api/__init__.py backend/app/api/ai.py backend/app/api/clusters.py backend/app/api/demo.py backend/app/api/dependencies.py backend/app/api/exports.py backend/app/api/graph.py backend/app/api/nodes.py backend/app/config.py backend/app/contracts.py backend/app/demo/__init__.py backend/app/demo/build.py backend/app/demo/derive.py backend/app/demo/generator.py backend/app/demo/locking.py backend/app/demo/models.py backend/app/demo/repository.py backend/app/demo/rules.json backend/app/errors.py backend/app/main.py backend/app/openapi.py backend/app/snapshots.py backend/app/store.py compose.artifacts.yaml compose.real.yaml compose.yaml config/input_mapping.json config/rules.json contracts/openapi.json docker/healthcheck.py docs/ai.md docs/contracts/money-graph-v1.md docs/data-intake-2026-09-23.md docs/demo-script.md docs/docker.md docs/methodology.md docs/real-data-verification.md docs/submission.md docs/superpowers/plans/2026-09-23-money-graph-a-analytics.md docs/superpowers/plans/2026-09-23-money-graph-b-backend.md docs/superpowers/plans/2026-09-23-money-graph-c-frontend.md docs/superpowers/plans/2026-09-23-money-graph-d-ai.md docs/superpowers/plans/2026-09-23-money-graph-parallel.md docs/superpowers/plans/2026-09-23-real-data-pipeline.md docs/superpowers/plans/2026-09-23-submission-ready.md docs/superpowers/plans/2026-09-23-synthetic-demo-editor.md docs/superpowers/specs/2026-09-23-money-graph-design.md docs/superpowers/specs/2026-09-23-synthetic-demo-editor-design.md frontend/.env.fixture frontend/.gitignore frontend/README.md frontend/docs/frontend-fixture-1366.png frontend/docs/implementation.md frontend/docs/review-fixes.md frontend/docs/verification.md frontend/index.html frontend/package-lock.json frontend/package.json frontend/src/App.tsx frontend/src/api/client.ts frontend/src/api/demo.ts frontend/src/components/AiPanel.tsx frontend/src/components/ClusterDetails.tsx frontend/src/components/ClusterList.tsx frontend/src/components/DemoEditor.tsx frontend/src/components/ExportMenu.tsx frontend/src/components/GraphPanel.tsx frontend/src/components/NodeDetails.tsx frontend/src/components/NodePicker.tsx frontend/src/components/PriorityTable.tsx frontend/src/components/SearchPanel.tsx frontend/src/format.ts frontend/src/main.tsx frontend/src/names.ts frontend/src/state/useDemoMutation.ts frontend/src/state/useWorkspace.ts frontend/src/styles.css frontend/src/types/api.ts frontend/src/types/demo.ts frontend/tests/ai-panel.test.tsx frontend/tests/api-client.test.ts frontend/tests/app.test.tsx frontend/tests/data-source.test.tsx frontend/tests/demo-editor.test.tsx frontend/tests/demo-state.test.tsx frontend/tests/graph-import-failure.test.tsx frontend/tests/graph-lazy-loading.test.tsx frontend/tests/graph-panel.test.tsx frontend/tests/helpers.ts frontend/tests/node-names.test.tsx frontend/tests/real-exports.test.tsx frontend/tests/serve-fixtures.mjs frontend/tests/workspace.test.tsx frontend/tsconfig.json frontend/vite.config.ts pyproject.toml requirements.lock scripts/docker_smoke.py scripts/package_submission.py scripts/real_smoke.py scripts/verify_task_exports.py tests/ai/.gitignore tests/ai/__init__.py tests/ai/examples/fallback.json tests/ai/examples/success.json tests/ai/fixtures.py tests/ai/test_provider.py tests/ai/test_service.py tests/ai/test_smoke.py tests/ai/test_tools.py tests/ai/test_validation.py tests/analytics/test_checks.py tests/analytics/test_cli.py tests/analytics/test_derive.py tests/analytics/test_exports.py tests/analytics/test_features.py tests/analytics/test_io.py tests/analytics/test_pipeline.py tests/analytics/test_temporal.py tests/api/conftest.py tests/api/test_ai_boundary.py tests/api/test_config_startup.py tests/api/test_demo.py tests/api/test_frontend_boundaries.py tests/api/test_graph_ordering.py tests/api/test_http_contract.py tests/api/test_identifier_routes.py tests/api/test_integration_regressions.py tests/api/test_merged_ai.py tests/api/test_result_loading.py tests/api/test_server_cli.py tests/api/test_snapshot_publication.py tests/demo/test_build.py tests/demo/test_generator.py tests/demo/test_models.py tests/demo/test_repository.py tests/fixtures/contract-v1/README.md tests/fixtures/contract-v1/ai_response.json tests/fixtures/contract-v1/audit.json tests/fixtures/contract-v1/clusters.json tests/fixtures/contract-v1/edges.json tests/fixtures/contract-v1/manifest.json tests/fixtures/contract-v1/nodes.json tests/fixtures/contract-v1/priorities.csv tests/fixtures/contract-v1/roles.csv tests/fixtures/contract-v1/rules.json tests/packaging/test_submission.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.