О проекте
Команда: PsychAI
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Рабочий MVP
Задача кейса: «Граф денег»: восстановить структуру организованной группы по сети транзакций (AML)
Суть
Объяснимый пайплайн AML-анализа: каждому из 2 248 клиентов присваивается гипотеза о роли (одна из шести), граф кластеризуется методом Louvain, узлы ранжируются. Три CSV и локальный HTML-дашборд с поиском по gid.
Как устроено
run_pipeline.py валидирует Parquet-файлы и сверяет транзакции с рёбрами. Затем считает степени, суммы, взвешенный PageRank, достижимость от исходных клиентов и порядок переводов по дням (для роли transit). Роли consolidator, transit, distributor, terminal, coordinator и peripheral назначаются детерминированными правилами с порогами, рассчитанными по данным. Louvain даёт кластеры, гипотезы о группах строятся по составу ролей, приоритет считает отдельная формула. viewer.py поднимает локальный SVG-дашборд. LLM не используется.
Ключевые решения
- NetworkX
- weighted PageRank
- Louvain
- rule-based roles
- temporal transit check
Особенности
Команда verify_project.py заново считает результат во временной папке и сравнивает его с submission/, что даёт воспроизводимость. Для роли transit проверяется порядок переводов по календарным дням. На Windows демо запускается двойным щелчком.
Стек
Python · pandas · pyarrow · NetworkX · NumPy · SciPy · HTML/CSS/SVG
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-2c05448e-psychai.
README
README.mdГраф денег
Решение кейса HackAlem AI «Граф денег: восстановление финансовой структуры организованной группы по транзакционной сети».
Задача и пользователь
Аналитику нужно изучить сеть переводов вокруг 81 исходного клиента. Вручную проследить 2 248 участников и 3 119 направленных связей трудно. Проект рассчитывает наблюдаемые признаки, присваивает каждому клиенту гипотезу о роли, группирует клиентов и предлагает порядок дальнейшей проверки. Роль и приоритет не означают виновность и не являются вероятностью правонарушения.
Что реализовано
- Проверка входных Parquet-файлов и согласованности отдельных транзакций с агрегированными рёбрами.
- Направленный взвешенный граф со всеми 2 248
gid, включая изолированные исходные узлы. - Шесть объяснимых ролей:
consolidator,transit,distributor,terminal,coordinator,peripheral. Дляtransitучитывается порядок переводов по календарным дням. - Кластеры Louvain, осторожные гипотезы о группах на основе состава ролей, рейтинг узлов и числовые обоснования решений.
- Три CSV-файла по схеме кейса, готовый снимок выгрузок в submission/ и локальная страница: обзор сети с числом клиентов, связей, исходных клиентов и групп, состав ролей в виде полосы, группы с крупнейшим внутренним оборотом, поиск любого
gid, сравнение его видимого входа и выхода, направленные связи с отдельной строкой и крупной суммой для каждого соседа, подсветка при наведении, усиленная обводка членов того же кластера и причины попадания в топ-20. - Автоматические проверки полного сценария, воспроизводимости результатов и ограничений данных.
- Команда
verify_project.pyпроверяет версию Python и зависимости, заново рассчитывает результаты во временной папке, сравнивает их сsubmission/и формирует страницу просмотра. - Для Windows есть запуск двойным щелчком по
LaunchDemo.vbsпосле установки зависимостей: расчёт и браузер запускаются без терминала; кнопка на странице останавливает локальный сервер.
Основной сценарий и архитектура
run_pipeline.pyчитает включённые в репозиторийdata/nodes.parquet,data/edges.parquetиdata/transactions.parquet.- Программа проверяет схему, количество записей, идентификаторы и суммы рёбер. Затем она вычисляет степени узлов, видимые суммы, PageRank, достижимость от исходных клиентов и поддержку позднейших исходящих переводов.
- Детерминированные правила назначают одну роль каждому узлу. Louvain разбивает граф на кластеры; состав ролей в группе формирует гипотезу для проверки, отдельная формула рассчитывает приоритет узлов.
- Программа создаёт в
out/файлыnodes_roles.csv,clusters.csvиtop_nodes.csvи проверяет их содержимое. viewer.pyпоказывает локальную страницу: аналитик сначала видит обзор набора и распределение ролей, затем вводитgid, читает обоснование и видит входящие и исходящие связи. Сортировка групп по внутреннему обороту не является оценкой угрозы.
Схема компонентов находится в ARCHITECTURE.md. Основной расчёт не зависит от LLM, внешнего API или ручного редактирования данных.
Технологии
| Компонент | Назначение |
|---|---|
| Python 3.10 | Расчёт, проверки и локальный HTTP-сервер |
| pandas, pyarrow | Чтение Parquet, агрегирование транзакций, запись CSV |
| NetworkX, NumPy, SciPy | Графовые показатели, PageRank, кластеры Louvain |
run_pipeline.py | Валидация → признаки → роли → кластеры → рейтинг → CSV |
viewer.py | Локальный HTML/CSS/SVG-дашборд с переходом от обзора к выбранному gid |
verify_project.py | Быстрая офлайн-проверка установки и опубликованных результатов |
LaunchDemo.vbs, launch_demo.pyw | Windows-запуск без терминала; ошибки записываются в out/launcher.log |
tests/test_pipeline.py | Проверка полного сценария и контрольных графов |
AI-модели, GPU, облачные сервисы и API OpenAI/NVIDIA для запуска не используются. AI-агент Codex применялся при разработке проекта.
Системные требования и настройка
- Проверенная среда: 64-битная Windows 10, 64-битный Python 3.10.11. Команды для macOS/Linux приведены ниже, но отдельно на этих системах не проверялись.
- Нужны модули Python
venvиpip, браузер для просмотра результата и доступ к Python Package Index либо локальным пакетам при установке зависимостей. - Точные версии пяти библиотек закреплены в
requirements.txt. Все входные данные находятся вdata/; отдельная загрузка датасета не нужна. - Переменные окружения не требуются. Ключи API, личные аккаунты, подписки и демонстрационные учётные записи не нужны. После установки пакетов расчёт и просмотр работают без интернета.
- По умолчанию входная папка —
data/, выходная —out/, адрес страницы —127.0.0.1:8765. Параметры--dataи--outдоступны обеим программам;--hostи--port— просмотрщику.
Установка и запуск
Откройте PowerShell в корне репозитория (там находятся README.md, requirements.txt, run_pipeline.py и папка data/). Выполните команды по порядку:
python --version
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python run_pipeline.py
Первая команда должна показать Python 3.10.x. Последняя печатает Validated 2248 nodes, 3119 edges, количество кластеров, путь к out/ и время работы. На проверенной машине расчёт занимает около одной секунды; требование кейса — менее пяти минут.
Запуск без терминала на Windows после установки: дважды щёлкните LaunchDemo.vbs. Программа заново рассчитает out/ и откроет страницу в браузере. Внизу страницы нажмите «Остановить сервер», когда закончите работу. Отдельный .exe в репозитории не нужен: запуск использует установленный Python из .venv и стандартные средства Windows. Если браузер не открылся, адрес и сообщения находятся в out/launcher.log; при ошибке появится окно с причиной. Для жюри также сохранён обычный запуск командами ниже.
На macOS/Linux создайте окружение командой python3 -m venv .venv, затем используйте .venv/bin/python вместо .venv\Scripts\python в остальных командах. Если входные или выходные файлы расположены иначе, запустите run_pipeline.py --data PATH --out PATH, а затем viewer.py --data PATH --out PATH с теми же путями.
Как жюри проверить решение
Быстрая проверка одной командой после установки зависимостей:
.venv\Scripts\python verify_project.py
Ожидаются четыре строки OK и сообщение PASS. Команда не меняет out/ или submission/: новый расчёт делается во временной папке. При отсутствии зависимости, входного файла или несовпадении CSV она завершится с конкретной причиной и ненулевым кодом. Это также удобно выполнить перед отправкой итоговой версии.
-
После расчёта должны появиться
out/nodes_roles.csv(2 248 строк данных),out/clusters.csv(66 строк данных на предоставленном наборе) иout/top_nodes.csv(20 строк данных). Вnodes_roles.csvу каждогоgidзаполненыrole,role_score,cluster_id,priority_score,evidence; оценки лежат в[0, 1]. -
Запустите тесты:
.venv\Scripts\python -m unittest discover -s tests -vОжидается
Ran 6 testsиOK. Тесты повторно рассчитывают CSV, сравнивают их с готовыми файлами вsubmission/, проверяют полный охватgid, защиту обрыва четвёртого колена, порядок переводов, поиск произвольного узла, открытие браузера и завершение сервера кнопкой с токеном сеанса. -
Запустите страницу просмотра:
.venv\Scripts\python viewer.pyСтраница откроется в браузере автоматически. Если браузер не открылся, используйте напечатанный адрес, обычно
http://127.0.0.1:8765/. Вверху виден обзор сети, ниже — первый узел рейтинга. В таблице групп нажмите «Открыть участника» или скопируйте любойgidизout/nodes_roles.csv, вставьте в поиск и нажмите «Показать». Должны появиться роль, числовое обоснование, кластер и направленные связи. Поле ввода хранит 18-значныйgidкак текст, без округления. Остановить сервер можно сочетаниемCtrl+C.
Если порт 8765 занят, используйте viewer.py --port 8766. В среде без браузера используйте viewer.py --no-browser: адрес останется в терминале. Если входной файл отсутствует либо имеет неверную структуру, расчёт завершится с сообщением об ошибке вместо фиктивного результата. Папка out/ создаётся локально и в Git не хранится; готовые проверяемые выгрузки находятся в submission/.
Данные и результаты
Источник — анонимизированный набор организаторов HackAlem AI за июль 2026 года; он включён в репозиторий. Полная схема описана в data/README.md.
| Входной файл | Содержимое | Строк |
|---|---|---|
data/nodes.parquet | gid, минимальная глубина, признак исходного клиента | 2 248 |
data/edges.parquet | Плательщик → получатель, общая сумма, число переводов | 3 119 |
data/transactions.parquet | Отдельные переводы с датами и суммами | 4 840 |
| Выходной файл | Основные поля |
|---|---|
out/nodes_roles.csv | gid, role, role_score, cluster_id, priority_score, evidence; также вычисленные признаки |
out/clusters.csv | cluster_id, n_nodes, n_seed, sum_kzt_internal, top_gids, hypothesis |
out/top_nodes.csv | rank, gid, role, priority_score, why |
Внешних интеграций при работе программы нет. Сторонние AML-датасеты не нужны и не загружаются.
Три файла в submission/ являются опубликованным результатом на предоставленных данных; команда run_pipeline.py позволяет жюри независимо получить их заново.
Правила ролей и приоритета
Признаки вычисляются только по видимым переводам: количество контрагентов на входе и выходе, суммы и число переводов, взвешенный PageRank, глубина, достижимость от исходных клиентов и отношение out_kzt / in_kzt. Для предоставленных данных расчётные пороги: fan-in ≥3, fan-out ≥8, вход ≥166 819,7 KZT, выход ≥397 500 KZT, PageRank ≥0,00180444, двусторонний поток ≥43 500 KZT. Программа печатает пороги при каждом запуске.
| Роль | Формальное условие кандидата |
|---|---|
consolidator | Число входящих контрагентов не ниже 90-го процентиля среди положительных значений (минимум 3), а видимый вход — не ниже 75-го процентиля. Для исходных клиентов оценка роли ограничена из-за неполноты входящих данных. |
distributor | Число исходящих контрагентов не ниже 90-го процентиля (минимум 3), а видимый выход — не ниже 75-го процентиля. |
transit | Не исходный клиент; есть вход и выход; меньшая из сумм не ниже медианы двустороннего потока; отношение выхода к входу в [0.8, 1.2]; поддержанный более поздними днями выход не ниже 50% меньшей из двух сумм. |
coordinator | Не менее двух входящих и двух исходящих связей, достижимость от двух исходных клиентов и PageRank не ниже 98-го процентиля. |
terminal | Не исходный клиент на глубине менее 4, с видимым входом и без видимого выхода. Это конечная точка в пределах выгрузки. |
peripheral | Более сильного ролевого признака нет. Узел глубины 4 без видимого выхода не считается автоматически terminal. |
Для временно́й проверки дневные суммы обрабатываются по порядку. Исходящий перевод может опираться только на видимый вход из предыдущего календарного дня или ранее: внутри одного дня время операций неизвестно. Это не доказывает, что далее ушли те же деньги. Порог 50% — открытое эвристическое решение, а не норматив или обученная модель. Столбцы temporal_support_kzt и temporal_support_ratio позволяют проверить его влияние.
Если одному узлу подходят несколько ролей, выбирается наиболее сильный сигнал с фиксированным разрешением равенства. role_score означает силу видимых признаков, не вероятность преступления. Для кластеров Louvain используется ненаправленная проекция с логарифмом сумм и фиксированным начальным значением; направление сохраняется при расчёте ролей и показе связей. priority_score объединяет равновесные процентильные ранги силы роли, числа контрагентов, видимого оборота, PageRank и связи с исходными клиентами. Узлы на границе четвёртого колена получают понижение приоритета. В evidence и why приводятся числовые основания.
Гипотеза кластера описывает сочетание наблюдаемых ролей: признаки сбора и/или распределения, возможного транзита либо отсутствие достаточного сигнала. Она дополняется числом узлов, исходных клиентов и внутренним оборотом. Общая цель участников по этим данным неизвестна; каждая гипотеза требует проверки аналитиком.
Известные ограничения
- Обход ограничен четырьмя коленами. У 444 узлов глубины 4 без видимого выхода дальнейшие переводы неизвестны.
- Выгрузка построена по исходящим переводам от исходных клиентов. Входящие переводы к ним извне графа и переводы меньше 5 000 KZT не видны; наблюдаемый поток не равен полному балансу.
- Даты имеют точность до дня. Внутридневной порядок переводов определить нельзя; временная проверка не устанавливает происхождение средств.
- В наборе нет эталонной разметки ролей. Качество ролей нельзя честно представить как точность классификатора; это объяснимые гипотезы для аналитика.
- Просмотрщик выводит до 16 крупнейших входящих и исходящих рёбер выбранного узла; суммарные счётчики учитывают все связи.
- Реализация рассчитана на предоставленные 2 248 узлов. Для графа на миллионы узлов понадобятся другие средства хранения и обработки.
- Испытания выполнены на Windows 10; запуск на других системах отдельно не подтверждён. Командная репетиция пятиминутной демонстрации остаётся организационной задачей.
Что изменится при росте до ~1 млн узлов
Текущие pandas и NetworkX подходят для предоставленных 2 248 узлов, но миллионный граф нельзя обоснованно обещать обработать тем же способом и за то же время. При таком росте чтение Parquet и агрегацию транзакций пришлось бы выполнять порциями, хранить рёбра в более компактной структуре и заменить графовые расчёты на инструменты для крупных графов, например igraph. Подсчёт seed_reach должен сохранить число разных исходных клиентов, достигающих узла: простой обход от всех seed сразу этого не даёт. PageRank и кластеризацию пришлось бы считать отдельно с контролем памяти и времени. Просмотрщик должен запрашивать по индексу только соседей выбранного gid, а не загружать в память всю таблицу рёбер. Это план развития, а не реализованная или измеренная производительность текущего проекта.
Развёрнутая версия
Публичного развёртывания нет. Проект запускается локально по инструкции выше без учётных записей.
Источники и раскрытие сторонних материалов
data/и data/README.md — набор, предоставленный организаторами HackAlem AI.starter/— предоставленный организаторами стартовый код для загрузки Parquet и базовых графовых метрик. Его выходные роли и приоритеты были заглушками; правила, проверки и просмотрщик этого проекта разработаны в ходе соревнования.- Перечень Python-библиотек и их версии находятся в
requirements.txt; применяются соответствующие открытые лицензии. - Статья IBM AMLworld использована как методический источник терминов fan-in и fan-out. Паттерн транзакций не является меткой виновности конкретного клиента. Чужой код и внешние AML-датасеты в проект не включены.
- Примеры дашбордов ZoomCharts послужили визуальным ориентиром для перехода от сводки к деталям. Их код, изображения, шаблоны и платные компоненты не включены; просмотрщик использует собственные HTML/CSS/SVG и не требует Power BI или лицензии ZoomCharts.
Авторы коммитов
История коммитов
- 2026-09-23 17:34feat: make transaction links readable by rowAhmet Sakbaev
- 2026-09-23 17:25feat: refine dashboard visual hierarchy and flow infographicAhmet Sakbaev
- 2026-09-23 16:58feat: add Windows one-click demo launcherAhmet Sakbaev
- 2026-09-23 16:38feat: open viewer in browser on startupAhmet Sakbaev
- 2026-09-23 16:29feat: add offline submission verification commandAhmet Sakbaev
- 2026-09-23 16:26feat: add explainable network overview dashboardAhmet Sakbaev
- 2026-09-23 16:19feat: publish reproducible submission CSV artifactsAhmet Sakbaev
- 2026-09-23 15:44feat: explain cluster hypotheses from observed rolesAhmet Sakbaev
Все коммиты (16)
- 2026-09-23 15:40feat: explain analysis in local viewerAhmet Sakbaev
- 2026-09-23 15:18docs: provide Russian jury setup and verification guideAhmet Sakbaev
- 2026-09-23 15:10feat: validate transit timing and document reproducible setupAhmet Sakbaev
- 2026-09-23 14:58fix: preserve CSV headers and full gid lookupAhmet Sakbaev
- 2026-09-23 14:54feat: build explainable Money Graph pipeline and viewerAhmet Sakbaev
- 2026-09-23 14:26docs: define Money Graph hackathon solution scopeAhmet Sakbaev
- 2026-09-23 13:51chore: add project development baselineAhmet Sakbaev
- 2026-09-23 13:35chore: establish pre-development project baselineAhmet Sakbaev
Структура репозитория (24 файлов)
.gitignore AGENTS.md ARCHITECTURE.md CONSTITUTION.md DEVELOPMENT.md LaunchDemo.vbs README.md data/README.md data/edges.parquet data/nodes.parquet data/transactions.parquet launch_demo.pyw requirements.txt run_pipeline.py starter/README.md starter/requirements.txt starter/starter.py submission/README.md submission/clusters.csv submission/nodes_roles.csv submission/top_nodes.csv tests/test_pipeline.py verify_project.py viewer.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.