AAYMA
RU
Войти

Граф денег

AML-инструмент: загружает три Parquet, строит направленный граф переводов, по правилам назначает 6 ролей, выделяет сообщества Louvain, формирует…

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

О проекте

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

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

Суть

AML-инструмент: загружает три Parquet, строит направленный граф переводов, по правилам назначает 6 ролей, выделяет сообщества Louvain, формирует top-20 для проверки, а опциональный AI объясняет готовые факты.

Как устроено

Бэкенд на Python с чистой архитектурой (domain, application, infrastructure) читает и валидирует Parquet и считает граф в NetworkX. role_classifier назначает роли по явным правилам с оценкой силы эвристики, priority_calculator строит top-20 по связям, обороту и соседним seed, Louvain выделяет кластеры. Отдельно считаются временные паттерны (исходящие через 1–2 дня после поступления, синхронные поступления, всплески) и пробелы наблюдения с рекомендациями дозапроса данных. FastAPI и CLI выгружают nodes_roles, clusters и top_nodes. Фронтенд на React показывает интерактивный граф и карточку узла, AI-док (OpenAI) отвечает только по контексту выбранного снимка.

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

  • Louvain
  • NetworkX
  • rule-based role classification
  • priority scoring
  • temporal pattern detection
  • grounded LLM Q&A

Метрики

2 248 узлов / 3 119 рёбер, загрузка 6.51 с; CSV 2248/88/20 строк; 288 backend- и 25 frontend-тестов; AI-ответ за 9.76 с

Особенности

Бэкенд построен по чистой архитектуре (use cases, interfaces, adapters). Карточка узла явно показывает ограничения наблюдения и советует, какие данные дозапросить. README честно отмечает, что сборка в Docker не проверена.

Стек

Python 3.10 · FastAPI · NetworkX · React · TypeScript · Vite · Nginx · Docker Compose · pytest · Ruff

Модели

OpenAI


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

README

README.md

Граф денег

Инструмент для AML-аналитика / специалиста финансового мониторинга банка: из трёх Parquet он строит направленную сеть, назначает объяснимые роли, выделяет сообщества и приоритеты проверки. Карточка узла показывает связи, ограничения наблюдения, рекомендации по дополнительным данным и временные совпадения. Опциональный AI объясняет готовые факты, не изменяя расчёт.

Какую проблему решает

В таблице переводов видны отдельные операции, но трудно проследить связи между участниками и понять, с кого начать проверку. Аналитику финансового мониторинга нужно выделить участников со сходными схемами движения денег, увидеть возможный сбор, распределение или транзит средств и обосновать запрос дополнительных данных. При этом выборка неполна: отсутствие переводов в ней не означает отсутствие операций у клиента.

«Граф денег» превращает три файла Parquet в сеть переводов и единый рабочий экран: показывает окружение клиента, объясняет назначенную роль, выделяет сообщества и формирует приоритет проверки. Аналитик получает факты, ограничения наблюдения и рекомендации для дальнейшего расследования. Результаты — гипотезы для проверки, не доказательство виновности; решение принимает специалист.

Что реализовано

ВозможностьЧто получает аналитикСтатус
Загрузка и проверка данныхТри Parquet через сайт, API или CLI; понятные ошибки, сохранение предыдущего анализа при неудачной загрузке в APIРеализовано
Граф и поиск клиентаНаправленный подграф окружения, поиск точного gid по всем узлам, переходы между связанными клиентамиРеализовано
Объяснимые ролиШесть ролей по явным правилам, оценка силы эвристики и числовое объяснение для каждого узлаРеализовано
Приоритет проверкиTop-20 участников с баллами и объяснениями на основе связей, операций, оборота и соседних seedРеализовано
СообществаКластеры Louvain, их размер, seed, внутренний оборот, ключевые участники и гипотезаРеализовано
Карточка клиентаВходящие и исходящие связи, суммы, роль, глубина и ограничения наблюденияРеализовано
Временные наблюденияИсходящие через 1–2 дня после поступления, совпадения поступлений от разных отправителей и всплески активностиРеализовано
Пробелы данных и следующие запросыОбъяснение неполноты выборки и рекомендации, какие данные запросить; без процента полнотыРеализовано
AI-ассистентОбъяснение рассчитанных фактов со ссылками на узлы; роли и баллы остаются результатом алгоритмаРеализовано
Выгрузки и программный доступТри серверных CSV: nodes_roles.csv, clusters.csv, top_nodes.csv; CLI, HTTP API и SwaggerРеализовано

Подробные правила и ограничения приведены ниже; сопоставление с пунктами ТЗ — в разделе «Соответствие требованиям».

Проверено 23.09.2026: 288 backend-тестов, 25 frontend-тестов, Ruff и production-сборка. Chromium прошёл сценарий через локальный Nginx: загрузка 6,51 с, 2248 узлов / 3119 рёбер, CSV 2248 / 88 / 20 строк. Один реальный AI-вопрос через UI — HTTP 200 за 9,76 с. Compose-конфигурация проверена без печати секретов; контейнерные build/up не проверены: Docker Engine недоступен. Протокол интеграции. Предыдущая проверка чистого Python-окружения.

Полное приложение через Docker Compose

