О проекте
Команда: IIntenational
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Рабочий MVP
Задача кейса: «Граф денег»: восстановить структуру организованной группы по сети транзакций (AML)
Суть
Локальное приложение и пайплайн для AML-анализа транзакционной сети: импорт таблиц разных форматов, роли узлов по явным правилам, Louvain-кластеры, приоритетный список узлов и интерактивный граф.
Как устроено
Таблицы nodes/edges/transactions (CSV, TSV, XLSX, JSON, JSONL, Parquet) проходят валидацию, суммы нормализуются через Decimal без округления. По ним строится направленный граф NetworkX: компоненты, betweenness, PageRank, пороговые правила ролей и Louvain-кластеры. Строгий пайплайн run_pipeline.py пересчитывает три parquet и выдаёт nodes_roles.csv, clusters.csv, top_nodes.csv и автономный graph_view.html. Мастер импорта на http.server позволяет сопоставить колонки и хранит историю расчётов. ML-модели и облачные LLM не используются.
Ключевые решения
- NetworkX
- Louvain
- PageRank
- betweenness
- правила ролей с порогами
- Decimal-нормализация сумм
Особенности
Мастер импорта с ручным сопоставлением колонок для шести форматов. 24 синтетических тестовых файла, из них 6 намеренно ошибочных. XLSX читается без исполнения формул. Честно оговорено, что оригинального датасета не было и данные сгенерированы синтетически.
Стек
Python · pandas · PyArrow · NetworkX · openpyxl · http.server · HTML/CSS/JS · Playwright · unittest · PowerShell
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-30cf4f13-iintenational.
README
README.mdПотоки — объяснимый анализ транзакционной сети
Проект команды IIntenational для HackAlem AI.
«Потоки» помогает аналитику разобраться в транзакционной сети: проверить качество входных таблиц, увидеть направления денежных потоков и связанные группы, выбрать узлы для первоочередного изучения и понять основания каждого вывода.
Локальный мастер принимает собственные файлы разных форматов, позволяет сопоставить колонки и сохраняет историю расчётов. Отдельный строгий пайплайн пересчитывает три Parquet по заданию HackAlem одной командой. Оба сценария используют общее аналитическое ядро: явные правила ролей, Louvain-кластеры, ранжированный список и интерактивный граф. Результаты являются гипотезами для проверки, а не выводами о виновности.
Важно: оригинальный датасет отсутствовал в рабочей папке. Текущие parquet и выгрузки созданы детерминированным синтетическим генератором по схеме и выбранным контрольным агрегатам
AGENT.md, но не воспроизводят все показатели ТЗ. Они демонстрируют реализацию, но не являются результатом анализа оригинальных данных HackAlem. Подробности — вdata/README.md.
Быстрые переходы: запуск с нуля · тестовые файлы · если не запускается · проверка решения · правила ролей.
Технологии
| Компонент | Фактически используется |
|---|---|
| Ядро и локальный сервер | Python 3.12+ (кроме 3.14.1); стандартный http.server.ThreadingHTTPServer, доступ только через loopback; decimal.Decimal для нормализации денег без округления |
| Таблицы и экспорт | pandas 2.3.3; PyArrow 23.0.1 для Parquet |
| Анализ графа | NetworkX 3.7: направленный граф, компоненты, betweenness и Louvain; PageRank и пороговые правила реализованы в проекте |
| Чтение Excel | openpyxl 3.1.5, defusedxml 0.7.1; XLSX читается без исполнения формул, с проверкой ограничений |
| Интерфейс | HTML, CSS и JavaScript без frontend-фреймворка; SVG для связей узла и Canvas для обзора сети |
| Хранение | Локальные файлы: исходные таблицы, CSV, JSON и автономный HTML; отдельная БД не требуется |
| Проверки | Стандартный Python unittest; Playwright 1.62.1 и Node.js 20+ для дополнительных проверок UI |
Версии Python-пакетов закреплены в requirements.txt,
браузерных dev-зависимостей — в package.json и
package-lock.json. Облачные AI-сервисы, внешние API и
модели машинного обучения для назначения ролей не используются.
Что запускать
| Задача | Команда после однократной установки | Где смотреть результат |
|---|---|---|
| Загрузить свои файлы через интерфейс, настроить поля, выполнить анализ | powershell -NoProfile -ExecutionPolicy Bypass -File .\run_app.ps1 | Браузер: http://127.0.0.1:8765/ |
Пересчитать три data/*.parquet по строгому заданию HackAlem одной командой | powershell -NoProfile -ExecutionPolicy Bypass -File .\run.ps1 | Каталог outputs/, файл graph_view.html |
Для знакомства с системой начните с локального приложения. Сервер и
аналитика работают на вашем компьютере. Сам репозиторий GitHub не запускает
Python, а просмотр index.html на GitHub не заменяет запуск приложения.
Запуск с нуля: Windows PowerShell
1. Подготовьте окружение
Нужны:
- Python 3.12+ кроме 3.14.1 — её исключает установленная версия NetworkX. Проверенное окружение проекта: Windows, Python 3.14.6.
- Git для клонирования либо скачанный ZIP репозитория.
- Современный браузер; автоматические проверки выполнены в Chrome.
- Интернет для скачивания проекта и первоначальной установки зависимостей. После установки обработка и просмотр работают без интернета.
GPU, Docker, база данных, API-ключи, платные сервисы, Node.js и npm для обычного запуска не нужны. Node.js используется только в дополнительных тестах UI.
Откройте PowerShell и проверьте доступность команд:
python --version
git --version
Если python не найден, но установлен Windows Python Launcher, проверьте
py --version. При подходящей версии окружение можно создать командой
py -m venv .venv. Если Python или Git не установлены, сначала установите их
и откройте новый терминал; Git не обязателен при скачивании ZIP.
2. Скачайте проект и перейдите в его корень
Из папки, в которой хотите сохранить проект:
git clone https://github.com/BAITC-Hacks/hack-30cf4f13-iintenational.git
cd hack-30cf4f13-iintenational
Альтернатива: на GitHub выберите Code → Download ZIP, распакуйте архив и откройте PowerShell в распакованной папке. Не запускайте проект прямо из ZIP.
В текущей папке должны находиться README.md, requirements.txt, run_app.py
и run_app.ps1. Все команды ниже рассчитаны на этот корень репозитория.
3. Один раз установите зависимости
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install --upgrade "pip>=26.2" --index-url https://pypi.org/simple
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe -m pip check
Дождитесь завершения установки без ошибок. Последняя команда должна сообщить
No broken requirements found.. Если использовали py, замените только первую
команду на py -m venv .venv.
Активировать окружение через Activate.ps1 не требуется: все команды явно
используют Python из .venv. Эта папка не хранится в GitHub — после клонирования
на новом компьютере её нужно создать заново.
Минимальная версия установщика pip — 26.2: она содержит исправление
CVE-2026-13346, связанного
с обработкой URL пакетов из недоверенного индекса. Команда обновляет только
установщик в проектном .venv.
4. Запустите приложение одной командой
powershell -NoProfile -ExecutionPolicy Bypass -File .\run_app.ps1
Ожидаемая строка в терминале:
Потоки — локальный мастер импорта: http://127.0.0.1:8765
Откройте http://127.0.0.1:8765/ в браузере. Должны появиться заголовок «От таблицы к картине потоков», кнопка «Новый анализ» и четыре шага: «Файлы», «Поля и условия», «Проверка», «Результат». Браузер автоматически не открывается.
Оставляйте терминал открытым: он обслуживает приложение. Остановка —
Ctrl+C в этом терминале. Закрытие вкладки браузера сервер не останавливает;
для повторного запуска используйте ту же команду. Готовые расчёты останутся
в истории при использовании того же каталога данных.
ExecutionPolicy Bypass применяется только к этому процессу PowerShell,
а не меняет системную политику. Если предпочитаете запуск без .ps1:
.\.venv\Scripts\python.exe -X utf8 run_app.py
Сервер слушает только 127.0.0.1: адрес доступен на этом же компьютере.
Не публикуйте его через туннель, reverse proxy или общий хостинг.
5. Выполните первый анализ на готовом примере
Для проверки не нужны собственные финансовые файлы:
- На шаге «Файлы» выберите
examples/workbench/transactions.csvиз локальной копии репозитория. Тип таблицы — «Переводы». - Нажмите «Настроить поля». Проверьте: отправитель →
src, получатель →dst, сумма →amount, дата →date. «Валюта в строке» остаётся незаданной: в этом примере такой колонки нет. - В условиях анализа оставьте профиль «Пользовательский набор», укажите валюту USD, знаков после запятой 2, полноту наблюдений «Неизвестна». Размер приоритетного списка можно оставить 30.
- Нажмите «Проверить данные». Ожидайте 3 узла, 2 связи, 2 перевода, 1 компоненту и оборот 1250.25 USD. Отсутствие seed и глубины — объяснённое ограничение примера, не ошибка импорта.
- Нажмите «Запустить анализ». После статуса «Готово» появятся граф и кнопки
скачивания. Найдите GID
001: ведущие нули должны сохраниться. - Откройте таблицу связей и карточку узла, скачайте CSV. Перезагрузите вкладку и откройте тот же результат в истории.
Можно добавить ещё examples/workbench/nodes.json и edges.csv, назначив им
типы «Узлы» и «Агрегированные связи»: получится 4 узла и 2 компоненты,
включая изолированный ISOLATED. Этот маленький пример не подходит для строгого
профиля HackAlem — тот намеренно остановит расчёт при несовпадении с ТЗ.
Для проверки других форматов используйте готовые наборы из раздела «Тестовые данные».
6. Загрузите свой набор
Мастер принимает CSV, TSV, XLSX, JSON, JSONL и Parquet. Допустим один файл переводов либо отдельные таблицы узлов, связей и транзакций, не более одной каждого типа. Для переводов обязательны отправитель, получатель и сумма; названия колонок сопоставляются вручную. Для Excel выбирается лист, для CSV — разделитель и кодировка.
Суммы должны быть в основных единицах выбранной валюты: 12.50 означает
12,50 USD при точности 2, а не 1250 USD. Один расчёт — одна валюта, без
конвертации. Неизвестные seed/depth не выдумываются. Если полнота неизвестна,
правила транзита и удержания, требующие этого контекста, не применяются.
Пределы первой версии: 20 МиБ на файл, 100 000 строк на таблицу, 10 000 узлов и 50 000 направленных связей; один расчёт одновременно. Лимит приёма не является гарантией времени для любой топологии.
Полная схема, особенности форматов и ограничений — в руководстве мастера. Не загружайте ФИО, ИИН и другие личные атрибуты: подготовьте только нужные обезличенные поля.
Тестовые данные для самостоятельной проверки
Скачивать данные с внешних сайтов не нужно. В репозитории есть 24 готовых
синтетических файла: 18 корректных и 6 намеренно ошибочных. Они создаются
из маленького примера examples/workbench/, не содержат реальных клиентов
и не заменяют оригинальный датасет соревнования.
Для первого теста выберите один файл переводов:
| Формат | Готовый файл | Настройки чтения |
|---|---|---|
| CSV | transactions.csv | UTF-8, разделитель запятая |
| TSV | transactions.tsv | UTF-8, табуляция |
| Excel XLSX | transactions.xlsx | Первый лист «Данные» |
| JSON | transactions.json | Массив плоских объектов |
| JSONL | transactions.jsonl | Один объект на строку |
| Parquet | transactions.parquet | Дополнительные настройки не нужны |
В каждой папке формата также есть nodes.* и edges.*. В мастере выберите
«Пользовательский набор», валюту USD, точность 2, полноту
«Неизвестна». Сопоставьте src, dst, amount, date с отправителем,
получателем, суммой и датой. Десятичный разделитель — точка, даты — ISO.
Для узлов достаточно gid, для связей дополнительно сопоставляется n_tx.
| Состав входа | Ожидаемый результат |
|---|---|
Только transactions.* | 3 узла, 2 связи, 2 перевода, 1 компонента, 1250.25 USD |
nodes.* + edges.* + transactions.* | 4 узла, 2 связи, 2 перевода, 2 компоненты, 1250.25 USD; сохранён ISOLATED |
nodes.* + edges.* | 4 узла, 2 связи, 2 компоненты, тот же оборот; счётчик отдельных переводов —, поскольку таблица транзакций не загружена |
Все шесть форматов описывают одинаковые переводы. Для одинакового состава
таблиц выходные CSV должны совпадать. Разрешено смешивать форматы разных
таблиц, например JSON-узлы + Parquet-связи + XLSX-переводы. Не загружайте шесть
версий переводов одновременно и не смешивайте маленькие примеры с data/.
Неизвестные seed и глубина — ожидаемые предупреждения, а не ошибка.
В examples/workbench/invalid/ находятся
отрицательная сумма, петля, Excel-формула, смешанные валюты, неверная дата и
отсутствующий отправитель. Каждый файл проверяйте отдельно: правильный результат
— отказ с объяснением, без запуска анализа. Для проверки валюты и даты
нужно сопоставить соответствующие колонки.
Полная инструкция, все 24 файла и ожидаемые ошибки.
Для большого графа используйте три синтетических Parquet в data/ со строгим
профилем HackAlem, KZT и точностью 0. Маленькие примеры для этого профиля
не подходят.
Файлы уже включены в репозиторий. Только если нужно их восстановить:
.\.venv\Scripts\python.exe -X utf8 scripts/generate_workbench_examples.py --overwrite
Команда заменяет только 24 сгенерированных примера в formats/ и invalid/;
сохраните свои правки к ним заранее. Она не меняет data/, исходные три маленькие
таблицы или workbench_data/. Без --overwrite существующие файлы не заменяются.
Где находятся данные и результаты
Приложение не скачивает транзакции из интернета, не подключено к банковским системам и не добавляет сведения о клиентах из внешних источников.
| Источник | Что это и когда используется |
|---|---|
data/*.parquet | Синтетический демонабор, созданный scripts/generate_demo_data.py по схеме и контрольным агрегатам AGENT.md. Его читает run.ps1; это не оригинальная выгрузка организаторов |
examples/workbench/ | Маленький вымышленный пример, 18 эквивалентных файлов в formats/ и 6 негативных сценариев в invalid/ |
| Файлы, выбранные пользователем | Фактический вход мастера. Он не подставляет демонабор автоматически; выводы зависят от загруженных данных и выбранных условий |
Оригинального датасета HackAlem в исходной рабочей папке не было. Синтетика
проверяет работоспособность, но не доказывает качество выявления ролей на
реальных данных. Известное противоречие контрольных условий и маркировка
демонабора описаны в data/README.md.
По умолчанию локальное приложение создаёт:
workbench_data/
uploads/ копии выбранных файлов под серверными ID
uploads.json индекс загрузок
runs/<run_id>/
state.json состояние отдельного расчёта
artifacts/
nodes_roles.csv
clusters.csv
top_nodes.csv
graph_view.html
report.json
result.json
У каждого запуска своя папка; новый анализ не перезаписывает предыдущий.
Скачивания браузера сохраняются в выбранную браузером папку, а серверные копии
остаются здесь. workbench_data/ исключён из Git, но не зашифрован.
Не переносите реальные загрузки в отслеживаемые data/, outputs/ или публичные
коммиты. Удаление файла из текущего набора мастера не удаляет его копию на диске.
Очистка выполняется при остановленном сервере по инструкции.
Другой порт или отдельная история
После остановки прежнего процесса, если нужно сменить порт:
powershell -NoProfile -ExecutionPolicy Bypass -File .\run_app.ps1 -Port 8766
Откройте адрес, который напечатает терминал: http://127.0.0.1:8766/.
Для отдельного хранилища, например вне репозитория:
powershell -NoProfile -ExecutionPolicy Bypass -File .\run_app.ps1 -Port 8766 -DataDir "$env:LOCALAPPDATA\Potoki"
Смена -DataDir показывает другую историю, а не переносит старую. Один и тот
же каталог нельзя одновременно открыть двумя серверами, даже на разных портах.
Для произвольного каталога самостоятельно обеспечьте права доступа и защиту
от публикации/синхронизации. Справка: .\.venv\Scripts\python.exe run_app.py --help.
Строгий пайплайн HackAlem: одна команда
После той же установки зависимостей полный пересчёт от трёх сырых Parquet до трёх CSV и экрана, без ручных действий между стадиями:
powershell -NoProfile -ExecutionPolicy Bypass -File .\run.ps1
Либо без обёртки PowerShell:
.\.venv\Scripts\python.exe -X utf8 run_pipeline.py
Этот режим не запускает веб-сервер: он читает data/nodes.parquet,
edges.parquet, transactions.parquet и обновляет результаты в outputs/.
CSV, экран и отчёт сначала полностью формируются во временном каталоге.
Ошибка расчёта или сборки HTML сохраняет предыдущие результаты. Финальное
копирование нескольких файлов не является общей файловой транзакцией:
при ошибке диска или доступа на этом этапе устраните причину и повторите запуск.
Откройте локальный outputs/graph_view.html двойным щелчком в проводнике:
он автономен и работает без сервера. Ссылка на HTML на GitHub показывает файл
репозитория, а не живое приложение.
Ожидаемые контрольные числа: 2248 узлов, 3119 связей, 4840 транзакций,
16 компонент; создаются три CSV, HTML и run_report.json.
Пятиминутное требование относится к этому объёму, не к установке зависимостей.
Если контрольные значения не сходятся, расчёт останавливается, данные не
подгоняются. Произвольный набор анализируйте через пользовательский профиль.
Демонстрационные Parquet уже входят в репозиторий. Команда
.\.venv\Scripts\python.exe scripts\generate_demo_data.py нужна только для
их восстановления и перезаписывает три файла data/ и demo manifest.
Не выполняйте её поверх своих данных. Она не является частью анализа и не
заменяет получение оригинального датасета.
macOS и Linux
Прямые Python-команды не требуют PowerShell. После клонирования перейдите в
корень и проверьте python3 --version: нужны те же версии, что указаны выше.
python3 -m venv .venv
./.venv/bin/python -m pip install --upgrade "pip>=26.2" --index-url https://pypi.org/simple
./.venv/bin/python -m pip install -r requirements.txt
./.venv/bin/python -m pip check
./.venv/bin/python -X utf8 run_app.py
Откройте напечатанный локальный URL, оставьте терминал открытым; остановка —
Ctrl+C. Для строгого пересчёта отдельно выполните
./.venv/bin/python -X utf8 run_pipeline.py.
Это эквивалентный способ запуска; полный регрессионный прогон проекта пока
выполнен на Windows, а не на macOS/Linux.
Если не запускается
| Симптом | Что проверить |
|---|---|
python/git не найден | Установка и доступность в PATH; откройте новый PowerShell. Для Python можно использовать подходящий py, для проекта — ZIP вместо Git |
Скрипт просит установить зависимости / нет .venv | Перейдите в корень репозитория и выполните шаг 3. Виртуальное окружение с другого компьютера копировать не нужно |
ModuleNotFoundError, например openpyxl | Выполните .\.venv\Scripts\python.exe -m pip install -r requirements.txt, затем pip check тем же Python; системный pip может относиться к другому окружению |
| Ошибка совместимости при установке | Проверьте .\.venv\Scripts\python.exe --version; Python 3.14.1 не подходит. Установите поддерживаемый интерпретатор и создайте для него окружение, не меняя закреплённые зависимости наугад |
PowerShell блокирует .ps1 | Используйте приведённую команду с -ExecutionPolicy Bypass либо прямой запуск .venv\Scripts\python.exe -X utf8 run_app.py; менять политику всей системы не требуется |
Порт занят / WinError 10048 | Возможно, приложение уже работает: откройте его адрес. Иначе остановите свой прежний процесс через Ctrl+C или используйте свободный порт; не завершайте неизвестные процессы |
| «Хранилище уже открыто другим экземпляром» | Если это ваш работающий сервер, откройте http://127.0.0.1:8765/ (либо его выбранный порт). Для перезапуска остановите прежний сервер через Ctrl+C в его терминале. Смена одного порта не освобождает хранилище; не удаляйте .server.lock |
WinError 183 на workbench_data\uploads/runs, «Отказано в доступе» / WinError 5 | Проверьте, что путь — каталог и вашей учётной записи разрешён доступ. Для существующих каталогов старой версии см. пояснение ниже; не удаляйте загрузки или историю |
| Страница не открывается | Посмотрите ошибку в терминале, используйте именно http://127.0.0.1:<напечатанный порт>/, не HTTPS, не адрес GitHub и не URL с другого компьютера |
| Ошибка сессии / 403 после перезапуска | Перезагрузите вкладку по адресу сервера: токен меняется при рестарте. Завершённые расчёты останутся в истории |
| Ошибка суммы или колонок | Проверьте тип таблицы, mapping, валюту, десятичную точность, кодировку/разделитель. После изменения настроек чтения нажмите «Применить и перечитать» |
| «Лимит хранилища загрузок исчерпан» | Квота исходных копий — 200 МиБ. Остановите сервер, сохраните нужные результаты и следуйте инструкции очистки; перезагрузка вкладки место не освобождает |
Старые версии создавали каталоги хранилища с mode=0o700. На Windows это
может исключить обычную учётную запись из прав доступа, если каталог создал
служебный процесс: каталог существует, но проверка доступа при повторном запуске
заканчивается WinError 183. Это одна из возможных причин, а не признак того,
что существующий каталог нужно удалить.
Теперь новые каталоги на Windows наследуют ACL родителя; на POSIX сохраняется
режим 0700. Приложение не переписывает ACL существующих каталогов автоматически.
Обновление через Git не переносит права Windows с чужого компьютера и само по
себе не исправляет ACL уже созданного недоступного хранилища.
Если они уже недоступны, попросите администратора адресно проверить и восстановить
доступ владельцу данных к нужным каталогам и файлам. Не выдавайте доступ
Everyone/Users и не используйте постоянный запуск приложения от администратора
как обход ошибки. Сохраните исходное хранилище и его данные.
Для отдельного нового хранилища можно использовать -DataDir, как в разделе
«Другой порт или отдельная история». Новый путь показывает другую историю:
старые загрузки и результаты не переносятся и не удаляются.
Состояние уже запущенного сервера можно проверить из второго PowerShell:
(Invoke-WebRequest -Uri 'http://127.0.0.1:8765/' -UseBasicParsing).StatusCode
Ожидается 200. Это подтверждает доступность страницы, а не корректность
аналитики; для проверки расчёта используйте пример выше и раздел «Тесты».
Обновление установленного проекта
Остановите приложение. Если копия получена через Git:
git status
git pull --ff-only
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
powershell -NoProfile -ExecutionPolicy Bypass -File .\run_app.ps1
При локальных изменениях или конфликте не используйте принудительный reset:
сначала сохраните свою работу и разберите сообщение Git. workbench_data/
не синхронизируется с GitHub; резервные копии локальных результатов нужны отдельно.
Проверенные возможности и границы тестирования — в протоколе мастера. Следующие разделы описывают строгий пайплайн HackAlem; отличия пользовательского режима — в руководстве мастера.
Вход и проверка целостности
Схема полей описана в data/README.md. Пайплайн останавливается,
если не совпадают обязательные колонки, число строк, ссылки рёбер на узлы,
агрегация транзакций, контрольный оборот или структура компонент.
Текущий демонстрационный набор:
| Проверка | Значение |
|---|---|
| Узлы / рёбра / транзакции | 2 248 / 3 119 / 4 840 |
| Seed | 81 |
| Общий оборот | 365 890 012 KZT |
| Depth 0 / 1 / 2 / 3 / 4 | 81 / 472 / 462 / 789 / 444 |
| Слабосвязные компоненты | 16 |
| Размеры двух крупнейших | 1 877 / 270 |
| Seed без исходящих | 31 |
Известные расхождения с описанием исходного набора в DOCX:
| Показатель | Текущая синтетика | Описано в DOCX |
|---|---|---|
Узлы с наблюдаемым sum_out > sum_in | 869 | 354 |
| Seed, отсутствующие в рёбрах | 0 | 19 |
| Seed только с входящими рёбрами | 31 | 12 |
| Узлы вне крупнейшей компоненты | 371 | 352 |
Это не результаты на оригинальных данных. Первые три расхождения не скрываются
и не исправляются подгонкой генератора или правил. В ТЗ есть два противоречия:
19 изолированных seed несовместимы с 16 компонентами, все из которых имеют
размер не меньше двух; кроме того, 2248 − 1877 = 371, а не 352. Подробности и
границы воспроизведения — в описании данных.
Проверки ниже относятся к выбранному строгому контракту; их успех не означает
совпадения со всеми утверждениями DOCX.
Строго проверяются контрольные объёмы и схема, уникальность узлов и пар рёбер,
ссылки всех концов рёбер и транзакций на существующие gid, соответствие
edges.depth глубине получателя, агрегация транзакций, минимальная сумма, даты
июля 2026 года, точное распределение по глубинам и размеры 16 слабосвязных
компонент. Для двух крупнейших проверяются размеры 1 877 и 270 и число seed
46 и 1, для остальных — диапазон 2–17 из ТЗ. Денежные суммы проверяются точными
целыми Python до группировки, чтобы переполнение int64 не скрывало неверный
оборот. Проверяется также отсутствие исходящих у depth=4.
Метрики
Для каждого узла считаются in_degree, out_degree, уникальные плательщики и
получатели, sum_in, sum_out, sum_out / sum_in, наблюдаемое удержание,
компонента, PageRank и приближённая betweenness. Для компонент больше 300 узлов
betweenness использует детерминированную выборку из 128 источников. PageRank,
betweenness и оборот дополнительно переводятся в процентиль внутри компоненты,
чтобы малая компонента не сравнивалась напрямую с крупнейшей.
Процентиль вычисляется как rank(method="average", pct=True): средний ранг
среди равных значений, делённый на число узлов группы. Глобальный процентиль
оборота считается так же по всей сети. Это относительный ранг, а не доля узлов,
которые значение строго превосходит: при равенстве метрик фраза «выше N% узлов»
была бы неточной. Например, у двух одинаковых значений процентиль каждого 0.75.
Баланс не трактуется как полный баланс клиента: граф содержит только наблюдаемую часть исходящих переводов от исходной выборки.
Правила ролей и пороги
Правила применяются сверху вниз. Первое сработавшее правило задаёт роль.
| Порядок | Роль | Формальное правило |
|---|---|---|
| 1 | coordinator | in_degree ≥ 2, out_degree ≥ 2, максимум компонентных процентилей PageRank/betweenness ≥ 0.95, компонентный процентиль оборота ≥ 0.75 |
| 2 | distributor | out_degree ≥ 10 уникальных получателей |
| 3 | consolidator | in_degree ≥ 8 уникальных плательщиков |
| 4 | transit | не seed, depth < 4, есть вход и выход, 0.8 ≤ sum_out/sum_in ≤ 1.2 |
| 5 | terminal | не seed, depth < 4, in_degree ≥ 1, наблюдаемое удержание (sum_in − sum_out)+ / sum_in ≥ 0.8, и out_degree = 0 либо sum_out/sum_in ≤ 0.2 |
| 6 | peripheral | ни одно правило выше не выполнено; в том числе граница depth=4, out=0, не достигшая структурных порогов |
role_score — детерминированная сила сработавшего правила, не вероятность
виновности и не оценка accuracy. Обозначения: clip(x) = min(1, max(0, x)),
C — максимум компонентных процентилей PageRank/betweenness, T — компонентный
процентиль оборота, r = sum_out/sum_in, h = max(sum_in − sum_out, 0)/sum_in.
Все итоговые значения ограничиваются диапазоном [0, 1].
| Роль | Формула role_score |
|---|---|
coordinator | 0.50 × C + 0.25 × clip((in_degree + out_degree)/20) + 0.25 × T |
distributor | 0.55 + 0.45 × clip((out_degree − 10)/50) |
consolidator | 0.55 + 0.45 × clip((in_degree − 8)/16) |
transit | 0.60 + 0.40 × clip(1 − abs(r − 1)/0.2) |
terminal | 0.65 + 0.25 × clip(h) + 0.10 × T |
peripheral | 0.32 для seed без исходящих; 0.30 для границы без исходящих; иначе 0.35 |
Формулы применяются только после выбора роли по таблице выше. Ratio seed не
используется в транзитном правиле из-за неполного входящего потока. evidence
содержит наблюдаемые основания роли; пороги и полный порядок выбора приведены
здесь. Условия пользовательского профиля с неизвестной полнотой наблюдений
описаны отдельно в руководстве мастера.
Артефакт четвёртого колена
Все 444 узла depth=4 имеют нулевой out_degree из-за границы обхода. Правила
terminal и transit прямо исключают depth=4. При этом наблюдаемые входящие
связи могут дать роль consolidator: её структурный порог проверяется раньше,
чем выбирается peripheral. Если он не достигнут, узел границы получает
peripheral с объяснением обрыва. В текущем синтетическом наборе все 444 таких
узла — peripheral; это результат данных, а не принудительное правило для
любой границы. Нулевой выход не интерпретируется как «деньги остались на счёте».
Кластеризация
Сначала находятся слабосвязные компоненты. Louvain запускается отдельно в
каждой из 16 компонент на неориентированной проекции; вес для кластеризации —
log(1 + sum_kzt), чтобы один крупный перевод не подавлял структуру связей.
Порядок компонент и кластеров детерминирован, seed алгоритма равен 42.
Перед сборкой графа узлы сортируются по идентификатору, рёбра — по паре
отправитель/получатель. При одинаковых данных и версиях библиотек перестановка
строк входных таблиц не меняет роли, приоритеты и три CSV; это проверяется
отдельно для строгого и пользовательского профилей.
clusters.csv содержит размер, число seed, сумму только внутренних рёбер,
структурно заметные gid и осторожную гипотезу. На демонстрационных данных найдено
89 кластеров, 21 из них содержит больше одного seed.
Приоритет проверки
Формула не обучается и одинакова для всех узлов:
priority_score = 0.45 × base(role)
+ 0.25 × role_score
+ 0.20 × max(component_pct(PageRank), component_pct(betweenness))
+ 0.10 × global_pct(turnover)
Базовые веса ролей: coordinator 1.00, consolidator 0.82, distributor 0.78,
transit 0.62, terminal 0.48, peripheral 0.18. Для depth=4, out=0 итог
умножается на 0.85, чтобы граница обхода не давала ложный приоритет.
top_nodes.csv содержит 30 строк. Узлы сортируются по исходному,
неокруглённому priority_score по убыванию; при равенстве — по
role_score по убыванию; при полном равенстве обоих — по числовому gid
по возрастанию. rank — позиция в этом порядке, а не дополнительная метрика.
В CSV priority_score и role_score округляются до шести знаков после запятой
уже после сортировки. Поэтому одинаковые отображаемые значения не всегда
означают равенство исходных оценок.
Поле why показывает итоговый приоритет и силу правила с шестью знаками после
запятой, базовый вес роли с двумя, а также компонентный процентиль центральности
и глобальный процентиль оборота по шкале 0–100 с шестью знаками. Это объяснение
тех же рассчитанных признаков, а не отдельный пересчёт приоритета. Штраф границы
уже учтён в итоговой оценке. Процентили трактуются как средний ранг при равенстве,
не как доля строго уступающих узлов. Настоящие равенства допустимы: why не
делается искусственно уникальным добавлением rank/gid или выдуманных причин.
Результаты
После запуска каталог outputs/ содержит:
nodes_roles.csv— 2 248 узлов, обязательные поляgid, role, role_score, cluster_id, priority_score, evidence;clusters.csv— одна строка на каждый кластер;top_nodes.csv— 30 приоритетных узлов;graph_view.html— автономный интерфейс «Потоки» (SVG + Canvas);run_report.json— контрольные числа и время стадий.
Откройте outputs/graph_view.html в браузере — сервер
не нужен. Интерфейс работает без сети, внешних шрифтов и JavaScript-библиотек.
Как пользоваться интерфейсом
- Начните с очереди проверки: все узлы упорядочены так же, как топ-лист, по приоритету, затем силе правила и gid. Баллы в интерфейсе показаны из 100; в CSV остаётся исходная шкала 0–1. Это не вероятности нарушения.
- Выберите узел или введите точный
gid. Карточка показывает роль, суммы, число контрагентов, раскрываемые объяснения правила и приоритета. - На схеме — крупнейшие входящие и исходящие связи: до 12 каждого направления, на узком экране до четырёх. Подпись сообщает, сколько показано из общего числа. На телефоне схема вертикальная: деньги движутся сверху вниз.
- Таблица связей содержит всех соседей, точные суммы и число переводов, без усечения. Клик по соседнему gid открывает его карточку.
- Фильтры роли, компоненты, кластера, глубины обхода и seed-статуса ограничивают очередь и режим «Вся сеть». Схема/таблица выбранного узла всегда показывает его полный контекст, включая соседей вне фильтров. Глобальный поиск сбрасывает несовместимые фильтры и сообщает об этом. При пустой выборке доступен сброс.
- Для общего обзора переключите цвет на кластеры, используйте масштаб, перетаскивание и «Вместить». Компоненты разложены отдельно; расстояния на схеме служат только для удобства просмотра и не являются метрикой риска.
- Кнопка «Скачать» сохраняет любой из трёх полных CSV прямо из автономного HTML; фильтры не меняют выгрузку. Встроенная справка объясняет обозначения.
Основные действия доступны клавиатурой. Есть видимый фокус, текстовые роли,
табличная альтернатива Canvas и сообщения о результатах поиска. В общем графе
стрелки перемещают схему, +/− меняют масштаб, Home вписывает сеть. Справка
закрывается через Escape и возвращает фокус.
Исходники интерфейса находятся в web/. Пайплайн встраивает их и данные в один
HTML, поэтому после изменения CSS/JS нужно повторить пересчёт. Не редактируйте
генерируемый outputs/graph_view.html вручную.
Мастер и граф используют бело-серую основу, почти чёрный текст и тёмно-бирюзовый акцент для действий, выбора и клавиатурного фокуса. Акцент не обозначает риск или «безопасность» узла: роли и кластеры имеют отдельные обозначения и подписи. Сохранённые в истории HTML-файлы сохраняют оформление на момент расчёта; новая тема появляется в новых результатах, без перезаписи старых артефактов.
Пометка «Демонстрационные данные» выводится только при совпадении SHA-256 трёх
parquet с data/demo_manifest.json, созданным генератором. Если файлы заменены
или manifest повреждён, экран просит проверить источник. Это локальная проверка
соответствия демонабору, а не цифровая подпись или подтверждение происхождения.
Безопасность автономного экрана
Текст данных экранируется при передаче в HTML и при выводе. gid передаётся
строкой, чтобы не потерять точность int64 в JavaScript. CSP запрещает сетевые
соединения, внешние ресурсы, объекты и отправку форм. Учётных записей, API,
секретов и серверного доступа нет. Сам HTML содержит всю выгрузку: обращаться
с ним нужно как с данными, не публиковать рабочие финансовые выгрузки.
Тесты
Выполняйте команды из корня после установки Python-зависимостей. Проверки
строгого режима рассчитаны на включённый в репозиторий синтетический data/.
Сначала пересоберите результаты из текущего кода, затем запустите Python-тесты:
powershell -NoProfile -ExecutionPolicy Bypass -File .\run.ps1
.\.venv\Scripts\python.exe -X utf8 -m unittest discover -s tests -v
Первая команда обновляет outputs/, но не историю в workbench_data/.
Пересборку нельзя пропускать после изменений web/: проверки автономного
интерфейса читают готовый outputs/graph_view.html, а Python-тесты создают свои
результаты во временных каталогах.
Быстрые адресные проверки новых сценариев:
.\.venv\Scripts\python.exe -X utf8 -m unittest discover -s tests -p test_startup.py -v
.\.venv\Scripts\python.exe -X utf8 -m unittest discover -s tests -p test_example_files.py -v
Первая группа проверяет повторный запуск, сохранность загрузок, режимы создания
каталогов Windows/POSIX и понятные ошибки доступа. Вторая читает готовые файлы
из examples/workbench/, сравнивает шесть форматов и выходные CSV, проверяет
смешанные таблицы и все шесть намеренных ошибок. Личное хранилище не изменяется.
Для статической и функциональной проверки UI нужны Node.js 20+ и отдельные dev-зависимости. Проверьте JavaScript собранного HTML и установите зависимости:
node scripts/check_graph_view.js
node scripts/check_theme.js
npm.cmd ci
Для повторяемой полной проверки проекта после установки используйте одну команду:
powershell -NoProfile -ExecutionPolicy Bypass -File .\verify.ps1
Она проверяет зависимости через pip check, пересобирает outputs/, запускает
все Python-тесты, статические проверки JavaScript/темы и оба набора браузерных
сценариев. Ошибка любого этапа останавливает скрипт с ненулевым кодом.
Для проверки без Node.js/npm/браузера используйте:
powershell -NoProfile -ExecutionPolicy Bypass -File .\verify.ps1 -SkipUi
Этот режим выполняет pip check, пересборку и Python-тесты; статические
JavaScript-проверки и оба браузерных набора пропускаются. Оба режима обновляют
outputs/, личное хранилище workbench_data/ не используется.
На Windows тесты автоматически находят Chrome в стандартных папках установки. Если подходящего браузера нет, перед следующим шагом установите Chromium Playwright (потребуется интернет):
npx.cmd playwright install chromium
Для нестандартного расположения установленного Chromium-браузера можно задать
BROWSER_PATH, как описано в инструкции тестирования.
Затем запустите обе группы браузерных сценариев:
npm.cmd run test:ui
npm.cmd run test:workbench
Node.js и Playwright не требуются для обычного запуска пайплайна или мастера.
На macOS/Linux используйте npm/npx вместо npm.cmd/npx.cmd, Python из
.venv/bin/, а вместо run.ps1 — ./.venv/bin/python -X utf8 run_pipeline.py;
учитывайте указанный выше статус проверки этих платформ.
test:ui проверяет автономный граф, test:workbench сам поднимает отдельный
сервер и проверяет импорт, анализ и историю во временном каталоге.
Успех — код завершения 0, OK у Python и все сценарии PASS у браузерных тестов.
Базовые Python-тесты проверяют схемы и повреждённый вход, точность идентификаторов,
метрики, правила, границу depth=4, компоненты, воспроизводимость CSV,
экранирование HTML и время выполнения. Браузерный сценарий действительно
нажимает кнопки, ищет узлы, меняет фильтры и скачивает все три CSV для сравнения
с файлами. Он сохраняет отчёт и снимки в artifacts/.
Полная инструкция по тестированию содержит ожидаемые числа, ручные сценарии, негативные проверки и критерии приёмки. Результаты проверок: первоначальный редизайн, локальный мастер, нейтральная бирюзовая тема. Сверка с заданием — соответствие DOCX, таблица артефактов и критерии. Последующая проверка исправления запуска и набора тестовых файлов — протокол выпуска. Свежий аудит воспроизводимости, импорта, мастера и запуска — протокол исправлений от 23.09.2026.
Ограничения подхода
- Видны только внутрибанковские переводы, прослеженные наружу от seed за июль 2026 года; сеть не является полным финансовым окружением узлов.
- Входящие seed системно неполны. Отрицательный наблюдаемый баланс не понижает роль сам по себе.
- Переводы ниже 5 000 KZT отсутствуют и не могут быть обнаружены этим набором.
depth=4— обрезанная граница, а не доказательство терминального поведения.- Центральности и сообщества зависят от наблюдаемой выборки; betweenness крупных компонент приближённая.
- Ground truth ролей отсутствует. Пороги создают объяснимые сигналы для проверки, а не классификацию с измеренной accuracy.
- Данные обезличены. Решение работает только с синтетическим
gid, структурой, датой и суммой; внешнее обогащение и персональные атрибуты не используются. - Синтетика повторяет выбранные контрольные агрегаты, но не исходную сеть:
sum_out > sum_inу 869 узлов вместо описанных 354, изолированных seed — 0 вместо 19, seed только с входом — 31 вместо 12. За пределами крупнейшей компоненты 371 узел; число 352 из DOCX противоречит2248 − 1877. Эти расхождения нельзя использовать как доказательство качества ролей.
Внешние условия перед окончательной приёмкой
- Получить оригинальные три Parquet и проверить их происхождение; отдельно выполнить на них пайплайн и сравнить результаты, не заменяя ошибки синтетикой.
- Уточнить у организаторов противоречия про 19 изолированных seed и 352 узла вне крупнейшей компоненты; согласовать ожидаемые числа с фактическими данными.
- Провести репетицию пятиминутного живого демо: запуск, разбор 2–3 узлов, объяснение ограничений. Сценарий подготовлен; проведённое выступление и полный успех на оригинальном наборе пока не заявляются.
Масштабирование до примерно 1 млн узлов
На миллионе узлов pandas/networkx и отрисовка каждого элемента становятся узким местом. Сохраняются те же правила и выходные контракты, но меняется исполнение:
- Хранить вход и промежуточные признаки в партиционированном Parquet; читать только нужные колонки через PyArrow/Polars.
- Заменить NetworkX на igraph/graph-tool либо распределённый движок; сначала разделять слабосвязные компоненты, затем считать их параллельно.
- Использовать приближённые PageRank/betweenness и Leiden/Louvain по компонентам, фиксируя seed и версию алгоритма для воспроизводимости.
- Считать степени и суммы потоково при чтении рёбер, не материализуя лишние копии таблиц в памяти.
- Выгружать CSV потоково или по партициям, а витрину хранить в аналитической БД.
- В интерфейсе показывать агрегированные кластеры и подгружать окрестность выбранного gid по запросу; не отправлять миллион узлов в браузер.
Структура репозитория
data/ схема и parquet
starter/ загрузка, граф, базовые метрики, схемы CSV
aml_graph/ валидация, метрики, роли, кластеры, экспорт, UI
web/ исходники HTML/CSS/JS автономного интерфейса
workbench/ адаптеры форматов, профили, локальный сервер и мастер
examples/workbench/ маленькие синтетические файлы для первого импорта
formats/ 18 корректных файлов: 6 форматов × 3 таблицы
invalid/ 6 файлов с ожидаемым отказом валидации
workbench_data/ локальные загрузки и история (не входят в git)
scripts/ генераторы демо/примеров, статические и браузерные проверки
tests/ acceptance, импорт, запуск/хранение, безопасность и готовые примеры
docs/ схема решения, сценарий демо, инструкция тестирования
outputs/ три CSV, экран и отчёт запуска
artifacts/ локальные отчёты и снимки тестов (не входят в git)
run_pipeline.py единая точка запуска
run.ps1 одна команда для PowerShell
run_app.py запуск локального сервера мастера
run_app.ps1 запуск мастера через проектный .venv на Windows
verify.ps1 единая проверка проекта и UI-регрессий
Авторы коммитов
История коммитов
- 2026-09-23 17:43Add files via uploadtrepytha
- 2026-09-23 17:38Fix analysis reproducibility, bounded imports and verification reliabilityCodex
- 2026-09-23 17:20Add one-command project verificationCodex
- 2026-09-23 17:13Add depth and seed graph filtersCodex
- 2026-09-23 17:02Fix Windows storage startup and add multi-format test datasetsCodex
- 2026-09-23 16:40Fix validation and score explanations; verify HackAlem deliverablesCodex
- 2026-09-23 16:11Document approved neutral teal visual directionCodex
- 2026-09-23 16:03Document complete setup, first analysis, and data provenanceCodex
Все коммиты (14)
- 2026-09-23 15:54Add local multi-format import workbench and verified analysis workflowCodex
- 2026-09-23 15:18Document approved local import workbench design and planCodex
- 2026-09-23 15:06Redesign graph workspace and strengthen validation and testsCodex
- 2026-09-23 14:12Delete HackAlem AI_ Граф денег_ восстановление финансовой структуры организованной группы по транзакционной сети.docxdont_forgiveme
- 2026-09-23 14:05Merge project with GitHub repository baseCodex
- 2026-09-23 14:04Add explainable AML graph analysis projectCodex
Структура репозитория (104 файлов)
.gitignore AGENT.md README.md aml_graph/__init__.py aml_graph/clustering.py aml_graph/config.py aml_graph/exports.py aml_graph/metrics.py aml_graph/pipeline.py aml_graph/roles.py aml_graph/validation.py aml_graph/visualization.py data/README.md data/demo_manifest.json data/edges.parquet data/nodes.parquet data/transactions.parquet docs/architecture.svg docs/compliance-verification.md docs/demo_script.md docs/reliability-audit-2026-09-23.md docs/startup-and-examples-verification.md docs/superpowers/plans/2026-09-23-local-import-workbench.md docs/superpowers/plans/2026-09-23-neutral-teal-theme.md docs/superpowers/specs/2026-09-23-local-import-workbench-design.md docs/superpowers/specs/2026-09-23-neutral-teal-theme-design.md docs/testing.md docs/theme-verification.md docs/verification.md docs/workbench-verification.md docs/workbench.md examples/workbench/README.md examples/workbench/edges.csv examples/workbench/formats/csv/edges.csv examples/workbench/formats/csv/nodes.csv examples/workbench/formats/csv/transactions.csv examples/workbench/formats/json/edges.json examples/workbench/formats/json/nodes.json examples/workbench/formats/json/transactions.json examples/workbench/formats/jsonl/edges.jsonl examples/workbench/formats/jsonl/nodes.jsonl examples/workbench/formats/jsonl/transactions.jsonl examples/workbench/formats/parquet/edges.parquet examples/workbench/formats/parquet/nodes.parquet examples/workbench/formats/parquet/transactions.parquet examples/workbench/formats/tsv/edges.tsv examples/workbench/formats/tsv/nodes.tsv examples/workbench/formats/tsv/transactions.tsv examples/workbench/formats/xlsx/edges.xlsx examples/workbench/formats/xlsx/nodes.xlsx examples/workbench/formats/xlsx/transactions.xlsx examples/workbench/invalid/formula.xlsx examples/workbench/invalid/invalid_date.jsonl examples/workbench/invalid/missing_sender.parquet examples/workbench/invalid/mixed_currencies.json examples/workbench/invalid/negative_amount.csv examples/workbench/invalid/self_transfer.tsv examples/workbench/nodes.json examples/workbench/transactions.csv outputs/clusters.csv outputs/graph_view.html outputs/nodes_roles.csv outputs/run_report.json outputs/top_nodes.csv package-lock.json package.json requirements.txt run.ps1 run_app.ps1 run_app.py run_pipeline.py scripts/check_graph_view.js scripts/check_theme.js scripts/generate_demo_data.py scripts/generate_workbench_examples.py scripts/test_ui.js scripts/test_workbench_ui.js starter/__init__.py starter/basic_metrics.py starter/data_loader.py starter/export_templates.py starter/graph_builder.py tests/test_example_files.py tests/test_explanations.py tests/test_importing.py tests/test_launch_scripts.py tests/test_pipeline.py tests/test_reliability.py tests/test_startup.py tests/test_visualization.py tests/test_workbench_analysis.py tests/test_workbench_server.py verify.ps1 web/app.js web/index.html web/styles.css workbench/__init__.py workbench/analysis.py workbench/importing.py workbench/models.py workbench/server.py workbench/web/app.js workbench/web/index.html workbench/web/styles.css
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.