AAYMA
RU
Войти

MoneyGraph

Объяснимый AML-пайплайн и Streamlit-дашборд: направленный граф переводов, роли узлов (consolidator, distributor, transit, terminal, coordinator)…

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

О проекте

Команда: BrokySpace
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Доведённый продукт

Задача кейса: «Граф денег»: восстановить структуру организованной группы по сети транзакций (AML)

Суть

Объяснимый AML-пайплайн и Streamlit-дашборд: направленный граф переводов, роли узлов (consolidator, distributor, transit, terminal, coordinator), приоритет проверки, Louvain-сообщества и помощник аналитика.

Как устроено

Из nodes/edges/transactions.parquet строится взвешенный направленный граф. Считаются flow-признаки, PageRank, HITS, betweenness и близость к seed. Временные признаки: задержка пересылки через merge_asof, всплески, синхронные входы; отдельно ищутся циклы, повторяющиеся цепочки и дробление. Пять формульных role scores строятся на percentile-рангах: ниже 0.38 узел считается peripheral, для узлов, усечённых на depth=4, terminal score штрафуется. priority_score — взвешенная сумма структурной важности, роли, связи с seed и аномальности. Louvain работает на неориентированной проекции, evidence до 200 символов содержит фактические числа. Выход: nodes_roles.csv, clusters.csv, top_nodes.csv и Streamlit-дашборд из 9 вкладок с опциональным OpenAI-режимом помощника.

Ключевые решения

  • NetworkX
  • PageRank
  • HITS
  • betweenness
  • Louvain
  • merge_asof
  • поиск циклов
  • детекция дробления
  • percentile ranks
  • формульные role scores

Особенности

Формулы ролей прозрачны и опубликованы. Учтено усечение обхода на depth=4: terminal×0.35 с предупреждением в evidence. role_score и priority_score разделены, вес оборота ограничен, чтобы крупные клиенты не вытесняли структурно значимые узлы. Модуль «белых пятен» подсказывает аналитику следующий запрос данных.

Стек

Python · pandas · NetworkX · Streamlit · Parquet · pytest · GitHub Actions

Модели

OpenAI API (опционально)


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-31599fbc-brokyspace.

README

README.md

MoneyGraph — аналитика финансовой сети

Локальный, воспроизводимый и объяснимый инструмент восстановления финансовой структуры организованной группы по транзакционной сети. Решение строит направленный граф движения денег, назначает функциональные роли всем узлам, ранжирует их для AML-проверки, выделяет сообщества и формирует доказательную базу в понятной аналитику форме.

1. Задача и решение

Оборот сам по себе редко показывает, кто управляет сетью. Координатор может переводить меньше денег, но связывать критические части графа; узел на границе четырёх шагов может выглядеть терминальным только из-за усечения выборки. Поэтому решение сочетает направленные потоки, центральности, связь с seed, временное поведение и структуру сообществ. Black-box классификатор не используется: каждая роль и позиция в рейтинге выводятся из формальных признаков.

2. Архитектура