Нужны Git, работающий Docker Engine с Linux-контейнерами и Docker Compose v2, доступ к Docker Hub/npm/PyPI при первой сборке. Используются multi-stage Node 24.11.0 → Nginx 1.28 и Python 3.10. Самостоятельно устанавливать Node/Python на хост для этого способа не требуется. На WSL включите интеграцию нужной дистрибуции в Docker Desktop и запустите Engine.

Проверка окружения (без вывода конфигурации секретов):

docker version
docker compose version

Первоначальная настройка, из каталога для проектов:

git clone --branch refactor/clean-architecture https://github.com/BAITC-Hacks/hack-b7a04bbf-zhastyq.git
cd hack-b7a04bbf-zhastyq
cp -n .env.example .env
mkdir -p backend/data

Если репозиторий и .env уже есть, сохраните их; не заменяйте ключи. Распакуйте архив организаторов отдельно и скопируйте его data/nodes.parquet, data/edges.parquet, data/transactions.parquet в backend/data/. Файлы организаторов не входят в Git/образы. Можно вместо копирования задать в корневом .env DATA_DIR=/absolute/path/to/data; каталог должен существовать. Монтирование данных read-only для CLI не загружает анализ автоматически в API. Для сайта выберите те же файлы через браузер.

Без AI оставьте OPENAI_API_KEY и OPENAI_MODEL пустыми. Из корня проекта:

docker compose config --quiet
docker compose up --build -d

Приложение: http://127.0.0.1:8080; Swagger: http://127.0.0.1:8080/docs; health: http://127.0.0.1:8080/api/health. Порт меняется через APP_PORT. Он привязан только к localhost; публичное размещение и аутентификация этим этапом не реализуются.

Два сервиса: один Uvicorn worker в backend, production React через Nginx в frontend. Nginx сохраняет /api/..., поддерживает SPA, тело до 76 MiB и ожидание до 360 с. Backend проверяет ≤25 MiB на каждый файл. Браузер не обращается к имени контейнера backend. CORS для единого origin не нужен; для отдельного клиента задайте CORS_ORIGINS списком origins через запятую.

Одна команда CLI-пайплайна внутри запущенного контейнера:

docker compose exec backend money-graph analyze --data-dir /app/data --output-dir /app/out/cli

Результаты — nodes_roles.csv, clusters.csv, top_nodes.csv в /app/out/cli, записываемом named volume analysis_results. Скопировать их на хост:

mkdir -p backend/out
docker compose cp backend:/app/out/cli backend/out/

На хосте CSV окажутся в backend/out/cli/. Веб-экспорты активного анализа скачиваются тремя кнопками интерфейса; версии API хранятся отдельно в /app/out/api. Временные загрузки удаляются после обработки; /tmp доступен для записи. После рестарта процесса повторно загрузите анализ: снимок в памяти, сохранённые CSV не восстанавливают его автоматически. Named volume остаётся при обычном down.

AI в Compose: в корневом .env задайте OPENAI_API_KEY и доступную вашему аккаунту OPENAI_MODEL; опционально OPENAI_BASE_URL и OPENAI_TIMEOUT_SECONDS (по умолчанию 30). Compose читает этот файл для подстановки; передаются только перечисленные настройки и только backend во время запуска, не при сборке. Переменные shell имеют приоритет. После изменения повторите docker compose up --build -d. Frontend собирается с относительным API и без mocks; Vite-настройки фиксируются во время сборки, ключи ему не передаются. Не запускайте docker compose config без --quiet в публикуемом терминале: он может раскрыть значения.

Без AI-конфигурации анализ, карточки и CSV работают; AI показывает понятную недоступность. ai_configured означает наличие настройки, а не гарантию доступности модели.

Логи, проверка и остановка:

docker compose ps
docker compose logs --tail=100 backend frontend
docker compose down

Область проверки: на этой машине docker compose в WSL требует включить интеграцию Docker Desktop, а Windows docker.exe сообщает отсутствие dockerDesktopLinuxEngine. Windows Compose 2.38.2 выполнил config --quiet; локальный Nginx 1.22.1, Uvicorn и Chromium подтвердили пользовательский сценарий. Это не проверка сборки образов, volume-разрешений или контейнерной сети. После включения Engine необходимо выполнить build/up по команде выше. Далее приведён проверенный запуск без Docker и методика анализа.

Актуальный контракт: backend/docs/data_contract.md. Корневой docs/data_contract.md — устаревший документ, не инструкция по подключению: вход сейчас только Parquet, все HTTP gid — точные строки.

Требования и проверенная среда

  • Python 3.10.17, Linux x86_64 / WSL2, модули venv и ensurepip; Bash или совместимый shell. В pyproject указано Python >=3.10, но другие версии этой проверкой не подтверждены.
  • Git и доступ к репозиторию команды для получения исходников; curl для HTTP-примеров; доступ к индексу Python-пакетов при установке.
  • Архив организаторов с исходными Parquet не включён в Git.
  • Все данные и NetworkX-граф хранятся в памяти. Проверка выполнена на 2248 узлах, а не на миллионе; минимальные требования RAM/CPU нагрузочными тестами не установлены.
  • Версии зависимостей зафиксированы для повторения проверки в constraints-файле. Это ограничения для установки проекта через -c, не самостоятельный список для -r. Python и зависимости сборки setuptools отдельно не поставляются.

Проверить Python до установки:

python3 --version
python3 -m ensurepip --version

Если нет venv/ensurepip, сначала установите соответствующий пакет вашей ОС или полноценную сборку Python. В проверяемой системе /usr/bin/python3 версии 3.11.2 не содержит ensurepip; использовался доступный python3 версии 3.10.17. Команды установки системных пакетов и нативная Windows в этой проверке не тестировались.

Установка с нуля

Из каталога, в котором ещё нет этой копии репозитория:

git clone --branch refactor/clean-architecture https://github.com/BAITC-Hacks/hack-b7a04bbf-zhastyq.git
cd hack-b7a04bbf-zhastyq/backend

Уже имея репозиторий, просто перейдите в его backend/. Следующие команды выполняются оттуда. Активация окружения и uv не требуются:

mkdir -p .tmp
MG_VENV="${MG_VENV:-.venv}"
test ! -e "$MG_VENV" &&
python3 -m venv "$MG_VENV" &&
TMPDIR="$PWD/.tmp" "$MG_VENV/bin/python" -m pip install --no-cache-dir \
  -c docs/requirements-verified.txt -e '.[dev]'

Выполняйте шаги последовательно; при ошибке остановитесь. Если .venv уже существует, не пересоздавайте его. Для отдельной проверки вместо него перед блоком выше задайте:

MG_VENV=".tmp/jury-$(date +%Y%m%d-%H%M%S)-$$"

Каталог .tmp/ исключён из Git. Существующий .venv остаётся нетронутым. В новом терминале снова задайте MG_VENV=.venv либо путь к созданному временному окружению.

Данные и один полный расчёт

Распакуйте ZIP организаторов отдельно; путь к архиву замените на фактический:

"$MG_VENV/bin/python" -m zipfile -e /absolute/path/to/organizer-data.zip .tmp/organizer

В предоставленном архиве файлы находятся в папке data/. Скопируйте их без переименования в backend/data/:

backend/data/nodes.parquet
backend/data/edges.parquet
backend/data/transactions.parquet

После распаковки командой выше, из backend:

mkdir -p data
DATA_SOURCE=.tmp/organizer/data
cp -n "$DATA_SOURCE/nodes.parquet" data/nodes.parquet
cp -n "$DATA_SOURCE/edges.parquet" data/edges.parquet
cp -n "$DATA_SOURCE/transactions.parquet" data/transactions.parquet

cp -n сохраняет уже имеющиеся файлы; при повторной загрузке убедитесь, что используете нужный набор. Вместо копирования можно передать исходный каталог непосредственно в --data-dir. Данные исключены из Git; приложение их не исправляет и не перезаписывает.

Проверка и одна команда полного анализа:

"$MG_VENV/bin/money-graph" validate --data-dir data
"$MG_VENV/bin/money-graph" analyze --data-dir data --output-dir out

Результаты: backend/out/nodes_roles.csv, clusters.csv, top_nodes.csv, UTF-8. Для исходного набора validate должен показать 2248 узлов, 3119 рёбер, 4840 транзакций, 81 seed; это проверка этого набора, не ограничение приложения. CLI печатает время полного расчёта. Ошибка валидации — понятное сообщение и ненулевой exit code.

Проверка CSV без внешних утилит:

"$MG_VENV/bin/python" - <<'PY'
import csv
from pathlib import Path
expected = {'nodes_roles.csv': 2248, 'clusters.csv': 88, 'top_nodes.csv': 20}
for name, count in expected.items():
    with (Path('out') / name).open(encoding='utf-8', newline='') as stream:
        rows = list(csv.DictReader(stream))
    assert len(rows) == count, (name, len(rows))
    print(name, len(rows))
PY

88 кластеров — фактический результат исходного набора при текущей версии расчёта. Для другого набора числа могут отличаться. CLI создаёт CSV, но не загружает данные в память отдельно запущенного HTTP-сервера.

Backend, Swagger и CORS

Из backend, в отдельном терминале, с тем же MG_VENV. Запуск без AI и без чтения .env:

MG_VENV="${MG_VENV:-.venv}"
OPENAI_API_KEY= OPENAI_MODEL= \
CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173 \
TMPDIR="$PWD/.tmp" "$MG_VENV/bin/uvicorn" money_graph.bootstrap:create_api_app \
  --factory --host 127.0.0.1 --port 8000 --workers 1

В другом терминале, из backend:

curl --fail-with-body http://127.0.0.1:8000/api/health
curl --fail-with-body -F 'nodes=@data/nodes.parquet' \
  -F 'edges=@data/edges.parquet' -F 'transactions=@data/transactions.parquet' \
  http://127.0.0.1:8000/api/analyze
curl --fail-with-body http://127.0.0.1:8000/api/analysis
curl --fail-with-body http://127.0.0.1:8000/api/nodes/100000003684369100
curl --fail-with-body -o out/downloaded-top_nodes.csv \
  http://127.0.0.1:8000/api/exports/top_nodes.csv

До загрузки health возвращает analysis_ready=false, ai_configured=false; после загрузки analysis_ready=true. /api/analysis и карточки до загрузки возвращают 404 NO_ANALYSIS. Один активный снимок, один процесс/worker. После перезапуска загрузите три файла заново. Ошибка новой загрузки сохраняет старый анализ; параллельный пересчёт получает 409. Лимит 25 MiB на файл. CSV HTTP-анализов хранятся по версиям в out/api/, независимо от CLI-выгрузок.

