En
Написать
‹ Назад

ds-eval

Бенчмаркopen source

Агент, который пишет интерфейс, часто рисует кнопку и цвет сам, хотя в дизайн-системе уже есть готовый компонент. ds-eval нужен, чтобы это поймать: одна задача, два ответа и проверка, взял ли ответ компонент системы. Ниже — один опубликованный прогон и то, каким его цифрам можно верить.

Задача

Кейс component-button-001 просит панель с Save, Cancel и Delete. Ответ, который знает систему, берёт компонент и его варианты:

import { Button } from "@ds";

export function Task() {
  return (
    <div className="ds-row">
      <Button>Save</Button>
      <Button variant="secondary">Cancel</Button>
      <Button variant="danger">Delete</Button>
    </div>
  );
}

Наивный ответ оставляет те же три надписи и рисует контролы сам:

export function Task() {
  return (
    <div>
      <button style={{ background: "#3B82F6", color: "#fff", padding: "12px 16px" }}>Save</button>
      <button style={{ background: "#6B7280", color: "#fff", padding: "12px 16px" }}>Cancel</button>
      <button style={{ background: "#EF4444", color: "#fff", padding: "12px 16px" }}>Delete</button>
    </div>
  );
}

Оба файла — кнопки. На скриншоте разница легко теряется. Для системы это разные интерфейсы. Кейс adversarial-color-001 просит кнопку цвета #8B5CF6 и при этом запрещает сырой цвет: правдоподобие здесь и есть нарушение.

ds-eval нужен, чтобы поймать такое нарушение и сравнить два ответа, когда задача, система и измеряемое свойство одни и те же. Это не заявление, что команда стала реже обходить систему. Такого наблюдения нет.

Что видно в опубликованном прогоне

Smoke — 10 кейсов, стоимость $0. Модели в записи — fixture и fixture-naive: заготовленные файлы, не вызов Claude или Codex. Задачи и демо-система не меняются. Меняется пакет ответа.

Архивный fixture-прогон · 10 заготовленных ответов · $0 · не живая модельСоблюдение дизайн-системы, чтение исходника (до фикса парсера)Значения ниже записаны старой версией проверяльщика; в текущем коде разбор импорта исправлен, но архивный JSON не пересчитывался

С системой87

Свои контролы5.7

ОсьС системойСвои контролыЧто важно знать
DS875.7В архивном прогоне импорт Button распознан некорректно
Доступность10078Текстовая эвристика, не аудит через axe
Функциине измерялосьне измерялосьВ JSON подставлено 70
Сборкане измерялосьне измерялосьБраузер не запускался
Сводка87.859.3Агрегат доступных проверок старого прогона, не оценка модели

Разница в сводке почти целиком обусловлена проверкой соблюдения дизайн-системы. Но опубликованные значения получены старой версией проверяльщика: он не распознал существующий импорт Button. Поэтому 87.8 и 59.3 — архивная запись конкретного прогона до фикса, а не итоговая оценка качества двух интерфейсов.

Ось кода при этом почти стоит на месте, 98 против 99. Наивные кнопки её проходят: проверка смотрит на any, длину файла и наличие export function Task, не на то, чья это кнопка.

Проверка импорта снимает баллы за строку, которая в файле есть

  • Файлimport { Button } from "@ds" и три варианта Button.
  • Регулярное выражение прогонаЖдёт from "@ds" import. Обычный импорт TypeScript не совпадает. В рассуждении — «no @ds imports».
  • Балл в архивеМинус 25. Статика 75. Тег Button находится отдельно, compliance ставит 100. Ось DS на кейсе — среднее, 87.5. По набору — 87.

Старый проверяльщик обнаруживает различия между файлами: в наивном варианте нет Button, есть нативные button и цвета #3B82F6, #6B7280, #EF4444. Но итоговый разрыв 87 против 5.7 нельзя считать точной оценкой соблюдения системы: существующий импорт в хорошем файле не был засчитан.

В текущем раннере проверка импорта перенесена на Babel (grade_ast) и закреплена тестом в tests/test_graders.py. Опубликованный JSON-прогон runs/fixture-good намеренно оставлен в том виде, в каком он был записан до фикса.

Проверка исправления на двух файлахСтарый регексп (в архиве)Текущий Babel AST (grade_ast)
fixture (с import { Button } from "@ds")75 статика · 100 compliance · 87.5 ось DS100 статика · 100 compliance · 100 ось DS
fixture-naive (сырые <button> и hex)20 статика · 0 compliance · 10 ось DS20 статика · 0 compliance · 10 ось DS

Почему это не один судья

Детерминированные проверки отвечают за конкретные нарушения в исходнике. Браузер и модель-судья — за свойства, которые одним чтением кода не установить. Если проверка не запускалась, её результат нельзя считать измерением.

Импорт, hex и нативный тег я не отдаю модели. Правило на одном и том же файле отвечает одинаково, даже когда ошибается. В опубликованном прогоне модельные судьи не запускались, skipped: true. Их воспроизводимость в этом прогоне не оценивалась.

В текущем коде судья включается только вместе — провайдер Anthropic или OpenAI и скриншот. Визуальный ставит 0–5 за сетку, иерархию, ритм. UX смотрит, решена ли задача. Поле kind отделяет deterministic от llm. Ноль с пометкой skipped — не вердикт об экране. Их 30% веса переносятся на остальные оси, поэтому сводка не обнуляется и при этом не содержит суждения о внешнем виде.

Что можно утверждать

На этих десяти файлах проверка исходника отделяет тег Button от нативного button с зашитым цветом. Сравнивать так можно, потому что задача и система одни и те же, а свойство — то, что проверка действительно прочитала. Подставленные 70 сравнивать нельзя: измеряемого свойства там нет.

Разрыв нельзя переносить на живую модель: такого прогона нет. Нельзя говорить, что команда стала реже обходить систему. На странице agent-ui граница та же: влияние правил на правильную вставку через ds-eval ещё не считалось.

Рядом с остальными

ds-context собирает короткий пакет и модель не вызывает. ui-repair кладёт патч по правилам системы. prompt-regress смотрит, не сломала ли новая версия промпта то, что уже проходило. Они не вызывают ds-eval. ds-eval оценивает уже написанный файл и только теми проверками, которые в этом прогоне запускались.

Как запустить

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

Живые модели читают ANTHROPIC_API_KEY и OPENAI_API_KEY из окружения. Флаг --runtime включает браузер: сборку, Playwright и axe. Сам по себе он не запускает судей — им нужны живая модель и скриншот. Если флаг не передан, проверка не стартует: текущий раннер пишет функциональной оси 0 с пометкой runtime: skipped. Это отсутствие прогона, не результат. Если браузер стартовал и упал на сборке или странице, в записи остаётся ошибка исполнения, а ноль на функциональной оси уже без этой пометки. Подставленные 70 в опубликованном прогоне — третье состояние: браузер не запускался, и старый проверяльщик записал константу.

Есть задача?

Напишите мне. Вместе разберёмся, с чего начать.