07 Tahapan pipeline yang sudah dibangun & berjalan
AML+DATA · SQL · MACHINE LEARNING

KENAPA PIPELINE INI

Dibangun untuk terlihat, bukan cuma jalan.

Setiap tahap — dari data sintetik sampai model ML — dijalankan terhadap Postgres 17 + pgvector nyata, diuji terhadap ground truth yang sudah diketahui jawabannya, dan setiap angka di halaman ini berasal dari hasil run yang sama.

Deterministik & reproducible

Satu seed (42) menurunkan RNG stream independen untuk tiap tahap generator — jalankan ulang, hasilnya identik sampai ke bit. Bug bisa direproduksi persis, bukan "kadang muncul kadang tidak".

Dua lapis deteksi

Rule berbasis threshold menangkap pola yang jelas; Isolation Forest unsupervised menangkap yang tidak. Dibuktikan, bukan diasumsikan: 31% kasus yang lolos semua rule justru masuk top 1% skor anomali ML.

Peta lengkap pipeline — solid biru sudah dibangun (01–07), garis putus abu-abu belum

flowchart LR
    A["01 Data Sintetik"] --> B["02 Raw Tables"]
    B --> C["03 Validasi"]
    C --> D["04 Analytics Base"]
    D --> E["05 Feature Engineering"]
    E --> F["06 Rule-Based"]
    E --> G["07 ML Anomaly"]
    F --> H["Sanctions Screening"]
    G --> H
    H --> I["Hybrid Scoring"]
    I --> J["Alert & Investigation"]
    classDef built fill:#3452d1,stroke:#3452d1,color:#ffffff
    classDef todo fill:transparent,stroke:#8a93a8,color:#8a93a8,stroke-dasharray: 4 3
    class A,B,C,D,E,F,G built
    class H,I,J todo
      

01 · SYNTHETIC DATA

Data sintetik, deterministik, dan sudah tahu jawabannya

Lima entitas dibuat berurutan mengikuti dependency foreign key, lalu dua jenis pola mencurigakan (5 tipologi AML, 5 teknik sanctions evasion) disuntikkan sebagai ground truth — dipakai nanti untuk mengukur seberapa bagus tiap tahap deteksi.

Volume data (seed = 42)

500Customers
1,030Accounts
2,567Counterparties
500Watchlist entities
110,896Transactions
74Ground truth suspicious (0.067%)
Tipologi AMLCara kerjaGround truth
Structuring3–5 setoran tunai $8,500–$9,900 dalam 72 jam — di bawah ambang pelaporan $10,0008
Smurfing5–10 transfer masuk dari counterparty berbeda dalam 96 jam (fan-in)34
Layering3–5 wire keluar berturut-turut ke counterparty berbeda, tiap hop 2–24 jam11
Rapid movementSetoran besar diikuti penarikan/wire hampir senilai dalam 1–48 jam (pass-through)4
Round-trippingWire keluar ke counterparty X, dana serupa kembali dari X dalam 2–10 hari6
Teknik sanctions evasionContoh nyata dari data
name_variationKapitalisasi satu huruf diubah acak — "AnThony Clark"
alias_useMemakai alias yang memang tercatat di watchlist
front_companyNama asli + embel bisnis — "Hannah Russell Trading LLC"
transliterationHomoglyph a→4, e→3, i→1, o→0, s→5 — "C4ldw3ll-M1ll3r"
split_transactionSatu wire besar dipecah jadi 2–4 wire kecil, total nominal sama

02 · SUPABASE LOCAL RAW TABLES

Skema dengan foreign key penuh dan kolom pgvector

Lima tabel raw.*, diindeks untuk join transaksi-level dan rolling window. Tiga kolom vector(384) disiapkan untuk contextual sanctions screening, walau isinya untuk saat ini masih placeholder.

flowchart LR
    customers["raw.customers"] -->|1:N| accounts["raw.accounts"]
    accounts -->|1:N| transactions["raw.transactions"]
    counterparties["raw.counterparties"] -.->|0:N nullable| transactions
    watchlist["raw.watchlist_entities"]
    transactions --> aml["ground_truth.aml_scenarios"]
    transactions --> evasion["ground_truth.sanctions_evasion"]
    counterparties --> evasion
    watchlist --> evasion
      