AI: необязательная настройка

Реализован только OpenAI Responses API. Нужны OPENAI_API_KEY и доступная аккаунту OPENAI_MODEL; OPENAI_BASE_URL по умолчанию https://api.openai.com/v1, OPENAI_TIMEOUT_SECONDS по умолчанию 30, допустимо 1–120 секунд.

backend/.env.example содержит имена переменных без ключа. При первом запуске скопируйте его, только если .env ещё нет:

cp -n .env.example .env

Заполните ключ и модель локально в редакторе. Не выводите файл и не добавляйте его в Git. После настройки, остановив предыдущий сервер, запустите:

TMPDIR="$PWD/.tmp" "$MG_VENV/bin/uvicorn" money_graph.bootstrap:create_api_app \
  --factory --env-file .env --host 127.0.0.1 --port 8000 --workers 1

.env читает Uvicorn до создания приложения, через установленный python-dotenv; автоматического поиска нет. Если файл лежит в корне проекта, укажите --env-file ../.env. Уже заданные переменные окружения, даже пустые, имеют приоритет; перед AI-запуском уберите прежние пустые экспорты ключа/модели или откройте новый терминал. Не выполняйте .env через source.

Health сообщает наличие полной конфигурации, а не доступность провайдера. Вопрос отправляется через Swagger POST /api/ask: текущий analysis_id, вопрос 1–2000 символов и 1–5 выбранных строковых context_gids. Без настройки AI отвечает 503, граф, карточки и CSV работают.

Ранее выполненная приёмка AI: 23.09.2026, OpenAI gpt-6-luna, один реальный /api/ask, HTTP 200 за 8,04 с. Ответ для 100000003684369100 сверён с данными. В интеграционной приёмке выполнен один дополнительный вопрос через UI: HTTP 200 за 9,76 с; доступность модели в другом аккаунте не гарантируется. Сервер проверяет ссылки и добавляет факты, но это не доказывает истинность всего свободного текста модели.

Пользовательский сценарий и frontend

Для запуска без Docker сначала поднимите backend по команде выше, затем в другом терминале из корня:

cd frontend
npm ci
npm test
npm run build
npm run dev

Требуется Node >=24.11.0 <25; проверены Node 24.21.0 / npm 11.19.0. Приложение http://127.0.0.1:5173, относительный /api проксируется на backend:8000. Production-сборку локально можно проверить через npm run preview вместо dev. Подробности frontend.

Аналитик загружает три Parquet и ждёт подтверждения расчёта, выбирает узел из top-20 или вводит точный gid + Enter. Поиск использует все узлы, включая изолированные. Граф показывает непосредственное окружение выбранного узла; ограничение обозначено, стрелка направлена к получателю. В карточке доступны суммы, полные списки входящих/исходящих связей, пробелы данных и следующие запросы, раскрываемые временные наблюдения с ограничениями. Ссылки выбирают другой узел.

AI-панель задаёт вопрос по выбранному узлу и текущему analysis_id. Ответ выводится текстом, ссылки открывают карточки; при STALE_ANALYSIS снимок обновляется, вопрос нужно повторить вручную. Три кнопки скачивают серверные CSV. При ошибке загрузки прежний анализ сохраняется; сеть не заменяется демоданными. Смена analysis_id сбрасывает старые карточки и AI-ответы.

Роли, приоритет и кластеры

I/O — числа входящих/исходящих контрагентов, Vin/Vout — суммы из edges, S — число разных соседних seed, r=Vout/Vin. r трактуется только для не-seed с положительным входом. Применяется первое подходящее правило:

№РольУсловиеrole_score
1coordinatorI≥3, O≥3, S≥2min(1, 0.6+0.05·min(I+O−6,8))
2consolidatorI≥3, Vin≥100000; seed либо r≤0.6min(1, 0.65+0.05·min(I−3,7))
3distributorO≥5, Vout≥100000min(1, 0.65+0.05·min(O−5,7))
4transitне-seed, I>0, O>0, 0.8≤r≤1.20.9−abs(r−1)
5terminalI>0, O=0, depth<4min(0.85, 0.55+0.05·min(I,6))
6peripheralостальные, включая изолированные0.5

Баллы — сила эвристики, не статистическая вероятность. Evidence содержит числа и ограничение, максимум 200 символов. Известные gid не используются для назначения ролей.

Для приоритета: D=I+O; T — входящие+исходящие операции из transactions; V=Vin+Vout; S — соседние seed. Для каждого признака N(x)=ln(1+x)/max_dataset(ln(1+x)), при нулевом максимуме N=0.

priority_score = 0.35·N(D) + 0.25·N(T) + 0.25·N(V) + 0.15·N(S).

Топ — первые min(20,N) по убыванию балла, затем числовому gid. Баллы сравнимы внутри одного набора. Self-loop в существующем приоритете входит на обеих сторонах; во временной активности считается один раз.

Кластеры: Louvain, NetworkX 3.4.2, seed=42, resolution=1. В ненаправленной проекции встречные суммы складываются; веса нормируются общим максимумом. Внутренний оборот включает каждое исходное направленное ребро ровно один раз. Все узлы сохраняются, изолированные получают отдельные кластеры. Нумерация стабильна по отсортированным gid, ключевые узлы выбираются по приоритету. Гипотезы кластеров формируются локальными правилами.

