Kesalahan metadata katalog seperti kategori produk yang salah, atribut spesifikasi yang hilang, atau pelanggaran penamaan sering lolos dari unit test konvensional. Pendekatan berbasis aturan regex gagal menangkap konteks semantik, sedangkan review manual memperlambat siklus rilis. Mengintegrasikan pola LLM Jury ke dalam pipeline CI/CD memungkinkan evaluasi otomatis berbasis konsensus multi-model sebelum metadata di-merge ke branch produksi.

Arsitektur Pola LLM Jury

Pola LLM Jury menggunakan minimal dua atau tiga model yang berbeda (atau prompt yang diisolasi) untuk mengevaluasi data masukan secara independen. Keputusan rilis didasarkan pada majority voting atau tingkat kesepakatan (consensus score).

  • Model Heterogen: Menggabungkan model dari penyedia berbeda (misalnya OpenAI GPT-4o-mini dan Anthropic Claude 3.5 Haiku) mencegah bias inheren atau failure mode yang sama dari satu model keluarga.
  • Structured Output & Skema JSON: Output evaluasi dipaksa mengikuti JSON schema ketat yang divalidasi dengan runtime parser (seperti Pydantic atau Zod).
  • Gatekeeper CI: Pipeline akan exit 1 jika konsensus gagal tercapai atau nilai validitas metadata berada di bawah ambang batas minimum.

Script Evaluasi Multi-Model (Python)

Script berikut menerima file JSON berisi metadata produk, mengirimkannya secara paralel ke dua penyedia LLM, memvalidasi respons menggunakan Pydantic, dan menghitung konsensus kelayakan rilis.

import os
import sys
import json
import asyncio
from typing import Literal
from pydantic import BaseModel, Field, ValidationError
from openai import AsyncOpenAI
import anthropic

class EvaluationResult(BaseModel):
    passed: bool = Field(description="Apakah metadata memenuhi kriteria katalog")
    category_accurate: bool
    title_compliant: bool
    reason: str = Field(description="Alasan evaluasi maksimal 2 kalimat")

PROMPT = """Evaluasi metadata katalog berikut:
Judul: {title}
Kategori: {category}
Atribut: {attributes}

Aturan:
1. Judul tidak boleh clickbait dan harus mencakup merek serta tipe.
2. Kategori harus relevan secara spesifik.
3. Atribut tidak boleh mengandung HTML atau karakter rusak.

Kembalikan hasil dalam format JSON persis sesuai skema:
{{"passed": boolean, "category_accurate": boolean, "title_compliant": boolean, "reason": string}}
"""

async def judge_openai(client: AsyncOpenAI, payload: dict) -> EvaluationResult:
    content = PROMPT.format(**payload)
    resp = await client.chat.completions.create(
        model="gpt-4o-mini",
        temperature=0.0,
        response_format={"type": "json_object"},
        messages=[{"role": "user", "content": content}]
    )
    raw = json.loads(resp.choices[0].message.content)
    return EvaluationResult.model_validate(raw)

async def judge_anthropic(client: anthropic.AsyncAnthropic, payload: dict) -> EvaluationResult:
    content = PROMPT.format(**payload)
    resp = await client.messages.create(
        model="claude-3-5-haiku-20241022",
        max_tokens=256,
        temperature=0.0,
        messages=[{"role": "user", "content": content}]
    )
    raw = json.loads(resp.content[0].text)
    return EvaluationResult.model_validate(raw)

async def main():
    if len(sys.argv) < 2:
        print("Usage: python eval.py <metadata.json>")
        sys.exit(2)

    with open(sys.argv[1], "r") as f:
        catalog_items = json.load(f)

    oa_client = AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"])
    ant_client = anthropic.AsyncAnthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

    failed_items = []

    for item in catalog_items:
        try:
            r1, r2 = await asyncio.gather(
                judge_openai(oa_client, item),
                judge_anthropic(ant_client, item)
            )
            
            # Majority vote: kedua model harus sepakat lolos
            if not (r1.passed and r2.passed):
                failed_items.append({
                    "sku": item.get("sku"),
                    "gpt_reason": r1.reason,
                    "claude_reason": r2.reason,
                    "disagreement": r1.passed != r2.passed
                })
        except (ValidationError, json.JSONDecodeError) as e:
            print(f"Validation failure on item {item.get('sku')}: {e}")
            failed_items.append({"sku": item.get("sku"), "error": str(e)})

    if failed_items:
        print(f"CI Check Failed: {len(failed_items)} item tidak valid.")
        print(json.dumps(failed_items, indent=2))
        sys.exit(1)

    print("Semua metadata katalog lolos evaluasi LLM Jury.")
    sys.exit(0)

if __name__ == "__main__":
    asyncio.run(main())

Konfigurasi GitHub Actions Workflow

Eksekusi evaluator dibatasi hanya saat ada perubahan file pada direktori metadata atau file seed katalog. Menggunakan secret credentials secara aman dan menghasilkan ringkasan langsung pada step summary.

name: Catalog Metadata LLM Evaluator

on:
  pull_request:
    paths:
      - 'catalog/seeds/**.json'
      - 'catalog/metadata/**.json'

jobs:
  llm-jury:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'

      - name: Install Dependencies
        run: |
          pip install openai anthropic pydantic

      - name: Run LLM Jury Evaluation
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          # Evaluasi data seed katalog produk
          python scripts/eval_catalog.py catalog/seeds/products.json

Mitigasi Non-Deterministik dan Flaky Tests

Evaluasi model di CI/CD rentan terhadap masalah flakiness. Gunakan langkah-langkah berikut untuk memastikan hasil deterministik:

  • Temperature 0: Selalu set parameter temperature=0.0 dan top_p=1.0 untuk menekan variansi token generation.
  • Pinned Model Versions: Hindari menggunakan tag alias dinamis seperti gpt-4o-mini jika penyedia mendukung snapshot versioning berbasis tanggal (misal gpt-4o-mini-2024-07-18).
  • Retry dengan Backoff: Pasang retry logic khusus untuk menangani status 429 (Rate Limit) dan 5xx, bukan untuk mengulang penilaian yang gagal.

Analisis Trade-Off: Latency vs Akurasi

Penerapan konsensus multi-model memperkenalkan overhead biaya API dan waktu pipeline yang sebanding dengan penurunan false positive/negative.

Strategi EvaluatorRata-rata LatencyBiaya per 1.000 ItemAkurasi SemantikRisiko Flakiness
Single Small Model (e.g. GPT-4o-mini)~600msSangat Rendah (~$0.02)ModeratSedang (bias rentan)
Multi-Model Consensus (2 Model paralel)~1.2s (bounded by slower model)Rendah-Moderat (~$0.05)TinggiRendah (terfilter disagreement)
Single Frontier Model (e.g. Claude 3.5 Sonnet)~2.5sTinggi (~$0.30)Sangat TinggiRendah

Untuk efisiensi build time, batasi input yang dievaluasi dalam CI hanya pada data yang mengalami git diff terhadap target branch, bukan keseluruhan basis data katalog.