Automatiser les bases : utiliser du code pour détecter les erreurs simples
Maintenant que vous savez quelles défaillances vous souhaitez détecter avec les évaluations basées sur des règles, il est temps d'implémenter les fonctions d'évaluateur correspondantes :
evalDataFormat(): vérifie que le format des données est correct. Cela inclut un format JSON valide, toutes les clés présentes, aucune valeur vide, un slogan de moins de six mots et des couleurs hexadécimales.evalContrastRatio(): vérifie que le rapport de contraste entre le texte et la couleur d'arrière-plan est accessible.
Implémenter des évaluations basées sur des règles
Choisir une méthode de notation
Les critères d'évaluation sont binaires. Vos fonctions d'évaluation basées sur des règles doivent produire une sortie binaire, telle qu'un libellé PASS ou FAIL.
- Sortie de l'application ThemeBuilder (objet de thème complet) →
evalDataFormat()→ libelléPASSouFAIL.PASSsi le format des données respecte toutes les contraintes.FAILdans le cas contraire. - Sortie de l'application ThemeBuilder (objet de palette de couleurs) →
evalContrastRatio()→ libelléPASSouFAIL.PASSsi le rapport est supérieur à 4,5.FAILdans le cas contraire.
Définir un type d'évaluations
La métrique PASS ou FAIL est un booléen, mais vous pouvez choisir de l'implémenter en tant que libellé de chaîne (catégorie) pour faciliter la lecture.
Pour plus de simplicité, vous pouvez utiliser le même type TypeScript pour vos évaluations basées sur des règles et pour les évaluations du juge LLM que vous implémenterez ultérieurement. Créez un type EvalResult qui encapsule une catégorie binaire EvalLabel et un champ rationale pour que le modèle de juge explique sa note.
enum EvalLabel {
PASS = "PASS",
FAIL = "FAIL"
}
interface EvalResult {
label: EvalLabel;
rationale?: string;
}
Implémenter des évaluateurs
Zod est un excellent outil de validation de schéma, car il gère à la fois la structure JSON et les règles personnalisées. Il est déclaratif, ce qui rend le code de validation lisible. Définissez des rapports d'erreur détaillés avec des chemins et des raisons spécifiques pour les échecs, afin de faciliter le dépannage.
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)
});
Rapport de contraste
Conservez la logique de domaine, telle que les calculs du rapport de contraste, dans des fonctions utilitaires distinctes.
/*
* 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?)." };
}
}
Consultez notre code d'évaluateur
pour evalDataFormat() et evalContrastRatio().
Tester les évaluations basées sur des règles
Les évaluations basées sur des règles sont déterministes. Vous pouvez donc implémenter des tests unitaires classiques pour vérifier leur comportement. Créez vos tests pour exécuter différentes sorties via les évaluateurs et affirmer s'ils renvoient le libellé PASS ou FAIL attendu.
Si un cas de test attend que l'évaluateur renvoie un FAIL et qu'il le fait, le test génère un PASS car l'évaluateur s'est comporté comme prévu.
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;
// ...
});