Время, полнота и ограничения

  • rapid_outflow: поступления D и исходящие D+1/D+2; одно наблюдение на узел и D. Self-loop исключён. Это совпадение, а не доказанная трассировка денег; пересекающиеся окна нельзя суммировать как независимые потоки.
  • synchronized_inflow: минимум три разных внешних отправителя за день; повторные операции не увеличивают число отправителей. Координация не доказана.
  • activity_spike: минимум пять операций за день и минимум тройное превышение среднего за все 31 день июля, включая нулевые дни. Self-loop учитывается один раз.
  • Карточка выдаёт первые 50 наблюдений по дате и виду; total_count и temporal_summary учитывают все находки. Даты без времени не устанавливают порядок операций одного дня.
  • data_gaps и next_requests объясняют границу depth=4, неполные входящие seed, изоляцию, превышение исходящих над входящими и общие ограничения. Процент полноты не вычисляется.

Выборка: внутрибанковские переводы за июль 2026 от 5000 KZT, четыре колена от seed. Межбанковские и меньшие переводы могли остаться вне наблюдения; их наличие не утверждается. Полные остатки, внешние поступления, персональные атрибуты и размеченные роли неизвестны. depth=4 без исходящих не доказывает удержание денег; для seed out/in не является достоверным балансом.

Суммы читаются из float64 как Decimal(str(value)), арифметика использует precision=400. Потерянная в источнике точность не восстанавливается. HTTP-суммы — числа для отображения; gid никогда не преобразуются через float. Сверка агрегированных сумм/n_tx edges с transactions пока отсутствует. CSV-вход, циклы/повторяющиеся маршруты, аномалии и анализ устойчивости не реализованы. Авторизации и постоянного восстановления снимка нет; сервер предназначен для локального MVP.

Схема решения

flowchart LR
  F[React: загрузка, поиск, граф, карточки] --> N[Nginx /api]
  N --> H[HTTP API и Swagger]
  P[Три Parquet] --> R[Reader и валидация]
  R --> A[AnalyzeDataset]
  A --> G[NetworkX: признаки и Louvain]
  G --> D[Domain: роли и приоритет]
  A --> T[Domain: временные наблюдения]
  D --> S[Снимок анализа]
  T --> S
  D --> C[Три CSV]
  S --> H[HTTP API и Swagger]
  H --> Q[AskQuestion: ограниченный контекст]
  Q --> L[OpenAI, опционально]

Domain не зависит от NetworkX/FastAPI. CLI и API используют один сценарий application; bootstrap подключает адаптеры. Карточка получает готовые результаты того же снимка.

Соответствие требованиям

Матрица сверена с оригинальным «ТЗ кейса для HackAlem AI — Граф денег» (разделы 7–10) и README организаторского датасета. Документы прочитаны из предоставленных локальных материалов, в репозиторий не копируются. Пункты must-have и порядок восьми дополнительных функций ниже соответствуют ТЗ.

Пункт ТЗТребованиеПроверенное состояние / пробел
Must-have 1Воспроизводимый пайплайн: один запуск → три CSV, <5 минутПроверен в чистом Python-окружении: 6,234291 с; новая ОС отдельно не устанавливалась
Must-have 2Роль, score и evidence у каждого узла2248 точных уникальных исходных gid, заполненные поля, допустимые роли/score и evidence ≤200 проверены
Must-have 3Документированные объяснимые критерии ролейВсе шесть правил, пороги и порядок приведены выше; карточки содержат фактические признаки
Must-have 4Кластеризация с размером, seed, оборотом и гипотезой88 кластеров; CSV и cluster_id узлов проверены
Must-have 5Топ-лист ≥20 с объяснениями и визуализация с поиском gidТоп-20, направленный подграф, поиск полного набора и карточки проверены в Chromium на исходных данных
Доп. 1Учёт обрыва графаГраница depth=4 учитывается в ролях, карточке и рекомендациях; удержание денег не утверждается
Доп. 2Временные паттерныТри вида, даты, суммы, показатели и ограничения; доступны в API и карточке интерфейса
Доп. 3Повторяющиеся маршруты и возвратные потокиНе реализовано
Доп. 4Детекция аномалийНе реализовано; activity_spike — временная эвристика, не отдельный детектор аномалий
Доп. 5Устойчивость сети при изъятии топ-NНе реализовано
Доп. 6AI-ассистент со ссылками на узлыOpenAI: реальный вопрос через интерфейс, HTTP 200 за 9,76 с; автоматические проверки с fake
Доп. 7Автогенерация карточки узлаAPI и интерфейс: роль, потоки, связи, пробелы, временные наблюдения; проверено в Chromium
Доп. 8Полнота и следующий запрос аналитикуЛокальные рекомендации с фактами; процента полноты нет

Дополнительно к must-have backend поддерживает загрузку новой тройки Parquet через CLI/API с сохранением старого анализа при ошибке. CSV-вход отсутствует; он не входит в исходный формат входа ТЗ. Обязательные CSV генерируются одной командой или скачиваются с сайта; исходные данные и результаты исключены из Git. На демо покажите интерфейс и созданные выгрузки.