Garis putus-putus = foreign key nullable (transaksi tunai tidak punya counterparty). name_embedding ada di counterparties & watchlist_entities; description_embedding ada di transactions.

03 · SQL DATA VALIDATION & STANDARDIZATION

Tidak ada baris yang dibuang, hanya dicatat

Setiap tabel raw.* punya pasangan 1:1 di staging.* setelah dibersihkan (trim, upper-case kode negara/mata uang). Masalah data dicatat ke satu log audit, bukan menghapus datanya.

4 pemeriksaan yang dijalankan

  • Tanggal transaksi/pembukaan akun di masa depan
  • Akun dibuka sebelum tanggal onboarding nasabahnya
  • Mata uang transaksi tidak cocok dengan mata uang akun
  • Kemungkinan transaksi duplikat
0 data quality issues ditemukan pada dataset ini — wajar, karena generator Phase 2 memang menghasilkan data yang konsisten secara desain. Fungsi staging.run_validation() tetap penting untuk data dari sumber lain yang tidak sebersih data sintetik ini.

04 · SQL ANALYTICS BASE TABLE

Menjahit lima tabel jadi satu materialized view

analytics.transactions_base menghitung join 5-arah sekali, menyimpannya sebagai tabel fisik, lalu di-refresh eksplisit — supaya tahap feature engineering yang men-scan berulang-ulang tidak perlu join ulang tiap kali.

