AML over the transfer graph
pragmatiq's own extension (not in the PRAGMA paper) — a GraphSAGE ablation that recovers money-mule rings a per-user embedding cannot see.
pragmatiq is an independent implementation inspired by the PRAGMA paper (arXiv 2604.08649) and is not affiliated with or endorsed by Revolut.
This is pragmatiq's own extension — not in the PRAGMA paper
Everything in Architecture is the paper's recipe. The graph-based AML work here is built standalone on top of the paper's user embeddings to explore a question the paper does not: can a graph neural network over a money-transfer graph recover signal a per-user embedding cannot? Our addition
Why a graph
Mule-ring (money-laundering) detection is relational: a launderer is defined by who they
transact with — fan-in of small credits, layering among the ring, rapid fan-out — not just
their own behavior. A per-user embedding, however good, cannot by construction see a
counterparty pattern that spans accounts. So we build a directed transfer graph from
transfers.parquet and run a four-arm comparison.
The four-arm ablation
api.gnn(...) trains a 2–3 layer GraphSAGE and reports held-out ROC-AUC,
mean ± std over seeds, for (full-scale benchmark: 12k accounts × 3 seeds, small model):
- (a) isolated pragmatiq embeddings — a probe on each user's embedding alone, no graph.
0.498 - (b) GraphSAGE + pragmatiq features — message passing over the transfer graph with
pragmatiq embeddings as node features.
0.554 - (c) GraphSAGE + hand-crafted features — the same graph, with generic structural node
features (degree, volume, counterparties).
0.670 - (d) logistic control — the same hand-crafted features, no graph.
0.604
The synthetic mule rings are multi-hop layered laundering chains: mule amounts and
counterparty degree are drawn to match ordinary accounts, so 1-hop degree is not a trivial
oracle (the degree-only control (d) reaches only 0.604). The gated result is relational
recovery: a GraphSAGE over the transfer graph recovers rings that a probe on the isolated
embedding cannot — (c) 0.670 ≫ (a) 0.498 — so the AML signal lives in the multi-hop transfer
structure an isolated embedding misses, and message passing adds over the same features without
a graph ((c) > (d)).
pip install -e .
pragmatiq gnn data/tok --run runs/aml \
--transfers data/synth/transfers.parquet \
--aml-label data/synth/labels/aml.parquetThe honest limitation
The learned per-user embedding adds only a little over the isolated probe ((b) 0.554 > (a) 0.498) and does not beat hand-crafted features ((b) 0.554 < (c) 0.670). The isolated
embedding sits near chance, so the model does not capture the multi-hop laundering signal in the
per-user representation; recovering it in a learned representation is the open challenge.
This is consistent with the PRAGMA paper's own observation that AML is a setting where the model
underperforms because it processes user histories in isolation — the GNN here is pragmatiq's
honest extension that probes that gap, not a "the learned embedding wins" result.
See MODEL_CARD.md and
the 04_aml_gnn notebook
for the full discussion and the latest numbers.