Масштабирование до ~1 млн узлов — план, не реализация

  1. Измерить объём рёбер/операций, степень узлов, память и время этапов: число узлов само по себе не определяет стоимость.
  2. Перейти от загрузки всего Parquet в Python-объекты к чтению колонок/батчей и внешней агрегации по узлам и дням; хранить агрегаты в колонночном хранилище. Проверять уникальность и ссылки с дисковыми индексами/сортировкой.
  3. Заменить объектный NetworkX-граф компактным CSR/CSC или специализированным графовым движком; отдельно выбрать и проверить масштабируемую реализацию сообществ, воспроизводимость и качество разбиения.
  4. Вынести расчёт в фоновые задачи с идентификатором задания и прогрессом. Хранить неизменяемые версии результатов и CSV в общем хранилище вместо памяти одного worker.
  5. Отдавать подграфы, фильтры, страницы узлов/событий, предварительные агрегаты и top-k. Не отправлять миллион узлов браузеру одним JSON.
  6. Добавить лимиты, авторизацию, аудит, наблюдаемость, конкурентный доступ и нагрузочные испытания; AI оставить на ограниченном контексте. Гарантия «<5 минут» на таком объёме сейчас отсутствует.

Тесты и пятиминутное демо

Из backend, в установленном окружении:

TMPDIR="$PWD/.tmp" "$MG_VENV/bin/python" -m pytest -q -p no:cacheprovider
"$MG_VENV/bin/ruff" check --no-cache .
"$MG_VENV/bin/ruff" format --check --no-cache .

Автоматические тесты не требуют исходного большого набора и блокируют внешний HTTP провайдера. Подробные фактические результаты чистой установки — протокол воспроизводимости.

Демо после установки зависимостей и подготовки файлов; установка не входит в пять минут:

ВремяДействие и ожидаемое наблюдение
0:00–0:40validate и одна команда analyze; показать три CSV и измеренное время
0:40–1:20Открыть сайт, загрузить три Parquet; дождаться подтверждения расчёта
1:20–2:20Показать 2248 узлов, 3119 рёбер и top-20; выбрать на графе или через поиск 100000003684369100 — consolidator, 24 входящих контрагента, seed с неполными входящими
2:20–3:10Открыть 100000000018102100: depth=4, исходящих нет; показать data_gaps и запрос дополнительных данных
3:10–4:10Открыть 100000000089154100: всплеск 16.07, 5 операций на 126500 KZT, среднее 22/31; показать ограничения
4:10–5:00Скачать три CSV, открыть AI-панель выбранного узла; при наличии ключа задать вопрос. Без ключа показать понятную недоступность и рассказать о зафиксированной реальной приёмке

Все три gid взяты из исходных данных, не являются правилами назначения ролей. Для визуального демо откройте сайт, найдите эти gid и раскройте соответствующие блоки карточки. Swagger остаётся дополнительным способом проверки API.

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

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

whiteRR-R 26IliasGalikhanov 23AlmasOlz 9

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

  • 2026-09-23 17:56Fix formatting in AI-assistant section of READMEwhiteRR-R
  • 2026-09-23 17:56Update README to remove unimplemented featureswhiteRR-R
  • 2026-09-23 17:55docs: explain project problem and feature statuswhiteRR-R
  • 2026-09-23 17:41docs: publish jury guide preserving latest frontend changeswhiteRR-R
  • 2026-09-23 17:40docs: publish jury README and verified backend setup on mainwhiteRR-R
  • 2026-09-23 17:40fix(ai-chat): refresh readiness without resetting the workspaceIliasGalikhanov
  • 2026-09-23 17:40docs(frontend): clarify env loading and AI connection checksIliasGalikhanov
  • 2026-09-23 17:36feat: integrate frontend with live API and add Docker Compose deploymentwhiteRR-R
