Разрабатывайте оценки на основе правил.

Автоматизируйте основные процессы: используйте код для перехвата простых ошибок.

Теперь, когда вы знаете, какие ошибки вы хотите выявлять с помощью оценок на основе правил, пришло время реализовать соответствующие функции оценки:

  • evalDataFormat() : Проверяет правильность формата данных. Это включает в себя корректный JSON, наличие всех ключей, отсутствие пустых значений, девиз, состоящий менее чем из шести слов, и шестнадцатеричные значения цветов.
  • evalContrastRatio() : Проверяет, доступен ли коэффициент контрастности цвета текста и фона.

Внедрить оценки на основе правил.

Выберите метод оценки

Критерии оценки являются бинарными. Ваши функции оценки, основанные на правилах, должны выдавать бинарный результат, например, метку PASS или FAIL .

  • Результат выполнения приложения ThemeBuilder (полный объект темы) → evalDataFormat() → метка PASS или FAIL . PASS если формат данных соответствует всем ограничениям. FAIL в противном случае.
  • Результат работы приложения ThemeBuilder (объект цветовой палитры) → evalContrastRatio() → метка PASS или FAIL . PASS если коэффициент > 4,5. FAIL в противном случае.

Определите тип evals

Показатель PASS или FAIL является логическим значением, но для удобства чтения его можно представить в виде строковой метки (категории).

Чтобы упростить задачу, вы можете использовать один и тот же тип TypeScript как для оценок, основанных на правилах, так и для оценок, которые вы реализуете позже в рамках LLM-системы. Создайте тип 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() .

Тестирование на основе правил оценки

Оценки, основанные на правилах, детерминированы, поэтому вы можете реализовать классические модульные тесты для проверки их поведения. Создайте тесты, которые будут пропускать различные выходные данные через оценщики и проверять, возвращают ли они ожидаемую метку PASS или 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;
 // ...
});

Попробуйте!