Mengapa Fraud Engine yang Lebih Baik Dirilis dalam Shadow Mode
Pada paket bersih yang noisy di gold set development, false positive turun dari 36,4% ke 9,1%. Meski hasilnya membaik, saya belum dapat menggunakan engine baru untuk menentukan verdict.
Paket yang diuji adalah paket pengadaan bersih dengan noise administratif di gold set development. Pada saat yang sama, recall High-risk turun dari 1,000 ke 0,841, sehingga lebih banyak paket berisiko yang tidak terdeteksi.
Saya menyelesaikan risk engine generasi kedua ini pada akhir September 2026 untuk platform internal review dokumen berbasis AI di Metrodata. Hasil evaluasinya tidak memenuhi release gate untuk recall yang sudah saya tetapkan sebelumnya.
Saya kemudian merilisnya ke produksi dalam shadow mode. Engine dijalankan secara paralel dengan pipeline live dan output-nya disimpan untuk perbandingan, tanpa pernah ditampilkan kepada pengguna.
Paket Bersih yang Terus Mendapat Flag
Platform ini memeriksa paket dokumen transaksi: invoice, kuitansi, purchase order, formulir vendor, dan kontrak. Azure Document Intelligence menangani OCR, lalu Azure OpenAI menghasilkan temuan per dokumen, verdict lintas dokumen, dan risk score 0 sampai 100. Hasil tersebut menjadi bahan pertimbangan reviewer, sementara keputusan tetap di tangan manusia.
Pada versi awal, paket bersih dengan ketidakteraturan administratif justru sering menimbulkan masalah. Stempel yang tidak ada, format tanggal yang tidak lazim, atau hasil scan yang miring dapat memicu temuan fraud meskipun hanya berupa noise.
Saya khawatir reviewer akan kehilangan kepercayaan jika paket yang jelas aman mendapat red flag. Mereka mungkin kemudian meragukan flag berikutnya, termasuk temuan yang benar. Menurut penilaian saya, satu false positive pada paket bersih dapat lebih merusak kepercayaan daripada satu flag yang terlewat. Dampak terhadap kepercayaan ini belum saya ukur.
Karena itu, saya mengukur input bersih yang noisy sebagai slice tersendiri. Jika hanya melihat rata-rata seluruh paket, masalah yang ingin saya perbaiki bisa tidak terlihat.
Perbaikan Awal pada Guardrail
Sebelum mengubah prompt, saya membangun evaluation harness. Isinya gold set berlabel, masing-masing 21 sampai 90 kasus ditambah variasi kasus hasil mutasi. Saya memisahkan set development dan held-out, membekukan baseline, lalu mengukur false-positive rate (FPR), false-negative rate, recall High-risk, fail-open rate, dan prompt-injection success dengan confidence interval. Gate dijalankan lewat workflow eval khusus dan CI lokal.
Baseline pertama menandai 90,9% paket bersih yang noisy di set development. Guardrail-nya juga bermasalah: fail-open rate mencapai 76,2%.
Guardrail ini seharusnya menurunkan tingkat risiko pada verdict ketika temuannya jelas tidak berbahaya. Aturannya justru memakai mayoritas: jika kira-kira separuh temuan cocok dengan safe pattern, tingkat risikonya diturunkan. Beberapa pattern juga mencakup terlalu banyak kondisi.
Saya memperketat kedua aturan tersebut. Semua temuan kini harus cocok dengan safe pattern, dan safe list dibatasi pada noise administratif. Setiap penyesuaian juga wajib dicatat di output agar dapat ditinjau. Jika syaratnya tidak terpenuhi, guardrail harus fail closed dan tidak meloloskan paket tanpa penjelasan.
Secara offline, FPR paket bersih yang noisy turun dari 90,9% ke 36,4% di development, dan dari 100% ke 50,0% di held-out. Fail-open turun dari 76,2% ke 0%. Recall High-risk berada di 0,91.
Upgrade model berikutnya memperlihatkan masalah lain. Ada blok prompt yang menyebut pembeli sebagai pihak tepercaya. Dengan model baru, framing itu membuat verdict bias. Setelah saya pangkas, run evaluasi live menunjukkan FPR turun dari 26,1% ke 21,7%, dan prompt-injection success dari 11,1% ke 3,7%.
Saya juga membandingkan instruksi yang netral terhadap pihak-pihak di dokumen dengan framing lama lewat paired run: 23 kasus, masing-masing diulang 3 kali. FPR-nya 18,8% dengan instruksi netral, berbanding 36,9% dengan framing lama. Ini hasil evaluasi, bukan hasil produksi.
Dari evaluasi itu, saya melihat bahwa prompt ikut menentukan perilaku classifier. Karena itu, perubahan prompt maupun versi model perlu diperlakukan sebagai rilis dan selalu disertai pengujian ulang seluruh gate.
Penentuan Verdict di Luar LLM
FPR 36,4% masih terlalu tinggi, sementara saya belum menemukan perubahan aman lain pada desain yang menyerahkan semua keputusan kepada LLM. Saya kemudian menulis spec engine v2 dengan pembagian tugas berikut:
- Normalizer merapikan nama, nomor rekening, dan tanggal hasil ekstraksi sebelum dibandingkan.
- Katalog rules deterministik memunculkan temuan, seperti ketidakcocokan antardokumen, dengan severity masing-masing.
- Rubric risk engine menggabungkan temuan lewat noisy-OR dalam satu grup, mengambil nilai maksimum antargrup, lalu menerapkan severity floor agar hasil rata-rata tidak menurunkan skor untuk temuan kritis.
- Evidence verifier memastikan tiap temuan menunjuk ke teks yang benar-benar ada di dokumen hasil ekstraksi.
- Duplicate fingerprint dengan HMAC menangkap dokumen yang dipakai ulang di paket lain.
- LLM tetap menghasilkan temuan, semantic review, dan narasi. Skor tidak lagi ditentukan oleh LLM.
Perhitungan skornya kira-kira seperti ini. Ini versi sederhana, bukan kode produksi:
p_group = 1 - prod(1 - p_i for p_i in group)
score = max(p_group for each group)
score = max(score, floor[worst_severity])
Setiap skor disertai trace rules yang aktif agar dasar penentuan verdict dapat dijelaskan dan prosesnya di-replay.
Precision Naik, Recall Turun
Saya membandingkan engine v2, baseline N1, dengan pipeline LLM terbaik, baseline B2, secara offline di gold set v0.2.1. Gate sudah ditetapkan sebelum run:
- FPR paket bersih yang noisy: 36,4% menjadi 9,1%.
- FPR untuk semua paket yang seharusnya Low risk: 17,4% menjadi 9,1%.
- Recall High-risk: 1,000 menjadi 0,841.
- Set held-out v0.3: FPR 50,0% menjadi 0,0%, tetapi recall High-risk hanya 0,429.
Precision membaik, tetapi ada paket High-risk yang tidak terdeteksi oleh engine baru padahal terdeteksi oleh pipeline lama. Recall held-out 0,429 menunjukkan bahwa penurunan ini juga terjadi di luar set development. Hasilnya tidak memenuhi gate G3 untuk recall High-risk.
Menurut saya, pengaturan threshold pada rules meningkatkan precision dengan mengorbankan recall. Saat tuning, saya terlalu berfokus pada false positive. Dengan gate yang sudah ditetapkan sebelum run, saya tetap harus memeriksa recall sebelum merilis perubahan. Tanpa acuan itu, saya mungkin terlalu mengandalkan FPR yang membaik dan merilis sistem yang justru lebih buruk.
Ukuran sampelnya juga terbatas karena beberapa slice berisi sedikit kasus. Pola perubahan FPR paket bersih yang noisy sesuai dengan jumlah sekitar sebelas paket, sehingga satu paket saja dapat mengubah hasil sekitar sembilan poin. Jumlah itu merupakan inferensi saya; ukuran slice persisnya tidak tercatat di sini.
Produksi Tanpa Wewenang Menentukan Verdict
Saya memiliki tiga pilihan: mengaktifkan engine dan menerima penurunan recall, melanjutkan tuning offline, atau memproses paket nyata tanpa memakai hasil engine untuk mengambil keputusan. Saya memilih opsi ketiga agar dapat mengevaluasi data nyata melalui shadow mode tanpa memengaruhi keputusan yang diterima pengguna.
Pipeline yang sudah ada tetap menghasilkan verdict dan laporan untuk pengguna. Engine v2 berjalan di sampingnya, lalu menyimpan output hanya untuk perbandingan:
verdict = current_pipeline(packet) # user sees
shadow = engine_v2(packet) # stored only
save_shadow(packet.id, shadow)
Satu paket melewati dua jalur di platform ini:
- Reviewer mengunggah paket dokumen di web app Next.js. Web app mengirimnya ke backend FastAPI dan menampilkan progresnya secara live.
- Backend menjalankan OCR dengan Document Intelligence.
- Agent dari Agent Framework memeriksa setiap dokumen, lalu seluruh paket, lewat Azure OpenAI, dengan input yang disaring Prompt Shields. Hasilnya adalah temuan dan verdict yang dilihat reviewer.
- Backend menyimpan riwayat review dan laporan di Cosmos DB dan Blob Storage.
- Secara paralel, backend menjalankan risk engine hybrid dalam shadow run. Hasilnya disimpan untuk perbandingan dan tidak pernah kembali ke web app.
Dalam catatan rollout, ada 0 dari 3.780 run yang output penggunanya berubah akibat shadow engine. Angka tersebut menunjukkan bahwa jalur shadow terisolasi, tetapi tidak mengukur akurasi engine.
Volume kasus nyata di platform ini masih kecil. Catatan saya juga tidak merinci isi 3.780 run tersebut.
Temuan Setelah Deployment
Saat deployment sungguhan, saya menemui defect yang belum terdeteksi dalam pengujian gold set. Temuan shadow pada salah satu rollout menunjukkan tiga masalah kualitas data:
- Rule conflicting values aktif karena ada titik di akhir nama pemilik rekening pada salah satu dokumen.
- Ekstraksi per file bisa berakhir parsial tanpa terlihat jelas.
- Pesan status OCR menyesatkan.
Ketiganya sudah diperbaiki. Saya tidak mencatat metrik sebelum dan sesudah perbaikan, dan tidak punya detail root cause selain deskripsi tersebut.
Masalah serupa muncul saat beralih ke ekstraksi Document Intelligence Layout. Nomor rekening yang terpotong ke baris berikutnya dengan tanda hubung menghasilkan 7 temuan palsu ketidakcocokan rekening bank. Setelah perbaikan normalisasi, evaluasi live di v0.2.1 mencatat recall High-risk 1,000 dan FPR 0.
Meski hasil pada set development membaik, saya tetap menjalankan engine dalam shadow mode.
Syarat Sebelum Engine Menentukan Verdict
Sebelum menggunakan engine v2 untuk menentukan verdict, saya ingin memastikan hal-hal berikut. Ini masih berupa pertimbangan saya, belum rencana yang tercatat:
- Recall High-risk lolos G3 di held-out, bukan hanya development, dan konsisten di run berulang.
- FPR paket bersih yang noisy tetap jauh di bawah pipeline saat ini pada kedua set.
- Fail-open tetap 0%.
- Verdict shadow sejalan dengan keputusan reviewer untuk paket nyata. Ini membutuhkan lebih banyak kasus nyata daripada yang ada sekarang.
- Setiap perbedaan antara kedua engine ditinjau manual, karena masing-masing berarti fraud yang terlewat atau false alarm yang berhasil dihindari.
Saya menyajikan angka held-out bersama hasil development karena recall 0,429 menunjukkan keterbatasan yang tidak terlihat dari metrik development saja. Hasil baik pada satu run development belum memenuhi syarat yang saya tetapkan untuk menggunakan engine ini sebagai penentu verdict.