Automatize o básico: use o código para detectar erros simples.
Agora que você sabe quais falhas quer detectar com 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
Escolher um método de pontuação
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 > 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 do 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, já que 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.