AI-агент действительно ловит баги? Пусть докажет на бенчмарке

от автора

«Кажется, стало лучше. Или хуже. Или так же. Не знаю» — эта фраза стала последней каплей. После неё я сел писать бенчмарк.

Привет! Это снова Михаил Федоров. В первой статье — архитектура QA Assist: 11 AI-агентов от декомпозиции требований до готовых автотестов. Во второй — как «4 часа подключения» превращаются в неделю корпоративной реальности. В третьей — почему пирамида тестирования ломается, когда тест-дизайнером работает LLM. Сегодня — про то, как я решил наконец-то перестать оценивать агента «на глаз» и собрал отдельный проект-бенчмарк, на котором можно честно сравнивать прогоны: версии агента, отдельные «улучшалки», даже эксперименты с моделями. В качестве бонуса покажу все артефакты, которые агент готовит за один прогон пайплайна. И бенчмарк, и артефакты — в публичном доступе, ссылки в конце статьи. Обсудить всё это можно в Telegram-группе.


Оглавление

  1. Проблема: как понять, что агент стал лучше

  2. Идея бенчмарка

  3. Что внутри проекта CallMeBack

  4. Как я готовил тестовый проект к прогону агента

  5. QA Assist vs нативный Claude Code: что на выходе

  6. Результаты бенчмарка: QA Assist vs Claude Code «в лоб»

  7. Что показал бенчмарк


1. Проблема: как понять, что агент стал лучше

AI-агент — как новый джун в команде: сначала ему надо подробно объяснить, что и как, и только потом помощь становится по-настоящему ощутимой. Поэтому вы постоянно его «обучаете»: переписываете системные промты, добавляете шаги в пайплайн, подкручиваете правила консолидации тест-кейсов, меняете провайдера моделей, вводите кеширование, чтобы сэкономить токены.

И каждый раз — одна и та же беспомощность: похоже, стало лучше. Но доказать не могу.

Это очень неприятное чувство. Вы тратите часы на оптимизацию, делаете коммит — и не можете уверенно сказать, что коммит улучшил систему. Особенно весело с расходом токенов: вы переписали один из шагов так, чтобы он расходовал в три раза меньше, — но убедиться, что качество детекции при этом не просело, на живых проектах проблематично.

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


2. Идея бенчмарка

Как мы обычно понимаем, что улучшение процессов QA сработало? Замеряем метрику до, внедряем изменение, замеряем после. Простая и проверенная схема. Только растянутая во времени на месяцы и подверженная куче параллельных факторов: пока вы измеряете эффект перехода на shift-left, в команду пришёл новый разработчик, поменялся техлид, команда переехала на другой фреймворк и т.п. Любая дельта в метрике может объясняться чем угодно — кроме вашего изменения.

С AI-агентом ситуация принципиально другая. Изменения делаются часто: переписали системный промт, добавили шаг в пайплайн, поменяли провайдера моделей. И каждый раз нужно быстро понять — стало лучше или хуже. Ждать пару месяцев, пока накопится статистика на живых проектах, нельзя: к этому моменту сама версия агента уже устареет.

Здесь нужна 100% контролируемая среда.

В итоге возможны два сценария:

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

  2. Считать на песочнице — фиксированный проект с заложенными багами, повторяемые прогоны, результаты сравнимы между собой. Быстро, дёшево, чисто.

Именно второго — быстрых подсказок для принятия решений — мне и не хватало. Так появился бенчмарк. Он фиксирует условия. Один и тот же проект. Одно и то же окружение. Один и тот же набор багов, заложенных намеренно. Одна и та же система оценки результата. Меняется только то, что я хочу измерить — версия агента, версия промта, конкретная улучшалка.

С его помощью можно отвечать на практические вопросы:

  • Какие типы багов агент ловит, а какие систематически пропускает?

  • Насколько стабильно он их ловит между запусками?

  • Помогает ли конкретная улучшалка или это иллюзия?

  • Сколько это стоит?

Я искал готовый инструмент. Что-то под учебные задачи есть, под LLM-кодинг есть, под QA-агентов в нужном мне виде — нет. Поэтому собрал свой.

Ключевой принцип: бенчмарк — это отдельный проект, а не часть основного репозитория ассистента. Агент должен работать с проектом точно так же, как с боевой задачей.

Второй принцип — компактность фичи. Гоняю бенчмарк часто, поэтому он должен давать сигнал минимальным числом токенов. Большой проект — это большой контекст, длинный пайплайн и дорогая итерация. Маленький — это быстро, дёшево и можно проверять гипотезы хоть несколько раз в день.

