ds-eval

Может ли агент пользоваться дизайн-системой, которую он не придумал сам? ds-eval прогоняет одни и те же UI-задачи через разные модели и оценивает собранный интерфейс.

Пример · фикстуры, не живой вызов APIОтвет со системой против наивного · smoke10 прогонов · $0 · офлайн

Фикстура87.8

Наивный59.3

ОсьФикстураНаивный
DS876
Функции7070
A11y10078
Код9899
Сборка100100

DS −81.3, когда ответ игнорирует дизайн-систему. 10 регрессий.

Зачем

Агент собирает страницу настроек за секунды. Гораздо хуже он пользуется уже существующей системой: рисует свой Button, хардкодит #8B5CF6, пропускает токены и игнорирует правила композиции.

ds-eval превращает это в измеримый вопрос. Одни и те же задачи, одна система, разные модели или промпты — и счёт, который можно сравнить и прогнать на регрессию.

  • Сравнить моделиClaude, Codex, Gemini, Grok или локальная модель — один набор, один отчёт
  • Ловить регрессии промптаПромпт v12 против v13: что выросло, что сломалось, какие кейсы перевернулись
  • Своя системаТокены, React-компоненты, Storybook, документация и UX-паттерны как источник правды

Как устроено

Каждый кейс стартует из изолированного приложения. Модель получает дизайн-систему и задачу и должна собрать рабочий интерфейс. Дальше harness собирает проект, снимает скриншот и ставит оценку.

Design System
↓
Eval Task
↓
AI Coding Agent
↓
Generated UI
↓
────────────────────────
AST / DS Compliance
Runtime + Playwright
Accessibility (axe)
Visual Judge
UX Judge
Code Quality
────────────────────────
↓
Benchmark + Regression

Запуск

Движок на Python. Фикстуры работают офлайн. Живые модели читают ключи из окружения, не из конфига.

cd ds-eval
python3 -m venv .venv
source .venv/bin/activate
pip install -e .

ds-eval run --model fixture --suite smoke
ds-eval run --model fixture-naive --suite smoke
ds-eval compare RUN_FIXTURE RUN_NAIVE
ds-eval dashboard --port 8001

С ключами API:

export ANTHROPIC_API_KEY=…
export OPENAI_API_KEY=…
ds-eval run --model claude --suite smoke
ds-eval run --model codex --suite smoke

CLI

ds-eval init                         scaffold a DS manifest
ds-eval list                         print the dataset
ds-eval run --model claude --limit 20
ds-eval compare RUN_1 RUN_2
ds-eval report ./runs/run-id

report пишет report.json, report.html и summary.md из артефактов прогона. Таблица ниже — офлайн smoke на фикстурах, не Claude и не Codex.

МодельОбщийDSA11yКод
fixture87.88710098
fixture-naive59.35.77899

Датасет

100 кейсов, написанных как продуктовые задачи, а не сто копий «сделай кнопку». Лёгкие, средние и сложные, плюс кейсы, которые специально спорят с правилами системы.

КатегорияСколькоЧто проверяет
Компоненты25Верное использование Button, Select, Tabs, Dialog, Toast…
Паттерны20Подтверждение удаления, пустые состояния, массовые действия, несколько шагов
Страницы25Настройки, оплата, права, профиль сотрудника
Доступность10Клавиатура, подписи, ARIA, контраст, фокус
Крайние случаи10Длинные строки, пустые данные, 100+ строк, мобильный, i18n
Намеренные ловушки10Промпт подталкивает к сырому цвету или лишним действиям, которые система запрещает

Кейсы — это YAML со схемой Pydantic: обязательные компоненты, запрещённые паттерны, проверки, рубрика, вьюпорт.

id: component-select-001
category: component
prompt: |
  Create a country selector containing 10 countries.
  It should have a label, helper text and error state.
requirements:
  required_components: [Select]
  forbidden: [native-select]
  accessibility:
    keyboard_navigation: true
    label_required: true

Проверки

Общий счёт — не одно мнение модели. Проверки по правилам и судьи на LLM на дашборде лежат отдельно.

  • Статика / AST правилоИмпорты из системы против сырого <button>, захардкоженные цвета, токены отступов, дубли компонентов
  • Соответствие компонентам правилоОбязательные и запрещённые компоненты, варианты, пропсы, правила композиции
  • Рантайм правилоСборка, Playwright, ошибки консоли, диалоги, табы, отправка формы
  • Доступность правилоaxe-core плюс подписи, клавиатура, фокус, семантика, ARIA
  • Визуал LLMРаскладка, иерархия, отступы, выравнивание, цельность, аккуратность — каждый критерий от 0 до 5 с объяснением
  • Продуктовый UX LLMЗадача сделана, паттерн верный, ошибки предотвращаются, есть обратная связь, ясна структура
  • Качество кода LLMСтруктура, дубли, типы, практики React, лишняя абстракция