Все коммиты (58)
  • 2026-09-23 17:34fix(graph): keep dense neighborhoods readable at every zoom levelIliasGalikhanov
  • 2026-09-23 17:34docs(frontend): document live API setup and graph verificationIliasGalikhanov
  • 2026-09-23 17:34feat(workspace): connect live analysis and investigation workflowsIliasGalikhanov
  • 2026-09-23 17:34feat(api): add typed backend client with validated responsesIliasGalikhanov
  • 2026-09-23 17:29docs(graph): align acceptance notes with real data integrationAlmasOlz
  • 2026-09-23 17:28feat(frontend): connect real analysis data to graph workspaceAlmasOlz
  • 2026-09-23 17:17Merge branch 'main' of https://github.com/BAITC-Hacks/hack-b7a04bbf-zhastyq into refactor/clean-architecturewhiteRR-R
  • 2026-09-23 17:12fix(backend): use portable ids for oversized response testsIliasGalikhanov
  • 2026-09-23 17:11docs: add jury guide and verify clean backend installationwhiteRR-R
  • 2026-09-23 17:07Merge origin/main into codex/graph-visualizationAlmasOlz
  • 2026-09-23 16:55feat(backend): analyze daily transaction patterns and expose node observationswhiteRR-R
  • 2026-09-23 16:48Merge backend features 1–5 into mainwhiteRR-R
  • 2026-09-23 16:46docs(frontend): describe docked panels and stable graph sizingIliasGalikhanov
  • 2026-09-23 16:45feat(workspace): dock collapsible panels inside full-width graphIliasGalikhanov
  • 2026-09-23 16:44feat(backend): explain observation gaps and suggest node verification requestswhiteRR-R
  • 2026-09-23 16:43fix(graph): preserve viewport across data updates and gesturesAlmasOlz
  • 2026-09-23 16:37fix(backend): support explicit env-file startup and verify live AI acceptancewhiteRR-R
  • 2026-09-23 16:34feat(graph): implement reference-inspired interactive network viewAlmasOlz
  • 2026-09-23 16:26docs(frontend): document dashboard workflows and API integration gapsIliasGalikhanov
  • 2026-09-23 16:26test(frontend): cover import validation filters and CSV escapingIliasGalikhanov
  • 2026-09-23 16:25feat(workspace): redesign dashboard with filters and CSV exportIliasGalikhanov
  • 2026-09-23 16:25feat(import): add independent Parquet slots with validation statesIliasGalikhanov
  • 2026-09-23 16:25feat(ai): answer analyst questions from bounded snapshot contextwhiteRR-R
  • 2026-09-23 16:25feat(ui): introduce Freedom-inspired light design tokensIliasGalikhanov
  • 2026-09-23 16:08feat(api): upload Parquet analyses and serve versioned graph snapshotswhiteRR-R
  • 2026-09-23 15:52feat(backend): rebuild validated Parquet-to-CSV analysis pipelinewhiteRR-R
  • 2026-09-23 15:28docs(frontend): document light design and graph agent handoffIliasGalikhanov
  • 2026-09-23 15:28feat(frontend): add light AML dashboard with slice metrics and AI dockIliasGalikhanov
  • 2026-09-23 15:28feat(graph): add organic layout and priority-scaled circular nodesIliasGalikhanov
  • 2026-09-23 15:28test(frontend): cover slice metrics and graph normalizationIliasGalikhanov
  • 2026-09-23 15:19style: preserve local spacing in observation ratiowhiteRR-R
  • 2026-09-23 15:18feat(frontend): apply graph design tokensAlmasOlz
  • 2026-09-23 15:07test: verify real dataset upload and explicit AI contextwhiteRR-R
  • 2026-09-23 15:05docs: document backend rules, API and reproducible launchwhiteRR-R
  • 2026-09-23 15:02fix: ground AI responses in selected graph factswhiteRR-R
  • 2026-09-23 14:57feat: add temporal, route, anomaly and resilience analysiswhiteRR-R
  • 2026-09-23 14:55Merge branch 'main' of https://github.com/BAITC-Hacks/hack-b7a04bbf-zhastyq into codex/frontend-graphAlmasOlz
  • 2026-09-23 14:54feat: expose shared analysis through CLI and local APIwhiteRR-R
  • 2026-09-23 14:48docs(frontend): document graph token handoff and local previewIliasGalikhanov
  • 2026-09-23 14:48feat(frontend): add tokenized analyst dashboardIliasGalikhanov
  • 2026-09-23 14:41feat: validate datasets and publish graph analysis atomicallywhiteRR-R
  • 2026-09-23 14:36build: make src package importable in Python and PylancewhiteRR-R
  • 2026-09-23 14:30feat: add analysis lifecycle and grounded question use caseswhiteRR-R
  • 2026-09-23 14:29feat: add pure role rules and scored graph modelswhiteRR-R
  • 2026-09-23 14:27docs: fix API contract and input schema for rebuildwhiteRR-R
  • 2026-09-23 14:26feat(frontend): add AML graph visualizationAlmasOlz
  • 2026-09-23 14:05feat(frontend): add AML graph visualizationAlmasOlz
  • 2026-09-23 13:49feat(frontend): add typed API contract and mock fixturesIliasGalikhanov
  • 2026-09-23 13:48chore(frontend): pin runtime and install dependenciesIliasGalikhanov
  • 2026-09-23 13:48docs(frontend): define MVP architecture and agent boundariesIliasGalikhanov
Структура репозитория (136 файлов)
AGENTS.md
README.md
backend/.env.example
backend/.gitignore
backend/AGENTS.md
backend/README.md
backend/docs/data_contract.md
backend/docs/integration.md
backend/docs/reproducibility.md
backend/docs/requirements-verified.txt
backend/pyproject.toml
backend/src/money_graph/__init__.py
backend/src/money_graph/application/dto/active_analysis.py
backend/src/money_graph/application/dto/analysis_summary.py
backend/src/money_graph/application/dto/question.py
backend/src/money_graph/application/dto/validation_result.py
backend/src/money_graph/application/exceptions.py
backend/src/money_graph/application/interfaces/active_analysis_store.py
backend/src/money_graph/application/interfaces/analysis_exporter.py
backend/src/money_graph/application/interfaces/analysis_files.py
backend/src/money_graph/application/interfaces/dataset_reader.py
backend/src/money_graph/application/interfaces/graph_calculator.py
backend/src/money_graph/application/interfaces/language_model.py
backend/src/money_graph/application/use_cases/analyze_dataset.py
backend/src/money_graph/application/use_cases/ask_question.py
backend/src/money_graph/application/use_cases/get_analysis.py
backend/src/money_graph/application/use_cases/get_export.py
backend/src/money_graph/application/use_cases/get_node.py
backend/src/money_graph/application/use_cases/publish_analysis.py
backend/src/money_graph/application/use_cases/question_context.py
backend/src/money_graph/application/use_cases/validate_dataset.py
backend/src/money_graph/bootstrap.py
backend/src/money_graph/config.py
backend/src/money_graph/domain/entities/edge.py
backend/src/money_graph/domain/entities/node.py
backend/src/money_graph/domain/entities/transaction.py
backend/src/money_graph/domain/models/analysis.py
backend/src/money_graph/domain/models/dataset.py
backend/src/money_graph/domain/models/observation.py
backend/src/money_graph/domain/services/cluster_summary.py
backend/src/money_graph/domain/services/dataset_validator.py
backend/src/money_graph/domain/services/observation_assessment.py
backend/src/money_graph/domain/services/observation_policy.py
backend/src/money_graph/domain/services/priority_calculator.py
backend/src/money_graph/domain/services/role_classifier.py
backend/src/money_graph/infrastructure/ai/openai_language_model.py
backend/src/money_graph/infrastructure/exporters/csv_analysis_exporter.py
backend/src/money_graph/infrastructure/graph/networkx_calculator.py
backend/src/money_graph/infrastructure/readers/parquet_dataset_reader.py
backend/src/money_graph/infrastructure/repositories/file_analysis_storage.py
backend/src/money_graph/infrastructure/repositories/memory_analysis_store.py
backend/src/money_graph/presentation/api/app.py
backend/src/money_graph/presentation/api/dependencies.py
backend/src/money_graph/presentation/api/exception_handlers.py
backend/src/money_graph/presentation/api/routers/analysis.py
backend/src/money_graph/presentation/api/routers/exports.py
backend/src/money_graph/presentation/api/routers/health.py
backend/src/money_graph/presentation/api/routers/nodes.py
backend/src/money_graph/presentation/api/routers/questions.py
backend/src/money_graph/presentation/api/schemas/analysis.py
backend/src/money_graph/presentation/api/schemas/question.py
backend/src/money_graph/presentation/cli/main.py
backend/tests/conftest.py
backend/tests/integration/test_analysis_cli_export.py
backend/tests/integration/test_api.py
backend/tests/integration/test_ask_api.py
backend/tests/integration/test_env_file.py
backend/tests/integration/test_graph_calculator.py
backend/tests/integration/test_node_observation.py
backend/tests/integration/test_openai_language_model.py
backend/tests/integration/test_parquet_validation.py
backend/tests/unit/test_active_analysis.py
backend/tests/unit/test_ai_config.py
backend/tests/unit/test_analysis_rules.py
backend/tests/unit/test_analyze_dataset.py
backend/tests/unit/test_ask_question.py
backend/tests/unit/test_dataset_validator.py
backend/tests/unit/test_observation_assessment.py
backend/tests/unit/test_validate_dataset.py
docs/data_contract.md
frontend/.env.example
frontend/.gitignore
frontend/.nvmrc
frontend/AGENT_APP.md
frontend/AGENT_GRAPH.md
frontend/ARCHITECTURE.md
frontend/README.md
frontend/index.html
frontend/package-lock.json
frontend/package.json
frontend/src/App.tsx
frontend/src/features/graph/GraphView.css
frontend/src/features/graph/GraphView.tsx
frontend/src/features/graph/graphLabels.test.mjs
frontend/src/features/graph/graphLabels.ts
frontend/src/features/graph/graphModel.test.mjs
frontend/src/features/graph/graphModel.ts
frontend/src/features/workspace/AgentDock.css
frontend/src/features/workspace/AgentDock.tsx
frontend/src/features/workspace/DataImport.css
frontend/src/features/workspace/DataImport.tsx
frontend/src/features/workspace/ErrorBoundary.tsx
frontend/src/features/workspace/ExportMenu.tsx
frontend/src/features/workspace/GraphStatistics.tsx
frontend/src/features/workspace/Icon.tsx
frontend/src/features/workspace/ImportSlot.tsx
frontend/src/features/workspace/NodeCard.css
frontend/src/features/workspace/NodeCard.tsx
frontend/src/features/workspace/PriorityIndicator.tsx
frontend/src/features/workspace/WorkspaceDock.tsx
frontend/src/features/workspace/WorkspaceState.tsx
frontend/src/features/workspace/analysisModel.test.mjs
frontend/src/features/workspace/analysisModel.ts
frontend/src/features/workspace/importModel.test.mjs
frontend/src/features/workspace/importModel.ts
frontend/src/features/workspace/labels.ts
frontend/src/features/workspace/statistics.test.mjs
frontend/src/features/workspace/statistics.ts
frontend/src/features/workspace/useWorkspace.ts
frontend/src/features/workspace/workspaceModel.test.mjs
frontend/src/features/workspace/workspaceModel.ts
frontend/src/main.tsx
frontend/src/shared/api/client.test.mjs
frontend/src/shared/api/client.ts
frontend/src/shared/api/fixtures/graph-slice-empty.json
frontend/src/shared/api/fixtures/graph-slice.json
frontend/src/shared/api/fixtures/node-details.json
frontend/src/shared/api/fixtures/top-nodes.json
frontend/src/shared/api/types.ts
frontend/src/shared/api/workspace.ts
frontend/src/shared/contracts.ts
frontend/src/styles/app.css
frontend/src/styles/tokens.css
frontend/tsconfig.json
frontend/vite.config.ts
pyrightconfig.json
Ветки (6)

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

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

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