Tantangan Akurasi Semantic Search di Sisi Klien

Menjalankan inferensi model embedding langsung di peramban menggunakan WebAssembly (Wasm) atau WebGPU via ONNX Runtime Web memangkas latensi jaringan dan biaya infrastruktur server. Kendati demikian, deployment model di sisi klien umumnya memerlukan kuantisasi bobot (misalnya dari FP32 ke INT8 atau Q4) untuk meminimalkan ukuran berkas unduhan. Proses kompresi ini menyebabkan hilangnya presisi numerik secara halus.

Dampaknya adalah pergeseran representasi vektor (drift embedding) yang menurunkan metrik relevansi dokumen, memicu ranking flakiness, serta mengubah urutan dokumen teratas. Tanpa harness pengujian regresi deterministik, penurunan kualitas pencarian sering kali lolos ke tahap produksi tanpa terdeteksi oleh unit test konvensional.

Strategi Evaluasi: Pasangan Golden Dataset dan Kueri Sintetis

Evaluasi model berbasis embedding memerlukan dataset evaluasi beranotasi. Untuk menjaga skalabilitas pengujian, gunakan golden dataset yang memetakan kueri sintetis (variasi sinonim, salah ketik realistis, parafrase) ke ID dokumen rujukan (ground truth).

Dua metrik utama yang digunakan untuk mendeteksi degradasi akurasi:

  • Mean Reciprocal Rank (MRR): Mengukur posisi rank pertama dokumen relevan di daftar hasil pencarian. Nilai berkisar antara 0 hingga 1; semakin tinggi posisinya, semakin mendekati 1.
  • Cosine Similarity Threshold: Memastikan batas toleransi deviasi vektor antara model FP32 baseline dan model kuantisasi lokal tidak melewati ambang batas tertentu (misalnya selisih skor ≤ 0.05).

Format Dataset Evaluasi

Simpan dataset evaluasi dalam berkas JSON sederhana tanpa dependensi eksternal yang rumit:

[
  {
    "query": "cara deploy worker laravel",
    "expected_id": "doc-402",
    "min_mrr": 1.0
  },
  {
    "query": "background task queue redis supervisor",
    "expected_id": "doc-402",
    "min_mrr": 0.5
  }
]

Implementasi Test Harness Menggunakan Playwright

Pengujian regresi akurasi peramban membutuhkan eksekusi runtime sebenarnya (dukungan SIMD Wasm dan alokasi memori Float32Array). Mocking runtime ONNX meniadakan tujuan pengujian karena degradasi justru bersumber dari mesin inferensi peramban.

Gunakan Playwright headless untuk menjalankan engine pencarian di lingkungan Chromium asli.

Struktur File Test Playwright

import { test, expect } from '@playwright/test';
import testDataset from './fixtures/semantic-golden-dataset.json';

test.describe('Uji Regresi Akurasi Semantic Search Browser via Synthetic Queries', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/search-sandbox.html');
    // Pastikan model ONNX Web dan indeks lokal selesai dimuat ke memori
    await page.waitForFunction(() => window.searchEngineReady === true, {
      timeout: 30000,
    });
  });

  test('Evaluasi batas bawah MRR pada golden dataset', async ({ page }) => {
    const results = await page.evaluate(async (dataset) => {
      let reciprocalRankSum = 0;
      const failures = [];

      for (const item of dataset) {
        // Jalankan eksekusi search engine lokal di browser
        const hits = await window.inBrowserSearch(item.query, 5);
        const rankIndex = hits.findIndex((hit) => hit.id === item.expected_id);

        let reciprocalRank = 0;
        if (rankIndex !== -1) {
          reciprocalRank = 1 / (rankIndex + 1);
        }

        reciprocalRankSum += reciprocalRank;

        if (reciprocalRank < item.min_mrr) {
          failures.push({
            query: item.query,
            expected: item.expected_id,
            actualRank: rankIndex !== -1 ? rankIndex + 1 : 'not_found',
            hits: hits.map((h) => h.id),
          });
        }
      }

      const mrr = reciprocalRankSum / dataset.length;
      return { mrr, failures };
    }, testDataset);

    // Target MRR minimum seluruh suite pengujian tidak boleh turun di bawah 0.8
    expect(results.failures, `Kegagalan relevansi: ${JSON.stringify(results.failures, null, 2)}`).toHaveLength(0);
    expect(results.mrr).toBeGreaterThanOrEqual(0.8);
  });
});

Memverifikasi Konsistensi Cosine Similarity

Selain ranking MRR, validasi perhitungan dot-product/cosine similarity secara langsung untuk mencegah numeric overflow atau silent NaN pada kernel SIMD Wasm. Pasang fungsi assert di layer peramban:

function assertCosineStability(vectorA, vectorB, maxAllowedDifference = 0.05) {
  let dotProduct = 0;
  let normA = 0;
  let normB = 0;

  for (let i = 0; i < vectorA.length; i++) {
    dotProduct += vectorA[i] * vectorB[i];
    normA += vectorA[i] * vectorA[i];
    normB += vectorB[i] * vectorB[i];
  }

  const similarity = dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
  if (isNaN(similarity)) {
    throw new Error('Inferensi menghasilkan NaN pada kalkulasi dot-product.');
  }
  return similarity;
}

Integrasi pada CI Pipeline dan Trade-off

Model ONNX (bahkan setelah kuantisasi INT8) berukuran puluhan megabyte. Mengunduh model pada setiap build CI memperlambat pipeline pengujian. Terapkan strategi berikut:

  • Asset Caching: Simpan checkpoint model ONNX dan berkas Wasm di layer cache CI (seperti GitHub Actions Cache).
  • Wasm Memory Limits: Eksekusi peramban headless pada container CI memerlukan alokasi flag memori peramban --js-flags="--max-old-space-size=4096" untuk mencegah crash Out-Of-Memory saat memuat bobot model besar.
  • Bypass Over-Mocking: Jangan memalsukan perhitungan embedding via stub JavaScript. Kesalahan akurasi kuantisasi hanya terlihat saat tensor numerik melewati runtime asli Wasm.

Skema uji ini memastikan bahwa setiap iterasi perubahan model atau pembaruan kamus tokenizer tidak menurunkan fungsionalitas pencarian semantik sebelum artefak didistribusikan ke end-user.