Pembaruan model dasar (model checkpoint update) atau perubahan kecil pada system prompt sering kali memicu degradasi output yang luput dari unit test konvensional. Masalah ini mencakup repetisi token degenerate, halusinasi bertele-tele (output slop), penurunan densitas informasi, hingga kegagalan struktur sintaksis. Karena inferensi LLM bersifat non-deterministik, string assertion eksak (seperti assert response == expected) tidak dapat digunakan. Diperlukan pipeline uji regresi semantik otomatis untuk menangkap anomali ini sebelum masuk ke tahap deployment.

Arsitektur Dual-Layer Evaluation

Mengevaluasi setiap output LLM menggunakan model besar lain sebagai evaluator (LLM-as-a-judge) memerlukan biaya komputasi tinggi dan memperpanjang durasi pipeline CI/CD. Solusi efisien untuk masalah ini adalah arsitektur dual-layer evaluation:

  • Layer 1 (Heuristic & Deterministic): Filter berbasis kalkulasi matematika cepat, zero-cost, dan tanpa latensi eksternal. Layer ini mengukur rasio kompresi data untuk entropi repetisi, keragaman n-gram, panjang token, dan validitas skema struktural. Jika output gagal di Layer 1, eksekusi test langsung dibatalkan (fail fast).
  • Layer 2 (LLM-as-a-Judge Terkalibrasi): Dijalankan hanya jika output lolos Layer 1. Menggunakan model yang lebih kecil dan hemat biaya dengan rubrik terstruktur (scoring 1-5 dengan kriteria ketat) untuk menilai aspek semantik abstrak seperti nada bicara, grounding fakta, dan ketiadaan frasa filler.

Layer 1: Metrik Entropi dan Deteksi Repetisi Heuristik

Teks yang mengalami repetisi simpul (token loops) atau pengulangan pola frasa memiliki entropi informasi yang sangat rendah. Algoritma kompresi lossless seperti DEFLATE (melalui pustaka zlib) mengeksploitasi redundansi ini. Rasio ukuran byte terkompresi terhadap byte mentah memberikan estimasi entropi teks.

Metrik pendukung lainnya adalah Distinct-N ratio, yaitu perbandingan antara jumlah n-gram unik dengan total n-gram dalam teks. Kombinasi kedua metrik ini secara efektif mengidentifikasi repetisi tanpa memerlukan panggilan API tambahan.

import zlib
from typing import Set

def calculate_compression_ratio(text: str) -> float:
    raw_bytes = text.encode("utf-8")
    if not raw_bytes:
        return 0.0
    compressed = zlib.compress(raw_bytes, level=9)
    # Rasio rendah menandakan teks sangat redundan/repetitif
    return len(compressed) / len(raw_bytes)

def calculate_distinct_n_ratio(text: str, n: int = 3) -> float:
    tokens = text.lower().split()
    if len(tokens) < n:
        return 1.0
    ngrams = [tuple(tokens[i:i + n]) for i in range(len(tokens) - n + 1)]
    unique_ngrams: Set[tuple] = set(ngrams)
    return len(unique_ngrams) / len(ngrams)

Layer 2: Kalibrasi LLM-as-a-Judge Berbasis Rubrik

Ketika output lolos evaluasi struktur dan kompresi, Layer 2 mengevaluasi keberadaan frasa filler korporat dan kata mubazir tanpa substansi ("slop"). Untuk menjaga konsistensi, prompt evaluator harus menghasilkan output terstruktur (JSON schema) dan menghindari skala penilaian terbuka.

import json
from pydantic import BaseModel, Field

class QualityRubric(BaseModel):
    slop_score: int = Field(
        ..., 
        description="1 jika sangat padat dan objektif, 5 jika dipenuhi filler/klise berlebih"
    )
    is_concise: bool
    reasoning: str

JUDGE_PROMPT_TEMPLATE = """
Analisis teks berikut berdasarkan rubrik efisiensi komunikasi.
Teks: "{output_text}"

Kriteria Slop:
1: Tidak ada basa-basi, langsung ke inti teknis, tidak ada pujian kosong.
3: Mengandung 1-2 frasa transisi generik yang tidak menambah informasi.
5: Penuh dengan filler repetitif, retorika berlebihan, atau bahasa marketing hampa.

Keluarkan respon HANYA dalam format JSON valid sesuai schema.
"""

