emylton's picture
Upload folder using huggingface_hub
9338a41 verified
Raw
History Blame Contribute Delete
15 kB
"""
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 yang secara inheren membutuhkan penerima (PENERIMA slot)
VERBA_PERLU_PENERIMA: set = {
"beri", "kirim", "serah", "kasih", "titip", "jual", "hadiahi",
"sampaikan", "laporkan", "ceritakan",
}
# Verba yang mengindikasikan kalimat pasif lebih natural
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 # kalimat yang dihasilkan
proposisi_asal: Optional[Proposisi] # proposisi sumber (jika dari proposisi)
inferensi_asal: Optional[Inferensi] # inferensi sumber (jika dari inferensi)
verba_bentuk: str # bentuk verba yang digunakan
templat_nama: str # nama template yang dipilih
skor_verifikasi: Optional[float] = None # skor dari verifikasi CPE (jika dijalankan)
valid: bool = True # apakah lolos verifikasi
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
# ── Operasi utama ──────────────────────────────────────────────────────
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] = []
# ── Langkah 1: tentukan sifat verba ──────────────────────────────
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)
# ── Langkah 2: realisasikan verba ────────────────────────────────
verba_bentuk = pilih_bentuk_verba(
root=proposisi.aksi,
transitif=transitif,
pasif=pasif,
sufiks=sufiks,
)
# Tambahkan negasi jika proposisi negatif
if not proposisi.polaritas:
verba_bentuk = "tidak " + verba_bentuk
# Tambahkan modalitas jika ada
if proposisi.modalitas:
verba_bentuk = proposisi.modalitas + " " + verba_bentuk
# ── Langkah 3: pilih template ─────────────────────────────────────
templat = pilih_templat(proposisi, pasif=pasif)
if templat is None:
# Fallback: kalimat minimal dengan agen + verba saja
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,
)
# ── Langkah 4: isi template ────────────────────────────────────────
kalimat = isi_templat(templat, proposisi, verba_bentuk)
# ── Langkah 5: verifikasi (opsional) ──────────────────────────────
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:
# Coba sekali lagi dengan pasif jika tersedia slot AGEN + PASIEN
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:
# Fallback: relasi(subjek, objek) dalam bentuk natural
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, # JendelaWacana β€” hindari circular import dengan type hint string
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]
# ── Internals ──────────────────────────────────────────────────────────
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 # gagal silently, asumsikan valid