О проекте
Команда: AItushka
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Доведённый продукт
Задача кейса: «Граф денег»: восстановить структуру организованной группы по сети транзакций (AML)
Суть
Кабинет AML-аналитика для сети переводов: направленный граф, сообщества Louvain, шесть объяснимых гипотез ролей, приоритет проверки, временные паттерны и циклы, AI-помощник для черновика заключения.
Как устроено
Три Parquet-таблицы проходят строгую проверку, затем Python-пайплайн на NetworkX строит направленный граф и считает признаки (seed_reach, betweenness, степени, суммы), сообщества Louvain и роли по правилам с порогами из config.json. priority_score — взвешенная сумма нормированных признаков, в поле why записываются два главных вклада. Отдельные модули находят всплески, групповые поступления, маршруты и циклы с учётом дат. FastAPI отдаёт результат в Next.js. AI-помощник (OpenAI Responses API, gpt-5.4-mini) получает только посчитанные факты под псевдонимами и отвечает со ссылками F1, F2…
Ключевые решения
- NetworkX
- Louvain
- betweenness centrality
- rule-based роли
- временные паттерны и поиск циклов
- PyVis
- OpenAI Structured Outputs
- псевдонимизация ID
Метрики
2248 клиентов, 3119 связей, 4840 операций; 88 сообществ, 974 события; расчёт 1,98 с (3,40 с при повторе)
Особенности
У каждого события хранятся правило, даты и ссылки на исходные операции. Перед отправкой в LLM идентификаторы заменяются псевдонимами, а ссылки в ответе проверяются. Мультиархитектурный Docker-образ публикуется через CI, есть инструкция деплоя на NVIDIA Brev.
Стек
Python 3.12 · pandas · NetworkX · FastAPI · Next.js 16 · React 19 · TypeScript · Docker · GitHub Actions · GHCR
Модели
gpt-5.4-mini
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-912ce0e6-aitushka.
README
README.md«Граф денег» — AItushka
Инструмент для исследования сети банковских переводов. Он помогает аналитику найти клиентов с приоритетом проверки, понять их роль в наблюдаемой сети, увидеть маршруты и временные паттерны переводов и получить объяснение каждого вывода на основе исходных операций.
Проект создан для аналитиков финансового мониторинга и жюри хакатона. Решение не выносит юридических решений и не объявляет клиента нарушителем: оно формирует проверяемые гипотезы и сокращает путь от большого графа операций до конкретных оснований для дальнейшего анализа.
Что реализовано
- загрузка и строгая проверка трёх Parquet-таблиц: клиентов, агрегированных связей и операций;
- построение направленного графа переводов с сохранением точности больших
int64-идентификаторов; - выделение сообществ алгоритмом Louvain;
- шесть объяснимых гипотез ролей:
consolidator,distributor,transit,terminal,coordinator,peripheral; - приоритет проверки от 0 до 1 с расшифровкой вклада каждого признака;
- поиск временных событий: всплесков, групповых поступлений, повторов состава и последующих выходов;
- поиск повторяющихся маршрутов и циклов с учётом дат и неизвестного порядка операций внутри дня;
- интерактивный кабинет аналитика: обзор, очередь клиентов, фильтры, карточка клиента, граф связей и выгрузки;
- переход от события к его участникам и исходным операциям;
- автономная HTML-справка клиента и JSON-пакет доказательств;
- AI-помощник: объяснение клиента или события, вопросы по текущему контексту и TXT-черновик заключения со ссылками F1, F2… на основания;
- обязательные CSV-выгрузки и метаданные воспроизводимого запуска;
- production-контейнер, Docker Compose и GitHub Actions для тестирования и публикации образа.
Подтверждённый результат на данных хакатона
| Показатель | Значение |
|---|---|
| Клиентов | 2 248 |
| Направленных связей | 3 119 |
| Операций | 4 840 |
| Период | 01.07.2026–31.07.2026 |
| Сообществ | 88 |
| Найденных событий | 974 |
| Клиентов хотя бы с одним событием | 1 177 |
Найденные события включают 133 всплеска, 66 групповых поступлений, 5 повторов состава, 297 последующих выходов, 86 повторяющихся маршрутов и 387 циклов. Это совпадения формализованных правил, а не количество подтверждённых нарушений.
Зафиксированный в outputs/run_metadata.json контрольный прогон занимает 1,98 секунды чистого расчёта и 2,84 секунды от холодного старта до готовых файлов. Повторная проверка команды из README на полном наборе заняла 3,40 секунды. Это значительно ниже ограничения ТЗ в 5 минут; время зависит от компьютера и версий библиотек.
Как работает решение
- Backend читает
nodes.parquet,edges.parquetиtransactions.parquet, проверяет схему, ссылки на клиентов и совпадение агрегатов с операциями. - Python-пайплайн строит направленный граф, вычисляет признаки, сообщества, гипотезы ролей и приоритет проверки.
- Отдельные модули анализируют дневные потоки, маршруты и циклы. Каждое событие хранит правило, даты, измерения и ссылки на исходные операции.
- FastAPI держит текущий
AnalysisResultв памяти и отдаёт типизированные данные, граф, справки и CSV. - Next.js показывает аналитику очередь клиентов. Из карточки можно перейти к основаниям, событию, контрагенту или графу.
- По явному нажатию AI-помощник получает ограниченный пакет уже рассчитанных фактов. Идентификаторы участников заменяются локальными псевдонимами. Ответ проходит проверку структуры и ссылок на источники.
- Аналитик скачивает обычную справку или AI-черновик и принимает решение о следующей проверке самостоятельно.
Критерии ролей и приоритета
Роли являются проверяемыми гипотезами, а role_score показывает силу срабатывания правила и не является вероятностью нарушения. Пороговые значения читаются из config.json.
| Роль | Формальное условие допуска по умолчанию |
|---|---|
consolidator | не менее 3 разных плательщиков и достижимость минимум от 2 seed |
distributor | не менее 10 разных получателей и отношение out_deg / max(in_deg, 1) >= 2 |
transit | не seed и не граница 4-го колена; есть вход и выход; 0,8 <= out_kzt / in_kzt <= 1,2 |
terminal | не seed и не граница 4-го колена; есть входящие средства и out_deg = 0 |
coordinator | положительное посредничество не ниже 90-го перцентиля, достижимость минимум от 2 seed и связи минимум с 2 чужими сообществами |
peripheral | резервная роль, если содержательные правила выше не сработали |
Неподходящая роль получает оценку 0. Допустимые роли ранжируются по объяснимой формуле; неотрицательные признаки нормируются на 95-й перцентиль положительных значений. Узлы на четвёртом колене не признаются конечными получателями только из-за отсутствия видимого продолжения. Каждая строка получает человекочитаемое evidence длиной до 200 символов.
Приоритет проверки рассчитывается независимо от выбранной роли:
priority_score = 0.35 * N(seed_reach)
+ 0.25 * N(max(in_kzt, out_kzt))
+ 0.25 * N(betweenness)
+ 0.15 * N(in_deg)
В why сохраняются два крупнейших вклада с исходными значениями. Полные формулы силы каждой роли, правила FIFO и ограничения интерпретации приведены в docs/METHODOLOGY.md.
Технологии
| Слой | Используется |
|---|---|
| Аналитика | Python 3.12, pandas, NumPy, SciPy, NetworkX, PyArrow |
| API | FastAPI, Uvicorn, Pydantic, HTTPX |
| Frontend | Next.js 16, React 19, TypeScript |
| Граф | NetworkX, PyVis, Louvain communities |
| AI | OpenAI Responses API, Structured Outputs, модель по умолчанию gpt-5.4-mini |
| Данные | Parquet, CSV, JSON |
| Поставка | Docker, Docker Compose, GitHub Actions, GHCR |
| Проверка | pytest, TypeScript compiler, Next.js production build, container smoke-test |
AI не рассчитывает роли, суммы или рейтинг. Все фактические показатели вычисляет Python; модель только помогает интерпретировать выбранный контекст.
Архитектура
flowchart LR
D[Parquet: nodes, edges, transactions] --> V[Проверка данных]
V --> P[Python analytics pipeline]
P --> R[Роли и приоритет]
P --> E[События, маршруты и циклы]
R --> A[AnalysisResult в памяти]
E --> A
A --> F[FastAPI :8000]
F --> N[Next.js :3000]
N --> U[Аналитик]
A --> X[CSV / JSON / HTML]
F --> C[Ограниченный AI-контекст]
C --> O[OpenAI Responses API]
O --> F
Основные компоненты:
src/money_graph/— проверка данных, признаки, роли, рейтинг, события, отчёты и API;frontend/— отдельный интерфейс Next.js;run.py— единая локальная команда расчёта и запуска двух серверов;data/— исходные данные хакатона;tests/— автоматические проверки аналитики и HTTP-контрактов;Dockerfile,compose.yaml,deploy/— production-запуск;.github/workflows/ci-cd.yml— CI и доставка контейнера.
Подробная схема и инварианты модулей находятся в docs/architecture.md.
Установка и локальный запуск
Требования: Python 3.12, Node.js 24 и npm.
Windows PowerShell
git clone https://github.com/BAITC-Hacks/hack-912ce0e6-aitushka.git
cd hack-912ce0e6-aitushka
py -3.12 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
npm install --prefix frontend
.\.venv\Scripts\python.exe run.py --data data --out outputs --ui
Linux / macOS
git clone https://github.com/BAITC-Hacks/hack-912ce0e6-aitushka.git
cd hack-912ce0e6-aitushka
python3.12 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
npm install --prefix frontend
.venv/bin/python run.py --data data --out outputs --ui
После сообщения о готовности откройте http://localhost:3000. Ctrl+C останавливает оба сервера. Для расчёта без интерфейса уберите --ui.
Один воспроизводимый запуск пайплайна
После установки зависимостей одна команда читает три исходных Parquet-файла, пересчитывает граф и создаёт все обязательные CSV:
.\.venv\Scripts\python.exe run.py --data data --out outputs
На Linux и macOS используйте .venv/bin/python вместо Windows-пути. Успешный запуск создаёт outputs/nodes_roles.csv, outputs/clusters.csv, outputs/top_nodes.csv и дополнительный outputs/run_metadata.json с контрольными суммами, параметрами и временем этапов.
Другие порты и конфигурация:
.\.venv\Scripts\python.exe run.py --data data --out outputs --config config.json --ui --port 3000 --api-port 8000
Запуск через Docker
Сборка и запуск из репозитория:
cp .env.example .env
docker compose up -d --build
docker compose ps
Приложение доступно на http://localhost:3000. Контейнер запускается без root, публикует только порт 3000 и проверяет полный путь Next.js → FastAPI → анализ через /api/health. CI выпускает один multi-architecture образ для linux/amd64 и linux/arm64.
После успешного CI готовый образ публикуется как:
ghcr.io/baitc-hacks/hack-912ce0e6-aitushka:latest
Тогда вместо локальной сборки можно выполнить:
docker compose pull
docker compose up -d --no-build
Запуск в NVIDIA Brev / DGX Cloud
Приложение не использует CUDA, поэтому для демонстрации достаточно CPU-инстанса. На Brev:
git clone https://github.com/BAITC-Hacks/hack-912ce0e6-aitushka.git
cd hack-912ce0e6-aitushka
cp .env.example .env
docker compose up -d --build
Для приватного доступа выполните на своём компьютере:
brev port-forward ИМЯ_ИНСТАНСА --port 3000:3000
Для демонстрации добавьте порт 3000 в Brev Console → Access → Secure Links. Полученный HTTPS URL укажите в .env как AI_ALLOWED_ORIGINS=https://ваш-домен и повторно выполните docker compose up -d.
Полная инструкция, обновление и откат: docs/DEPLOY_NVIDIA.md.
Настройка AI
AI необязателен: вся основная аналитика работает без API-ключа и интернета. Для включения скопируйте .env.example в .env и заполните:
OPENAI_API_KEY=YOUR_KEY
OPENAI_MODEL=gpt-5.4-mini
AI_MAX_REQUESTS=50
# Для публичного размещения:
AI_ALLOWED_ORIGINS=https://ваш-домен
Ключ читает только backend, он не попадает во frontend и исключён из Git и Docker build context. Запрос создаётся только после действия пользователя. В OpenAI передаются выбранные показатели, даты, события и вопрос; исходные gid заменяются обозначениями участников. Используется store: false, но псевдонимизация не равна полной анонимизации.
Подробности: docs/AI.md.
Как жюри проверить решение
- Запустите проект локально или через Docker и откройте обзор.
- В строке поиска введите
100000003684369100. - Убедитесь, что карточка показывает гипотезу «Координатор», приоритет
0,965, входящий поток3 848 436 KZTи исходящий8 588 655 KZT. - Посмотрите «Основания приоритета»: достижимость от 9 seed, наблюдаемый поток, посредничество и число плательщиков показаны отдельно.
- Откройте «События и маршруты», раскройте повторяющийся маршрут и проверьте его даты, шаги и исходные операции.
- Нажмите «Показать на графе» и убедитесь, что граф ограничился участниками выбранного события.
- Скачайте HTML-справку клиента или один из CSV в разделе «Выгрузки».
- Если настроен OpenAI, нажмите «Объяснить клиента», затем откройте основание F3: суммы в ответе должны совпадать с локальными показателями.
Полный пятиминутный сценарий: docs/DEMO.md.
Выходные файлы
| Файл | Обязательные поля по ТЗ | Фактический результат |
|---|---|---|
nodes_roles.csv | gid, role, role_score, cluster_id, priority_score, evidence | 2 248 строк; дополнительно содержит исходные метрики, оценки всех ролей, вклады приоритета и ограничения наблюдения |
clusters.csv | cluster_id, n_nodes, n_seed, sum_kzt_internal, top_gids, hypothesis | 88 строк, по одной на найденное сообщество |
top_nodes.csv | rank, gid, role, priority_score, why | 20 ранжированных узлов с объяснением |
run_metadata.json | дополнительный артефакт | конфигурация, контрольные суммы входов, статистика, версии библиотек и время этапов |
Идентификаторы во всех JSON передаются строками. Сохранённые файлы в outputs/ не используются API как кеш: HTTP-выгрузки формируются из текущего расчёта в памяти.
Данные и интеграции
Исходные файлы находятся в data/:
nodes.parquet— клиенты, seed и глубина обхода;edges.parquet— агрегаты направленных пар;transactions.parquet— отдельные переводы с датами и суммами.
Набор ограничен одним банком, июлем 2026 года, переводами от 5 000 KZT и обходом исходящих связей от 81 seed-клиента до глубины 4. Поля описаны в data/README.md.
Единственная внешняя runtime-интеграция — OpenAI Responses API, и она включается только при наличии ключа. NVIDIA Brev используется как площадка запуска контейнера; специфические NVIDIA API и GPU в вычислениях проекта не используются.
Автоматические проверки
.\.venv\Scripts\python.exe -m pytest -q
npm.cmd run typecheck --prefix frontend
npm.cmd run build --prefix frontend
Локально подтверждено: 148 passed, успешные TypeScript-проверка и production-сборка Next.js. GitHub Actions дополнительно собирает контейнер, запускает его, ждёт /api/health, проверяет UID 10001 и только затем публикует образ в GHCR.
Ограничения текущей версии
- Это аналитические гипотезы без размеченной выборки нарушений; precision/recall не заявляются.
- В данных нет времени внутри дня, назначения платежа, баланса, внешних переводов и полной истории до/после периода.
- Seed-клиенты имеют неполную входящую часть, а узлы на глубине 4 могут иметь обрезанные исходящие связи.
- FIFO показывает совместимость дневных сумм, но не доказывает движение одних и тех же денег.
- Разные события могут ссылаться на одни операции; их суммы нельзя складывать в общий «подозрительный оборот».
- NetworkX-реализация рассчитана на текущий хакатонный набор и не заявлена как решение для миллиона узлов.
- AI может ошибиться в интерпретации даже при корректной ссылке на источник; финальный вывод проверяет аналитик.
- Лимиты AI и кеш находятся в памяти одного процесса и сбрасываются при перезапуске.
- В приложении нет собственной пользовательской аутентификации и системы ролей доступа. Демонстрационная ссылка намеренно открыта для жюри; при работе с непубличными данными доступ необходимо закрыть средствами платформы.
Масштабирование до 1 млн узлов
Текущая реализация на pandas и NetworkX рассчитана на предоставленный граф из 2 248 узлов. При росте примерно до миллиона узлов сохраняются контракт выходных файлов, формулы ролей и объяснения, но вычислительный слой потребуется заменить:
- читать Parquet пакетами и выполнять агрегации в Polars, DuckDB, Spark или другом columnar/distributed-движке;
- хранить рёбра и рассчитанные признаки в постоянном аналитическом хранилище вместо одного
AnalysisResultв памяти; - использовать эффективный графовый движок или разреженные структуры, а посредничество считать приближённо на выборке;
- пересчитывать независимые компоненты и временные окна параллельно и инкрементально;
- отдавать интерфейсу подграф вокруг выбранного клиента, кластера или маршрута, а не весь граф;
- вынести длительные расчёты в фоновые задания с версионированием результата и fingerprint входных данных.
Такой переход меняет способ хранения и исполнения, но не аналитический контракт между пайплайном, API и интерфейсом.
Deployed-версия
Открыть Hackalen в NVIDIA Brev
Сервис развёрнут на ARM64-инстансе NVIDIA Brev. Ссылка открывается публично без авторизации для проверки жюри.
Документация
Авторы коммитов
История коммитов
- 2026-09-23 17:39Align README with hackathon specificationthedids10
- 2026-09-23 17:34Document public demo accessthedids10
- 2026-09-23 17:30Document NVIDIA Brev deploymentthedids10
- 2026-09-23 17:13Publish containers for ARM64 Brev instancesthedids10
- 2026-09-23 17:08Rewrite README for hackathon evaluationthedids10
- 2026-09-23 17:04Add container CI/CD for NVIDIA cloud deploymentthedids10
- 2026-09-23 16:54Add grounded AI assistant for client and event analysisthedids10
- 2026-09-23 16:54Add evidence-backed transaction pattern analysisthedids10
Все коммиты (11)
- 2026-09-23 15:41Improve dashboard typography and collapsible navigationthedids10
- 2026-09-23 15:18Split analytics app into Next.js and FastAPIkLot2005
- 2026-09-23 14:16Build explainable transaction graph analyzerkLot2005
Структура репозитория (80 файлов)
.dockerignore .env.example .github/workflows/ci-cd.yml .gitignore Dockerfile README.md compose.yaml config.json data/README.md data/edges.parquet data/nodes.parquet data/transactions.parquet deploy/healthcheck.py deploy/serve.py docs/AI.md docs/ANALYTICS_EXTENSION_DESIGN.md docs/DEMO.md docs/DEPLOY_NVIDIA.md docs/IMPLEMENTATION_PLAN.md docs/METHODOLOGY.md docs/VALIDATION.md docs/architecture.md frontend/.gitignore frontend/next-env.d.ts frontend/next.config.ts frontend/package-lock.json frontend/package.json frontend/src/app/globals.css frontend/src/app/layout.tsx frontend/src/app/page.tsx frontend/src/components/analyst-ai.tsx frontend/src/components/client-patterns.tsx frontend/src/components/icon.tsx frontend/src/lib/types.ts frontend/tsconfig.json outputs/clusters.csv outputs/nodes_roles.csv outputs/run_metadata.json outputs/top_nodes.csv pyproject.toml requirements-lock.txt requirements.txt run.py src/money_graph/__init__.py src/money_graph/ai_context.py src/money_graph/ai_service.py src/money_graph/analyst_report.py src/money_graph/api.py src/money_graph/communities.py src/money_graph/config.py src/money_graph/data.py src/money_graph/event_data.py src/money_graph/explain.py src/money_graph/export.py src/money_graph/features.py src/money_graph/pipeline.py src/money_graph/ranking.py src/money_graph/roles.py src/money_graph/route_patterns.py src/money_graph/temporal.py src/money_graph/temporal_patterns.py src/money_graph/visualization.py starter/README.md starter/requirements.txt starter/starter.py tests/conftest.py tests/test_ai.py tests/test_api.py tests/test_communities.py tests/test_data.py tests/test_graph.py tests/test_launcher.py tests/test_pattern_api.py tests/test_pipeline.py tests/test_real_dataset.py tests/test_roles_and_ranking.py tests/test_route_patterns.py tests/test_temporal.py tests/test_temporal_patterns.py tests/test_visualization.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.