Бенчмарк
ds-eval
Может ли агент пользоваться дизайн-системой, которую он не придумал сам? ds-eval прогоняет одни и те же UI-задачи через разные модели и оценивает собранный интерфейс.
Фикстура87.8
Наивный59.3
| Ось | Фикстура | Наивный |
|---|---|---|
| DS | 87 | 6 |
| Функции | 70 | 70 |
| A11y | 100 | 78 |
| Код | 98 | 99 |
| Сборка | 100 | 100 |
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 smokeCLI
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.
| Модель | Общий | DS | A11y | Код |
|---|---|---|---|---|
| fixture | 87.8 | 87 | 100 | 98 |
| fixture-naive | 59.3 | 5.7 | 78 | 99 |
Датасет
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 |
| Продуктовый UX | 15% | 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 в разделе «Регрессия» показывает форму отчёта, не этот прогон.
| Кейс | Категория | Общий | DS | A11y | Код |
|---|---|---|---|---|---|
| a11y-labels-001 | accessibility | 88.3 | 87.5 | 100.0 | 100.0 |
| adversarial-actions-001 | adversarial | 88.3 | 87.5 | 100.0 | 100.0 |
| adversarial-color-001 | adversarial | 88.3 | 87.5 | 100.0 | 100.0 |
| component-button-001 | component | 88.3 | 87.5 | 100.0 | 100.0 |
| component-input-001 | component | 88.3 | 87.5 | 100.0 | 100.0 |
| component-select-001 | component | 86.7 | 87.5 | 100.0 | 90.0 |
| edge-long-001 | edge-cases | 85.2 | 82.5 | 100.0 | 90.0 |
| page-settings-001 | pages | 88.3 | 87.5 | 100.0 | 100.0 |
| pattern-confirm-001 | patterns | 88.3 | 87.5 | 100.0 | 100.0 |
| pattern-empty-001 | patterns | 88.3 | 87.5 | 100.0 | 100.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-судьи выключены: ноль в записи означает пропуск, не оценку экрана. Их вес перераспределяется на остальные оси, поэтому общий балл не равен нулю.
| Ось | Балл | |
|---|---|---|
| DS | 87.5 | |
| Функции | 70.0 | |
| Визуал | 0.0 | пропуск, вес ушёл на остальные оси |
| UX | 0.0 | пропуск, вес ушёл на остальные оси |
| A11y | 100.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