Репозиторий: qa-benchmark-callmeback.
Живой стенд: http://45.151.31.218:5000/.


3. Что внутри проекта CallMeBack

Бенчмарк-проект — это маленькое Flask-приложение «заявка на обратный звонок». Лендинг с формой (имя, телефон, удобное время, комментарий), страница /success, админка с Basic Auth для просмотра заявок.

В репозитории — две ветки. В main лежит то, что мы показываем агенту: код проекта и требования (бизнес-контекст, User Stories с AC, спецификация API). В ветке benchmark — то, что агент видеть не должен: эталонный список багов и методика оценки.

Важный момент: эталонные списки лежат в ветке benchmark (bugs_us1.yaml — 14 багов под публичную форму, bugs_us2.yaml — 11 багов под админку) и должны быть исключены из контекста перед прогоном агента — иначе нечестно.

Внутри проекта намеренно заложены 25 багов. По категориям:

Расшифровка категории

Код категории

Пример

Безопасность

security

XSS, SQL injection, утечка stack trace

Валидация

validation

не проверяется длина поля, не подрезаются пробелы

Функциональность

functional

кнопка не реагирует, фильтр возвращает не то

Соответствие API-контракту

api_contract

неверный HTTP-код, неверный формат поля

UI/UX

ui_ux

опечатки, не адаптивная вёрстка

Доступность

a11y

нет лейблов у полей, отключён focus-outline

Производительность

performance

долгий ответ ручки, выборка без пагинации

Целостность данных

data_integrity

данные пишутся в БД в разных форматах

Локализация

i18n

даты в неверной таймзоне

Забытые артефакты

stray

console.log в проде, мёртвый код


4. Как я готовил тестовый проект к прогону агента

Подготовка описана в статье «Возможно ли запустить AI-тестирование за 4 часа?». Сейчас нас интересует раздел 3, шаги 1 и 2.

Кратко опишу основное:

1. Завести новый проект в Jira, создать там US, скопировав описание из файла требований в репозитории.

2. Собрать Global Context с помощью global-context-builder.

3. Запустить агента:

test-orchestrator pipeline JTPZ-40

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

Бонус. Для сравнения я прогнал тот же проект «в лоб» — просто скормил Claude требования и попросил найти баги. Никакого пайплайна, никакой инфраструктуры, один промт. Результаты — в разделе 6.

claude --chrome \  "Ты — QA-инженер. Перед тобой требования к проекту: [REQUIREMENTS_US1.md](https://github.com/spoon03/qa-benchmark-callmeback/blob/benchmark/benchmark/requirements/REQUIREMENTS\_US1.md). \  Реализация доступна по адресу http://localhost:5000. \  Изучи требования и найди все баги в реализации."

5. QA Assist vs нативный Claude Code: что на выходе

Прежде чем сравнивать результаты бенчмарка, давайте сравним артефакты, генерируемые агентами. QA Assist за один прогон оставляет в репозитории целый слой документации и кода — всё, что появилось ниже, я не писал и не редактировал руками.
Нативный Claude Code на выходе даёт список найденных багов — и всё. Если вам нужны дополнительные артефакты — traceability matrix, отчёт по расхождениям, структурированные сценарии и так далее — всё это придётся описывать в системном промте вручную. В скиллах QA Assist это уже заложено: каждый шаг знает, что производить и в каком формате.

5.1. Декомпозиция требований

Папка: testing/requirements/JTPZ-40/

Файл

Что внутри

US-JTPZ-40.md

User Story по шаблону: бизнес-ценность, формулировка, use case, основной сценарий, расширения (альтернативы и ошибки), acceptance criteria, ссылки на технические задачи

TASK-JTPZ-40-01.md

Frontend-таска: разметка формы, клиентская валидация, обработка кодов ответа (200/201/400/429), сетевые ошибки, страница успеха

TASK-JTPZ-40-02.md

Backend-таска: контракт API, схема валидации, нормализация телефона, rate-limit, формат created_at, security-требования

REQUIREMENTS_REDUNDANCY_REPORT.md

Отчёт оптимизатора требований: какие пункты дублировались между US и TASK, какие смерджились/удалены с алиасами

Каждое требование получает уникальный ID. Это нужно потом, чтобы каждый тест-кейс ссылался на конкретный пункт спецификации, и можно было собрать матрицу покрытия.

Контроль дублей на уровне требований. REQUIREMENTS_REDUNDANCY_REPORT.md — недавнее нововведение. Проблема выросла из дублирования требований на различных уровнях пирамиды: одно и то же поведение описывалось и в US, и в TASK, и генерация сценариев плодила по два-три TC на одну суть. Сейчас после причёсывания требований по шаблонам за декомпозицию по пирамиде и дедупликацию отвечает отдельный агент — test-requirements-optimizer.

