AAYMA
RU
Войти

Граф денег

Локальное приложение для AML-аналитика: по транзакционной сети (nodes/edges/transactions.parquet) присваивает узлам роли и score, строит кластеры…

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

О проекте

Команда: 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-origin http://localhost:5173; другой локальный origin задаётся AML_FRONTEND_ORIGIN. После сборки frontend/dist/index.html FastAPI автоматически раздаёт этот каталог и 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/.

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

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

Mubtal Dinmukhamed 20Birzhan 5leonalib 5

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

  • 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
Ветки (9)

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

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

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