О проекте
Команда: Pixie
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Рабочий MVP
Задача кейса: «Граф денег»: восстановить структуру организованной группы по сети транзакций (AML)
Суть
Пайплайн AML-анализа транзакционной сети: роли и priority_score для 2248 узлов, кластеры, SAR-нарративы, анализ устойчивости сети и одностраничный граф-вьюер с AI-ассистентом.
Как устроено
main.py загружает edges/nodes/transactions.parquet, строит граф NetworkX, считает метрики узлов, кластеризует сеть Louvain (python-louvain), по правилам и порогам присваивает роли (consolidator, transit, distributor, terminal, coordinator, peripheral) и priority_score. Затем выгружает обязательные CSV, рёбра с признаком in_cycle, SAR-нарративы по топ-узлам и Key Player disruption-анализ (падение наибольшей компоненты при изъятии топ-N узлов). graph_viewer.html на Cytoscape.js показывает граф, кластеры и кривую устойчивости. «Спросить граф» — LLM-ассистент через OpenRouter поверх top_nodes и clusters с ключом пользователя.
Ключевые решения
- NetworkX
- Louvain (python-louvain)
- rule-based роли
- детекция циклов
- Key Player disruption analysis (Borgatti 2006)
- SAR-нарративы
Особенности
Анализ устойчивости сети по Borgatti показывает, как распадается сеть при изъятии приоритетных узлов. Готовые SAR-нарративы для запроса в правоохранительные органы. Подробно разобраны ограничения данных, например обрыв на 4-м колене и занижение входящих сумм у seed-клиентов.
Стек
Python · pandas · NetworkX · python-louvain · Cytoscape.js · PapaParse · HTML/JS
Модели
OpenRouter
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-78e8e63e-pixie.
README
README.mdhack-78e8e63e-pixie
Hackathon team repository for Pixie
Граф денег: восстановление финансовой структуры организованной группы по транзакционной сети
HackAlem AI · направление "Финансы"
1. Проблема
AML-аналитику банка второго уровня известен только нижний уровень цепочки — 81 клиент, ранее выявленный как участник незаконного оборота наркотиков. Кто стоит выше — кто собирает эти деньги, через кого их прогоняют и кто ими распоряжается — до сих пор приходится восстанавливать вручную, трассируя каждый узел на 4 колена.
Реальная сеть вокруг этих 81 клиента — 2 248 узлов. Инструмент строит по ней граф, присваивает каждому узлу роль и приоритет проверки и отвечает на вопрос аналитика: «кого из этих 2 248 клиентов смотреть первым и почему».
Формулировки инструмента — это гипотезы для проверки («признаки консолидации», «кандидат в координаторы»), а не утверждения о виновности. Итоговые материалы предназначены для внутренней приоритизации и последующего запроса в правоохранительные органы, а не для самостоятельных выводов о причастности.
2. Быстрый старт
pip install -r requirements.txt # pandas, networkx, python-louvain и т.п. — см. main.py
python main.py
Пайплайн:
- Загрузка данных (
edges.parquet,nodes.parquet,transactions.parquet) - Построение графа
- Расчёт метрик для узлов
- Кластеризация сети
- Определение ролей и расчёт
priority_score - Сохранение обязательных выгрузок в CSV
- Экспорт рёбер для визуализации
- Генерация SAR-нарративов для топ-узлов
- Анализ устойчивости сети (Key Player disruption)
Полный пересчёт от сырых .parquet до всех выгрузок укладывается в < 5 минут
(ограничение ТЗ) без ручных шагов.
По завершении поднимается локальный сервер с экраном просмотра:
Дэшборд: http://localhost:8765/graph_viewer.html
Ctrl+C — остановить сервер.
Открыть можно и напрямую как файл (graph_viewer.html подхватит CSV/JSON из той же
папки автоматически; если запуск не через python main.py, а просто открытием файла —
можно догрузить те же файлы вручную кнопками в шапке).
3. Входные данные
| Файл | Строк | Содержание |
|---|---|---|
edges.parquet | 3 119 | плательщик→получатель, агрегировано за период: src, dst, sum_kzt, n_tx, depth |
nodes.parquet | 2 248 | уникальный клиент: gid, depth, is_seed |
transactions.parquet | 4 840 | отдельная транзакция: src, dst, date, sum_kzt |
Период: 2026-07-01 — 2026-07-31. Только исходящие переводы от 81 seed-клиента,
глубина обхода — 4 колена, порог отсечения — переводы < 5 000 KZT. Суммарный оборот
в графе: 365 890 012 KZT. Данные обезличены: gid — синтетический идентификатор без
привязки к личности.
4. Выходные файлы
nodes_roles.csv — 2 248 строк, по одной на узел
| Колонка | Тип | Смысл |
|---|---|---|
gid | int64 | идентификатор клиента |
role | str | роль из словаря (см. ниже) |
role_score | float | уверенность в роли, 0–1 |
cluster_id | int | номер кластера |
priority_score | float | приоритет для аналитика, 0–1 |
evidence | str | почему присвоена роль, до 200 символов, человекочитаемо |
Словарь ролей
| Роль | Смысл |
|---|---|
consolidator | точка консолидации — аккумулирует средства от нескольких участников |
transit | транзитный счёт — пропускает средства дальше, не удерживая |
distributor | веерное распределение средств на много получателей |
terminal | конечный получатель — деньги приходят и остаются |
coordinator | координирующий узел, кандидат в организаторы |
peripheral | периферия, признаков роли не выявлено |
Фактическое распределение по последнему прогону:
peripheral 1625
terminal 444
distributor 104
coordinator 45
consolidator 29
transit 1
clusters.csv — по одной строке на кластер
cluster_id, n_nodes, n_seed, sum_kzt_internal, top_gids, hypothesis
top_nodes.csv — ранжированный список ≥ 20 узлов
rank, gid, role, priority_score, why
Дополнительные выгрузки (сверх обязательного минимума)
| Файл | Назначение |
|---|---|
edges_export.csv | рёбра для визуализации в graph_viewer.html, включая признак in_cycle |
sar_narratives.json | развёрнутый нарратив по топ-узлам для материалов на запрос в правоохранительные органы (см. п. 6) |
network_resilience.csv | результат disruption-анализа: removed_top_k, largest_component_size, largest_component_pct, n_components |
5. Экран просмотра — graph_viewer.html
Одностраничное приложение (Cytoscape.js + PapaParse), без сборки и бэкенда; читает
CSV/JSON из той же папки при открытии через python main.py, либо файлы можно
догрузить вручную кнопками в шапке.
Вкладки:
- Граф — схема сети: цвет узла = роль, размер =
priority_score, толщина ребра = оборот; циклы переводов (in_cycle) выделены красным и пульсирующим свечением вместе со всеми узламиpriority_score ≥ 0.75. Наведение показывает тултип (роль, score, кластер) без клика; клик открывает карточку узла справа и центрирует граф. Поиск поgid, фильтр по кластеру и ролям, переключение «топ-200 / вся сеть». - Обзор — KPI (узлов, связей, оборот KZT, seed-клиентов, кластеров, узлов на проверку) + распределение ролей.
- Кластеры — таблица с гипотезами; клик по строке переключает на «Граф» с фильтром по этому кластеру.
- Устойчивость — кривая disruption-анализа (методика Borgatti, 2006): как падает размер наибольшей компоненты сети при изъятии топ-N узлов по приоритету.
Рабочие функции, а не только просмотр:
- «📋 копировать SAR» в карточке узла — копирует готовый нарратив в буфер обмена.
- «⬇ экспорт отчёта» — выгружает топ-30 узлов с нарративами в
.txt, готовый текст для передачи в подразделение. - «🤖 Спросить граф» — AI-ассистент поверх
top_nodes.csv/clusters.csv: вопрос на естественном языке → ответ со ссылками на конкретныеgidиз данных, без домыслов. Требует собственный API-ключ OpenRouter, вводимый пользователем локально в браузере — ключ никуда, кроме OpenRouter, не отправляется и не сохраняется на сервере.
6. Соответствие критериям ТЗ (must have)
| № | Критерий | Статус |
|---|---|---|
| 1 | Воспроизводимый пайплайн, один запуск, < 5 минут | python main.py создаёт все выгрузки без ручных шагов |
| 2 | Роль, role_score, evidence у всех 2 248 узлов | nodes_roles.csv, все колонки заполнены |
| 3 | Критерии ролей задокументированы и объяснимы | формальные правила/пороги — см. main.py / раздел «Методология» |
| 4 | Кластеризация сети | clusters.csv + cluster_id у каждого узла в nodes_roles.csv |
| 5 | Топ-лист приоритетов + визуализация направления потоков и ролей | top_nodes.csv (≥ 20 узлов) + вкладка «Граф» |
Реализованные опциональные пункты: оценка устойчивости сети (disruption-анализ), AI-ассистент аналитика, автогенерация карточки узла с нарративом.
Не реализовано в текущей версии (см. «Дальнейшие шаги»): временные паттерны из
transactions.parquet (синхронные переводы от нескольких плательщиков в один день,
сквозной транзит за 1–2 дня), явное отделение настоящих конечных получателей от
артефакта обрыва графа на 4-м колене, автоматический отчёт о полноте данных.
7. Известные ограничения данных (объявлены в ТЗ, учтены при интерпретации)
- Обрыв на 4-м колене: 444 узла с
out_degree = 0наdepth = 4— это артефакт обхода, а не обязательно «деньги осели»; наивная интерпретация даёт 444 ложных «стока». - Только исходящие переводы: входящие потоки на узлы за пределами выборки не видны, полный баланс узла посчитать нельзя.
- У seed-клиентов входящие суммы занижены (граф собран от них): 354 узла в графе формально «отдают» больше, чем «получили» по имеющимся данным.
- Порог 5 000 KZT — дробление сумм ниже порога в данных не видно.
- 19 из 81 seed отсутствуют в рёбрах, ещё 12 — только как получатели; 31 seed не имеет исходящих переводов.
- 16 слабосвязных компонент: 1 877 узлов (46 seed) в крупнейшей, 352 узла — вне неё.
- Нет атрибутов клиента (ФИО, тип операции, остатки) — только структура и суммы.
- Нет размеченных ролей (ground truth) — оценка велась по обоснованности критериев, не по accuracy.
8. Дальнейшие шаги
- Временные паттерны по
transactions.parquet: синхронные переводы от 5+ плательщиков в один день, сквозной транзит «пришло и ушло за 1–2 дня» — сильный сигнал, которого сейчас нет в пайплайне; код небольшой, данные уже загружаются. - Автоотчёт о полноте данных: вывод раздела 7 этого README в машиночитаемом виде (какие 19/81 seed без исходящих, 352 узла вне главной компоненты и т.д.) — прямое попадание в критерий «Ценность и применимость».
- Явная маркировка узлов, «обрезанных» артефактом 4-го колена, отдельно от настоящих конечных получателей.
9. Приватность и назначение данных
Данные обезличены (gid — синтетический идентификатор без привязки к личности) и
предоставлены исключительно для использования в рамках хакатона. Инструмент не делает
и не должен использоваться для вынесения выводов о виновности — только для
приоритизации внутренней проверки и формирования материалов на запрос в
правоохранительные органы.
Авторы коммитов
История коммитов
- 2026-09-23 17:48Update README.mdmarzhanaa
- 2026-09-23 17:40Expand README with project details and instructionsAruzhan-kenshbaeva
- 2026-09-23 17:38прокAruzhan-kenshbaeva
- 2026-09-23 17:36проAruzhan-kenshbaeva
- 2026-09-23 15:06Add files via uploadAruzhan-kenshbaeva
Структура репозитория (13 файлов)
README.md про/clusters.csv про/data/edges.parquet про/data/nodes.parquet про/data/transactions.parquet про/edges_export.csv про/graph_viewer.html про/main.py про/network_resilience.csv про/nodes_roles.csv про/requirements.txt про/sar_narratives.json про/top_nodes.csv
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.