Membangun evaluasi berbasis aturan

Mengotomatiskan hal-hal mendasar: gunakan kode untuk mendeteksi error sederhana.

Setelah Anda mengetahui kegagalan mana yang ingin Anda deteksi dengan evaluasi berbasis aturan, saatnya menerapkan fungsi evaluator yang sesuai:

  • evalDataFormat(): Memeriksa apakah format data sudah benar. Hal ini mencakup JSON yang valid, semua kunci ada, tidak ada nilai kosong, motto kurang dari enam kata, warna heksadesimal.
  • evalContrastRatio(): Memeriksa bahwa rasio kontras warna teks dan latar belakang dapat diakses.

Menerapkan evaluasi berbasis aturan

Memilih metode pemberian skor

Kriteria evaluasinya bersifat biner. Fungsi evaluasi berbasis aturan Anda harus menghasilkan output biner, seperti label PASS atau FAIL.

  • Output aplikasi ThemeBuilder (objek tema lengkap) → evalDataFormat()PASS atau label FAIL. PASS jika format data memenuhi semua batasan. FAIL jika tidak.
  • Output aplikasi ThemeBuilder (objek palet warna) → evalContrastRatio()PASS atau label FAIL .PASS jika rasio > 4,5. FAIL jika tidak.

Menentukan jenis evaluasi

Metrik PASS atau FAIL adalah boolean, tetapi Anda dapat memilih untuk menerapkannya sebagai label string (kategori) agar mudah dibaca.

Agar tetap sederhana, Anda dapat menggunakan jenis TypeScript yang sama untuk evaluasi berbasis aturan dan evaluasi hakim LLM yang akan Anda terapkan nanti. Buat jenis EvalResult yang membungkus kategori biner EvalLabel, dan kolom rationale untuk model hakim menjelaskan ratingnya.

enum EvalLabel {
    PASS = "PASS",
    FAIL = "FAIL"
}

interface EvalResult {
    label: EvalLabel;
    rationale?: string;
}

Menerapkan evaluator

Zod adalah alat yang sangat baik untuk validasi skema, karena menangani struktur JSON dan aturan kustom. Validasi ini bersifat deklaratif, sehingga kode validasi mudah dibaca. Tentukan laporan error mendetail dengan jalur dan alasan spesifik untuk kegagalan, agar pemecahan masalah lebih mudah.

import { z } from 'zod';
import { MAX_WORD_COUNT } from './app.config';

const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;

// Reusable schema for hex colors
const HexColor = z.string().regex(hexColorRegex, { message: "Invalid hex color code" });

// zod schema definition for AppOutput
export const AppOutputSchema = z.object({
  motto: z.string().min(1, { message: "Motto is missing or empty" }).refine((val) => {
    const words = val.replace(/[^\w\s]|_/g, "").trim();
    const count = words ? words.split(/\s+/).length : 0;
    return count > 0 && count <= MAX_WORD_COUNT;
  }, { message: `Motto must be between 1 and ${MAX_WORD_COUNT} words` }),
  colorPalette: z.object({
    textColor: HexColor,
    backgroundColor: HexColor,
    primary: HexColor,
    secondary: HexColor
  }).catchall(HexColor)
});

Rasio kontras

Simpan logika domain, seperti penghitungan rasio kontras, dalam fungsi utilitas terpisah.

/*
 * Input: ColorPalette {"textColor":"#333333","backgroundColor":"#000000", ...}
 * Output: EvalResult {"status":"FAIL","rationale":"Contrast ratio is 1.66:1 (must be >= 4.5:1)."}
 * minContrastRatio is an app config variable, MIN_CONTRAST_RATIO = 4.5
*/
export function evalContrastRatio(colorPalette: ColorPalette, minContrastRatio: number): EvalResult {
  if (!colorPalette || !colorPalette.textColor || !colorPalette.backgroundColor) {
    return { status: EvalLabel.FAIL, rationale: "Missing textColor or backgroundColor." };
  }
  try {
    const ratio = getContrastRatio(colorPalette.textColor, colorPalette.backgroundColor);
    const rationale = `Contrast ratio is ${ratio.toFixed(2)}:1 (must be >= ${minContrastRatio}:1).`;
    if (ratio < minContrastRatio) {
      return { status: EvalLabel.FAIL, rationale };
    }
    return { status: EvalLabel.PASS, rationale };
  } catch (e) {
    return { status: EvalLabel.FAIL, rationale: "Could not calculate contrast ratio (invalid hex?)." };
  }
}

Lihat kode evaluator kami untuk evalDataFormat() dan evalContrastRatio().

Menguji evaluasi berbasis aturan

Evaluasi berbasis aturan bersifat deterministik, sehingga Anda dapat menerapkan pengujian unit klasik untuk memeriksa perilakunya. Buat pengujian untuk menjalankan berbagai output melalui evaluator dan menegaskan apakah output tersebut menampilkan label PASS atau FAIL yang Anda harapkan.

Jika kasus pengujian mengharapkan evaluator menampilkan FAIL dan evaluator melakukannya, maka pengujian akan menampilkan PASS karena evaluator berperilaku seperti yang diharapkan.

import { MIN_CONTRAST_RATIO } from '../src/app.config'; // 4.5

const testCases = [
  {
  // ...
      appOutput: {
        motto: "Test motto",
        colorPalette: {
          textColor: "#333333",
          backgroundColor: "#000000",
          primary: "#FF0000",
          secondary: "#333333"
        }
      },
    expected: {
      // Dark grey on black (low contrast): FAIL
      contrast: EvalLabel.FAIL
    }
  }
  // ... more test cases
];

testCases.forEach((testCase) => {
  const result = evalContrastRatio(
    testCase.appOutput.colorPalette as any, MIN_CONTRAST_RATIO
  );
  const actualEvalLabel = result.label;
  const expectedEvalLabel = testCase.expected.contrast;
  const isSuccess = actualEvalLabel === expectedEvalLabel;
 // ...
});

Coba