Regelbasierte Bewertungen erstellen

Grundlagen automatisieren: Einfache Fehler mit Code abfangen

Nachdem Sie nun wissen, welche Fehler Sie mit regelbasierten Auswertungen erfassen möchten, ist es an der Zeit, die entsprechenden Auswertungsfunktionen zu implementieren:

  • evalDataFormat(): Überprüft, ob das Datenformat korrekt ist. Dazu gehören gültiges JSON, alle Schlüssel vorhanden, keine leeren Werte, Motto mit weniger als sechs Wörtern und Hexadezimalfarben.
  • evalContrastRatio(): Prüft, ob das Kontrastverhältnis zwischen Text und Hintergrundfarbe barrierefrei ist.

Regelbasierte Auswertungen implementieren

Die Bewertungskriterien sind binär. Ihre regelbasierten Auswertungsfunktionen sollten eine binäre Ausgabe erzeugen, z. B. das Label PASS oder FAIL.

  • Ausgabe der ThemeBuilder-App (vollständiges Designobjekt) → evalDataFormat() → PASS oder FAIL-Label. PASS, wenn das Datenformat alle Einschränkungen erfüllt. Andernfalls FAIL.
  • Ausgabe der ThemeBuilder App (Farbpalettenobjekt) → evalContrastRatio() → Label PASS oder FAIL . PASS, wenn das Verhältnis größer als 4,5 ist. Andernfalls FAIL.

Evals-Typ definieren

Der Messwert PASS oder FAIL ist ein boolescher Wert. Sie können ihn aber zur besseren Lesbarkeit auch als Stringlabel (Kategorie) implementieren.

Um es einfach zu halten, können Sie denselben TypeScript-Typ für Ihre regelbasierten und die LLM-Judge-Evaluierungen verwenden, die Sie später implementieren. Erstellen Sie einen Typ EvalResult, der eine binäre Kategorie EvalLabel umschließt, und ein Feld rationale, in dem das Bewertungsmodell seine Bewertung erläutert.

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

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

Bewerter implementieren

Zod ist ein hervorragendes Tool für die Schemavalidierung, da es sowohl die JSON-Struktur als auch benutzerdefinierte Regeln verarbeitet. Es ist deklarativ, was den Validierungscode lesbar macht. Definieren Sie detaillierte Fehlerberichte mit spezifischen Pfaden und Gründen für Fehler, um die Fehlerbehebung zu erleichtern.

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)
});

Kontrastverhältnis

Lagern Sie die Domainlogik, z. B. die Berechnungen des Kontrastverhältnisses, in separate Hilfsfunktionen aus.

/*
 * 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?)." };
  }
}

Evaluator-Code für evalDataFormat() und evalContrastRatio()

Regelbasierte Auswertungen testen

Regelbasierte Auswertungen sind deterministisch. Sie können also klassische Einheitentests implementieren, um ihr Verhalten zu prüfen. Erstellen Sie Tests, um verschiedene Ausgaben durch die Evaluatoren zu leiten und zu prüfen, ob sie das erwartete Label PASS oder FAIL zurückgeben.

Wenn in einem Testfall erwartet wird, dass der Evaluator FAIL zurückgibt, und dies auch geschieht, gibt der Test PASS aus, da sich der Evaluator wie vorgesehen verhalten hat.

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;
 // ...
});

Jetzt ausprobieren