5.2. Тестовые сценарии

Папка: testing/scenarios/JTPZ-40/

Файл

Что внутри

TASK-JTPZ-40-01-SC-UI.json

UI тест-кейсы (isolated): валидация формы, состояния кнопки, обработка ответов сервера, focus, label, мобильная вёрстка

TASK-JTPZ-40-02-SC-API.json

API тест-кейсы: happy path, валидация полей, формат телефона, нормализация, rate-limit, SQL-инъекции (TC-JTPZ-40-BE-001..030)

US-JTPZ-40-SC-E2E.json

E2E сценарии: полный happy path, обработка серверной валидации, обработка 429

Coverage.md

Requirements traceability matrix: какой TC закрывает какое требование, итоговый процент покрытия

SCENARIOS_REDUNDANCY_REPORT.md

Отчёт оптимизатора по тест-кейсам (cross-level дубли, e2e step overlap, excessive granularity)

ERRORS_DISCREPANCIES.md

Расхождения между требованиями и реальностью, найденные на этапе генерации сценариев (7 штук)

ERRORS_DISCREPANCIES_API.md

Дополнительные API-расхождения, всплывшие при отладке тестов (9 штук)

ERRORS_DISCREPANCIES_UI.md

Дополнительные UI-расхождения, всплывшие при отладке тестов (8 штук)

ERRORS_TEST-DATA*.md

Списки недостающих тестовых данных (общий + per track)

5.3. Автоматизация

Папка: testing/auto/JTPZ-40/

Файл

Что внутри

repoapi/tests/JTPZ-40/

Сгенерированные API-тесты (pytest)

repoui/tests/JTPZ-40/

Сгенерированные UI-тесты (Playwright + pytest)

AUTOMATION_REPORT.md

Сводный отчёт по сгенерированным тестам

DEBUG_REPORT.md

Лог отладочного цикла

Всё пишется в клонированные автотест-репозитории (_repo_api/, repoui/) — агент работает в их структуре, а не в своей, чтобы потом одним коммитом улететь в MR.

Что получилось на JTPZ-40:

Трек

Тестов

PASSED

xFailed

Найдено дефектов

API

33

6

27

9 (JTPZ-49..57)

UI (Playwright)

28

18

10

8 (JTPZ-58..65)

Каждый xfail в коде помечен ссылкой на Jira-тикет с багом. Логика простая: тесты не должны быть «зелёными» из-за того, что мы знаем про баг, — они должны быть xfail с явной причиной. Когда баг чинят, тест становится зелёным сам.


6. Результаты бенчмарка: QA Assist vs Claude Code «в лоб»

Скоринг считается по простому правилу: для каждой находки ставится одна из трёх меток — TP (совпадает с эталоном), FP (выдумки или баги, не включённые в эталон), Miss (эталонный баг не найден). Дальше из этого считается recall = TP / (TP + Miss) — доля найденных эталонных багов, и FP-rate = FP / (TP + FP) — доля ложных срабатываний среди всех заведённых тикетов.

Оба прогона — по одному и тому же проекту, одному и тому же набору из 14 эталонных багов (US-1).

Найденные баги зафиксированы:

6.1. Общие метрики

Метрика

QA Assist

Claude Code «в лоб»

TP

11 / 14

7 / 14

FP

6

4

Miss

3

7

Recall

79%

50%

FP-rate

35%

36%

6.2. Recall по категориям

Категория

Эталон

QA Assist

Claude «в лоб»

validation

3

3 (100%)

2 (67%)

api_contract

2

2 (100%)

2 (100%)

ui_ux

2

2 (100%)

1 (50%)

a11y

2

2 (100%)

1 (50%)

performance

1

1 (100%)

0 (0%)

security

2

1 (50%)

1 (50%)

functional

1

0 (0%)

0 (0%)

stray

1

0 (0%)

0 (0%)

6.3. Recall по сложности

Difficulty

Эталон

QA Assist

Claude «в лоб»

easy

11

8 (73%)

4 (36%)

medium

2

2 (100%)

2 (100%)

hard

1

1 (100%)

1 (100%)

6.4. Recall по происхождению

Origin

Эталон

QA Assist

Claude «в лоб»

requirements

10

9 (90%)

7 (70%)

general_expertise

4

2 (50%)

0 (0%)


7. Что показал бенчмарк

7.1. Главное — инструменты, а не модель

