Home Blog Series C C04
Series C: Prompt / Context / Harness / Loop Engineering · C04 AI & Agentic Systems 3 November 2025 10 min read

Membangun Test Harness untuk LLM Pipeline

Deploy LLM pipeline tanpa test harness adalah seperti deploy ke production tanpa staging — Anda akan tahu kalau ada yang salah hanya setelah user merasakannya.

RM
Ryan Muliadi
Digital Systems Architect · Ryzen Digital Systems

Membangun LLM pipeline tanpa test harness adalah seperti deploy ke production tanpa staging environment. Anda akan tahu kalau ada yang salah — tapi hanya setelah user yang merasakannya.

Kenapa Butuh Test Harness Khusus untuk LLM

Perbedaan dari Software Testing Biasa
Unit test software: input sama → output sama (deterministic). LLM test: input sama → output bisa berbeda tiap run. Harness LLM harus mengukur distribusi output, bukan equality.
AspekSoftware Testing BiasaLLM Harness
Assertion typeassertEqual(result, expected)Semantic similarity score ≥ 0.85
DeterminismDeterministicProbabilistic — perlu multiple runs
Ground truthDari spec / requirementsDari expert annotation
RegressionOutput harus sama persisScore tidak boleh turun dari threshold
CostHampir gratisAda biaya per run (API token)

Membangun Eval Dataset

Ini fondasi dari semua test harness — dan bagian yang paling banyak menguras waktu. Tapi tidak ada shortcut: ground truth yang buruk menghasilkan evaluasi yang menyesatkan.

01
Kumpulkan dari real user inputs
Minimal 50–100 pasang (input, expected output). Ambil dari actual usage logs, bukan dibuat-buat. Real inputs mengandung edge case dan variasi bahasa yang tidak terpikir saat pembuatan sistem.
02
Kategorikan dan distribusikan
Dataset harus merepresentasikan distribusi real: 60% kasus normal, 25% edge case, 15% adversarial. Kalau dataset Anda hanya kasus mudah, harness akan memberikan false confidence.
03
Annotate dengan domain expert
Untuk domain-specific knowledge (hukum, medis, keuangan), "expected output" harus divalidasi oleh expert — bukan oleh developer yang build sistem. Jangan gunakan LLM lain untuk generate ground truth.

Tiga Jenis Test dalam LLM Harness

Python · Complete Test Harness Structure
import json
from dataclasses import dataclass
from typing import List

@dataclass
class EvalCase:
    input: str
    expected_output: str
    category: str  # "normal" | "edge" | "adversarial"
    checks: list   # list fungsi evaluasi yang harus pass

class LLMHarness:
    def __init__(self, llm, eval_cases: List[EvalCase]):
        self.llm = llm
        self.cases = eval_cases
        self.results = []

    def run(self, n_runs: int = 3) -> dict:
        for case in self.cases:
            case_results = []
            for _ in range(n_runs):  # run beberapa kali untuk ukur variance
                output = self.llm.invoke(case.input)
                case_results.append({
                    "output": output,
                    "check_results": {
                        check.__name__: check(output, case.expected_output)
                        for check in case.checks
                    }
                })
            self.results.append({
                "case": case,
                "runs": case_results,
                "pass_rate": self._pass_rate(case_results)
            })
        return self._summary()

    def _pass_rate(self, runs: list) -> float:
        passed = sum(1 for r in runs if all(r["check_results"].values()))
        return passed / len(runs)

# Contoh check functions
def is_valid_json(output: str, _) -> bool:
    try: json.loads(output); return True
    except: return False

def has_required_fields(output: str, _) -> bool:
    try:
        d = json.loads(output)
        return all(k in d for k in ["category", "priority", "response"])
    except: return False

def correct_priority(output: str, expected: str) -> bool:
    try:
        out = json.loads(output)
        exp = json.loads(expected)
        return out.get("priority") == exp.get("priority")
    except: return False

A/B Prompt Testing

Sebelum deploy versi prompt baru ke production, selalu bandingkan secara head-to-head dengan versi yang sedang jalan:

Python · A/B Prompt Comparison
def compare_prompts(prompt_a: str, prompt_b: str, eval_cases, llm, n_runs=3):
    results_a = LLMHarness(build_chain(llm, prompt_a), eval_cases).run(n_runs)
    results_b = LLMHarness(build_chain(llm, prompt_b), eval_cases).run(n_runs)

    print(f"Prompt A: {results_a['overall_score']:.1%}")
    print(f"Prompt B: {results_b['overall_score']:.1%}")

    # Highlight categories where B is better or worse
    for cat in ["normal", "edge", "adversarial"]:
        delta = results_b["by_category"][cat] - results_a["by_category"][cat]
        icon = "↑" if delta > 0 else ("↓" if delta < 0 else "=")
        print(f"  {cat}: {icon} {delta:+.1%}")

    # PENTING: Jangan deploy B jika ada category yang turun signifikan
    # meski overall score lebih tinggi
    if any(results_b["by_category"][cat] < results_a["by_category"][cat] - 0.05
          for cat in ["adversarial"]):
        print("⚠ WARNING: Prompt B lebih buruk untuk adversarial cases")

CI/CD Integration

⚠️ Harness Harus Jalan di CI, Bukan Hanya Manual

Kalau harness hanya dijalankan manual "sesekali," ia tidak memberikan perlindungan nyata. Integrasikan ke GitHub Actions: setiap pull request yang mengubah prompt file harus melewati harness sebelum bisa di-merge.

YAML · GitHub Actions untuk Prompt Regression
name: LLM Prompt Regression Test

on:
  pull_request:
    paths:
      - 'src/prompts/**'  # hanya run saat file prompt berubah

jobs:
  eval:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v4
      - run: pip install -r requirements.txt

      - name: Run LLM Harness
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: python -m pytest tests/test_llm_harness.py -v

      - name: Check Acceptance Threshold
        run: |
          python scripts/check_scores.py \
            --min-overall 0.82 \
            --min-adversarial 0.75

Test harness bukan tentang mencapai 100% accuracy — itu mustahil untuk LLM. Ini tentang mengetahui dengan confidence bahwa sistem Anda tidak lebih buruk dari kemarin, dan regresi tertangkap sebelum sampai ke user.

Kesimpulan

Bangun eval dataset dari real user inputs, kategorikan ke normal/edge/adversarial, dan jalankan harness di CI/CD setiap ada perubahan prompt. Budget: ~$2–5 per run untuk 100 eval cases dengan Claude Haiku. Ini investasi kecil dibanding cost reputasi kalau prompt regression terlewat ke production.