data/*.parquet
  → src/data.py           чтение, приведение схемы, проверки
  → src/graph.py          directed weighted graph
  → src/features.py       flow + PageRank/HITS/betweenness/seed
  → src/temporal.py       forwarding + bursts + synchronous inflows
  → src/patterns.py       cycles + recurring edges/chains + structuring + anomalies
  → src/clustering.py     Louvain и межкластерные мосты
  → src/roles.py          пять role scores + peripheral
  → src/priority.py       AML priority score
  → src/evidence.py       объяснения с фактическими числами
  → src/completeness.py   белые пятна + следующий запрос аналитику
  → src/agent.py          read-only AI AML analyst
  → src/validation.py     контракт выходных данных
  → out/*                 CSV, parquet, metadata
  → app.py                Streamlit dashboard

3. Quick Start

Требуется Python 3.10+.

python -m venv .venv
# Windows: .venv\Scripts\activate
# Linux/macOS: source .venv/bin/activate
pip install -r requirements.txt
python run_pipeline.py --data ./data --out ./out
streamlit run app.py

Полный pipeline выполняется одной командой. Код возврата ненулевой при нарушении контракта. Для набора другого размера проверку 2248 можно изменить через --expected-nodes N или отключить значением 0.

Если командная строка неудобна, можно сразу открыть streamlit run app.py: при отсутствии результатов стартовый экран предложит загрузить три parquet-файла и запустить pipeline одной кнопкой. Обработка остаётся локальной.

Проверка решения:

pytest -q
python evaluate_solution.py --out ./out

OpenAI-режим AI-аналитика опционален и не нужен локальному evidence-agent. Для постоянной серверной настройки задайте OPENAI_API_KEY в окружении или создайте локальный файл .streamlit/secrets.toml:

OPENAI_API_KEY = "[скрыто]"

.streamlit/secrets.toml исключён из Git. При наличии серверного ключа он не передаётся в браузер и не отображается в интерфейсе; ручной password-ввод остаётся резервным вариантом только для текущей сессии. Перед каждым запросом пользователь отдельно подтверждает передачу вопроса и минимального аналитического контекста в OpenAI API.

4. Входные данные

В data/ должны находиться:

  • nodes.parquet: уникальный gid, а также depth и is_seed;
  • edges.parquet: source, target, sum_kzt, опционально n_tx;
  • transactions.parquet: source, target, сумма и timestamp.

Загрузчик понимает распространённые эквиваленты (src/dst, sender_gid/receiver_gid, amount_kzt, transaction_date и др.), но после чтения приводит всё к единой схеме. Проверяются уникальность GID, ссылки на существующие узлы, типы и неотрицательность сумм. Если timestamps отсутствуют, temporal-признаки честно принимают нулевые значения, а не имитируются.

5. Pipeline

Последовательность: load → validate → graph → network features → temporal features → Louvain → role scores → cluster importance → priority → evidence → validation → export. В консоли печатается короткий EDA, время каждого этапа и общий runtime; подробности сохраняются в out/metadata.json.

6. Feature engineering

На направленном графе вычисляются in/out_degree, in/out_kzt, in/out_tx, PageRank, betweenness, HITS hub/authority, weak component, расстояние/близость к seed, число соседей и непосредственных источников-seed. pass_through = min(in_kzt,out_kzt)/max(in_kzt,out_kzt) лежит в [0,1].

Для основных величин используются percentile ranks. Они сохраняют относительную редкость сигнала при тяжёлых хвостах денежных распределений и устойчивее абсолютных порогов при смене периода или размера сети. Например, in_deg_pct=0.99 означает, что узел превосходит 99% сети по числу входящих контрагентов.

7. Temporal analysis

По транзакциям считаются активные входящие/исходящие дни, медианная задержка между наблюдаемым входом и следующим исходящим событием, доля перенаправлений до 24/48 часов, всплеск дневной активности и синхронная консолидация от трёх и более разных плательщиков за день. Поиск предыдущего входа сделан через merge_asof, без квадратичного сопоставления транзакций.

temporal_anomaly_score объединяет percentile всплеска, rapid forwarding, синхронные входы, сигналы дробления, повторяющиеся связи/цепочки и циклы. depth_peer_anomaly_score сравнивает профиль узла с клиентами того же колена, а итоговый aml_anomaly_score объединяет обе оценки. Это приоритизация для расследования, а не вероятность нарушения.

8. Методология ролей

Для каждого узла независимо считаются пять scores в [0,1], затем выбирается самый сильный. Если максимум меньше 0.38, узел относится к peripheral. В формулах P(x) — percentile, R — observed retention, B — наличие входа и выхода, S — seed connectivity, X — bridge score:

consolidator = .20P(in_deg)+.18P(in_kzt)+.16P(authority)+.14P(seed_sources)+.10fan_in+.10R+.12sync
distributor  = .27P(out_deg)+.20P(out_tx)+.18P(hub)+.18fan_out+.10P(out_kzt)+.07S
transit      = .27B+.30pass_through+.18P(rapid)+.12same_next_day+.13(1-R)
terminal     = [.26P(in_kzt)+.18P(in_deg)+.24no_out+.20R+.12(1-P(out_kzt))] × has_input
coordinator  = .29P(betweenness)+.17P(PageRank)+.12max(P(hub),P(authority))+.22X+.12S+.08P(degree)

role_score — сила соответствия выбранной роли, не риск и не вероятность преступления. Для seed баланс вход/выход ненадёжен из-за неполного входящего контекста: их transit score считается отдельно без обычного pass-through.

9. Ограничение depth=4

Комбинация depth == max(depth) и out_degree == 0 помечается truncated_by_depth. Она означает границу обхода, а не доказанную остановку средств. Поэтому terminal score такого узла умножается на 0.35, а evidence предупреждает об усечении.

10. Priority scoring

priority_score отвечает на другой вопрос: насколько важно проверить узел AML-аналитику.

.28 structural importance + .18 non-peripheral role strength
+.16 seed connectivity + .14 AML anomaly
+.09 cluster importance + .08 transaction volume
+.04 cycle strength + .03 recurring edge/chain strength

Структура включает betweenness, PageRank, bridge и HITS. Денежный объём ограничен весом 10%, поэтому крупнейшие клиенты не вытесняют структурно значимые узлы.

11. Кластеризация

Louvain применяется к взвешенной неориентированной проекции, где встречные потоки суммируются по sum_kzt. Community detection ищет плотные группы связности, тогда как flow features, роли и транзитная логика остаются направленными. Для каждого кластера рассчитываются размер, seed, внутренний оборот, TOP GID и детерминированная hypothesis.

12. Explainability

Каждый evidence непустой, не длиннее 200 символов и содержит фактические числа: контрагентов, суммы, percentile, долю перенаправления или межкластерные связи. Это краткая причина назначения роли, а не шаблон «высокий score».

13. Выходные схемы

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; hypothesis формулирует предполагаемую функцию группы и явно требует проверки аналитиком.

out/top_nodes.csv: rank, gid, role, priority_score, why; минимум 20 строк, убывание priority.

Дополнительно создаются node_features.parquet, graph_edges.parquet, resilience.csv, cycles.csv, recurring_routes.csv, recurring_chains.csv, synchronous_inflows.csv, structuring_events.csv, anomalies.csv, completeness.csv и metadata.json; они питают dashboard и аудит расчётов. Три обязательные CSV хранятся в репозитории, остальные воспроизводятся pipeline и публикуются как CI artifact.

14. Dashboard и demo-сценарий

Dashboard рассчитан на пятиминутную демонстрацию:

Интерфейс имеет полноценные режимы «Светлая», «Тёмная» и «Системная». Выбор хранится в URL-параметре theme и st.session_state, поэтому переживает rerun, обновление страницы и навигацию назад/вперёд; системный режим следует теме браузера, доступной через st.context.theme. Все карточки, формы, загрузчик, таблицы, графики и canvas сети получают цвета из единого набора токенов. Главный экран содержит нативную CSS 3D-анимацию банка и переводов; она отключается при prefers-reduced-motion. Дизайн-токены описаны в UI system.

  1. «Обзор»: KPI, маршруты переводов, роли, структурная значимость и устойчивость сети.
  2. «Очередь»: TOP-20/50, объяснения и экспорт CSV.
  3. «Клиенты»: карточка GID с потоками, ролью и evidence.
  4. «Карта переводов»: высокий риск, окрестность клиента или группа; стрелки показывают направление денег.
  5. «Группы»: исходные клиенты, оборот, роли, hypothesis и экспорт CSV.
  6. «Сигналы»: cycles, recurring edges/chains, синхронные входы, дробление и depth-peer anomalies.
  7. «Помощник»: локальный grounded-разбор или явно включаемый OpenAI-режим.
  8. «Документы»: загрузка, просмотр, поиск, скачивание, повторная обработка и подтверждённое удаление.
  9. «О методике»: ограничения данных и отсутствие ground truth.

Помощник аналитика

Вкладка «Помощник» непосредственно отвечает на главный вопрос кейса: «кого из 2248 клиентов смотреть первым и почему?». Локальный режим детерминированно формирует ответ из pipeline artifacts. Опциональный режим OpenAI Agents SDK использует read-only function tools для рейтинга, карточки GID, группы, паттернов и поиска по документам дела. Он включается только после явного согласия на передачу минимального контекста в API.

Документы дела

На вкладке «Документы» можно добавить .pdf, .docx, .txt, .md, .csv, .json, .xlsx, .xls и .parquet размером до 15 MB. Система:

  • безопасно нормализует имя, извлекает текст и хранит оригинал локально в case_documents/;
  • проверяет расширение, MIME-тип, структуру файла и выводит понятную русскую ошибку;
  • исключает дубликаты по SHA-256, ведёт реестр и полнотекстовый поиск с фрагментами;
  • позволяет открыть извлечённый текст, скачать оригинал, повторить обработку и удалить документ с подтверждением;
  • сопоставляет упомянутые 12–20-значные GID только с клиентами текущего графа;
  • отдаёт найденные фрагменты AI-аналитику как явно помеченный непроверенный контекст;
  • не изменяет роли, признаки, кластеры и priority_score на основании текста документов.

Папка case_documents/ исключена из Git. Локальный агент не передаёт файлы наружу. В OpenAI-режиме API может получить только найденный текстовый фрагмент и только после явного согласия пользователя; инструкции внутри загруженных документов считаются недоверенными данными.

15. Автоматическая валидация

Перед экспортом проверяются число узлов, обязательные поля и отсутствие null, уникальность gid, допустимые роли, диапазоны scores, evidence/cluster, схема и непустота кластеров, размер и сортировка TOP. Ошибка прерывает pipeline до выдачи внешне корректных, но логически неверных CSV.

pytest повторно запускает pipeline на официальных parquet и проверяет must-have, ограничения depth/seed, bonus outputs и grounding локального агента. GitHub Actions выполняет тот же сценарий в чистом Python 3.12 окружении и сохраняет результаты как CI artifact. Связанные материалы: архитектура, demo-script, матрица критериев.

16. Bonus: AML patterns и network resilience

Из графа последовательно удаляются TOP-5/10/20 узлов по priority. resilience.csv показывает число weak components, размер/долю крупнейшей компоненты и fragmentation. График доступен на обзорной странице dashboard.

Также реализованы:

  • направленные циклы длиной 2–6 с bottleneck-суммой, наличием seed и cycle_score;
  • recurring edges и устойчивые цепочки A→B→C, наблюдаемые в несколько дней, с cadence regularity;
  • rapid transit по задержке до 24/48 часов;
  • синхронные входы от трёх и более плательщиков за день;
  • explainable structuring-сигналы по нескольким контрагентам, сходным, округлённым или близким к порогу суммам;
  • аномальные профили относительно узлов того же depth;
  • temporal/AML anomalies по всем перечисленным сигналам.

Порог recurring edges/chains определяется 75-м percentile среди многодневных паттернов, а список anomalies — 95-м percentile composite score. Сигналы дробления видят только операции, прошедшие порог выгрузки 5 000 KZT, и не утверждают наличие нарушения. Результаты доступны на вкладке «AML-паттерны».

17. Оценка полноты и следующий запрос

completeness.csv содержит строку на каждый GID: completeness_score, наблюдаемые белые пятна и конкретный recommended_request. Оценка снижает полноту для seed с неполным входом, узлов на границе depth=4, наблюдаемого дисбаланса и отсутствующих timestamps. В базовые ограничения включены невидимые межбанковские потоки, операции ниже 5 000 KZT, KYC и остатки. Карточка GID и AI-аналитик показывают, какие данные запросить следующими.

18. Ограничения

  • выборка ограничена четырьмя поколениями обхода;
  • наблюдение преимущественно outgoing и входы seed неполны;
  • роли без ground truth являются проверяемыми аналитическими гипотезами;
  • отсутствие timestamps снижает силу temporal-части;
  • PageRank и HITS отражают только наблюдаемую сеть.

19. Масштабирование до ~1 млн узлов

Текущая NetworkX-реализация оптимальна для локального кейса на 2248 узлах. Для миллиона узлов контракт и формулы сохраняются, но exact betweenness заменяется sampling approximation, граф переводится в igraph/graph-tool или Spark GraphFrames, parquet обрабатывается Polars/DuckDB, Louvain — пакетным Leiden/Louvain, а dashboard читает агрегаты и заранее подготовленные ego-графы. Расчёт остаётся explainable; меняется только backend.

#ВЫПОЛНИЛИ: АБДИЕВ ЕРАСЫЛ,ӘСІЛ ЕРАСЫЛ,БЕЙБОСЫНОВ БЕКСҰЛТАН

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

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

Yilin1207 12Erasyl010205 2Beksultan651 1

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

  • 2026-09-23 17:46Revise scaling approach for large node networksErasyl010205
  • 2026-09-23 17:33Add files via uploadErasyl010205
  • 2026-09-23 17:38Restore MoneyGraph project structure and Streamlit appYilin1207
  • 2026-09-23 17:34Add files via uploadBeksultan651
  • 2026-09-23 17:33Add files via uploadYilin1207
  • 2026-09-23 17:30Remove all project filesYilin1207
  • 2026-09-23 17:26Finalize adaptive UI and document workflowYilin1207
  • 2026-09-23 16:46Redesign dashboard as minimal banking productYilin1207
Все коммиты (15)
  • 2026-09-23 15:37Add local case document ingestion and searchYilin1207
  • 2026-09-23 15:27Redesign dashboard with premium adaptive themesYilin1207
  • 2026-09-23 15:17Complete AML bonus analytics and submission artifactsYilin1207
  • 2026-09-23 14:59Support persistent server-side OpenAI API keyYilin1207
  • 2026-09-23 14:46Add grounded AML analyst agent and quality gatesYilin1207
  • 2026-09-23 14:19Add cycle and recurring-route AML detectionYilin1207
  • 2026-09-23 14:13Build explainable AML graph analytics pipelineYilin1207
Структура репозитория (41 файлов)
.github/workflows/ci.yml
.gitignore
.streamlit/secrets.toml.example
README.md
app.py
data/edges.parquet
data/nodes.parquet
data/transactions.parquet
docs/ARCHITECTURE.md
docs/DEMO.md
docs/RUBRIC.md
docs/UI.md
evaluate_solution.py
out/clusters.csv
out/nodes_roles.csv
out/top_nodes.csv
pytest.ini
requirements.txt
run_pipeline.py
src/__init__.py
src/agent.py
src/clustering.py
src/completeness.py
src/data.py
src/documents.py
src/evidence.py
src/features.py
src/graph.py
src/patterns.py
src/pipeline.py
src/priority.py
src/resilience.py
src/roles.py
src/temporal.py
src/ui.py
src/validation.py
tests/conftest.py
tests/test_agent.py
tests/test_app.py
tests/test_documents.py
tests/test_pipeline.py

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

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

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