PostMedium~6 min

Верификация: почему «тесты зелёные» не значит «можно мёржить»

Table of contents

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

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

Почему тесты агента не доказывают ничего

У агента, который пишет и код, и тесты к нему, есть конфликт интересов. Его цель — закрыть задачу, и зелёный прогон — часть закрытия. Отсюда три типовых греха, которые я встречаю регулярно.

Первый — слабый тест. Проверка «функция вернула объект» вместо «объект содержит эти поля с этими значениями». Формально тест есть, по факту он пропустит что угодно. Второй — тест, написанный от решения. Агент сначала пишет код, потом описывает в тесте его фактическое поведение. Если логика ошибочна, тест честно закрепляет ошибку. Третий, самый неприятный, — подгонка под зелёное. Ослабить проверку, удалить падающий тест, пометить его как устаревший. У меня в правилах это красный флаг верхнего уровня: агент, который трогает чужие падающие тесты без явного разрешения, останавливается и объясняется.

Вывод из этого не «агентам нельзя доверять». Вывод — проверка должна быть независима от исполнителя. Точно так же, как мы не принимаем у разработчика утверждение «я сам проверил» без ревью, мы не принимаем у агента «тесты зелёные» без вопроса, чьи это тесты и что они доказывают.

Независимость проверки

Рабочий приём — развести источник требований и источник решения. Тест на пустой email из нашей сквозной задачи хорош ровно тогда, когда написан от тикета: «форма обязана вернуть ошибку валидации». Плох, когда написан от кода: «проверим, что функция validateEmail ведёт себя так, как я её написал».

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

Дальше — иерархия проверок. Типы и линтер ловят механические ошибки и стоят копейки. Юнит-тесты — логику. Интеграционные и контрактные — то, что модуль стыкуется с соседями, агенты здесь грешат чаще всего, потому что видят локальный код, а не систему. E2E — критичные пользовательские сценарии, дорого, но именно они отвечают на вопрос «а работает ли это вообще для пользователя». Проверять всё на одном уровне — либо медленно, либо слепо. Разумный harness гоняет дешёвые проверки на каждом шаге агента, а тяжёлые — на границе PR.

Трассировки: отладка агента как отладка системы

Когда проверка всё-таки упала, начинается второй акт: понять, почему агент поступил так, а не иначе. Тут агент надо признать тем, чем он является: недетерминированной распределённой системой. И отлаживать соответственно: не гадать, а читать тррассировки.

Трассировка агентной сессии — это последовательность: какой инструмент вызван, с какими аргументами, что вернулось, сколько токенов и времени ушло на шаг. Я смотрю трассы в трёх случаях. Первый — задача провалена: где агент свернул не туда, что он видел перед ошибочным решением. Второй — задача решена слишком дорого: где он крутился по кругу, какой шаг съел половину бюджета. Третий — регулярный аудит: беру несколько успешных сессий и смотрю, нет ли в них везучих проходов мимо грабель, которые завтра сработают.

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

Evals: качество агента как качество софта

Проверки выше касаются кода, который пишет агент. Остался вопрос уровнем выше: как оценить качество самого агента и не сломать его очередным изменением harness. Ответ тот же, что и в обычной разработке: тесты. Только тестируем мы не функции, а поведение агента, и называется это evals.

Схема, к которой я пришёл, простая. Собирается набор типовых задач: двадцать-тридцать реальных тикетов из проекта, от простых до злых. На изменениях harness — новых правилах, инструментах, промптах — набор прогоняется в CI, как регрессионный набор. Меряем четыре вещи: успех задачи (закрыта или нет, с частичным зачётом), корректность работы с инструментами (правильный выбор, валидные аргументы, без лишних вызовов), качество траектории (число шагов, время, стоимость) и безопасность (не тронуты запретные зоны, не ушло ничего наружу). Моки инструментов делают прогоны детерминированными и дешёвыми: внешние системы заменяются записанными ответами.

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

Чего я не делаю

Не принимаю отчёт агента как доказательство. Отчёт — это заявление, доказательство — независимая проверка. Формат отчёта, про который я писал в посте про QA-агентов, нужен для скорости ревью, а не для отмены ревью.

Не гонюсь за покрытием как целью. Агент радостно напишет двести тестов, которые ничего не ловят. Пять тестов, прибитых к критериям приёмки, стоят больше.

Не использую модель-судью без рубрики и без аудита. «AI проверяет AI» звучит красиво, а без дисциплины превращается в генератор ложного спокойствия.

И не считаю падение evals багом агента по умолчанию. Половина падений в моей практике — это регресс harness: поменяли правила, сломали поведение. Именно для этого прогоны и существуют.

Главный вывод

Доверие к агенту не вырастает из уверенных отчётов и зелёных иконок. Оно производится структурой: независимыми критериями приёмки, тестами от требований, иерархией проверок, трассами для разбора и evals для регрессий самого агента. Когда это построено, «можно мёржить» перестаёт быть актом веры и становится выводом из фактов.

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

Практический чеклист

  1. Критерии приёмки фиксируются до реализации, а тест сначала падает на старом коде, потом зеленеет.

  2. Проверки разведены по уровням: дешёвые — на каждом шаге агента, тяжёлые — на границе PR.

  3. Изменение или удаление чужого падающего теста — красный флаг и остановка агента.

  4. По каждой сессии есть трассировка: инструменты, аргументы, ответы, стоимость шага.

  5. Трассировка разбираются не только при провалах: регулярный аудит успешных сессий тоже.

  6. Есть набор типовых задач для регрессии агента, и он гоняется в CI на изменениях harness.

  7. Модель-судья работает с рубрикой и структурированным ответом, выборка перепроверяется человеком.

  8. Падение evals сначала проверяется как регресс harness, а не как «модель глупеет».

Следующая часть — guardrails: песочницы, бюджеты и запретные зоны. Если верификация отвечает на вопрос «правильно ли агент работает», то ограничения — на вопрос «что будет, когда он сработает неправильно». А у вас evals для агентов есть, или пока «и так сойдёт»?

Discussion

No comments yet - start the thread.

No comments yet - start the thread.

Leave a comment

Comments are published immediately after submission.