За последние пару месяцев я несколько раз переписывал своих QA-агентов с нуля. Не потому что они ломались, а потому что я понял: «агент для QA» — это не «напиши мне тест», а композиция из фаз, разных инструментов с разной ролью и явных правил, когда остановиться. Шесть приёмов, которые я вытащил из опыта и которые применимы не только к тестированию.
Этот пост продолжает серию «AI в разработке». До этого были эволюция AI-кодинга, границы AI-агентов, инженерное мышление с AI, agentic engineering и vibe coding и скиллы, агенты, подагенты. Здесь я свожу всё к самому практическому уровню — к агентам, которые пишут и поддерживают автотесты в реальном проекте. Не прототипы, не демо, не «поиграться».
Про конкретные инструменты вроде PyTest, Playwright, Appium, MCP-сервера для QA я писал и ещё напишу. Здесь речь про архитектуру агента: как устроен цикл, что он делает на каждом шаге, когда останавливается, что отдаёт человеку.
Откуда этот пост
Я долго не понимал, почему мои QA-агенты работают хуже, чем «должны были бы». Модель та же, инструменты те же, задачи типовые. Но результат скачет: иногда отличный тест, иногда дичь, которую приходится переписывать руками. Я перечитывал свои же промпты, перепроверял контекст, пробовал другие модели. Не помогало.
Помогло, когда я перестал смотреть на агента как на «умного помощника» и начал смотреть как на инженера, у которого есть сильные и слабые стороны. И выстроил процесс так, чтобы сильные стороны использовались, а слабые — компенсировались структурой. Не магией промптов, не выбором модели, а явной архитектурой цикла.
Получилось шесть приёмов. Они не универсальны — в другой команде или другом стеке что-то нужно делать иначе. Но эти шесть — то, что я готов защищать как работающее в моих условиях.
Что такое «агент для QA» в моём понимании
«Агент для QA» в 2026 году — это не «AI, который пишет тесты». Это композиция из четырёх вещей: большая языковая модель, набор инструментов, цикл работы и формат финального отчёта. Без любой из четырёх это или простой чат, или скрипт, или эксперимент, но не рабочий агент.
Модель — это понятно. Любая современная LLM уровня Sonnet, GPT-4o/5, Gemini Pro. Инструменты — это то, к чему у агента есть доступ: инструменты-разведка в режиме «только чтение», инструменты для написания кода, инструменты для запуска тестов, инструменты для отчётов. Цикл — та самая фазовая структура, про которую речь пойдёт ниже. Отчёт — формат, в котором агент возвращает результат человеку.
Агент не «умный» сам по себе. Он «умный» в рамках своей структуры. Плохая структура — даже лучшая модель делает плохие тесты. Хорошая структура — даже средняя модель даёт полезные результаты. Это, кстати, главный практический вывод из поста: качество агента определяется его архитектурой, а не выбором модели.
Фазы: сначала понять, потом сделать
Первое, что я сделал, — запретил агенту делать всё за один проход. «Напиши тест» — плохая инструкция, потому что она не различает «понять задачу», «написать код» и «убедиться, что работает». Агент в этом случае пытается совместить всё и какую-нибудь часть делает плохо.
Структура, которая у меня работает, — четыре фазы.
Анализ. Агент читает задачу, читает существующие тесты в проекте, находит сложившиеся правила: как именуются тесты, какие фикстуры используются, какие метки-маркеры, какие стили проверок. Без этого шага он напишет тест в своём стиле, и он не впишется в проект. В последующем с такими тестами будет тяжело взаимодействовать и поддерживать их.
Разведка. Агент находит селекторы, локаторы, тестовые данные. Для интерфейса — через разведочные инструменты, которые не пишут в файлы. Для API — через документацию или через существующие тесты. На этом шаге агент не трогает рабочий код. Он просто ходит по ссылкам, по страницам сайта или экранам мобильного приложения.
Реализация. Агент пишет минимальный код теста, используя данные из разведки и правила из анализа. Тут же — минимальный код фикстуры, если нужно, минимальный код page object (класс-обёртка над страницей интерфейса, чтобы тесты работали не с сырыми селекторами, а с понятными методами вроде «нажать кнопку входа»), если принято. Не больше, чем нужно для одного теста.
Проверка. Агент запускает тест, смотрит на результат, чинит, если упал по своей ошибке, отмечает в отчёте, если упал по внешней причине. После успешного прохода готовит финальный отчёт.
Агент не перепрыгивает между фазами. Заметил на разведке, что правила в проекте другие, — возвращается к анализу, а не пишет «как привык». Упал на проверке из-за неверного селектора — возвращается к разведке, а не правит селектор «на глаз» в коде теста.
Разные инструменты для разведки и для финального теста
Этот приём, пожалуй, самый контринтуитивный. Я долгое время использовал один и тот же инструмент и для «посмотреть, как выглядит DOM», и для финального теста. И получал плохие результаты: разведочный режим оставлял мусор в финальном коде, а финальный режим тащил в тест кучу отладочных вызовов.
Решение — два разных набора инструментов. Разведочные работают только на чтение, не пишут в файлы, могут выводить что угодно на экран, делать снимки экрана, «ощупывать» DOM и самое главное с меньшим шумом засорять контекст сессии. Финальные — те, которые пишут код теста. Они могут читать и писать файлы, запускать тесты, делать коммиты, но не должны использоваться для разведки.
В моём случае: для разведки интерфейса я использую отдельную утилиту командной строки, которая умеет делать снимок, заглядывать в элемент, скрывать всплывающие окна. Для финального теста — pytest и playwright, как код. Эти два мира не пересекаются. То, что я «посмотрел» в разведочном режиме, не попадает в финальный код. То, что я делаю в финальном коде, я не отлаживаю «руками» через разведочный инструмент — только через обычный запуск тестов с точками останова.
Когда один инструмент используется для всего, у агента нет чистой границы между «я ещё думаю» и «я уже пишу». В результате в финальный тест попадает «полу-разведочный» код, который ломается при первом же изменении интерфейса. Когда инструменты разделены, у каждой фазы свой инструмент, и результаты не пересекаются.
Лимит попыток: явный и не обсуждаемый
Без явного лимита попыток агент уходит в бесконечный цикл «попробую ещё раз». Тест упал — попробовал с другим селектором — упал — попробовал с задержкой — упал — попробовал с другим таймингом — упал. И так далее, пока не закончится контекст или деньги.
Я ввёл явный лимит для каждой фазы. Для мобильного теста — 15 попыток суммарно на всю сессию. Для теста по сайту — 20. Число подобрано опытным путём. Меньше — агент не справится с реально сложной задачей. Больше — начнёт «долбить», портя результат и значит нужно подключиться самому и прийти к какому либо решению, прежде чем отдавать агенту заново задачу.
Здесь важный нюанс. Лимит попыток — это не «попыток запустить тест». Это «попыток что-то сделать агентом». Каждый вызов инструмента, каждое изменение файла, каждый запуск — всё считается. Когда лимит исчерпан, агент обязан остановиться и сдать отчёт «не сделано» с объяснением, что именно не получилось.
Это защита от самой опасной проблемы AI-агентов: они не умеют сдаваться. Человек после 5 неудачных попыток с высокой вероятностью позовёт коллегу. Агент без лимита будет пробовать, пока не кончится контекст. С лимитом — останавливается в той точке, где человеку действительно нужно подключиться.
Сначала схема, потом вызов
Каждый раз, когда я давал агенту новый инструмент, я подробно объяснял в промпте, как он работает. Это работало плохо: инструкции путались, модель путала параметры, вызовы не проходили проверку.
Решение простое: сначала схема, потом вызов. У каждого инструмента есть схема данных — машиночитаемое описание того, какие параметры он принимает и что возвращает (JSON Schema, TypeScript-типы, Python-типы или спецификация OpenAPI). Агент перед первым использованием читает схему. Всегда, без исключений. Это заменяет «длинную инструкцию в промпте» коротким правилом «прочитай схему и следуй ей».
С MCP-серверами это естественно: схема инструмента идёт «бесплатно» в API обнаружения инструментов. С локальными Python-инструментами хватает подсказок типов и встроенной справки. С утилитами командной строки — встроенной справки по ключу `--help`. Агент, который читает схему перед вызовом, делает на порядок меньше ошибок типа «передал строку вместо массива».
Есть и побочный эффект. Правило «сначала схема» заставляет меня как автора инструмента писать схему аккуратно. Если схема плохая, агент будет делать плохие вызовы. Если схема хорошая — всё работает. Это давление снизу вверх, которое улучшает инструменты в целом.
Минимальные правки и коммит по запросу
Два связанных правила. Первое — минимальные правки. Агент должен менять ровно столько кода, сколько нужно для задачи. Не «заодно причесал соседний файл», не «поправил импорт на правильный», не «удалил закомментированный код». Только то, что относится к задаче.
Это нужно по двум причинам. Первая — ревью кода. Минимальную правку ревьюируешь за минуту. Список изменений на 200 строк с «заодно» никто нормально ревьюить не будет, и плохие правки заедут в основную ветку. Вторая — откат. Если тест оказался плохим, минимальную правку откатываешь одной командой. Список с «заодно» превращается в расследование «что мы ещё тут поменяли».
Второе — коммит только по запросу. Агент не делает коммит сам. Он готовит изменения, показывает список правок, описывает, что сделал, и ждёт «коммить» от человека. Это правило я вытащил из своего же поста про agentic engineering: agentic engineering — про ответственность, а не про самостоятельность. Коммит — это явное действие, которое должно быть одобрено.
Вместе эти два правила дают простую гарантию: в репозитории всегда только то, что человек видел и одобрил. Никаких «AI где-то что-то закоммитил, и мы узнали об этом через неделю».
Финальный отчёт по форме
Без структуры отчёта агент возвращает «сделал тест, посмотри», и человек тратит 10 минут, чтобы понять, что произошло. С ней — тратит минуту.
Формат, который у меня устоялся, выглядит так. Что сделано: список файлов, какие тесты добавлены или изменены, в чём суть каждого. Что пропущено: что не получилось, что было отложено, какие есть ограничения. Что нужно от человека: вопросы, решения, разрешения, на которых застрял. Какие инструменты и сколько раз вызваны: для наблюдаемости и понимания цены.
Пример отчёта для одной задачи. «Сделано: добавлен тест `test_login_with_invalid_password_returns_401` в `tests/api/test_auth.py`, использует фикстуру `client_with_test_user`. Что пропущено: не покрыт случай с заблокированным пользователем — нужна дополнительная фикстура, время вышло. Что нужно от человека: подтвердить, что для блокировки нужна отдельная задача, или расширить охват. Инструменты: pytest запущен 7 раз, MCP-инструменты вызваны 12 раз, итого лимит попыток израсходован на 18 из 20».
Этот формат экономит время и на стороне агента (дисциплинирует отчёт), и на стороне человека (быстро понятно, что делать дальше). И главное — он заставляет агента явно фиксировать, что не сделано. В моей практике самые полезные находки в ретроспективе — это не то, что «AI сделал», а то, что «AI честно сказал, что не смог».
Чего я в этих агентах не делаю
Несколько коротких «нет» — для баланса.
Не даю агенту доступ к продакшн-базе. Никогда. Только изолированные тестовые стенды, желательно с искусственными данными. Агент, у которого есть права на продакшн, рано или поздно сделает что-то «по ошибке», и это будет стоить дорого.
Не использую один инструмент для всего. Если у меня один универсальный «сделай хорошо» — это плохой дизайн. Разделение инструментов — не педантизм, а защита от пересечения разведочного и финального кода.
Не принимаю тест, который не прошёл сбор. «Сбор» в pytest — это проверка, что тест вообще импортируется, находит свои фикстуры, не падает на этапе подготовки. Тест, который не прошёл сбор, никогда не запустится в CI. Смысла в нём нет.
Не использую вайб-кодинг для автотестов. Для прототипа или проверки гипотезы — вайб-кодинг ок. Для тестов, которые будут гоняться в CI — нет. Только agentic engineering с рамками и отчётом. Если я пишу тест «на вайбе» — это сигнал, что я не понимаю задачу, и надо остановиться.
Не верю агенту, который говорит «я сам всё задеплою и закоммичу». Красный флаг. Если агент считает, что отправка изменений в основной репозиторий — это нормальная часть его работы, у нас разные представления об ответственности. Агент помогает. Решения — за человеком.
Главный вывод
QA-агент в 2026 году — это не про «AI пишет тесты». Это про архитектуру цикла. Шесть приёмов, которые я вытащил из работы: фазы (анализ, разведка, реализация, проверка), разделение инструментов (разведочные и финальные — разные), явный лимит попыток, «сначала схема» для всех инструментов, минимальные правки и коммит по запросу, формализованный финальный отчёт.
Качество QA-агента определяется не выбором модели и не магией промпта. Оно определяется структурой. Когда фазы разделены, инструменты не пересекаются, лимиты явные, схемы читаются, правки минимальны, а отчёты структурированы — даже средняя модель даёт полезный результат. Когда структуры нет — даже лучшая модель делает мусор.
Я не претендую на универсальность и единую точку зрения. В вашем проекте наверняка будут другие правила, другие инструменты, другие ограничения. Но сама идея — что агент это инженер, у которого есть рамки, лимиты и форматы, а не «умный помощник, который сам разберётся», — она работает. Проверено на мне.
Если что-то из этого сработает у вас в проекте — буду рад услышать. Если не сработает — тем более интересно, расскажите, почему.
Ссылки
pytest --collect-only — почему «сбор» обязателен для любого теста, который претендует на CI.
Playwright locators — про приоритет локаторов, привязанных к специальным возможностям интерфейса, над xpath.
Appium UiAutomator2 driver — про accessibility_id, предикаты, class chain и почему xpath — последнее средство.
MCP documentation — почему правило «сначала схема» для инструментов — это не «лучшая практика», а необходимость.
Equipping agents for the real world with Skills — система Skills от Anthropic, которая позволяет переиспользовать знания между агентами.
Agent HQ: a new home for agents — GitHub Blog, как они собирают систему управления несколькими агентами, которая ложится на приёмы из этого поста.
Discussion
No comments yet - start the thread.