О проекте
Команда: CaffeineGrinders
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Доведённый продукт
Задача кейса: «Граф денег»: восстановить структуру организованной группы по сети транзакций (AML)
Суть
Локальный AML-конвейер: строит направленный граф переводов по 2248 gid, присваивает объяснимые роли (coordinator/distributor/consolidator/transit/terminal/peripheral), делит сеть на сообщества Louvain и выдаёт ранжированную очередь проверки, 40 CSV и автономный network.html.
Как устроено
run.py проверяет входные Parquet (типы, суммы, совпадение агрегатов с транзакциями) и считает метрики in/out_deg, in/out_kzt, seed_reach (≤4 ребра), bridge (betweenness по 96 источникам, seed=42) и rapid_48h. Роли назначаются упорядоченными правилами с эвристическим role_score. Кластеры находит детерминированный Louvain на ненаправленной проекции с весом sum_kzt. Приоритет считается как взвешенная сумма процентильных рангов × role/boundary/seed-множители, у каждого узла есть разбор вкладов. Дополнительные сигналы (циклы nx.simple_cycles, relay-маршруты, синхронные входы, дробление, суммы у порога, оценка скрытых исходящих у 4-го колена) идут в колонку attention и не меняют роль. AI-ассистент работает поверх graph_tools через любой OpenAI-совместимый endpoint.
Ключевые решения
- NetworkX
- Louvain (seed=42)
- betweenness centrality (sampled)
- percentile-rank priority score
- nx.simple_cycles
- relay route detection
- resilience test vs random removal
- LLM assistant with graph tools
Метрики
Пересчёт ~2,5 с на 2248 узлах; изъятие топ-10 оставляет 74% seed-связей (топ-50 — 47%) против 96% при 50 случайных узлах
Особенности
Проверка устойчивости сети показывает, что приоритет действительно находит несущие узлы: удаление топ-N сравнивается с удалением случайных. Эмпирически оценивается вероятность скрытых исходящих у узлов 4-го колена. Около 40 тестов на граничные случаи и побайтовая воспроизводимость CSV при перемешивании входных строк.
Стек
Python · pandas · NetworkX · HTML viewer (JS) · pytest
Модели
OpenAI-compatible LLM endpoint
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-d5e94b09-caffeinegrinders.
README
README.mdГраф денег
Локальный инструмент для первичной AML-проверки транзакционной сети. По трём обезличенным parquet-файлам он строит направленный граф, присваивает каждому gid объяснимую роль, делит сеть на сообщества и выдаёт ранжированную очередь клиентов для углублённой проверки. Инструмент отвечает на вопрос аналитика «кого из 2 248 клиентов смотреть первым и почему».
Роль и позиция в очереди означают структурную гипотезу для проверки, а не причастность к преступлению.
Содержание
- Быстрый старт
- Сценарий аналитика
- Как устроено решение
- Дополнительные сигналы
- AI-ассистент аналитика
- Выгрузки
- Инструменты командной строки
- Ограничения интерпретации
- Проверка
- Демо на 5 минут
- При росте до миллиона узлов
Быстрый старт
Нужен Python 3.10+ и доступ к PyPI при первом запуске. Из корня репозитория:
./run.sh
Скрипт создаёт локальное окружение, устанавливает зависимости из requirements.txt и запускает полный пересчёт. Папка data/ должна содержать nodes.parquet, edges.parquet, transactions.parquet. Другой путь передаётся так: ./run.sh --data /path/to/data --out /path/to/out.
В Windows (PowerShell):
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe run.py
Если окружение .venv уже подготовлено, достаточно последней команды — она работает без интернета.
Что появляется в out/: три обязательные выгрузки nodes_roles.csv, clusters.csv, top_nodes.csv, ещё 37 CSV с опциональными разборами и автономная страница network.html. Полный перечень — в разделе Выгрузки. HTML открывается в браузере как обычный файл, без сервера и интернета.
Производительность. На предоставленных 2 248 узлах прямой расчёт run.py занимает около 2,5 секунды на тестовой машине; установка пакетов в это время не входит. Предел по ТЗ — 5 минут.
Сценарий аналитика
- Поместить выгрузку в
data/и запустить./run.sh. - Открыть
out/network.html, найтиgidиз списка правоохранительных органов или взять узел из топ-листа слева. - Переключиться на один или два шага связей, изучить направление стрелок, суммы и карточку узла. Переключить окраску между ролями и кластерами.
- Использовать
top_nodes.csvкак очередь углублённой проверки;evidenceи числовые поляnodes_roles.csvпозволяют проверить каждый вывод. - Сверить
attentionу выбранных узлов и сформировать запрос на недостающие данные поdata_gaps.csv. - Уточнить связи вопросом ассистенту:
assistant.py "кто собирает деньги с этих пятерых: …".
Как устроено решение
Построение графа
Граф направленный: ребро src → dst означает перевод от отправителя получателю. edges.parquet сверяется с суммами и количеством операций в transactions.parquet. Все узлы из nodes.parquet, включая изолированные seed, остаются в результате.
Проверка входных данных
Обязательные поля не допускают пропусков. Идентификаторы, глубина и число операций должны иметь целочисленный тип, is_seed — булев тип. Суммы должны быть конечными положительными числами; допустимое расхождение агрегата с транзакциями — не более 0,01 KZT на пару. Пустой список узлов отклоняется с сообщением об ошибке. Перед построением графа узлы и рёбра сортируются, поэтому перестановка строк во входных файлах не меняет результат.
Метрики узла
| Метрика | Смысл |
|---|---|
in_deg, out_deg | число разных плательщиков и получателей |
in_kzt, out_kzt | суммы, видимые внутри выгрузки |
seed_reach | из скольких seed узел достижим по направлению переводов за ≤ 4 ребра |
bridge | нормированное посредничество на направленном графе по 96 фиксированным источникам (seed=42) |
rapid_48h | доля входящих операций, после которых у того же узла есть исходящая в следующие 48 часов — временной сигнал, не сопоставление конкретных денег |
Правила ролей
Правила применяются в указанном порядке; первое выполненное задаёт основную роль:
| Роль | Условие |
|---|---|
coordinator | Не seed; seed_reach ≥ 3, in_deg ≥ 3, bridge не ниже 90-го процентиля положительных значений в текущей выгрузке. Кандидат на координирующий узел, не идентификация организатора. |
distributor | out_deg ≥ 8 и out_deg ≥ 2 × max(in_deg, 1). |
consolidator | in_deg ≥ 4 и in_deg ≥ 2 × max(out_deg, 1). |
transit | Не seed; есть вход и выход, 0.8 ≤ out_kzt / in_kzt ≤ 1.2. Сравнение сумм остаётся приблизительным из-за неполной видимости. |
terminal | Не seed; in_deg > 0, out_deg = 0, depth < 4. Это только наблюдаемый конечный получатель в границах выборки. |
peripheral | Остальные узлы, в том числе обрыв на 4-м колене без исходящих. |
role_score лежит в диапазоне 0–1 и отражает силу выполнения выбранного правила: базовое значение 0.60 растёт с запасом по числу связей, seed и близостью доли пропуска к единице; для терминалов базовое значение 0.62, для граничной периферии 0.55. Это эвристическая оценка, не калиброванная вероятность. Точные формулы — в score_roles() в run.py.
Для каждого gid поле evidence содержит числа, на которых основана роль, и не превышает 200 символов. Дополнительные колонки nodes_roles.csv позволяют разобрать любой узел за минуту. Например, для граничного узла пояснение прямо говорит, что исходящие после четвёртого колена неизвестны.
Кластеры
Кластеры вычисляются детерминированным Louvain (seed=42) по ненаправленной проекции графа с весом sum_kzt. Направление сохраняется в исходном графе, ролях, метриках и интерфейсе; проекция используется только для поиска плотно связанных групп. Изолированные узлы получают собственный кластер. sum_kzt_internal включает только рёбра, где оба конца в данном кластере. top_gids — пять клиентов кластера с наибольшим приоритетом. hypothesis описывает наблюдаемый характер группы и требует проверки.
Приоритет
Приоритет — взвешенная сумма процентильных рангов в текущей выгрузке:
base = 0.20·P(in_deg) + 0.20·P(in_kzt) + 0.20·P(seed_reach)
+ 0.20·P(bridge) + 0.15·P(out_deg) + 0.05·rapid_48h
priority_score = base × role_factor × boundary_factor × seed_factor
P — процентильный ранг признака; совпадающие значения получают одинаковый ранг.
| Множитель | Значение |
|---|---|
role_factor | 1 для coordinator/consolidator/distributor; 0,95 для transit; 0,85 для terminal; 0,75 для peripheral |
boundary_factor | 0,65 для узлов четвёртого колена без исходящих; иначе 1 |
seed_factor | 0,85 для известных seed; иначе 1 |
Итог ограничен диапазоном 0–1. Узлы сортируются по убыванию приоритета, при равенстве — по возрастанию gid; в top_nodes.csv попадают первые 100 (минимум по ТЗ — 20). Эти коэффициенты задают очередь проверки, а не оценку вины.
Чтобы приоритет был проверяемым, nodes_roles.csv содержит разбор формулы: priority_base, вклады priority_payers, priority_incoming, priority_seed_reach, priority_bridge, priority_recipients, priority_rapid и множители priority_role_factor, priority_boundary_factor, priority_seed_factor. Те же числа видны в карточке узла в network.html.
Дополнительные сигналы
Эти признаки закрывают опциональные пункты ТЗ. Они не меняют роль и приоритет, а попадают в колонку attention (nodes_roles.csv, top_nodes.csv) и в карточку узла. Так каждый флаг проверяется отдельно и не смешивается с правилами ролей. Если флагов нет, в attention записано нет.
| Признак | Колонка | Как считается | Флаг в attention |
|---|---|---|---|
| Возвратные потоки | cycles, cycle_with_seed | простые направленные циклы длиной ≤ 4 перевода (nx.simple_cycles); список — в cycles.csv, bottleneck_kzt — минимальная сумма на ребре цикла | узел лежит хотя бы на одном цикле |
| Повторная пересылка | relay_routes, chain_transits | A→B→C: B отправил C 50–105% входа от A в течение 0–2 дней минимум в 2 разных дня; A→B→C→D связывает два таких эпизода | узел — посредник маршрута или звено цепочки |
| Всплески активности | timeline.csv | входящие и исходящие переводы агрегируются по календарному дню и gid | показываются в карточке узла как самые активные дни |
| Синхронные входы | sync_payers_max | максимум разных плательщиков в один календарный день | ≥ 3 |
| Дробление | repeat_amount_max | сколько раз узел отправил одну и ту же сумму | ≥ 4 |
| Суммы у порога | near_threshold_in | входящие переводы 5 000–9 999 KZT | ≥ 3 и не меньше половины входящих |
| Выброс для колена | — | in_deg выше 99-го процентиля своего колена | при in_deg ≥ 3 |
| Скрытый выход | p_hidden_outgoing | см. ниже | ≥ 50%: «продлить обход» |
Обрыв 4-го колена
Обход раскрывает исходящие всех узлов колен 0–3, поэтому у них p_hidden_outgoing = 0. Для 444 узлов 4-го колена исходящие не выгружались. Их вероятность оценивается эмпирически: какая доля не-seed узлов колен 1–3 с тем же числом плательщиков (1, 2, 3–4, 5+) имеет исходящие. На текущей выгрузке это 29% для одного плательщика, 57% для двух и 73% для 3–4. Так 434 граничных узла с одним плательщиком ближе к настоящим конечным получателям, а 10 узлов с 2+ плательщиками стоит раскрыть дальше в первую очередь. Все граничные узлы остаются peripheral: оценка задаёт очередь дозапроса, а не роль.
Устойчивость сети
resilience.csv сравнивает изъятие топ-N по priority_score с N случайными узлами (seed=42). seed_reach_share — доля пар «seed → узел в пределах 4 переводов», которые остались связаны. На текущей выгрузке изъятие топ-10 оставляет 74% таких пар и 70 связных фрагментов, топ-50 — 47%; 50 случайных узлов — 96%. Значит, приоритет действительно выделяет узлы, на которых держится сеть.
Маршруты пересылки
routes.csv показывает повторные A→B→C и сквозные A→B→C→D цепочки, где суммы и даты похожи на пересылку. Совпадение времени и размера перевода остаётся структурной гипотезой: оно не доказывает, что дальше ушли именно те же деньги.
Белые пятна
data_gaps.csv перечисляет, чего не хватает в выгрузке, сколько узлов это затрагивает и какой запрос сделать следующим: продление обхода от граничных узлов, входящие и межбанковские переводы seed без исходящих, переводы ниже 5 000 KZT для узлов с суммами у порога, источники для узлов, которые отдали больше, чем получили, и для фрагментов вне основной компоненты.
В network.html эти же правила показываются в карточке конкретного узла как короткий список «что запросить дальше». Экспорт Markdown-отчёта включает эти запросы вместе с evidence, attention и заметками аналитика.
AI-ассистент аналитика
Вопрос на естественном языке превращается в ответ по графу со ссылками на gid:
# Один раз задайте адрес внутреннего или локального OpenAI-compatible endpoint:
export OPENAI_BASE_URL=http://127.0.0.1:8000/v1
export OPENAI_MODEL=my-local-model
# export OPENAI_API_KEY=... # только если внутренний endpoint требует токен
.venv/bin/python assistant.py "Кто собирает деньги с 100000000437046100, 100000000331309100 и 100000008346837100?"
.venv/bin/python assistant.py # диалог с уточняющими вопросами
.venv/bin/python assistant.py --offline "..." # без обращения к API
Параметры можно передать и явно: assistant.py --base-url http://127.0.0.1:8000/v1 --model my-local-model "...". Шаблон переменных с примерами для Ollama и LM Studio лежит в .env.example; скопируйте его в .env и примените командой set -a; source .env; set +a. Файл .env в репозиторий не попадает.
Как устроено. Модель за локальным или внутренним OpenAI-compatible Chat Completions API только планирует запросы и пишет ответ. Каждый факт она берёт из детерминированных инструментов graph_tools.py, которые работают на том же графе и тех же ролях, что run.py:
| Инструмент | Что возвращает |
|---|---|
node_profile | роль, место в очереди, метрики, evidence, attention |
counterparties | прямые плательщики и получатели с суммами |
shared_counterparties | кто получает деньги от нескольких gid (downstream) или платит им (upstream) в пределах N переводов |
money_paths | цепочки переводов от одного gid к другому с суммой на каждом шаге |
top_nodes | очередь приоритетов с фильтром по роли, кластеру, флагам |
cluster_summary | размер, seed, внутренний оборот, роли и лидеры кластера |
node_cycles | возвратные потоки через узел |
Системная инструкция требует ссылаться только на данные инструментов, писать gid полностью, формулировать выводы как гипотезы и учитывать ограничения выгрузки. Инструменты выполняются локально и доступны модели как стандартные function tools.
Доступ. OPENAI_BASE_URL обязателен: приложение не подставляет адрес публичного API и не отправляет данные во внешний сервис автоматически. OPENAI_MODEL задаёт имя модели на вашем endpoint. OPENAI_API_KEY необязателен и нужен, только если внутренний шлюз требует авторизацию. Без endpoint, модели или соединения ассистент сообщает об этом и отвечает офлайн: по gid в вопросе выбирает один инструмент (один gid — профиль и связи, несколько — общие получатели или, при словах «кто платит», «откуда», «источник», общие источники; без gid — очередь приоритетов).
Выгрузки
gid — синтетический идентификатор. В интерфейсе он передаётся строкой, чтобы JavaScript не терял точность 64-битного числа. В CSV это целочисленный идентификатор.
Обязательные по ТЗ
| Файл | Содержание |
|---|---|
nodes_roles.csv | Одна строка на каждый входной gid; обязательные поля gid, role, role_score, cluster_id, priority_score, evidence и дополнительные проверяемые метрики. |
clusters.csv | Одна строка на сообщество: cluster_id, n_nodes, n_seed, sum_kzt_internal, top_gids, hypothesis. |
top_nodes.csv | Очередь из 100 узлов: rank, gid, role, priority_score, why и attention. |
Опциональные пункты ТЗ
| Файл | Пункт ТЗ | Содержание |
|---|---|---|
boundary_review.csv | учёт артефакта обрыва | Очередь граничных узлов 4-го колена для продления обхода: gid, входящие связи, сумма, оценка скрытого выхода и следующий запрос. |
timeline.csv | временные паттерны | Дневная активность по каждому gid: входы, выходы, оборот, чистый поток и число уникальных контрагентов за день. |
daily_summary.csv | временные паттерны | Дневная активность всей видимой сети: число операций, сумма, уникальные отправители, получатели и активные узлы. |
cycles.csv | возвратные потоки | Возвратные потоки: cycle_id, length, path, bottleneck_kzt, n_seed. |
cycle_nodes.csv | возвратные потоки | Нормализованный состав cycles.csv: одна строка на узел цикла с позицией, ролью, кластером и bottleneck-суммой. |
routes.csv | повторяющиеся маршруты | Устойчивые маршруты пересылки и сквозные цепочки: kind, path, relay_days, forwarded_kzt, даты и число seed. |
route_nodes.csv | повторяющиеся маршруты | Нормализованный состав routes.csv: одна строка на узел маршрута с позицией, ролью, кластером, seed-флагом и priority. |
route_edges.csv | повторяющиеся маршруты | Нормализованные hops из routes.csv: src → dst, позиция, роли, кластеры, видимая сумма и число транзакций ребра. |
risk_flags.csv | детекция аномалий | Сводка флагов внимания: число узлов, доля, смысл и топовые примеры gid. |
attention_examples.csv | детекция аномалий | Детализация risk_flags.csv: одна строка на пару flag + gid с метрикой, ролью, кластером, evidence и attention. |
amount_bands.csv | детекция аномалий | Распределение транзакций по фиксированным диапазонам KZT: число переводов, сумма и доли по количеству и обороту. |
resilience.csv | устойчивость сети | Что остаётся от сети после изъятия топ-N узлов по приоритету и N случайных. |
data_gaps.csv | оценка полноты | Белые пятна выгрузки: gap, n_nodes, meaning, next_request, example_gids. |
Оставшиеся два опциональных пункта закрыты не выгрузками, а кодом: AI-ассистент — assistant.py, автогенерация карточки узла — report_node.py и карточка в network.html.
Вспомогательные срезы
Агрегаты поверх выгрузок выше. Нужны для быстрой навигации по сети и разбора на демо; каждый выводится из уже перечисленных файлов.
| Файл | Содержание |
|---|---|
role_summary.csv | Сводка по структурным ролям: число узлов, доля сети, seed, граничные узлы, средние скоры, оборот и топовые gid. |
role_depths.csv | Матрица «роль × колено обхода»: число узлов, доля внутри роли, seed, граничные узлы и топовые gid. |
role_flows.csv | Агрегированные видимые потоки между структурными ролями src_role → dst_role: сумма, число рёбер и транзакций. |
role_attention.csv | Флаги внимания по структурным ролям: число уникальных gid, доля внутри роли и топовые примеры. |
depth_summary.csv | Сводка по коленям обхода 0–4: число узлов, seed, граница выборки, оборот, средний приоритет и топовые gid. |
depth_flows.csv | Агрегированные видимые потоки между коленями обхода src_depth → dst_depth с направлением outward, same_depth или backward. |
depth_attention.csv | Флаги внимания по коленям обхода: число уникальных gid, доля внутри слоя и топовые примеры. |
attention_overlap.csv | Совместная встречаемость пар флагов внимания на одних и тех же gid: число узлов и топовые примеры. |
cluster_flows.csv | Межкластерные потоки: src_cluster, dst_cluster, сумма, число рёбер и транзакций. |
cluster_flow_summary.csv | Сводка по каждому кластеру: внутренний оборот, входящие и исходящие межкластерные суммы, рёбра, транзакции и крупнейшие соседние кластеры. |
cluster_roles.csv | Матрица «кластер × роль»: число узлов, доля внутри кластера, оборот, средний приоритет и топовые gid. |
cluster_depths.csv | Матрица «кластер × колено обхода»: число узлов, доля внутри кластера, seed, граничные узлы и топовые gid. |
cluster_attention.csv | Флаги внимания по кластерам: сколько уникальных gid с каждым флагом, доля внутри кластера и топовые примеры. |
seed_coverage.csv | Покрытие каждого seed: достижимые узлы за 4 перевода, прямые получатели, прямой исходящий поток и кластер. |
seed_components.csv | Компонента каждого seed: размер фрагмента, число seed в нём, внутренний оборот, рёбра, кластер и охват за 4 перевода. |
seed_role_reach.csv | Для каждого seed: сколько достижимых за 4 перевода узлов попадает в каждую структурную роль, плюс граничные и флаговые узлы. |
seed_overlap.csv | Пары seed, которые достигают одних и тех же видимых узлов за 4 перевода: размер пересечения, доли охвата и топовые общие gid. |
seed_attention.csv | Флаги внимания внутри достижимой зоны каждого seed за 4 перевода: число узлов, доля охвата и топовые примеры. |
components.csv | Слабосвязные фрагменты сети: размер, seed, внутренний оборот, число рёбер, топовые gid и признак главной компоненты. |
component_roles.csv | Матрица «компонента × роль»: число узлов, доля внутри компоненты, seed, граничные узлы и топовые gid. |
component_attention.csv | Флаги внимания по слабосвязным компонентам: число уникальных gid, доля внутри компоненты и топовые примеры. |
isolated_nodes.csv | Узлы без видимых входящих и исходящих рёбер: gid, seed-флаг, роль, кластер, компонента, приоритет и объяснение. |
top_edges.csv | До 200 крупнейших видимых пар src → dst с суммой, числом операций, ролями и кластерами обоих концов. |
top_counterparties.csv | До трёх крупнейших входящих и исходящих прямых контрагентов для каждого gid, с ролью и кластером контрагента. |
Экран просмотра
network.html — самодостаточная страница: поиск по gid, стрелки направления переводов, фильтры по кластеру, роли, флагам, seed, границе обхода и приоритету, окраска ролей и кластеров, топ-лист, карточки узлов, route explorer с подсветкой маршрутов и циклов, карточка выбранного кластера, локальная рабочая подборка с заметками, поиск общих источников и получателей, список недостающих данных и экспорт Markdown-отчёта аналитика.
Инструменты командной строки
| Скрипт | Назначение |
|---|---|
run.py | Полный пересчёт: от сырых parquet до всех выгрузок и network.html. Запускается через ./run.sh. |
assistant.py | Вопросы к графу на естественном языке; работает офлайн без LLM-endpoint. |
report_node.py <gid> | Карточка одного узла в терминале: роль, потоки, связи, на что обратить внимание. |
demo.py --out out | Генерирует сценарий пятиминутного показа из текущих выгрузок. |
compare_outputs.py old new | Сравнивает два пересчёта: изменения статуса узла, роли, кластера, флагов и priority_score. |
check_viewer.py out/network.html | Проверяет, что HTML самодостаточен и payload собран корректно. |
audit_explainability.py | Проверяет объяснимость случайно выбранных топ-узлов. |
graph_tools.py | Библиотека детерминированных запросов к графу; используется ассистентом, отдельно не запускается. |
Ограничения интерпретации
- Граф построен только по исходящим переводам от seed на четыре колена. 444 узла на 4-м колене без исходящих не считаются terminal: это артефакт границы обхода;
p_hidden_outgoingлишь оценивает вероятность продолжения. У других узлов также могут быть невидимые связи за рамками выгрузки. - Цикл, маршрут пересылки, синхронность и повтор суммы — структурные совпадения. Они не доказывают, что по цепочке прошли одни и те же деньги.
- Входящие seed из-за пределов выборки не видны. Для seed не применяется правило транзита по отношению
out_kzt / in_kzt. Нельзя считать это отношение полным балансом и для остальных узлов. - Переводы ниже 5 000 KZT и операции в других банках отсутствуют. Временной признак не доказывает прохождение одних и тех же средств.
- Клиентских атрибутов и размеченных истинных ролей нет. Числовые пороги и веса являются проверяемыми эвристиками; качество оценивается аналитиком по подтверждённым внешним данным.
Проверка
69 тестов в 38 файлах покрывают схемы выгрузок, правила ролей, воспроизводимость при перестановке входных строк и граничные случаи инструментов ассистента:
.venv/bin/python -m unittest discover -s tests -v
В Windows: .\.venv\Scripts\python.exe -m unittest discover -s tests -v.
| Проверка | Команда |
|---|---|
| Интерфейс: переключение кластеров и фильтры (нужен Node.js) | node tests/test_viewer.cjs |
| Самодостаточность HTML payload | .venv/bin/python check_viewer.py out/network.html |
| Объяснимость случайных топ-узлов | .venv/bin/python audit_explainability.py --out out --sample 10 |
| Карточка одного узла | .venv/bin/python report_node.py <gid> |
| Сравнение двух пересчётов | .venv/bin/python compare_outputs.py old_out new_out --out out_compare.csv |
Повторный запуск run.py на тех же данных даёт побайтово идентичные выгрузки: все случайные процедуры зафиксированы (seed=42), суммы округляются до тиынов.
Демо на 5 минут
Готовый сценарий, собранный из текущих выгрузок:
.venv/bin/python demo.py --out out
Порядок показа: за первую минуту запуск и выгрузки; затем топ-1 в HTML с объяснением плательщиков, охвата seed и посредничества; поиск произвольного gid из зала с показом входящих и исходящих связей; граничный gid (boundary=True) и объяснение, почему ему не присвоен terminal. Завершение — кластер с несколькими seed и его гипотеза, затем resilience.csv (изъятие топ-10 против случайных) и data_gaps.csv как план следующего запроса.
При росте до миллиона узлов
Parquet читать по частям через Polars или DuckDB, хранить агрегаты в колонночном формате и строить сжатое представление смежности. Louvain и посредничество переносить в igraph/graph-tool или распределённый графовый движок; посредничество считать на выборке с заданной погрешностью. Сохранять рассчитанные метрики по батчам и обновлять затронутые компоненты. В браузер отдавать только выбранный кластер или окрестность gid, а не миллион узлов сразу. Пороги ролей и формулу ранжирования можно сохранить, но их нужно заново проверить на распределении признаков крупной сети.
Авторы коммитов
История коммитов
- 2026-09-23 16:45Merge pull request #78 from BAITC-Hacks/docs/readme-refreshRuslan
- 2026-09-23 16:43docs: restructure README by topicRuslan Sibgatulin
- 2026-09-23 16:40docs: add jury summary and refresh stale README claimsRuslan Sibgatulin
- 2026-09-23 16:32Merge pull request #77 from BAITC-Hacks/feat/route-edgesAlibek Tugel
- 2026-09-23 16:30feat: summarize seed attention reachAlibek Tugel
- 2026-09-23 16:29feat: summarize cluster flow exposureAlibek Tugel
- 2026-09-23 16:28feat: export route edge membershipAlibek Tugel
- 2026-09-23 16:27Merge pull request #76 from BAITC-Hacks/test/latest-main-30Aruzhan Myrzakhmetova
Все коммиты (177)
- 2026-09-23 16:27Merge pull request #75 from BAITC-Hacks/test/latest-main-28Aruzhan Myrzakhmetova
- 2026-09-23 16:26feat: summarize seed attention reachAlibek Tugel
- 2026-09-23 16:26Merge pull request #74 from BAITC-Hacks/test/latest-main-27Aruzhan Myrzakhmetova
- 2026-09-23 16:26Merge pull request #73 from BAITC-Hacks/test/latest-main-22Aruzhan Myrzakhmetova
- 2026-09-23 16:25Merge pull request #72 from BAITC-Hacks/test/latest-main-23Aruzhan Myrzakhmetova
- 2026-09-23 16:24feat: summarize cluster flow exposureAlibek Tugel
- 2026-09-23 16:24Merge pull request #71 from BAITC-Hacks/test/latest-main-25Aruzhan Myrzakhmetova
- 2026-09-23 16:24Merge pull request #70 from BAITC-Hacks/test/latest-main-24Aruzhan Myrzakhmetova
- 2026-09-23 16:23Merge pull request #69 from BAITC-Hacks/test/latest-main-26Aruzhan Myrzakhmetova
- 2026-09-23 16:22Merge pull request #68 from BAITC-Hacks/test/latest-main-31Aruzhan Myrzakhmetova
- 2026-09-23 16:21Merge pull request #66 from BAITC-Hacks/test/latest-main-29Aruzhan Myrzakhmetova
- 2026-09-23 16:21Merge pull request #67 from BAITC-Hacks/feat/attention-overlapAlibek Tugel
- 2026-09-23 16:21Merge pull request #65 from BAITC-Hacks/feat/depth-attentionAlibek Tugel
- 2026-09-23 16:21Merge pull request #64 from BAITC-Hacks/feat/role-attentionAlibek Tugel
- 2026-09-23 16:20feat: report attention flag overlapAlibek Tugel
- 2026-09-23 16:18feat: summarize attention by depthAlibek Tugel
- 2026-09-23 16:17feat: summarize attention by roleAlibek Tugel
- 2026-09-23 16:17Merge pull request #63 from BAITC-Hacks/feat/seed-overlapAlibek Tugel
- 2026-09-23 16:16Merge pull request #62 from BAITC-Hacks/feat/depth-flowsAlibek Tugel
- 2026-09-23 16:16Test transaction count mismatch with matching amountsaruzhanmm
- 2026-09-23 16:16Test rejection of duplicate aggregated edgesaruzhanmm
- 2026-09-23 16:16Test priority change threshold in both directionsaruzhanmm
- 2026-09-23 16:16Test attention mapping by weak componentaruzhanmm
- 2026-09-23 16:16Test isolated node filtering and seed priorityaruzhanmm
- 2026-09-23 16:16Test resilience removal limits on small graphsaruzhanmm
- 2026-09-23 16:16Test seed component footprint and directed reacharuzhanmm
- 2026-09-23 16:16Test component role shares and equal-size orderingaruzhanmm
- 2026-09-23 16:16Test cluster attention counts across flagsaruzhanmm
- 2026-09-23 16:16Test role depth shares and orderingaruzhanmm
- 2026-09-23 16:16feat: report seed overlapAlibek Tugel
- 2026-09-23 16:16feat: summarize depth flowsAlibek Tugel
- 2026-09-23 16:15Merge pull request #61 from BAITC-Hacks/feat/role-flowsAlibek Tugel
- 2026-09-23 16:09Merge pull request #51 from BAITC-Hacks/test/latest-main-12Aruzhan Myrzakhmetova
- 2026-09-23 16:08Merge pull request #52 from BAITC-Hacks/test/latest-main-13Aruzhan Myrzakhmetova
- 2026-09-23 16:08Merge pull request #53 from BAITC-Hacks/test/latest-main-14Aruzhan Myrzakhmetova
- 2026-09-23 16:08Merge pull request #54 from BAITC-Hacks/test/latest-main-15Aruzhan Myrzakhmetova
- 2026-09-23 16:07Merge pull request #55 from BAITC-Hacks/test/latest-main-16Aruzhan Myrzakhmetova
- 2026-09-23 16:06Merge pull request #56 from BAITC-Hacks/test/latest-main-17Aruzhan Myrzakhmetova
- 2026-09-23 16:06Merge pull request #57 from BAITC-Hacks/test/latest-main-18Aruzhan Myrzakhmetova
- 2026-09-23 16:06Merge pull request #58 from BAITC-Hacks/test/latest-main-19Aruzhan Myrzakhmetova
- 2026-09-23 16:05Merge pull request #59 from BAITC-Hacks/test/latest-main-20Aruzhan Myrzakhmetova
- 2026-09-23 16:05Merge pull request #60 from BAITC-Hacks/test/latest-main-21Aruzhan Myrzakhmetova
- 2026-09-23 15:58feat: summarize role flowsAlibek Tugel
- 2026-09-23 15:55Merge pull request #50 from BAITC-Hacks/feat/isolated-nodes-reportAlibek Tugel
- 2026-09-23 15:55Merge pull request #49 from BAITC-Hacks/feat/component-attention-summaryAlibek Tugel
- 2026-09-23 15:54feat: report isolated nodesAlibek Tugel
- 2026-09-23 15:54Merge pull request #48 from BAITC-Hacks/feat/component-role-matrixAlibek Tugel
- 2026-09-23 15:53Test counterparty limits per node and directionaruzhanmm
- 2026-09-23 15:53Test per-cluster depth sharesaruzhanmm
- 2026-09-23 15:53Test depth summary totals and boundary countsaruzhanmm
- 2026-09-23 15:53Test cycle closure and self-loop membershiparuzhanmm
- 2026-09-23 15:53Test route positions and large node identifiersaruzhanmm
- 2026-09-23 15:53Test seed role reach without duplicate descendantsaruzhanmm
- 2026-09-23 15:53Test cluster role shares and tied leadersaruzhanmm
- 2026-09-23 15:53Test boundary review ordering and filteringaruzhanmm
- 2026-09-23 15:53Test exact transaction amount band boundariesaruzhanmm
- 2026-09-23 15:53Test daily active-node deduplicationaruzhanmm
- 2026-09-23 15:53feat: summarize component attention flagsAlibek Tugel
- 2026-09-23 15:51feat: summarize component rolesAlibek Tugel
- 2026-09-23 15:48Merge pull request #47 from BAITC-Hacks/feat/cluster-attention-summaryAlibek Tugel
- 2026-09-23 15:47feat: summarize cluster attention flagsAlibek Tugel
- 2026-09-23 15:46Merge pull request #46 from BAITC-Hacks/feat/role-depth-matrixAlibek Tugel
- 2026-09-23 15:46Merge pull request #45 from BAITC-Hacks/feat/seed-componentsAlibek Tugel
- 2026-09-23 15:46feat: summarize role depthsAlibek Tugel
- 2026-09-23 15:44Merge pull request #44 from BAITC-Hacks/test/latest-main-11Aruzhan Myrzakhmetova
- 2026-09-23 15:44feat: map seeds to componentsAlibek Tugel
- 2026-09-23 15:44Merge pull request #43 from BAITC-Hacks/feat/top-counterpartiesAlibek Tugel
- 2026-09-23 15:44Merge pull request #42 from BAITC-Hacks/feat/cluster-depth-matrixAlibek Tugel
- 2026-09-23 15:43Merge pull request #41 from BAITC-Hacks/feat/depth-summaryAlibek Tugel
- 2026-09-23 15:43Merge pull request #40 from BAITC-Hacks/test/latest-main-10Aruzhan Myrzakhmetova
- 2026-09-23 15:42feat: export top direct counterpartiesAlibek Tugel
- 2026-09-23 15:41Test seed coverage direction and four-hop limitaruzhanmm
- 2026-09-23 15:41Test directed cross-cluster flow aggregationaruzhanmm
- 2026-09-23 15:41feat: summarize cluster depthsAlibek Tugel
- 2026-09-23 15:40feat: summarize traversal depthsAlibek Tugel
- 2026-09-23 15:39feat: summarize seed reach by roleAlibek Tugel
- 2026-09-23 15:39feat: export route node membershipAlibek Tugel
- 2026-09-23 15:39feat: export cycle node membershipAlibek Tugel
- 2026-09-23 15:39feat: export attention flag examplesAlibek Tugel
- 2026-09-23 15:35Merge pull request #39 from BAITC-Hacks/test/latest-main-9Aruzhan Myrzakhmetova
- 2026-09-23 15:34Merge pull request #38 from BAITC-Hacks/test/latest-main-8Alibek Tugel
- 2026-09-23 15:34Merge pull request #37 from BAITC-Hacks/feat/cluster-role-matrixAlibek Tugel
- 2026-09-23 15:33feat: summarize daily network activityAlibek Tugel
- 2026-09-23 15:33feat: summarize roles by clusterAlibek Tugel
- 2026-09-23 15:33feat: queue boundary nodes for reviewAlibek Tugel
- 2026-09-23 15:32Test directed endpoint metadata in top edgesaruzhanmm
- 2026-09-23 15:32Test stable top edge ranking and row limitaruzhanmm
- 2026-09-23 15:28Merge pull request #36 from BAITC-Hacks/feat/top-edges-summaryAlibek Tugel
- 2026-09-23 15:27Merge pull request #35 from BAITC-Hacks/test/latest-main-7Aruzhan Myrzakhmetova
- 2026-09-23 15:27feat: summarize top transfer edgesAlibek Tugel
- 2026-09-23 15:27Merge pull request #34 from BAITC-Hacks/feat/role-summaryAlibek Tugel
- 2026-09-23 15:27Merge pull request #33 from BAITC-Hacks/feat/amount-band-summaryAlibek Tugel
- 2026-09-23 15:27Merge pull request #32 from BAITC-Hacks/test/latest-main-6Aruzhan Myrzakhmetova
- 2026-09-23 15:26feat: summarize assigned rolesAlibek Tugel
- 2026-09-23 15:25feat: summarize transaction amount bandsAlibek Tugel
- 2026-09-23 15:25Merge pull request #31 from BAITC-Hacks/feat/component-summaryAlibek Tugel
- 2026-09-23 15:24Merge pull request #30 from BAITC-Hacks/feat/seed-coverage-reportAlibek Tugel
- 2026-09-23 15:23feat: summarize graph componentsAlibek Tugel
- 2026-09-23 15:23Test report CLI error for unknown gidaruzhanmm
- 2026-09-23 15:23Test report CLI with explicit data path outside repositoryaruzhanmm
- 2026-09-23 15:21feat: report seed coverageAlibek Tugel
- 2026-09-23 15:20Merge pull request #29 from BAITC-Hacks/feat/viewer-path-highlightsAlibek Tugel
- 2026-09-23 15:19feat: highlight routes and cycles in viewerAlibek Tugel
- 2026-09-23 15:19Merge pull request #28 from BAITC-Hacks/feat/node-report-cliAlibek Tugel
- 2026-09-23 15:18Merge pull request #27 from BAITC-Hacks/test/latest-main-5Aruzhan Myrzakhmetova
- 2026-09-23 15:18feat: add node report CLIAlibek Tugel
- 2026-09-23 15:18Merge pull request #26 from BAITC-Hacks/feat/explainability-auditAlibek Tugel
- 2026-09-23 15:17Merge pull request #25 from BAITC-Hacks/test/latest-main-4Aruzhan Myrzakhmetova
- 2026-09-23 15:17feat: add explainability auditAlibek Tugel
- 2026-09-23 15:17Merge pull request #24 from BAITC-Hacks/feat/viewer-self-checkAlibek Tugel
- 2026-09-23 15:17Merge pull request #23 from BAITC-Hacks/feat/demo-scriptAlibek Tugel
- 2026-09-23 15:16Merge pull request #22 from BAITC-Hacks/feat/cluster-flowsAlibek Tugel
- 2026-09-23 15:16feat: add viewer payload self checkAlibek Tugel
- 2026-09-23 15:16Merge pull request #21 from BAITC-Hacks/feat/risk-flags-summaryAlibek Tugel
- 2026-09-23 15:16feat: add demo scriptAlibek Tugel
- 2026-09-23 15:15feat: summarize cluster ingress and egressAlibek Tugel
- 2026-09-23 15:15Test four-transfer cycle detection limitaruzhanmm
- 2026-09-23 15:15Test daily unique counterparties with repeated transfersaruzhanmm
- 2026-09-23 15:14feat: summarize risk flagsAlibek Tugel
- 2026-09-23 15:13feat: find shared counterparties in viewerAlibek Tugel
- 2026-09-23 15:13Merge pull request #20 from BAITC-Hacks/feat/viewer-route-explorerAlibek Tugel
- 2026-09-23 15:12feat: show forwarding routes in viewerAlibek Tugel
- 2026-09-23 15:11Merge pull request #19 from BAITC-Hacks/feat/stable-route-signalsAlibek Tugel
- 2026-09-23 15:11feat: detect stable forwarding routesAlibek Tugel
- 2026-09-23 15:09Merge pull request #18 from BAITC-Hacks/test/latest-main-3Aruzhan Myrzakhmetova
- 2026-09-23 15:08Test priority filter boundaries and clearing hidden selectionsaruzhanmm
- 2026-09-23 15:08Merge pull request #17 from BAITC-Hacks/feat/output-compareAlibek Tugel
- 2026-09-23 15:08Merge pull request #16 from BAITC-Hacks/feat/data-request-generatorAlibek Tugel
- 2026-09-23 15:07feat: compare pipeline output snapshotsAlibek Tugel
- 2026-09-23 15:07Merge pull request #15 from BAITC-Hacks/feat/report-exportAlibek Tugel
- 2026-09-23 15:07Merge pull request #13 from BAITC-Hacks/fix/tool-edge-casesRuslan
- 2026-09-23 15:07Merge pull request #14 from BAITC-Hacks/feat/case-workspaceAlibek Tugel
- 2026-09-23 15:06feat: generate next data requestsAlibek Tugel
- 2026-09-23 15:06Merge pull request #12 from BAITC-Hacks/feat/cluster-cardAlibek Tugel
- 2026-09-23 15:06fix: honest truncation and input validation in graph toolsRuslan Sibgatulin
- 2026-09-23 15:05feat: export analyst markdown reportAlibek Tugel
- 2026-09-23 15:05feat: add local case workspaceAlibek Tugel
- 2026-09-23 15:04feat: show selected cluster summaryAlibek Tugel
- 2026-09-23 15:03Merge pull request #11 from BAITC-Hacks/feat/viewer-filtersAlibek Tugel
- 2026-09-23 15:03feat: add viewer filtersAlibek Tugel
- 2026-09-23 15:02Merge pull request #10 from BAITC-Hacks/feat/priority-breakdownAlibek Tugel
- 2026-09-23 15:02feat: explain priority score breakdownAlibek Tugel
- 2026-09-23 15:02Merge pull request #9 from BAITC-Hacks/feat/node-timelineAlibek Tugel
- 2026-09-23 15:01feat: add node activity timelineAlibek Tugel
- 2026-09-23 14:55Merge pull request #8 from BAITC-Hacks/feat/openai-compatible-llmRuslan
- 2026-09-23 14:54chore: add .env.example for the LLM endpoint settingsRuslan Sibgatulin
- 2026-09-23 14:52Merge pull request #7 from BAITC-Hacks/test/latest-main-2Aruzhan Myrzakhmetova
- 2026-09-23 14:51feat: switch assistant to any OpenAI-compatible endpointRuslan Sibgatulin
- 2026-09-23 14:49Test rapid transfer signal at 48-hour boundariesaruzhanmm
- 2026-09-23 14:38Merge pull request #6 from BAITC-Hacks/test/latest-mainAruzhan Myrzakhmetova
- 2026-09-23 14:36Merge pull request #5 from BAITC-Hacks/feat/ai-assistantAlibek Tugel
- 2026-09-23 14:36Test output reproducibility after shuffling input rowsaruzhanmm
- 2026-09-23 14:33feat: add AI analyst assistant grounded in graph toolsAlibek Tugel
- 2026-09-23 14:31Merge pull request #4 from BAITC-Hacks/fix/deterministic-kztRuslan
- 2026-09-23 14:30Merge branch 'main' into fix/deterministic-kztRuslan Sibgatulin
- 2026-09-23 14:28fix: round KZT sums to tiyn so CSV output is byte-stable across runsRuslan Sibgatulin
- 2026-09-23 14:28Merge pull request #3 from BAITC-Hacks/feature/validation-testsAlibek Tugel
- 2026-09-23 14:26Merge latest main and adapt regression tests to extended pipelinearuzhanmm
- 2026-09-23 14:19Merge pull request #1 from BAITC-Hacks/feat/tz-extrasAlibek Tugel
- 2026-09-23 14:18feat: add risk signals, resilience check, and data gap reportAlibek Tugel
- 2026-09-23 14:13Fix input validation and cluster filtering with regression testsaruzhanmm
- 2026-09-23 13:59chore: add supplied dataset and starter kitRuslan Sibgatulin
- 2026-09-23 13:59feat: build explainable graph pipeline and local viewerRuslan Sibgatulin
- 2026-09-23 13:59test: add validated CSV exports and pipeline checksRuslan Sibgatulin
- 2026-09-23 13:59docs: explain workflow, role rules, and architectureRuslan Sibgatulin
- 2026-09-23 13:47feat: build explainable graph pipeline and local viewerRuslan Sibgatulin
- 2026-09-23 13:47test: add validated CSV exports and pipeline checksRuslan Sibgatulin
- 2026-09-23 13:47docs: explain workflow, role rules, and architectureRuslan Sibgatulin
- 2026-09-23 13:47chore: add supplied dataset and starter kitRuslan Sibgatulin
Структура репозитория (100 файлов)
.env.example .gitignore README.md architecture.svg assistant.py audit_explainability.py check_viewer.py compare_outputs.py data/edges.parquet data/nodes.parquet data/transactions.parquet demo.py graph_tools.py out/amount_bands.csv out/attention_examples.csv out/attention_overlap.csv out/boundary_review.csv out/cluster_attention.csv out/cluster_depths.csv out/cluster_flow_summary.csv out/cluster_flows.csv out/cluster_roles.csv out/clusters.csv out/component_attention.csv out/component_roles.csv out/components.csv out/cycle_nodes.csv out/cycles.csv out/daily_summary.csv out/data_gaps.csv out/depth_attention.csv out/depth_flows.csv out/depth_summary.csv out/isolated_nodes.csv out/nodes_roles.csv out/resilience.csv out/risk_flags.csv out/role_attention.csv out/role_depths.csv out/role_flows.csv out/role_summary.csv out/route_edges.csv out/route_nodes.csv out/routes.csv out/seed_attention.csv out/seed_components.csv out/seed_coverage.csv out/seed_overlap.csv out/seed_role_reach.csv out/timeline.csv out/top_counterparties.csv out/top_edges.csv out/top_nodes.csv report_node.py requirements.txt run.py run.sh starter/README.md starter/requirements.txt starter/starter.py tests/test_amount_band_boundaries.py tests/test_assistant.py tests/test_boundary_review_order.py tests/test_check_viewer.py tests/test_cluster_attention_flags.py tests/test_cluster_depth_shares.py tests/test_cluster_flow_direction.py tests/test_cluster_role_shares.py tests/test_compare_outputs.py tests/test_compare_priority_threshold.py tests/test_component_attention_mapping.py tests/test_component_role_ties.py tests/test_counterparty_direction_limits.py tests/test_cycle_limits.py tests/test_cycle_membership_closure.py tests/test_daily_active_nodes.py tests/test_demo.py tests/test_depth_summary_totals.py tests/test_duplicate_aggregated_edges.py tests/test_explainability_audit.py tests/test_features.py tests/test_graph_tools_edges.py tests/test_isolated_node_order.py tests/test_pipeline.py tests/test_report_cli_errors.py tests/test_report_cli_paths.py tests/test_report_node.py tests/test_resilience_small_graph.py tests/test_role_depth_denominators.py tests/test_route_membership_positions.py tests/test_seed_component_footprint.py tests/test_seed_coverage_limits.py tests/test_seed_role_deduplication.py tests/test_timeline_counterparties.py tests/test_top_edges_metadata.py tests/test_top_edges_ranking.py tests/test_transaction_count_mismatch.py tests/test_validation.py tests/test_viewer.cjs viewer.html
Ветки (59)
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.