Implementasi Test Harness dengan Pytest

Dalam integrasi CI/CD, assertion tidak boleh mengandalkan threshold biner kaku untuk metrik non-deterministik. Perlu toleransi batas ambang (*margin of tolerance*) untuk meminimalkan *false alarm* tanpa meloloskan degradasi nyata.

import pytest

# Threshold toleransi
MIN_COMPRESSION_RATIO = 0.35  # Teks repetitif biasanya jatuh di bawah 0.25
MIN_DISTINCT_3GRAM = 0.70      # Rasio n-gram unik minimal
MAX_ALLOWED_SLOP_SCORE = 2     # Skala 1 (bersih) hingga 5 (slop berat)

def run_llm_inference(prompt: str) -> str:
    # ponytail: integrasi SDK LLM nyata; fallback dummy untuk pengujian
    return "Hasil inferensi ringkas, langsung ke inti permasalahan tanpa basa-basi."

@pytest.mark.parametrize("test_input", [
    {"prompt": "Jelaskan idempotensi API dalam 1 paragraf."},
    {"prompt": "Bagaimana cara kerja connection pool PostgreSQL?"}
])
def test_semantic_regression_pipeline(test_input):
    output = run_llm_inference(test_input["prompt"])
    
    # 1. Layer Heuristik (Fast Fail)
    comp_ratio = calculate_compression_ratio(output)
    distinct_ratio = calculate_distinct_n_ratio(output, n=3)
    
    assert comp_ratio >= MIN_COMPRESSION_RATIO, (
        f"Degradasi entropi terdeteksi: zlib ratio {comp_ratio:.2f} < {MIN_COMPRESSION_RATIO}"
    )
    assert distinct_ratio >= MIN_DISTINCT_3GRAM, (
        f"Repetisi token terdeteksi: Distinct-3 {distinct_ratio:.2f} < {MIN_DISTINCT_3GRAM}"
    )

    # 2. Layer Evaluator Semantik (Simulasi hasil judge)
    judge_result = {"slop_score": 1, "is_concise": True}
    assert judge_result["slop_score"] <= MAX_ALLOWED_SLOP_SCORE, (
        f"Output slop terdeteksi: score {judge_result['slop_score']}"
    )

Mitigasi Flaky Test: Seed Freezing dan Bootstrap Sampling

Evaluasi semantik pada model probabilistik rentan menghasilkan *flaky tests* jika variansi sampling tidak dibatasi. Terapkan strategi berikut untuk menjaga stabilitas pipeline:

  1. Seed Freezing: Gunakan parameter inferensi seed (jika didukung provider model) dan atur temperature=0.0 untuk evaluasi regresi rutin guna meminimalkan stokastisitas sampling.
  2. Bootstrap Confidence Interval: Untuk pipeline kritis yang menggunakan temperature > 0, jalankan inferensi secara paralel sebanyak $N$ iterasi (misalnya $N=5$). Nilai median atau rata-rata skor dievaluasi terhadap threshold dengan interval kepercayaan (Confidence Interval), bukan mengandalkan satu sampel tunggal.

Reporting Delta Failure pada Pipeline CI

Saat pengujian regresi gagal, developer memerlukan visibilitas cepat terhadap bagian yang rusak tanpa harus membaca log CI mentah yang panjang. Integrasikan diff pelaporan yang membandingkan baseline 'golden dataset' terhadap output baru.

import difflib

def generate_semantic_delta_report(baseline_text: str, degraded_text: str) -> str:
    baseline_lines = baseline_text.splitlines(keepends=True)
    degraded_lines = degraded_text.splitlines(keepends=True)
    
    delta = difflib.unified_diff(
        baseline_lines,
        degraded_lines,
        fromfile="baseline_golden_output",
        tofile="current_run_output",
        n=2
    )
    return "".join(delta)

Terapkan pelaporan ini pada hook kegagalan pytest (misalnya via pytest_runtest_makereport) agar dev team dapat langsung mengidentifikasi perubahan struktur, kemunculan kata filler baru, atau anomali token secara deterministik.