构建基于规则的评估

自动执行基本操作:使用代码捕获简单错误。

现在,您已经知道要使用基于规则的评估来捕获哪些失败,接下来就该实现相应的评估器函数了:

  • evalDataFormat():检查数据格式是否正确。这包括有效的 JSON、所有键都存在、没有空值、格言不超过 6 个字、十六进制颜色。
  • evalContrastRatio():检查文本与背景颜色对比度是否符合无障碍标准。

实现基于规则的评估

选择评分方法

评估标准为二元。基于规则的评估函数应生成二元输出,例如 PASSFAIL 标签。

  • ThemeBuilder 应用输出(完整的主题对象)→ evalDataFormat()PASSFAIL 标签。如果数据格式满足所有限制条件,则为 PASS。否则为 FAIL
  • ThemeBuilder 应用输出(调色板对象)→ evalContrastRatio()PASSFAIL 标签。如果比率 > 4.5,则为 PASS。否则为 FAIL

定义评估类型

PASSFAIL 指标是一个布尔值,但您可以选择将其实现为字符串标签(类别),以提高可读性。

为了保持精简,您可以为基于规则的评估和稍后实现的 LLM 评判评估使用相同的 TypeScript 类型。创建一个 EvalResult 类型,用于封装二元 EvalLabel 类别,并创建一个 rationale 字段,供评判模型解释其评分。

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

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

实现评估器

Zod 是一款出色的架构验证工具,因为它既能处理 JSON 结构,也能处理自定义规则。它是声明式的,因此验证代码清晰易读。定义包含特定路径和失败原因的详细错误报告,以便更轻松地进行问题排查。

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

对比度

将网域逻辑(例如对比度计算)保留在单独的实用程序函数中。

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

不妨看看我们针对 evalDataFormat()evalContrastRatio()评估器代码

测试基于规则的评估

基于规则的评估是确定性的,因此您可以实现经典的单元测试来检查其行为。构建测试,以通过评估器运行各种输出,并断言它们是否返回了您预期的 PASSFAIL 标签。

如果测试用例预期评估器返回 FAIL,而评估器确实返回了 FAIL,则测试会输出 PASS,因为评估器的行为符合预期。

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

试用一下