Jawaban Salah Terlihat Persis Seperti Jawaban Benar
Anda mengirim prompt ke GPT-4o. Ia mengembalikan blob JSON dengan confidence 0,97. Anda mengirim prompt yang sama ke Claude 3.5 Sonnet. Ia mengembalikan blob JSON yang berbeda, juga dengan confidence 0,97. Kedua model terdengar yakin. Kedua-duanya salah dengan cara yang berbeda.
Ini bukan hipotesis. Jika Anda menjalankan beban kerja LLM non-trivial lebih dari beberapa ribu request, Anda akan menemukan kasus di mana dua model mampu tidak setuju tentang fakta, klasifikasi, atau output terstruktur. Ketidaksetujuan itu sendiri adalah sebuah sinyal. Sebagian besar tim mengabaikannya.
N-version programming, ide lama menjalankan beberapa implementasi dan melakukan voting pada hasilnya, dianggap terlalu mahal untuk sebagian besar perangkat lunak. Dengan LLM, “implementasi” tersebut hanyalah panggilan API yang berbeda. Biayanya nyata, tetapi biaya mengirim jawaban yang salah seringkali lebih buruk.
Seperti Apa Pemrograman N-Versi untuk LLM
Versi klasiknya berasal dari sistem safety-critical. Tiga program yang ditulis secara independen memproses input yang sama. Sebuah voter membandingkan output. Jika dua setuju dan satu tidak, mayoritas menang dan yang minoritas ditandai untuk inspeksi.
Dengan LLM, Anda tidak membutuhkan tiga tim rekayasa. Anda membutuhkan tiga API key. Independensinya lebih lemah, model berbagi data pelatihan, tetapi divergensinya masih terukur dan berguna. Dua model yang dilatih pada korpus yang tumpang tindih masih dapat menghasilkan fakta yang berbeda, menginterpretasikan ambiguitas secara berbeda, atau gagal pada edge case yang berlawanan.
Pertanyaannya bukan apakah mereka tidak setuju. Mereka akan. Pertanyaannya adalah apa yang Anda lakukan ketika mereka tidak setuju.
Detektor Ketidaksetujuan Sederhana yang Benar-Benar Berfungsi
Berikut adalah sistem konkret dalam Python. Ia mengirim prompt yang sama ke beberapa model, mengekstrak output terstruktur, dan membandingkannya menggunakan aturan yang sadar domain daripada kesetaraan string yang naif.
import asyncio
from dataclasses import dataclass
from typing import Callable, List, Any
import json
@dataclass(frozen=True)
class VariantResult:
model: str
output: dict
raw: str
latency_ms: float
class DisagreementResolver:
def __init__(
self,
comparators: List[Callable[[Any, Any], bool]],
tiebreaker: Callable[[List[VariantResult]], VariantResult],
):
# Each comparator returns True if two outputs are "the same"
self.comparators = comparators
# Tiebreaker picks a winner when no clear majority exists
self.tiebreaker = tiebreaker
def cluster(self, results: List[VariantResult]) -> List[List[VariantResult]]:
"""Group results into equivalence classes."""
clusters: List[List[VariantResult]] = []
for r in results:
placed = False
for cluster in clusters:
# A result joins a cluster if it matches the cluster's first member
if all(c(r.output, cluster[0].output) for c in self.comparators):
cluster.append(r)
placed = True
break
if not placed:
clusters.append([r])
return clusters
def resolve(self, results: List[VariantResult]) -> tuple[VariantResult, List[VariantResult]]:
"""Returns (winner, dissenters)."""
clusters = self.cluster(results)
clusters.sort(key=len, reverse=True)
if len(clusters) == 1:
# Unanimous agreement. Pick the fastest as winner.
winner = min(clusters[0], key=lambda r: r.latency_ms)
return winner, []
if len(clusters[0]) > len(clusters[1]):
# Clear majority.
winner = min(clusters[0], key=lambda r: r.latency_ms)
dissenters = [r for c in clusters[1:] for r in c]
return winner, dissenters
# No clear majority. Invoke tiebreaker.
winner = self.tiebreaker([c[0] for c in clusters])
dissenters = [r for c in clusters for r in c if r.model != winner.model]
return winner, dissenters
Pilihan desain kuncinya adalah comparator. String equality tidak berguna untuk output LLM. Dua model mungkin mengembalikan {"category": "refund"} dan {"category": "Refund"} dan tidak setuju pada hal yang tidak penting.
def category_comparator(a: dict, b: dict) -> bool:
return a.get("category", "").lower() == b.get("category", "").lower()
def amount_comparator(a: dict, b: dict) -> bool:
# Floats from different models can differ slightly
return abs(a.get("amount", 0) - b.get("amount", 0)) < 0.01
def strict_json_comparator(a: dict, b: dict) -> bool:
return json.dumps(a, sort_keys=True) == json.dumps(b, sort_keys=True)
# Use them in order of domain importance
resolver = DisagreementResolver(
comparators=[category_comparator, amount_comparator],
tiebreaker=lambda candidates: min(candidates, key=lambda r: r.latency_ms),
)
Di Mana Ini Benar-Benar Menangkap Bug
Deteksi ketidaksetujuan bukan tentang menemukan kesalahan yang jelas. Kesalahan yang jelas sudah tertangkap oleh cara yang lebih sederhana.
Ini tentang menangkap kesalahan halus yang terlihat benar bagi model tunggal mana pun. Berikut adalah tiga pola yang paling sering kami lihat:
Confident hallucination divergence. Satu model menciptakan SKU produk. Model lain menciptakan SKU yang berbeda. Kedua output memvalidasi terhadap schema. Ketidaksetujuan adalah satu-satunya sinyal bahwa sesuatu telah dibuat-buat.
Boundary condition splits. Satu model mengklasifikasikan transaction $999,99 sebagai “high value.” Model lain mengklasifikasikannya sebagai “standard.” Ambang batasnya ambigu dalam prompt. Ketidaksetujuan memberi tahu Anda bahwa prompt-nya kurang spesifikasi.
Temporal drift. Satu model mengembalikan tanggal dalam ISO 8601. Model lain mengembalikan tanggal yang sama dalam timezone yang berbeda karena prompt mengatakan “today” dan model memiliki asumsi jam yang berbeda pada waktu inferensi.
Dalam setiap kasus, tidak ada output yang secara jelas rusak. Ketidaksetujuannya adalah laporan bug.
Model Biayanya Ternyata Cukup Bisa Diterima
Tiga panggilan API berbiaya tiga kali lipat dari satu. Itu adalah matematika yang tampak di permukaan. Matematika yang sebenarnya bergantung pada apa yang Anda lakukan dengan hasilnya.
Jika Anda menjalankan tiga model pada setiap request dan mengambil output mayoritas, Anda membuang uang. Sebagian besar tim tidak boleh melakukan ini.
Pola yang lebih cerdas adalah bertingkat:
- Fast path: Satu model murah menangani request.
- Audit path: Sampel request, atau semua request di atas ambang batas confidence, dikirim ke model kedua secara asinkron.
- Escalation path: Ketika audit path mendeteksi ketidaksetujuan, model ketiga memutuskan hasil seri. Ketidaksetujuannya dicatat untuk ditinjau oleh tim prompt engineering.
Ini berarti Anda membayar tiga kali lipat hanya untuk subset request yang benar-benar memerlukan pengawasan. Untuk banyak beban kerja, itu di bawah 5% dari traffic.
async def tiered_resolve(prompt: str, primary_model: str, audit_model: str) -> dict:
primary = await call_model(primary_model, prompt)
# Async audit: do not block the fast path
audit_task = asyncio.create_task(call_model(audit_model, prompt))
try:
audit = await asyncio.wait_for(audit_task, timeout=2.0)
except asyncio.TimeoutError:
# Audit lagged. Ship the primary result.
return primary.output
if outputs_agree(primary.output, audit.output):
return primary.output
# Disagreement detected. Escalate.
tiebreaker = await call_model("gpt-4o", prompt)
log_disagreement(primary, audit, tiebreaker)
return tiebreaker.output
Panggilan log_disagreement adalah tempat nilai jangka panjang berada. Setiap ketidaksetujuan adalah titik data yang memberi tahu Anda di mana prompt Anda ambigu, di mana schema Anda kurang terbatas, atau di mana contoh pelatihan Anda bertentangan.
Apa yang Tidak Diperbaiki Oleh Ini
N-version programming dengan LLM memiliki batasan nyata, dan berpura-pura sebaliknya membuatnya berbahaya.
Shared failure modes. Jika kedua model gagal karena prompt mengandung typo, keduanya akan gagal dengan cara yang sama. Model-model tersebut tidak independen seperti tiga program yang ditulis tangan. Mereka berbagi arsitektur, tujuan pelatihan, dan bagian besar dari internet.
Systematic bias. Jika prompt Anda secara implisit mengkodekan bias, beberapa model mungkin mereproduksinya secara konsisten. Deteksi ketidaksetujuan hanya menangkap divergensi, bukan kesalahan bersama.
Cost at scale. Bahkan tingkat audit 5% menjadi mahal jika volume Anda miliaran request per bulan. Ekonominya berfungsi untuk keputusan high-stakes, bukan untuk setiap autocomplete.
Latency on the escalation path. Ketika dua model tidak setuju dan Anda membutuhkan model ketiga, Anda telah menambahkan dua round trip ke critical path. Untuk kasus penggunaan real-time, Anda mungkin perlu mengirim hasil utama dan menyelesaikan ketidaksetujuan secara asinkron, menerima jendela singkat kemungkinan kesalahan.
Cara Memulai Tanpa Over-Engineering
Anda tidak memerlukan framework voting yang lengkap pada hari pertama. Mulailah dengan perbandingan pairwise pada prompt dengan taruhan tertinggi.
Pilih satu panggilan output terstruktur di mana jawaban yang salah benar-benar memakan biaya uang atau kepercayaan. Tambahkan panggilan model kedua dalam background job. Bandingkan output dengan comparator spesifik domain. Catat ketidaksetujuan. Tinjau log setiap minggu.
Setelah dua minggu, Anda akan tahu apakah prompt Anda solid atau bencana. Sebagian besar prompt lebih rapuh daripada yang dipikirkan penulisnya. Log ketidaksetujuan adalah pemeriksaan kejujuran.
Setelah Anda memiliki data, Anda dapat memutuskan apakah akan membangun resolver, menyesuaikan prompt, atau menerima bahwa tugasnya terlalu ambigu untuk penanganan otomatis sepenuhnya.
Kemenangan Sebenarnya Adalah Umpan Balik
Tujuan dari LLM n-version programming bukan untuk membangun voter yang sempurna. Tujuannya adalah menutup loop antara ketidaksetujuan produksi dan perbaikan prompt.
Setiap kali dua model tidak setuju, Anda telah menemukan tempat di mana spesifikasi Anda tidak lengkap. Perbaiki spec-nya, bukan hanya output-nya. Seiring waktu, tingkat ketidaksetujuan menurun. Ketika cukup rendah, Anda dapat mengurangi sampel audit atau menurunkan model tiebreaker.
Sistem menjadi lebih murah seiring meningkatnya kualitasnya. Itu adalah kebalikan dari sebagian besar mekanisme keselamatan, yang menjadi lebih mahal seiring meningkatnya skala.
Mulailah dengan satu prompt, dua model, dan satu baris log. Ketidaksetujuan akan memberi tahu Anda persis di mana harus mencari.