Оценка

Веса настраиваются. Смесь по умолчанию для общего счёта:

ОсьВесИсточник
Следование системе25%AST и проверка компонентов
Функции20%Взаимодействия в Playwright
Визуал15%Vision-модель, рубрика 0–5
Продуктовый UX15%UX-судья
Доступность10%axe и проверки
Качество кода10%Статика и LLM
Надёжность сборки5%Установка, сборка, запуск

Каждый вызов генерации и судьи сохраняет входные и выходные токены, оценку стоимости и задержку. Дашборд показывает стоимость бенчмарка и цену одного кейса.

Регрессия

Сравнивает два прогона — версии модели или версии промпта — и показывает, что сдвинулось.

ds-eval compare runs/sonnet-v1 runs/sonnet-v2
overall +2.7
DS compliance +4.1
Visual +1.2
Accessibility −3.4 ⚠

7 improved · 3 regressions · 90 unchanged

У каждого перевернувшегося кейса остаются скриншоты до и после, объяснения проверок и сгенерированный код.

Дашборд

Локальный интерфейс исследователя поверх истории прогонов в SQLite. Обзор, модели, прогоны, кейсы и сравнение двух ответов на одну задачу.

  • ОбзорОбщий счёт, категории, доля прошедших, стоимость, токены, задержка
  • КейсыФильтр по модели, категории, сложности, прошёл или нет, проверке
  • Карточка кейсаПромпт, собранный интерфейс, скриншот, исходник, рубрика, логи, стоимость

Своя система

В репозитории есть демо-система (Button, Input, Select, Dialog, Table, токены и несколько UX-паттернов), чтобы проект запускался сразу после клона. Своя система подключается манифестом:

name: Demo DS
version: 1.0
components:
  path: ./src/components
tokens:
  path: ./src/tokens
documentation:
  path: ./docs
storybook:
  url: http://localhost:6006
ds-eval init ./my-design-system
ds-eval run --system ./my-design-system --suite smoke

Пределы

Судьи на LLM — не истина. Визуал и UX субъективны, даже с рубрикой и несколькими судьями. Надёжное ядро — проверки по правилам: импорты, токены, axe, Playwright. Остальное — сигнал, не приговор.

Высокий балл значит, что модель следовала этой системе на этом наборе. Он не значит, что экран готов в продакшен вашего продукта.

Опубликованный прогон

Таблица из 100 кейсов выше — устройство датасета, а не размер этого прогона. Опубликованный smoke лежит в ds-eval/runs/fixture-good: модель fixture, 10 кейсов, общий балл 87.8, стоимость $0. Это не вызов Claude или Codex. Блок compare в разделе «Регрессия» показывает форму отчёта, не этот прогон.

КейсКатегорияОбщийDSA11yКод
a11y-labels-001accessibility88.387.5100.0100.0
adversarial-actions-001adversarial88.387.5100.0100.0
adversarial-color-001adversarial88.387.5100.0100.0
component-button-001component88.387.5100.0100.0
component-input-001component88.387.5100.0100.0
component-select-001component86.787.5100.090.0
edge-long-001edge-cases85.282.5100.090.0
page-settings-001pages88.387.5100.0100.0
pattern-confirm-001patterns88.387.5100.0100.0
pattern-empty-001patterns88.387.5100.0100.0

Один кейс целиком: component-select-001. Задача — селект страны с подписью, подсказкой и ошибкой. Ответ фикстуры — source/Task.tsx.

import { Select } from "@ds";

const COUNTRIES = ["Austria","Belgium","Canada","Denmark","Estonia","Finland","Germany","Hungary","Ireland","Japan"];

export function Task() {
  return (
    <Select label="Country" helper="Used for billing" error="Select a country to continue" defaultValue="">
      <option value="">Choose one</option>
      {COUNTRIES.map((name) => (
        <option key={name} value={name}>{name}</option>
      ))}
    </Select>
  );
}

Оси этого кейса. Визуальный и UX-судьи выключены: ноль в записи означает пропуск, не оценку экрана. Их вес перераспределяется на остальные оси, поэтому общий балл не равен нулю.

ОсьБалл
DS87.5
Функции70.0
Визуал0.0пропуск, вес ушёл на остальные оси
UX0.0пропуск, вес ушёл на остальные оси
A11y100.0
Код90.0
Сборка100.0
Общий86.7

Цепочка

ds-eval ставит счёт после того, как у модели есть пакет контекста и, если нужно, проход починки:

ds-context prepares the pack
model writes UI
ui-repair patches violations
ds-eval scores the result ← you are here
prompt-regress diffs the next prompt

Код