| """ |
| pewujud.py β PewujudKalimat: realisasi proposisi β kalimat bahasa Indonesia. |
| |
| ANTI-TRANSFORMER: |
| Transformer: "generasi" = distribusi probabilitas atas vocab token demi token. |
| AKSARA: "perwujudan" = realisasi deterministik proposisi terstruktur |
| menggunakan aturan morfologi TBBBI + template kalimat + verifikasi CPE. |
| |
| Input: Proposisi{agen, aksi, pasien, slot...} β makna yang sudah dipahami |
| Output: "Hakim menjatuhkan hukuman kepada terdakwa." β perwujudan makna |
| |
| Loop unik AKSARA (tidak mungkin di Transformer): |
| Proposisi β [Perwujudan] β kalimat_draft |
| kalimat_draft β [AksaraFramework verifikasi] β skor_linguistik |
| skor < 0.4? β revisi morfologi/template β ulang |
| |
| Pipeline: |
| 1. Tentukan transitifitas verba (dari KB + pola slot proposisi) |
| 2. Realisasikan verba via morfologi TBBBI |
| 3. Pilih template kalimat terbaik berdasarkan slot tersedia |
| 4. Isi template dengan nilai slot |
| 5. Verifikasi via framework (opsional) |
| 6. Return kalimat + metadata audit |
| """ |
|
|
| from __future__ import annotations |
|
|
| from dataclasses import dataclass, field |
| from typing import Dict, List, Optional, Tuple |
|
|
| from aksara.primitives.krl.proposition import Proposisi, TipeSlot |
| from aksara.primitives.krl.inference import Inferensi |
| from aksara.realisasi.morfologi import ( |
| pilih_bentuk_verba, |
| VERBA_INTRANSITIF, |
| VERBA_SUFIKS_I, |
| ) |
| from aksara.realisasi.templat import ( |
| pilih_templat, isi_templat, TEMPLAT_INFERENSI, |
| ) |
|
|
|
|
| |
| VERBA_PERLU_PENERIMA: set = { |
| "beri", "kirim", "serah", "kasih", "titip", "jual", "hadiahi", |
| "sampaikan", "laporkan", "ceritakan", |
| } |
|
|
| |
| VERBA_PASIF_NATURAL: set = { |
| "tangkap", "tahan", "hukum", "vonis", "jatuh", "putus", "tetap", |
| "baca", "tulis", "buat", "bentuk", "bangun", |
| } |
|
|
|
|
| @dataclass |
| class HasilPerwujudan: |
| """ |
| Hasil perwujudan satu proposisi atau inferensi. |
| |
| Setiap hasil bisa diaudit lengkap β bukan opaque string dari model. |
| """ |
| kalimat: str |
| proposisi_asal: Optional[Proposisi] |
| inferensi_asal: Optional[Inferensi] |
| verba_bentuk: str |
| templat_nama: str |
| skor_verifikasi: Optional[float] = None |
| valid: bool = True |
| catatan: List[str] = field(default_factory=list) |
|
|
| def ke_dict(self) -> dict: |
| return { |
| "kalimat": self.kalimat, |
| "verba_bentuk": self.verba_bentuk, |
| "templat": self.templat_nama, |
| "skor_verifikasi": self.skor_verifikasi, |
| "valid": self.valid, |
| "catatan": self.catatan, |
| } |
|
|
|
|
| class PewujudKalimat: |
| """ |
| Pewujud proposisi β kalimat bahasa Indonesia. |
| |
| Mengubah representasi proposisi terstruktur (output KRL) menjadi |
| kalimat bahasa Indonesia yang gramatikal menggunakan: |
| - Aturan morfologi verba TBBBI (morfologi.py) |
| - Template kalimat S-P-O-K (templat.py) |
| - Verifikasi opsional via AksaraFramework (CPE constraint check) |
| |
| TIDAK menggunakan: |
| - Distribusi probabilitas |
| - Parameter yang dilatih |
| - Sampling atau beam search |
| |
| Contoh: |
| pewujud = PewujudKalimat(framework) |
| |
| # Dari proposisi |
| prop = framework.proses("Hakim menjatuhkan hukuman.").krl_result.proposisi |
| hasil = pewujud.wujudkan(prop) |
| print(hasil.kalimat) # "Hakim menjatuhkan hukuman." |
| |
| # Dari inferensi KRL |
| inf = Inferensi(relasi="STATUS_MENJADI", subjek="terdakwa", objek="terpidana", ...) |
| hasil = pewujud.wujudkan_inferensi(inf) |
| print(hasil.kalimat) # "Terdakwa menjadi terpidana." |
| |
| # Jawab pertanyaan dari wacana |
| jawaban = pewujud.jawab("Apa status terdakwa?", jendela) |
| print(jawaban["kalimat"]) # "Terdakwa menjadi terpidana." |
| """ |
|
|
| def __init__(self, framework=None) -> None: |
| """ |
| Args: |
| framework: AksaraFramework opsional β digunakan untuk verifikasi. |
| Jika None, verifikasi dilewati. |
| """ |
| self.framework = framework |
|
|
| |
|
|
| def wujudkan( |
| self, |
| proposisi: Proposisi, |
| pasif: bool = False, |
| verifikasi: bool = True, |
| ) -> HasilPerwujudan: |
| """ |
| Wujudkan proposisi menjadi kalimat bahasa Indonesia. |
| |
| Alur: |
| 1. Tentukan apakah verba transitif atau intransitif |
| 2. Realisasikan verba dengan morfologi yang tepat |
| 3. Pilih template berdasarkan slot yang tersedia |
| 4. Isi template β kalimat draft |
| 5. Verifikasi via CPE (jika framework tersedia) |
| |
| Args: |
| proposisi: Proposisi terstruktur dari KRL |
| pasif: True β gunakan struktur pasif di- |
| verifikasi: True β jalankan verifikasi CPE setelah perwujudan |
| |
| Returns: |
| HasilPerwujudan dengan kalimat + metadata audit lengkap |
| """ |
| catatan: List[str] = [] |
|
|
| |
| transitif = self._apakah_transitif(proposisi) |
| if pasif and TipeSlot.PASIEN not in proposisi.slot: |
| pasif = False |
| catatan.append("pasif dibatalkan: tidak ada slot PASIEN") |
|
|
| sufiks = self._pilih_sufiks(proposisi) |
|
|
| |
| verba_bentuk = pilih_bentuk_verba( |
| root=proposisi.aksi, |
| transitif=transitif, |
| pasif=pasif, |
| sufiks=sufiks, |
| ) |
|
|
| |
| if not proposisi.polaritas: |
| verba_bentuk = "tidak " + verba_bentuk |
|
|
| |
| if proposisi.modalitas: |
| verba_bentuk = proposisi.modalitas + " " + verba_bentuk |
|
|
| |
| templat = pilih_templat(proposisi, pasif=pasif) |
| if templat is None: |
| |
| agen = proposisi.agen or "" |
| kalimat = f"{agen} {verba_bentuk}".strip() |
| kalimat = kalimat[0].upper() + kalimat[1:] if kalimat else kalimat |
| catatan.append("fallback: template tidak ditemukan, gunakan S+P minimal") |
| return HasilPerwujudan( |
| kalimat=kalimat, |
| proposisi_asal=proposisi, |
| inferensi_asal=None, |
| verba_bentuk=verba_bentuk, |
| templat_nama="FALLBACK_MINIMAL", |
| catatan=catatan, |
| ) |
|
|
| |
| kalimat = isi_templat(templat, proposisi, verba_bentuk) |
|
|
| |
| skor_verifikasi = None |
| valid = True |
| if verifikasi and self.framework is not None and kalimat: |
| skor_verifikasi, valid, catatan_vf = self._verifikasi(kalimat) |
| catatan.extend(catatan_vf) |
|
|
| if not valid and not pasif: |
| |
| if (TipeSlot.AGEN in proposisi.slot |
| and TipeSlot.PASIEN in proposisi.slot): |
| verba_pasif = pilih_bentuk_verba( |
| root=proposisi.aksi, transitif=True, pasif=True |
| ) |
| templat_pasif = pilih_templat(proposisi, pasif=True) |
| if templat_pasif: |
| kalimat_alt = isi_templat(templat_pasif, proposisi, verba_pasif) |
| skor_alt, valid_alt, _ = self._verifikasi(kalimat_alt) |
| if valid_alt or (skor_alt is not None |
| and skor_verifikasi is not None |
| and skor_alt > skor_verifikasi): |
| kalimat = kalimat_alt |
| verba_bentuk = verba_pasif |
| skor_verifikasi = skor_alt |
| valid = valid_alt |
| catatan.append( |
| f"revisi: aktif gagal verifikasi, ganti pasif " |
| f"(skor={skor_alt:.3f})" |
| ) |
|
|
| return HasilPerwujudan( |
| kalimat=kalimat, |
| proposisi_asal=proposisi, |
| inferensi_asal=None, |
| verba_bentuk=verba_bentuk, |
| templat_nama=templat.nama, |
| skor_verifikasi=skor_verifikasi, |
| valid=valid, |
| catatan=catatan, |
| ) |
|
|
| def wujudkan_inferensi(self, inferensi: Inferensi) -> HasilPerwujudan: |
| """ |
| Wujudkan satu inferensi KRL menjadi kalimat pernyataan. |
| |
| Menggunakan template inferensi yang spesifik per jenis relasi. |
| Template dipilih dari TEMPLAT_INFERENSI berdasarkan inferensi.relasi. |
| |
| Contoh: |
| STATUS_MENJADI(terdakwa, terpidana) |
| β "Terdakwa menjadi terpidana." |
| """ |
| templat_pola = TEMPLAT_INFERENSI.get(inferensi.relasi) |
|
|
| if templat_pola is None: |
| |
| kalimat = f"{inferensi.subjek} {inferensi.relasi.lower().replace('_', ' ')} {inferensi.objek}" |
| kalimat = kalimat[0].upper() + kalimat[1:] + "." |
| return HasilPerwujudan( |
| kalimat=kalimat, |
| proposisi_asal=None, |
| inferensi_asal=inferensi, |
| verba_bentuk="", |
| templat_nama="FALLBACK_INFERENSI", |
| catatan=[f"relasi '{inferensi.relasi}' tidak ada di TEMPLAT_INFERENSI"], |
| ) |
|
|
| kalimat = templat_pola.format( |
| subjek=inferensi.subjek, |
| objek=inferensi.objek, |
| ) |
| if kalimat and not kalimat.endswith("."): |
| kalimat += "." |
| kalimat = kalimat[0].upper() + kalimat[1:] if kalimat else kalimat |
|
|
| return HasilPerwujudan( |
| kalimat=kalimat, |
| proposisi_asal=None, |
| inferensi_asal=inferensi, |
| verba_bentuk="", |
| templat_nama=f"INFERENSI_{inferensi.relasi}", |
| valid=True, |
| ) |
|
|
| def jawab( |
| self, |
| query: str, |
| jendela, |
| verifikasi: bool = True, |
| ) -> Optional[Dict]: |
| """ |
| Jawab pertanyaan dari konteks wacana. |
| |
| Alur: |
| 1. Tanya JendelaWacana β dapat inferensi paling relevan |
| 2. Wujudkan inferensi β kalimat jawaban |
| 3. Return kalimat + penjelasan audit trail lengkap |
| |
| Returns: |
| Dict dengan kunci: |
| kalimat β jawaban dalam bentuk kalimat Indonesia |
| inferensi β Inferensi sumber |
| keyakinan β float [0,1] |
| penjelasan β sumber aturan + kalimat asli |
| audit β metadata perwujudan (verba, template, skor) |
| atau None jika tidak ada jawaban. |
| """ |
| hasil_tanya = jendela.tanya(query) |
| if hasil_tanya is None: |
| return None |
|
|
| inferensi = hasil_tanya["inferensi"] |
| hasil = self.wujudkan_inferensi(inferensi) |
|
|
| return { |
| "kalimat": hasil.kalimat, |
| "inferensi": inferensi, |
| "keyakinan": hasil_tanya["keyakinan"], |
| "penjelasan": hasil_tanya["penjelasan"], |
| "audit": hasil.ke_dict(), |
| } |
|
|
| def wujudkan_semua( |
| self, |
| proposisi_list: List[Proposisi], |
| ) -> List[HasilPerwujudan]: |
| """Wujudkan daftar proposisi menjadi kalimat-kalimat.""" |
| return [self.wujudkan(p, verifikasi=False) for p in proposisi_list] |
|
|
| |
|
|
| def _apakah_transitif(self, proposisi: Proposisi) -> bool: |
| """ |
| Tentukan apakah verba bersifat transitif dari slot proposisi. |
| |
| Transitif jika: |
| - ada slot PASIEN atau TEMA, ATAU |
| - verba ada di daftar transitif eksplisit |
| Intransitif jika verba ada di VERBA_INTRANSITIF. |
| """ |
| if proposisi.aksi in VERBA_INTRANSITIF: |
| return False |
| if TipeSlot.PASIEN in proposisi.slot or TipeSlot.TEMA in proposisi.slot: |
| return True |
| return False |
|
|
| def _pilih_sufiks(self, proposisi: Proposisi) -> Optional[str]: |
| """ |
| Pilih sufiks verba berdasarkan slot proposisi. |
| |
| - Ada PENERIMA β -kan (transfer ke penerima) |
| - Verba dalam VERBA_SUFIKS_I β -i |
| - Lain β None (pilih otomatis) |
| """ |
| if TipeSlot.PENERIMA in proposisi.slot: |
| return "kan" |
| if proposisi.aksi in VERBA_SUFIKS_I: |
| return "i" |
| return None |
|
|
| def _verifikasi(self, kalimat: str) -> Tuple[Optional[float], bool, List[str]]: |
| """ |
| Verifikasi kalimat yang dihasilkan via AksaraFramework. |
| |
| Menjalankan 6 primitif pada kalimat hasil perwujudan untuk memastikan |
| tidak ada pelanggaran constraint berat. |
| |
| Returns: |
| (skor_linguistik, valid, catatan) |
| """ |
| catatan: List[str] = [] |
| try: |
| state = self.framework.proses(kalimat) |
| skor = state.skor_linguistik |
| valid = skor >= 0.4 and not any( |
| p.severitas >= 0.8 for p in state.pelanggaran |
| ) |
| if not valid: |
| catatan.append( |
| f"verifikasi GAGAL: skor={skor:.3f}, " |
| f"n_pelanggaran_berat=" |
| f"{sum(1 for p in state.pelanggaran if p.severitas >= 0.8)}" |
| ) |
| return skor, valid, catatan |
| except Exception as e: |
| catatan.append(f"verifikasi error: {e}") |
| return None, True, catatan |
|
|