flowchart LR
    st["staging.transactions"] --> base(("analytics.
transactions_base")) sa["staging.accounts"] --> base sc["staging.customers"] --> base scp["staging.counterparties"] --> base ga["ground_truth.aml_scenarios"] --> base gs["ground_truth.sanctions_evasion"] --> base

110,896 baris keluar — sama persis dengan jumlah baris raw.transactions: satu baris analytics per transaksi, tidak ada fan-out dari join.

05 · SQL FEATURE ENGINEERING

14 kolom fitur, 13 di antaranya rolling-window

Tiap fitur menjawab "bagaimana kondisi akun ini sampai saat transaksi ini terjadi" — tidak melihat masa depan, karena sistem deteksi nyata pun begitu. Satu pengecualian: amount bukan agregasi rolling, cuma nominal transaksi itu sendiri (lihat baris terakhir tabel).

Before / after: optimisasi distinct-counterparty count

8m 17s
correlated subquery
0.77s
array_agg + dedupe
650×
lebih cepat

Postgres tidak mendukung count(distinct x) over (...). Versi pertama query ulang tabel dasar per baris (110,896×). Versi final: array_agg(...) over w7d mengumpulkan array kecil per baris (window function biasa), lalu dedupe array itu di scalar subquery — tidak menyentuh tabel lagi.

FiturWindowKenapa penting
txn_count_1d1 hariVelocity sangat jangka pendek — ledakan mendadak
txn_count_7d7 hariBasis rule High Velocity
txn_count_30d30 hariBaseline aktivitas normal akun
total_amount_1d1 hariBasis rule Rapid Movement
total_amount_7d7 hariBasis rule Structuring & Smurfing
total_amount_30d30 hariBaseline volume bulanan
avg_amount_7d7 hariProfil "ukuran transaksi wajar" akun ini
max_amount_7d7 hariOutlier tunggal di balik rata-rata yang tampak wajar
credit_amount_7d7 hariArah dana masuk — pembeda fan-in (smurfing)
debit_amount_7d7 hariArah dana keluar — pembeda fan-out (layering)
near_threshold_cash_deposits_7d7 hariSinyal structuring paling langsung
distinct_counterparties_7d7 hariSinyal smurfing & layering
seconds_since_prev_txnSinyal pass-through / rapid movement
amountBaseline magnitude mentah untuk model ML

06 · RULE-BASED AML DETECTION

8 rule SQL, dikalibrasi ulang setelah dicek ke ground truth

Threshold awal R004 ternyata menyalakan alarm di ~13% dari SEMUA transaksi — diperbaiki ke persentil-95. Layering awalnya 0% tertangkap karena polanya murni debit; R008 ditambahkan khusus untuk itu.

Jumlah hit per rule (110,896 transaksi)

R004 High Velocity medium
5,967
R002 Smurfing high
2,156
R003 Rapid Movement high
366
R008 Layering high
350
R007 Round-Tripping medium
239
R005 Large Cash Txn medium
135
R006 High-Risk Corridor high
38
R001 Structuring high
6

Recall terhadap ground truth, per tipologi

Round-Tripping
83%
Rapid Movement
75%
Layering
55%
Structuring
50%
Smurfing
47%

Recall yang tidak 100% ini bukan bug: semua fitur di atas adalah trailing window, jadi transaksi pertama dalam sebuah "ledakan" pola belum punya riwayat untuk memicu threshold. Ini alasan konkret kenapa tahap ML dibutuhkan.

07 · MACHINE LEARNING / ANOMALY DETECTION

Isolation Forest, dilatih tanpa label

Model belajar murni dari fitur — ground_truth.* tidak pernah dipakai untuk training, hanya untuk mengevaluasi ranking anomali setelahnya. Ini meniru kondisi nyata: label suspicious yang terkonfirmasi selalu langka.

Input model — 20 kolom, bukan 14

SumberIsiKolom
Fitur rolling window (§05)Semua kolom features.account_transaction_features kecuali amount13
amountDiambil terpisah dari analytics.transactions_base, bukan hasil agregasi rolling1
transaction_type (one-hot)6 kategori jadi kolom biner — wire_transfer, cash_deposit, cash_withdrawal, card, ach, check6

"14 fitur" di §05 merujuk ke tabel SQL-nya saja. Kolom transaction_type ditambahkan khusus di tahap ML supaya model bisa membedakan — misalnya — nominal $9.000 yang wajar untuk wire_transfer tapi mencurigakan untuk cash_deposit.

Ringkasan evaluasi

0.939ROC-AUC
40×avg precision vs. baseline acak
43.2%Recall pada top 1,000 (0.9% data)
31%Kasus lolos rule, masuk top 1% ML

Recall bertambah seiring makin banyak transaksi teratas yang direview

45% 30% 15% 0% Top-50: 0% recall (0 TP) Top-100: 4.1% recall (3 TP) Top-250: 23.0% recall (17 TP) Top-500: 32.4% recall (24 TP) Top-1000: 43.2% recall (32 TP) 43.2% 50 100 250 500 1000 TOP-N TRANSAKSI BERDASARKAN SKOR ANOMALI
Dari 32 transaksi yang tidak tertangkap rule manapun di tahap 06, 10 (31%) masuk top 1% skor anomali ML dan 14 (44%) masuk top 5% — beberapa di rank #66 dan #78 dari 110,896 transaksi. Bukti konkret bahwa kedua lapis deteksi menangkap pola yang berbeda.

COBA SENDIRI · LIVE INFERENCE

Simulasi skor anomali, langsung dari model terlatih

Form ini memanggil backend FastAPI sungguhan (backend/api/main.py) yang memuat model isolation_forest_v1.joblib hasil training di §07 — bukan angka pura-pura. Pilih preset tipologi atau isi manual, lalu jalankan.

Mengecek koneksi ke backend…

Preset tipologi

Transaksi ini
Rolling window — 1 hari
Rolling window — 7 hari
Rolling window — 30 hari

Hasil

Belum ada hasil — pilih preset atau isi form, lalu klik "Jalankan inference".

STATUS PIPELINE

7 dari 10 tahap sudah berjalan

01 Data Sintetikselesai
02 Raw Tablesselesai
03 Validasi & Standardisasiselesai
04 Analytics Base Tableselesai
05 Feature Engineeringselesai
06 Rule-Based Detectionselesai
07 ML / Anomaly Detectionselesai
Contextual Sanctions Screeningbelum
Hybrid Risk Scoringbelum
Alert & Investigation Casebelum