Skip to content

Add beneficial-ownership-aml v1 template (control, family links, AML scoring, triage) - #105

Open
maxdemarzi wants to merge 1 commit into
mainfrom
add-beneficial-ownership-aml
Open

maxdemarzi wants to merge 1 commit into
mainfrom
add-beneficial-ownership-aml

Conversation

@maxdemarzi

Copy link
Copy Markdown

Summary

Adds v1/beneficial-ownership-aml, an Advanced, Financial Services template that uses all four reasoner types to find who really controls the banks behind suspicious transaction reports (STRs), even when the subject owns nothing directly.

  • Rules: company control computed depth by depth (> 50% directly, or together with controlled companies), accumulated ownership along paths, ECB close links, family control, AML pattern findings, noisy-OR suspicion scores, offence classes, and explanations built from the rules' own evidence.
  • Graph: VADA-LINK family-link prediction. Louvain communities on a person–person graph block candidate pairs, and a calibrated Graham classifier decides.
  • Predictive: a link-prediction GNN (one more family-link feature) and an account classifier fed with rule outputs.
  • Prescriptive: a MILP that picks which cases analysts investigate within an hours budget, compared against a naive ranking.

It implements four papers: Atzeni et al. (EDBT 2020 and a KR 2021 workshop paper), Bellomarini, Laurenza & Sallinger (2020), and Weber et al. (NeurIPS 2018 workshop). It reproduces the worked examples from those papers, including the Italian FIU's "Acme Bank" case, as golden fixtures.

No existing template covers beneficial ownership or company control. The closest are fraud-detection, money_laundering_motif_detection, entity_resolution and transaction_screening_local.

What's in the folder

  • beneficial_ownership_local.py: primary runner on bundled synthetic data.
  • beneficial_ownership.py: reference runner on Snowflake tables.
  • ownership_rules_walkthrough.ipynb and runbook.md.
  • model/ (one module per stage) and pipeline.py.
  • Synthetic data:
    • a sample of 5K companies, 3K persons, 33K transfers and 460 STRs;
    • a seeded generator that builds 100K companies in 12 seconds;
    • five fixtures from the papers, each with its expected values in expected.yaml.
  • calibrate.py and tune_weights.py, plus eval/, benchmarks/ and tests/.
  • make results reproduces every number in the README.

This PR also regenerates the v1 and root template indexes. The repo's trailing-whitespace hook fixed line 3 of the root README in the same pass.

Validation

  • ruff check sample-template v0.13 v0.14 v1 passes, as do pre-commit and generate_version_indexes.py --check.
  • Tests with relationalai[gnn]==1.32.0:
    • 19 offline tests (contracts, generator, loading, math);
    • 40 Snowflake tests: every reasoning stage against the papers' fixtures, the end-to-end pipeline, and notebook execution.
  • The README Quickstart (python -m pip install ., then python beneficial_ownership_local.py --no-gnn) was run from a fresh virtual environment. It finished in about 10 minutes (596 s) and reproduced the README's expected output exactly.
  • Scale, without GNNs, on one HIGHMEM_X64_S logic engine: 10K companies in 5.4 min, 100K in 6.8 min, 1M in 8.6 min. The 1M run needs MAX_CONTROL_DEPTH=20; with the default 12, the convergence guard stops it with a clear message rather than returning truncated control.

Results on the sample (from the README)

Loans from a bank the applicant's family controls, recall at score ≥ 0.8 0.96 with predicted family links, 0.76 without
Precision of the top 50 STRs 0.96
Hidden family links recovered recall 0.78, precision 0.90, from 449 candidates out of ~4.5M pairs
Triage value vs naive ranking 2.0×–5.3× across 100–800 analyst-hours
Account GNN, ROC-AUC 0.64 → 0.79 with rule features; rules alone 0.93

The README reports the weak spots too. On this synthetic data the GNNs add little on top of the rules. The 0.85 family-link recall target from the design was missed; the result is reported, not tuned to the test set.

Notes for reviewers

The template works around several PyRel 1.32 behaviors. Each is documented in the README's Troubleshooting table and may be worth a look from the SDK side:

  • Recursion through an aggregate is rejected on Snowflake and silently returns only one level on DuckDB. Control, ownership, family and case closures are therefore depth-unrolled.
  • Aggregates over recursive relations, when materialized with define(), are wrong on DuckDB. They're correct at query time and on Snowflake.
  • model.data(df) on Snowflake drops any row with a null or "" in any column, including excluded columns.
  • The GNN library needs to be the only live Model in the process; otherwise it raises [Ambiguous model].
  • The GNN export treats many-to-many relations on node concepts as functional.
  • The GNN matches features by property field name, not by Python attribute.
  • DuckDB compile time for deep layered rule stacks is very high: one fixture test took 23 min offline versus seconds on Snowflake. Offline tests are therefore limited to contracts, loading and math.
  • Stage uploads (write_pandas) and long result fetches failed intermittently with S3 403s and SSL errors. The loader and the GNN stages retry.

The ZIP link in the Quickstart follows the existing pattern (docs.relational.ai/templates/zips/v1/<name>.zip) and depends on the docs pipeline.

🤖 Generated with Claude Code

Multi-reasoner AML template: recursive company control and accumulated
ownership, close links, VADA-LINK family-link prediction (Louvain blocking,
calibrated Graham classifier, optional link-prediction GNN), family control,
AML pattern findings with noisy-OR scoring and explanations, a GNN account
classifier fed with rule outputs, and MILP triage of cases under an
analyst-hours budget.

Implements Atzeni et al. (EDBT 2020; KR 2021 workshop), Bellomarini,
Laurenza & Sallinger (2020) and Weber et al. (NeurIPS 2018 workshop).
Ships golden fixtures from the papers' worked examples, a seeded synthetic
data generator, evaluation scripts, a scale benchmark, tests and a runbook.
Regenerates the v1 and root template indexes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

The templates docs preview for this pull request has been deployed to Vercel!

✅ Preview: https://relationalai-docs-q7lb00o6u-relationalai.vercel.app/build/templates
🔍 Inspect: https://vercel.com/relationalai/relationalai-docs/4nM6aWUpkS1YELLRnoRUiwkqqhk4

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant