Automatize o básico: use o código para detectar erros simples.
Agora que você sabe quais falhas quer detectar com as avaliações baseadas em regras, é hora de implementar as funções de avaliador correspondentes:
evalDataFormat(): verifica se o formato de dados está correto. Isso inclui JSON válido, todas as chaves presentes, sem valores vazios, o lema tem menos de seis palavras e cores hexadecimais.evalContrastRatio(): verifica se a taxa de contraste de cor do texto para o plano de fundo é acessível.
Implementar avaliações baseadas em regras
Os critérios de avaliação são binários. As funções de avaliação baseadas em regras precisam produzir uma saída binária, como um rótulo PASS ou FAIL.
- Saída do app ThemeBuilder (objeto de tema completo) →
evalDataFormat()→ rótuloPASSouFAIL.PASSse o formato de dados atender a todas as restrições.FAILcaso contrário. - Saída do app ThemeBuilder (objeto de paleta de cores) →
evalContrastRatio()→ rótuloPASSouFAIL.PASSse a proporção for maior que 4,5.FAILcaso contrário.
Definir um tipo de avaliações
A métrica PASS ou FAIL é booleana, mas você pode implementá-la como um rótulo de string (categoria) para facilitar a leitura.
Para manter a simplicidade, você pode usar o mesmo tipo TypeScript para as avaliações baseadas em regras e as avaliações de juiz de LLM que serão implementadas mais tarde. Crie um tipo EvalResult que encapsula uma categoria EvalLabel binária e um campo rationale para o modelo de juiz explicar a classificação.
enum EvalLabel {
PASS = "PASS",
FAIL = "FAIL"
}
interface EvalResult {
label: EvalLabel;
rationale?: string;
}
Implementar avaliadores
Zod é uma ótima ferramenta para validação de esquema, porque processa a estrutura JSON e as regras personalizadas. É declarativo, o que torna o código de validação legível. Defina relatórios de erros detalhados com caminhos e motivos específicos para falhas, para facilitar a solução de problemas.
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)
});
Taxa de contraste
Mantenha a lógica de domínio, como os cálculos de taxa de contraste, em funções de utilitário separadas.
/*
* 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?)." };
}
}
Confira nosso código de avaliador
para evalDataFormat() e evalContrastRatio().
Testar avaliações baseadas em regras
As avaliações baseadas em regras são determinísticas. Portanto, você pode implementar testes de unidade clássicos para verificar o comportamento delas. Crie testes para executar várias saídas nos avaliadores e afirmar se eles retornam o rótulo PASS ou FAIL esperado.
Se um caso de teste espera que o avaliador retorne um FAIL e ele o faça, o teste gera um PASS, porque o avaliador se comportou conforme o esperado.
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;
// ...
});
Testar
- Clone o projeto evals-course.
- Configurar.
- Execute os testes do avaliador baseado em regras.