Перейти к содержанию

Anthropic

Claude Code проверяет, не зря ли вы написали плагин

7 мин чтения

Текст написан ИИ-моделью по источникам, на которые ведут ссылки, и сверен с ними второй моделью. Как мы работаем

Изображение: code.claude.com

Anthropic 11 сентября добавила в Claude Code команду claude plugin eval. Она прогоняет плагин или навык по набору тестовых запросов, выставляет оценки и параллельно повторяет те же прогоны совсем без плагина, чтобы было видно, что плагин добавил сам. Как устроены кейсы, проверки и подсчёт, подробно расписано в документации Claude. Нужен Claude Code 2.1.269 или новее: версию показывает claude --version, обновляет claude update.

Тесты для навыков в экосистеме Claude уже были: их писал плагин skill-creator. В статье о его обновлении Anthropic объясняла, что большинство авторов навыков - специалисты по своим рабочим процессам без инженерного опыта и проверить, работает ли навык на новой модели и срабатывает ли он когда нужно, им было нечем. Новая команда использует свой формат кейсов, файл evals/evals.json от skill-creator она не читает.

Как считается балл

Кейс - это каталог внутри evals/ с реалистичным запросом в prompt.md и одной или несколькими проверками (в документации они называются graders) в graders/. Запрос пишут так, как его набрал бы пользователь, не называя навык. На каждый прогон Claude Code поднимает свежую изолированную неинтерактивную сессию, где загружен только ваш плагин, отправляет запрос и ждёт, пока Claude закончит или упрётся в лимит: по умолчанию 10 ходов и 300 секунд. Потом каждая проверка смотрит на финальный ответ, транскрипт или созданный файл и выносит «прошло» или «не прошло».

Один прогон недетерминированного агента мало что говорит, поэтому кейс по умолчанию идёт трижды. Балл прогона - доля пройденных проверок (с весами, если вы их задали), балл кейса - среднее по прогонам. Порог --threshold по умолчанию жёсткий, 1,0: если хоть один кейс не набрал идеальный балл, команда завершается с кодом 1.

Главное здесь - второй прогон, без плагина. Вы получаете два балла, WITH и W/OUT, и их разницу Δ. Если кейс набирает 1,0 в обоих вариантах, плагин не то, благодаря чему он прошёл. Зачем это нужно, Anthropic объясняла ещё в статье про skill-creator: если базовая модель начала проходить тесты без навыка, его приёмы, возможно, уже вошли в её поведение по умолчанию, и навык не сломан, просто стал лишним.

Чтобы Δ не врал, часть проверок из счёта убрана. Проверка «навык был вызван» без плагина пройти не может в принципе, и если её считать, балл без плагина уйдёт к нулю, а разница раздуется. Поэтому все tool_used с инструментом Skill и всё, что помечено arm: with-only, исключаются из балла в обоих вариантах и показываются только как индикаторы с scored: false. Обратную проверку, «навык не должен вызываться» с min: 0 и max: 0, можно принудительно считать в обоих вариантах через arm: both. С --ablation none ничего не исключается, так что один и тот же набор в двух режимах может дать разный абсолютный балл.

Шесть типов проверок

Четыре типа считаются по транскрипту и файлам и ничего не стоят: regex, tool_used, tool_order и file_exists. Два зовут модель-судью: llm сверяет ответ с рубрикой и засчитывает его, если судья проголосовал PASS хотя бы в двух голосах из трёх, а baseline сравнивает прогон с эталонным транскриптом. Проверок на собственном коде нет. Судья по умолчанию - небольшая быстрая модель, для тонких рубрик документация советует --judge-model sonnet.

Вердикт судьи плавает от прогона к прогону, и тем сильнее, чем длиннее текст, который ему дали. Поэтому длинный результат вроде сгенерированного файла Anthropic советует проверять регуляркой по содержимому, а llm оставлять для коротких ответов с рубрикой из конкретных условий PASS и FAIL. На каждый кейс рекомендуется пара: одна проверка на результат, другая на путь к нему (tool_used или tool_order), чтобы видеть и правильность ответа, и то, что его дал именно плагин. Если навык сработал, а Δ отрицательный, подозревать сначала стоит судью: маленькая модель может завалить верный ответ только из-за формата.

Есть и ловушки в мелочах. Цель files - это список созданных путей, а не их содержимое, и если все кейсы вдруг набрали ноль при правильных файлах, причина обычно в этом. file_exists видит только файлы, созданные во время прогона. Судья llm из всей сессии читает первые 12 и последние 12 сообщений.

Во что обойдётся прогон

