Перейти к содержимому
Экосистема AI Vibe

Разборы

Автотесты от агента могут ухудшить repair-цикл: как отличить зелёный свет от ложного фикса

Редакция 12 сентября 2026 г.

Автотесты от агента могут ухудшить repair-цикл: как отличить зелёный свет от ложного фикса

Пороговый вопрос для соло-разработчика с агентом в цикле «патч → прогон тестов → следующий edit»: если ваш suite не различает None и пустой список, зелёный прогон закрепит неверный фикс, и агент пойдёт чинить дальше в ложном направлении.

Когда зелёные тесты ухудшают агентов правки кода

На SWE-bench Verified repair-агент Qwen-3.5-35B-A3B получал только разный test feedback. Слабые generated tests сбивали resolved rate сильнее, чем кажется по зелёному статусу в терминале.

Препринт ExecCritic (Leitian Tao и соавторы, arxiv/html/2609.09133v1, опубликован 8 сентября 2026) сравнивает источники тестового feedback при фиксированном repair-агенте. Sergei Parfenov на Dev.to пересказывает таблицу «Tasks resolved»:

Источник feedback Tasks resolved
Initial repair, до generated-test feedback 61,2%
Tests from the base Qwen Test agent 57,3%
Tests from GPT-5.6-sol 65,3%

Слабые автотесты снизили долю решённых задач на 3,9 п.п. (61,2% → 57,3%); более сильный test agent поднял показатель до 65,3%. ExecCritic разделяет создание тестов и repair, квалифицирует тесты на исходном репозитории и не меняет их во время revision, чтобы repair-агент не «подгонял» цель под меняющийся критерий.

Это пересказ бенчмарка с оговорками: среднее по трём repair runs, compute budgets не сопоставлены, resolution определяет отдельный evaluator (Method and results, §5). Assert задаёт направление следующего хода агента; слабый feedback хуже, чем его отсутствие.

Три режима фильтра: как патч агента проходит слабые assert

Чтобы увидеть провал без LLM и сети, разбирается намеренно простая фикстура: функция filter_orders(orders, statuses=None) с тремя требованиями:

  1. Без фильтра или statuses=None → вернуть все заказы.
  2. statuses=[] → вернуть пустой список.
  3. Список статусов → только совпадающие заказы.

Исходный баг: при отсутствии фильтра функция ничего не возвращает. Багованная реализация:

def filter_orders(orders, statuses=None):
    statuses = statuses or []
    return [order for order in orders if order["status"] in statuses]

Агент предлагает «правдоподобный» патч:

def filter_orders(orders, statuses=None):
    if not statuses:
        return list(orders)
    return [order for order in orders if order["status"] in statuses]

В Python и None, и [] дают falsey в условии. Патч с if not statuses сливает разные семантики: для пустого списка статусов вернутся все заказы вместо пустого результата.

Минимальный набор из двух assert проходит на ложном патче:

  • filter_orders(ORDERS) == ORDERS
  • filter_orders(ORDERS, ["paid"]) == [ORDERS[0]]

Ключевая добавочная проверка:

assert filter_orders(ORDERS, []) == []

На ложном патче она падает. Хуже ситуация с тестом, где неверное ожидание assert filter_orders(ORDERS, []) == ORDERS: он проходит на broken patch и ломает корректную реализацию. В цикле правки такой assert даёт агенту повод «чинить» код в неверном направлении.

Корректный фикс явно разделяет отсутствие фильтра и пустой список:

def filter_orders(orders, statuses=None):
    if statuses is None:
        return list(orders)
    return [order for order in orders if order["status"] in statuses]
Implementation Default + paid-filter checks + empty-list check
Original 1 pass, 1 fail 2 pass, 1 fail
Plausible patch 2 pass 2 pass, 1 fail
Corrected patch 2 pass 3 pass

Runnable companion на stdlib Python без LLM и сетевых вызовов описан у Parfenov на Dev.to; отдельный URL репозитория в тексте не назван. Для ручной проверки предлагается мутация statuses is Nonenot statuses: она меняет наблюдаемое поведение на обязательных входах.

Типичные ошибки интерпретации контракта, которые тест должен различать: игнорировать фильтр целиком; считать любой missing filter пустым; считать любой empty filter отсутствующим. Несколько примеров с paid orders информативнее, чем один «счастливый» кейс.

Четыре шага review перед следующим ходом агента

Четыре шага для agent workflow без обучения модели, в существующем проекте (по примеру Parfenov):

  1. Зафиксировать ожидаемое поведение до review патча. Обычный кейс, reported failure и соседний кейс, который легко спутать (None и [] здесь отдельные строки спецификации). Если issue не различает интерпретации, product decision до превращения в тест.
  2. Review expected values как production code. Assertion = claim о продукте; трассировать к requirement или независимо проверенному примеру. Копирование текущего output в expected value может закрепить именно то поведение, которое под вопросом.
  3. Держать принятый regression check стабильным во время repair. Агент меняет implementation, отдельный runner гоняет reviewed tests; защищать test command и config. Если тест неверный, явно пересмотреть, потом снова оценить кандидата.
  4. Разобрать failure до просьбы агенту чинить. Assertion с неверными orders = behavioral evidence; missing dependency или zero tests selected относятся к другому классу ответа. Записать, что запускалось и почему упало.

Финальный практический тест для AI-assisted fix: сохранить original code, proposed patch и new tests; назвать одну правдоподобную альтернативную реализацию, нарушающую requirement; прогнать тесты. Если обе дают green, suite не отвечает на конкретный вопрос: добавить check, разделяющий их, и review expected result до следующего repair.

Какой ложный фикс пропустит AI-generated suite

Which plausible wrong implementation would this test reject? Вопрос из канонического текста, на него стоит ответить до зелёного света.

Если ответа нет, агент получает ложное подтверждение. Слабый test agent в ExecCritic снижал resolved rate; сильный поднимал. Локальная фикстура filter_orders показывает тот же класс ошибки без бенчмарка: два assert на «обычный» и paid сценарии не отсекают патч, который ломает statuses=[].

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


Источники