Selama 60 tahun, mesin aturan berdiri di atas premis yang sama. Premis bahwa objek validasi adalah “fakta (fact)”.

Drools memasukkan objek Java sebagai “fact” ke working memory. Rego memperlakukan input sebagai data yang sudah benar. JSON Schema menganggap struktur dokumen sudah diberikan. Semuanya asumsi yang sama — data yang masuk adalah fakta.

Tapi apa sebenarnya alasan keberadaan mesin aturan? Memvalidasi apakah data memenuhi aturan. Menyebut objek yang perlu divalidasi sebagai “sesuatu yang sudah benar” adalah kontradiksi.

Bukan Fakta, Melainkan Klaim

Objek validasi bukan fact, melainkan claim (klaim). Sebuah pernyataan yang bisa benar atau salah. Keabsahannya harus dinilai oleh aturan.

JWT sudah mengikuti prinsip ini. sub, exp, iss disebut bukan “facts” melainkan “claims”. Itu adalah klaim dari penerbit token. Baru bisa dipercaya setelah memverifikasi tanda tangan, memeriksa kedaluwarsa, dan mencocokkan issuer.

Struktur ini sudah ditetapkan pada tahun 1958.

Model Argumentasi Toulmin

Stephen Toulmin pada tahun 1958 menganalisis struktur argumentasi menjadi 6 elemen:

  • Claim (Klaim): Objek penilaian. Sesuatu yang harus diverifikasi benar atau salah.
  • Ground (Dasar): Data bukti yang digunakan untuk penilaian.
  • Warrant (Jaminan): Aturan yang menilai bahwa dasar mendukung klaim.
  • Backing (Dukungan): Justifikasi mengapa aturan tersebut valid.
  • Qualifier (Kualifikasi): Tingkat keyakinan penilaian.
  • Rebuttal (Sanggahan): Kondisi pengecualian di mana klaim tidak berlaku.

Logika formal mengatakan “jika premis benar, maka kesimpulan juga benar”. Toulmin berbeda. “Klaim didukung oleh dasar dan aturan, tetapi bisa dibatalkan jika ada kondisi pengecualian.” Semua argumentasi dapat disanggah.

Selama 60 tahun, mesin aturan berdiri di sisi logika formal. Input adalah fact, hasilnya allow/deny, pengecualian ditangani mekanisme terpisah. Toulmin berada di sisi sebaliknya. Input adalah claim, hasilnya derajat (degree), pengecualian sudah tertanam.

Masalahnya — buku Toulmin ditempatkan di rak filsafat. Tidak terlihat di rak mesin aturan. Missing link selama 60 tahun.

Maka Dibuatlah Mesin Aturan

toulmin adalah implementasi model argumentasi Toulmin sebagai mesin aturan Go.

Kebutuhan Berevolusi

Mari lihat bagaimana if-else dan toulmin merespons evolusi yang sama.

// Senin: "Hanya pengguna terautentikasi yang boleh akses, blokir IP diterapkan, jaringan internal dikecualikan dari blokir"
g := toulmin.NewGraph("api:access")
auth    := g.Warrant(isAuthenticated, nil, 1.0)
blocked := g.Rebuttal(isIPBlocked, nil, 1.0)
exempt  := g.Defeater(isInternalIP, nil, 1.0)
g.Defeat(blocked, auth)
g.Defeat(exempt, blocked)

// Selasa: "Tambah Rate limiting"
limited := g.Rebuttal(isRateLimited, nil, 1.0)
g.Defeat(limited, auth)

// Rabu: "Pengguna premium dikecualikan dari Rate limit"
premium := g.Defeater(isPremiumUser, nil, 1.0)
g.Defeat(premium, limited)

// Kamis: "Saat penanganan insiden, premium pun dibatasi"
incident := g.Rebuttal(isIncidentMode, nil, 1.0)
g.Defeat(incident, premium)

Setiap hari tambah 2 baris, tidak ada perubahan kode yang sudah ada. Evolusi yang sama dengan if-else:

// Senin
if user != nil {
    if blockedIPs[ip] {
        if strings.HasPrefix(ip, "10.") {
            allow = true
        }
    } else {
        allow = true
    }
}

// Kamis — 4 level nesting, struktur tidak bisa dipahami
if user != nil {
    if blockedIPs[ip] {
        if strings.HasPrefix(ip, "10.") {
            allow = true
        }
    } else if isRateLimited(ip) {
        if isPremium(user) {
            if !incidentMode {
                allow = true
            }
        }
    } else {
        allow = true
    }
}

toulmin: 2 baris per kebutuhan, struktur tidak berubah. if-else: Setiap kali harus membongkar seluruh struktur.

Aturan Adalah Fungsi Go