Прогоны, судейские проверки и сам claude plugin eval init, который расспрашивает про плагин и пишет кейсы за вас, идут по вашим учётным данным: расходуют лимиты тарифа или счёт за API. Стоимость, которую печатает команда, - оценка по прайс-листу. По формуле из документации один кейс с одной llm-проверкой при настройках по умолчанию означает шесть прогонов агента (три с плагином, три без) и 18 коротких обращений к судье. Флаг --ablation none отключает прогоны без плагина и вдвое снижает расход, когда сравнение не нужно, например пока вы отлаживаете сами проверки.

--max-cost-usd ограничивает именно эту оценку, а не расход по тарифу. Её проверяют перед стартом каждого прогона, уже запущенные доигрывают, так что потолок можно немного перескочить; недозапущенный набор завершается с кодом 2 и частичными результатами. Параллельность -j задаётся от 1 до 8, но все прогоны делят один лимит запросов аккаунта, поэтому ускоряется только время на часах.

Опаснее лимит тарифа посреди набора. Каждый следующий прогон тогда заканчивается ошибкой, оценивается по тому, что успел сделать, и обычно получает 0, а сам набор доходит до конца и не помечается как частичный. Результат выглядит как регрессия, поэтому перед тем как верить баллам, документация советует заглянуть в колонку NOTES или в поле cases[].arms.with[].error в JSON.

Что видит агент под тестом

Каждый прогон получает одноразовые домашний каталог, рабочий каталог и конфигурацию Claude Code; агент работает там дочерним процессом claude -p. Ваши настройки, хуки, файлы CLAUDE.md, личные MCP-серверы, другие плагины и память не подгружаются, из переменных окружения проходит только белый список и переменные EVAL_*. Каталог с кейсами агенту недоступен, так что подсмотреть рубрику он не может. Инструмент Artifact выключен, а политики, развёрнутые администратором на машине, продолжают действовать.

Разрешений по ходу никто не спрашивает. По умолчанию доступны только инструменты на чтение, перечисленные в allowed_tools кейса; Bash, Write, Edit, WebFetch и WebSearch из сессии убираются, пока вы не выдадите их через --allow-tools. Bash работает в песочнице на уровне ОС: запись только в рабочий каталог прогона, домашний каталог не читается, сеть открыта лишь для явно разрешённых доменов. Если бэкенда песочницы нет, прогон с шеллом не запустится вовсе. На нативной Windows его нет, там нужен WSL2, а на Linux сначала ставят bubblewrap и socat.

MCP-серверы плагина по умолчанию не стартуют. Вместо них работают моки: по Markdown-файлу на инструмент в evals/mocks/<server>/<tool>.md, тело которого Claude получает как ответ. Блок expect: проверяет аргументы вызова, и если плагин попросил сервер не о том, прогон обрывается с нулём и причиной. Мок с type: agent отыгрывает сервер небольшой моделью, его ответы сохраняются и после ручного переноса в mocks/.replay/ воспроизводятся без обращения к модели; этот каталог документация советует коммитить, чтобы прогоны в CI повторялись.

Набор в CI

Для CI рецепт такой: --json для архива результатов, --trust-plugin, чтобы задача не зависла на вопросе о доверии к каталогу, --no-publish и потолок по стоимости. Обе модели, тестируемую и судью, стоит закрепить, чтобы выкатку новой модели не приняли за регрессию плагина. Поведение при смене модели действительно сдвигается: в сентябре Anthropic сама показывала, как старые приёмы промптинга на Opus 5 выполняются буквально и дорожают. Код выхода 0 значит, что все кейсы прошли порог, 1 - что какой-то не прошёл или набор не загрузился, 2 - частичный прогон из-за потолка стоимости или отклонённых ключей, 130 и 143 - прерывание и остановка. JSON версионирован (schemaVersion: 1), новые поля добавляются без переименования старых.

claude plugin eval init без терминала не работает, в CI вместо него есть --bare с пустым шаблоном. Отчёт report.html - один самодостаточный файл без внешних запросов: кейс с отрицательным Δ подсвечен красной полосой, у проваленной llm-проверки видны голоса судьи и то, что он читал. При входе через подписку claude.ai отчёт публикуется приватным артефактом, при работе по ключу API остаётся локальным.

При этом прогон набора - то же решение о доверии, что и claude --plugin-dir: хуки и реальные MCP-серверы плагина работают от вашего имени вне песочницы агента. В документации отдельно предупреждают, что пройденный набор тестов ничего не говорит о том, безопасен ли плагин.

Пока никто не написал. Будьте первым.

Присоединяйтесь к разговору

Войдите, чтобы оставить комментарий. Он появится после модерации.