Под капотом у обоих прогонов один и тот же Claude Opus. Разрыв в 29 п.п. recall — это не разница в «уме модели», а разница в инструментарии вокруг неё: у QA Assist есть автогенерация pytest-тестов по acceptance criteria, Playwright + DOM-инспекция, оптимизаторы требований и сценариев. У Claude «в лоб» — только curl, DevTools через MCP и интуиция.

Это значит, что Claude «в лоб» довольно легко подтянуть: дайте ему те же инструменты, и цифры выровняются. Модель одна и та же.

Главное преимущество QA Assist — не в победе по recall на одном прогоне, а в том, что после прогона остаётся:

  • переиспользуемые артефакты. Requirements → scenarios → tests, traceability matrix, discrepancies — следующая итерация по этому проекту стартует не с пустого чата, а из готовой структуры.

  • уровни контроля. Оркестратор контролирует форматы и полноту артефактов на каждом шаге.

  • накапливаемые оптимизации. Агенты постоянно допиливаются — новые проверки, чек-листы, оптимизаторы, форматы отчётов. Управлять всеми этими нюансами через один системный промт «в лоб» практически невозможно: правки начинают мешать друг другу, а сам промт быстро превращается в простыню, которую страшно трогать. У пайплайна со скиллами каждый шаг изолирован, и правки в одном не ломают другие.

  • тонкая настройка под проект. Чек-листы, шаблоны декомпозиции, форматы отчётов — всё конфигурируется отдельно от модели и продолжает работать при её апгрейде.

  • масштабирование. В агента закладываются хорошие практики с прицелом на рост: подключить новый проект — дело часов, а не недель, всё нужное (шаблоны, оптимизаторы, оркестрация) уже есть.

  • автоматизированный регресс. Сгенерированные pytest- и Playwright-тесты остаются в репозитории и перезапускаются без расхода токенов — в CI, на каждом PR, при каждом релизе. Claude «в лоб» каждый раз начинает с нуля и платит токенами за повторение той же работы.

Claude «в лоб» решает одну задачу здесь и сейчас — найти баги. QA Assist строит инфраструктуру вокруг этой задачи: изолированные скиллы, оркестрацию, переиспользуемые артефакты, заложенный путь к масштабированию.

7.2. Бенчмарк уже помогает, но требует допиливания

Базовая задача, ради которой я его и собирал, — сравнивать ассистент «вчера» и «сегодня» — закрыта. Я могу сказать «эта правка дала +X п.п. recall в категории Y» вместо «вроде стало лучше»: между двумя моими прогонами видны конкретные эффекты от конкретных правок. В этом плане всё работает как и хотелось.

Что нужно допилить:

  • Дополнить эталон: часть «FP» — реальные баги, которые просто не попали в фиксированный список. Ассистент нашёл несоответствия контракту (placeholder’ы, charset, nullable, размеры кнопки), которых нет в bugs_us1.yaml. По правилам бенчмарка это FP, по содержанию — кандидаты в новые BUG’и.

  • Гонять прогон 5×. Одна точка — это одна точка. Чтобы отличить настоящее улучшение от случайного шума, нужно мерить разброс.

  • Трекинг расхода токенов. Recall ↑ — здорово, но если новая версия стоит втрое больше, это тоже надо знать.

  • Подумать над размером выборки багов. Хочется больше — точнее метрика. Но больше багов = более громоздкий проект = дороже один прогон. Надо искать середину.

7.3. Пора прикручивать исследовательское тестирование

Самое чёткое разделение в результатах — по происхождению бага (см. таблицу 6.4):

  • requirements — 90%: всё, что описано в acceptance criteria, агент берёт почти полностью;

  • general_expertise — 50%: то, что без спеки заметил бы опытный инженер.

Все слепые зоны (functional, stray, недозакрытый security) — это про общеинженерную интуицию: stack trace в 500-х, console.log в проде, Enter без вызова validate() перед fetch. Их нельзя поймать ни pytest’ом по AC, ни DOM-инспекцией — только осознанным «потыкать руками с гипотезами в голове».

То есть исследовательским тестированием. И, похоже, это следующий большой шаг для пайплайна: добавить отдельный скилл с чек-листом «что инженер обычно проверяет, когда у него нет спеки». Это не сделает агента «человеком», но даст ему второй угол зрения — кроме requirements-driven. Сейчас этого больше всего не хватает.


Если у вас был похожий опыт с бенчмарками AI-агентов, вы автоматизируете тестирование сторонними инструментами, разрабатываете свои или только собираетесь, или есть вопросы и замечания по выложенным артефактам — заходите в Telegram-группу: обсуждаем идеи, делимся проблемами и успехами.


Ссылки:

ссылка на оригинал статьи https://habr.com/ru/articles/1036136/