func(claim any, ground any, backing any) (bool, any)
  • ground = bahan penilaian yang berubah setiap request (pengguna, IP, konteks)
  • backing = kriteria penilaian yang ditetapkan saat deklarasi graph (threshold, nama role, konfigurasi)
  • Return = (hasil penilaian, bukti). Bukti bertipe bebas sesuai domain.
func CheckOneFileOneFunc(claim, ground, backing any) (bool, any) {
    g := ground.(*FileGround)
    if len(g.Funcs) > 1 {
        return true, &Evidence{Got: len(g.Funcs), Expected: 1}
    }
    return false, nil
}

Tidak perlu belajar bahasa baru seperti Rego. Cukup tulis fungsi Go.

backing — Fungsi Sama, Kriteria Penilaian Berbeda

backing meneruskan kriteria penilaian aturan sebagai nilai runtime. Mendaftarkan fungsi yang sama dengan backing berbeda menjadi aturan terpisah:

g := toulmin.NewGraph("access")
admin  := g.Warrant(isInRole, "admin", 1.0)
editor := g.Warrant(isInRole, "editor", 0.8)
g := toulmin.NewGraph("line-limit")
strict  := g.Warrant(CheckLineCount, &LineLimit{Max: 100}, 0.7)
relaxed := g.Warrant(CheckLineCount, &LineLimit{Max: 200}, 0.5)
g.Defeat(relaxed, strict)

Jika backing nil, artinya aturan tidak memerlukan kriteria penilaian.

Pengecualian Dideklarasikan sebagai Graph

Deklarasikan hubungan antar aturan dengan Graph Builder API, mesin menangani sisanya. Fungsi adalah identifier. Tidak perlu nama string.

g := toulmin.NewGraph("filefunc")
w := g.Warrant(CheckOneFileOneFunc, nil, 1.0)
d := g.Defeater(TestFileException, nil, 1.0)
g.Defeat(d, w)

results, _ := g.Evaluate(claim, ground)

Fungsi yang sama dapat digunakan kembali di graph berbeda dengan hubungan defeat berbeda:

strictGraph := toulmin.NewGraph("strict")
strictGraph.Warrant(CheckOneFileOneFunc, nil, 1.0)
// Tanpa pengecualian — file test pun tidak diizinkan

lenientGraph := toulmin.NewGraph("lenient")
w := lenientGraph.Warrant(CheckOneFileOneFunc, nil, 1.0)
r1 := lenientGraph.Rebuttal(TestFileException, nil, 1.0)
r2 := lenientGraph.Rebuttal(GeneratedFileException, nil, 0.8)
lenientGraph.Defeat(r1, w)
lenientGraph.Defeat(r2, w)
// File test + file generated keduanya pengecualian

Melacak Dasar Penilaian

EvaluateTrace melacak tidak hanya verdict tetapi juga aturan mana yang aktif dan aturan mana yang mengalahkan aturan lainnya:

traced := g.EvaluateTrace(claim, ground)
// traced[0].Verdict: +0.6
// traced[0].Trace: [
//   {Name: "CheckOneFileOneFunc", Role: "warrant",  Activated: true,  Qualifier: 1.0},
//   {Name: "TestFileException",   Role: "rebuttal", Activated: true,  Qualifier: 1.0},
// ]

Ketika ada puluhan aturan, “mengapa verdict ini muncul” dapat dibaca oleh manusia.

Penilaian Dihitung dengan Satu Rumus

Menerapkan h-Categoriser dari Amgoud (2013):

raw = w / (1 + Σ raw(attackers))
verdict = 2 × raw - 1
  • +1.0 — pelanggaran terkonfirmasi
  • 0.0 — tidak dapat dinilai
  • -1.0 — sanggahan terkonfirmasi

Ketika aturan aktif, ia menjadi warrant. Ketika pengecualian juga aktif, ia menjadi attacker. Rumus menghitung kekuatan keduanya dan menghasilkan verdict. Bagaimana jika ada pengecualian dari pengecualian? Ia menjadi attacker dari attacker, memulihkan aturan asli. Prinsip Kompensasi (Compensation) — sifat yang hanya dipenuhi oleh h-Categoriser.

Aturan Memiliki Tiga Tingkat Kekuatan

Menerapkan klasifikasi Nute (1994):

KekuatanMaknaContoh
StrictTidak bisa dilumpuhkan“Tidak bisa akses admin API tanpa autentikasi”
DefeasibleBisa dilumpuhkan oleh pengecualian“Satu fungsi per file”
DefeaterTanpa klaim sendiri, hanya memblokir aturan lain“File test adalah pengecualian”

Aturan Strict menolak edge serangan. Defeater hanya menyerang tanpa penilaian sendiri. Ini mengekspresikan tingkat penegakan aturan secara struktural.

Apa Bedanya dengan Rego

Regotoulmin
Penulisan aturanPerlu belajar DSL RegoFungsi Go
Penanganan pengecualianPola default/else manualDeklarasi graph defeats
Penilaianallow/deny binerNilai kontinu [-1, +1]
Justifikasi aturan# METADATA (diabaikan mesin)backing (bagian dari struktur)
Kekuatan aturanTidak adastrict/defeasible/defeater
Ukuran mesinPuluhan ribu barisRatusan baris
KecepatanInterpreter (parsing->AST->evaluasi)Pemanggilan langsung fungsi Go

Rego itu luas — ada ekosistem integrasi Kubernetes, Terraform, Envoy. toulmin itu dalam — memiliki apa yang tidak dimiliki Rego (defeasibility, qualifier, backing).

Reposisi Qualifier

Dalam model asli Toulmin, Qualifier melekat pada Claim. “Mungkin pasien ini harus diberi penisilin” — modal qualifier yang mengekspresikan tingkat keyakinan klaim.

Mesin toulmin memindahkan Qualifier dari Claim ke setiap Rule. Dalam mesin aturan, claim hanyalah objek validasi. “File ini memiliki 3 fungsi” — itu verifikasi fakta, bukan sesuatu yang perlu tingkat keyakinan. Yang menentukan kualitas penilaian adalah tingkat keyakinan aturan:

  • “Satu fungsi per file” — qualifier 1.0 (aturan pasti)
  • “Disarankan di bawah 100 baris” — qualifier 0.7 (aturan fleksibel)

Qualifier setiap Rule menjadi bobot awal w(a) dari h-Categoriser, dan verdict akhir menggantikan peran yang seharusnya diemban Qualifier dalam model asli Toulmin — tingkat keyakinan penilaian.

Pembuktian: Konversi Toulmin untuk 22 Aturan filefunc

filefunc adalah alat konvensi struktur kode untuk pengembangan Go native LLM. Seluruh 22 aturan dikonversi menjadi warrant Toulmin.

Klasifikasi Kekuatan

StrengthJumlahRasioContoh
Strict1568%F1, F2, F3, F4, A1-A3, A6-A16
Defeasible418%Q1, Q2, Q3, C4
Defeater314%F5, F6, pengecualian file test

Mayoritas strict — konvensi struktur kode pada dasarnya meminimalkan pengecualian.

Hasil Kuantitatif

ProyekJumlah File (sebelum->sesudah)Rata-rata LOC/File (sebelum->sesudah)Penyelesaian Pelanggaran SRPPenyelesaian Pelanggaran depth
filefunc— (patuh sejak awal)25.100
fullend87->1.260244->25.466->0148->0
whyso12->99147.8->24.412->023->0

fullend bertambah dari 87 file menjadi 1.260. Jumlah file meledak tetapi rata-rata LOC turun dari 244 menjadi 25.4. 66 pelanggaran SRP dan 148 pelanggaran depth semuanya menjadi 0.

Dasar Teoritis

Tidak ada teori orisinal. Semuanya penelitian yang sudah ada:

ElemenKarya Asli
Struktur 6 elemenToulmin (1958)
strict/defeasible/defeaterNute (1994)
h-CategoriserAmgoud & Ben-Naim (2013)

Orisinalitasnya adalah penemuan bahwa hal-hal ini saling terhubung. Selama 60 tahun, hal-hal yang masing-masing berada di filsafat (Toulmin), logika (Nute), dan teori argumentasi (Amgoud) bertemu di satu titik — mesin aturan perangkat lunak.

Menghitung Kontrak

Alasan supremasi hukum bekerja bukan karena hakim pintar. Karena struktur memaksa penilaian. Ada aturan, pengecualian dideklarasikan, dan putusan dihasilkan berdasarkan bukti.

toulmin memindahkan struktur ini ke dalam kode.

  • Warrant = pasal undang-undang
  • Backing = maksud legislasi
  • Strength = ketentuan wajib vs ketentuan opsional
  • Rebuttal = pasal pengecualian
  • Claim = perkara
  • Ground = bukti
  • h-Categoriser = putusan

Deklarasikan kontrak (warrant), deklarasikan pengecualian (rebuttal), masukkan bukti (ground), maka penilaian (verdict) dihitung.

Bukan manusia yang menilai. Rumus yang menghitung.

Acc(a) = w(a) / (1 + Σ Acc(attackers))

Graph Dapat Didefinisikan dengan YAML

Tanpa kode Go, deklarasikan struktur graph dengan YAML dan generate kode:

graph: filefunc
rules:
  - name: CheckOneFileOneFunc
    role: warrant
    qualifier: 1.0
  - name: TestFileException
    role: rebuttal
    qualifier: 1.0
defeats:
  - from: TestFileException
    to: CheckOneFileOneFunc
toulmin graph filefunc.yaml    # graph_gen.go 생성

Cukup tulis fungsi aturan di Go. Struktur graph dideklarasikan oleh YAML.

MIT License. github.com/park-jun-woo/toulmin