Соавтор статьи — Сергей Левенец, CTO в команде ТестОпс.

Что останется делать людям, когда код будут писать машины? Практика показывает — на удивление много, без работы никто не останется. В двух статьях мы опишем реальный опыт создания внутреннего агента для исправления багов в команде ТестОпс. Расскажем, сколько такой агент экономит, как его отлаживать, и чем автоматическая правка отличается от диалога с Claude Code.

В команде ТестОпс разработка поставила себе задачу: ноль багов в бэклоге. Эта амбициозная задача запустила большую работу по исследованиям и разработке, в результате которой родился агент, способный исправлять и проверять баги. Команда стихийно назвала его «Агент Смит».

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

1. Знакомьтесь, Агент Смит

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

Сразу скажем про главное свойство этого процесса: это не цепочка промптов, а граф с ветвлениями и ранними остановками. Каждый шаг обязан вернуть машинно читаемый артефакт (JSON по схеме), а решение о том, какой шаг будет следующим, принимает код, читая эти артефакты. Модель не решает, куда идти дальше.

Прежде чем перечислять шаги — оговорка: текущий набор — не канон, а временная гипотеза; агент постоянно отлаживаеся, как именно — поговорим чуть позже.

Вот как выглядит начало прогона:

Шаги прогона
Шаги прогона

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

PREPARE

Агент готовит рабочую зону. Первое, что он определяет, — от какой ветки отталкиваться и куда потом пойдёт мёрж-реквест: в основную ветку разработки, в релизную или в фича-ветку. Это зависит от контекста задачи. Дальше выполняется чекаут, создаётся своя ветка под фикс и запускается переиндексация графа кода; если граф не собрался, следующий шаг не получит граф-контекст вовсе — устаревший граф хуже, чем никакого. Здесь же действует список защищённых путей (helm/, k8s/, Dockerfile, конфиги CI): багфикс не имеет права их трогать ни при каких обстоятельствах.

ACCEPTANCE

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

Отсюда требования к самим критериям. Критерий должен описывать наблюдаемое поведение, а не код: «должно быть 200 вместо 401» — годится, «метод должен вернуть false» — нет. Каждый критерий опирается на дословную цитату из репорта: если нечего процитировать — значит, это догадка, а не требование.

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

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

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

DIAGNOSE

Агент читает код, ничего не меняя, и ищет корневую причину — не место падения. На выходе он даёт гипотезу, файл и строки, отвергнутые версии и слой, в котором будет правка.

Два вопроса здесь дороже остальных. Это один баг или целый класс — бьют ли соседние входы в тот же дефектный путь? И меняет ли фикс инвариант, на который опирается чужой код? Если да, правка перестаёт быть локальной, какой бы маленькой она ни была.

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

Глубину фикса агент выбирает сам. Точнее, выбирал бы: рефакторинг ему пока запрещён — масштабные правки увеличивают технический долг. Почему — обсудим в разделе 3.

JUDGE

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

Здесь ловится классическая ошибка — зацепиться за яркую деталь из примера вместо сути репорта. При расхождении диагноз пересобирается с фидбеком судьи, до двух раз; дальше прогон останавливается, и задача возвращается человеку с обоими вердиктами: что агент считал причиной и почему судья с этим не согласился.

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

Шаги прогона
Шаги прогона

ESTIMATE

Оценка сложности в стори-поинтах по шкале {0.5, 1, 2, 3, 5}, причём для каждого значения в промпте прописаны объективные признаки — чтобы это была классификация, а не интуиция. Есть и второе основание для паузы: оценка радиуса поражения; практика показала, что это самостоятельная ось риска, ортогональная объёму правки.

Шкала асимметрична намеренно. Оценка от двух считается дорогой, и останавливает процесс до подтверждения человеком. Поэтому агент обязан обосновать, что это именно двойка, а не единица. По сути, этот порог — мера доверия к агенту, и может меняться со временем.

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

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

BRT

Тест воспроизводимости ошибки (Bug Reproduction Test): красный до фикса, зелёный после. Он пишется до самой правки, не трогая код продукта.

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

Агент не запускает тест сам — в песочнице нет ни сети, ни сборщика; благодаря этому шаг из декларации превращается в проверяемый факт. Красноту проверяет код снаружи и засчитывает единственный исход: падение именно на ассерте, при любом другом варианте тест откатывается. Без этой внешней проверки подход BRT-first превращается в «агент утверждает, что тест красный».

Проведённое в Google исследование даёт первую промышленную оценку
такого подхода: в тех случаях, где их агент мог написать воспроизводящий тест, система автоматического исправления выдавала правдоподобный патч в 74% случаев (17 из 23) против 57% (13 из 23) без теста. Здесь важны даже не сами цифры, а соотношение «с тестом / без теста»: подход BRT явно работает. Свою статистику мы сейчас как раз набираем: шаг BRT переключается тумблером на лету, чтобы можно было оценить результат с тестом и без него.

IMPLEMENT

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

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

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

SELF-REVIEW

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

Оговорка: это единственный шаг, где агент оценивает сам себя, и он намеренно ничего не решает. Интроспективная самокоррекция у языковых моделей работает плохо —
Huang et al. (ICLR'24) показали, что без внешнего сигнала качество рассуждений после самопроверки может даже падать. Важны независимые шаги и запуск тестов, а self-review —
дешёвая уборка перед ними; на повторных попытках он и вовсе пропускается.

QUALITY

Точечный запуск тестов. Выполняется не вся сюита, а тест, объявленный агентом, плюс фронтовые проверки (линтер, типы, локали), если затронут фронтенд. Если BRT был красным, теперь он обязан позеленеть.

Как и в любом решении, которое принимают по результатам прогонов, варианты «тест упал» и «не удалось проверить» дают разные исходы.

  • Инфраструктурный сбой не отправляет фикс в доработку и не жжёт попытки: доработка тут не поможет.

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

Шаги прогона — в этот раз шаги IMPLEMENT и QUALITY пришлось пройти дважды
Шаги прогона — в этот раз шаги IMPLEMENT и QUALITY пришлось пройти дважды

VALIDATE

Независимый валидатор смотрит на готовый дифф и выносит вердикт по уликам — бродить по репозиторию и гонять сборку ему запрещено. Это самый содержательный шаг пайплайна.

Жёстких оснований для вето есть два.

Первое — костыль или оверинжиниринг: catch-all, return true, закомментированная логика, отключение линтера — и отдельно то, что кодифицируют редко: глобальное состояние ради протаскивания данных, доступных локально; новый класс там, где хватило бы правки в естественной точке.

Второе — покрытие критериев приёмки: по каждому выносится вердикт с уликой из
диффа, и если хоть один из основных критериев не закрыт — на доработку. На этом шве может проскочить ситуация «починил заявленный пример, а не заявленное поведение».

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

Обратите внимание: здесь аудируется именно траектория, а не результат. На этапе VALIDATE зелёный запуск доказательством не является: агент мог подогнать тест.

Принципиальным решением было развести вето и мягкие сигналы. Блокируют выполнение только костыли и незакрытые основные критерии критерий; остальное едет в мёрж-реквест пометкой человеку (например, ослаблена защитная проверка, расширена видимость метода ради переиспользования, фронт-юнит мокает бэкенд и даёт ложное зелёное). Иначе валидатор становится слишком строгим — ломает годные фиксы и убивает пропускную способность.

REGRESSION

Запуск всей тестовой сюиты — этот шаг отключён из-за того, что стоит много ресурсов.

PUBLISH и TRANSITION

Агент публикует мёрж-реквест для созданной им ветки и переводит задачу в статус готовности к ревью.

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

2. Отладка Смита

Может возникнуть вопрос — зачем агенту столько шагов? Универсальный кодовый агент вроде Claude Code умеет сам планировать, ходить по репозиторию и чинить, причём с отдельно взятым багом справляется не хуже, а часто и лучше.

Разница не в том, кто умнее, а в том, что мы оптимизируем. Нам нужен не одиночный хороший результат, а предсказуемый поток, который можно улучшать. А улучшать можно только изолированные вещи: где именно прогон свернул не туда, какой шаг это сделал и что изменилось после правки промпта. Отсюда цепочка шагов, в которой каждый создаёт артефакт и передаёт его следующему: это не способ сделать агента умнее, а способ сделать его отлаживаемым.

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

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

Петля первая: правила.

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

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

Петля вторая: сами шаги.

Здесь мы собираем статистику о том, на каких шагах агент чаще всего просит человека и на каких проверки разворачивают решение назад: сколько раз судья отправил диагноз на пересборку, сколько прогонов вернулось из проверок в доработку, где чаще всего срабатывает порог подтверждения. Это не украшение дашборда, а инструмент проектирования пайплайна.

Читается такая статистика в обе стороны. Если шаг перестал что-либо находить, и его проверка всегда зелёная, это не значит, что он работает, просто проблема ушла куда-то ещё, и шаг перестал приносить пользу. Поэтому он — кандидат на удаление.

Обратный случай — шаг, заворачивающий половину прогонов: это кандидат на переработку,
причём чинить чаще всего надо не его, а то, что стоит выше по цепочке.

Читая эту статистику, мы постоянно отлаживаем шаги, и нынешняя конфигурация точно не окончательная: часть шагов со временем отвалится, часть срастётся. Способность отслеживать этот процесс и управлять им — такая же часть системы, как сами шаги.

3. Результаты работы агента

Достиг ли агент своей непосредственной цели? Статистика говорит, что да.

Агентная правка багов

На сегодняшний день у багофикс-агента 100 прогонов. 76 из них закончились открытым мёрж-реквестом, 62 влиты — merge-rate 81,6%. Из влитых 53 ушли в основную ветку без единой правки человеком: то есть примерно пять из шести принятых фиксов оказались готовыми как есть, и только 9 потребовали, чтобы человек что-то дописал руками.

Что же с остальными фиксами? 7 мёрж-реквестов ещё висят, 7 отклонены — но по-настоящему переписывать руками пришлось лишь в трёх случаях, остальные закрыли как экспериментальные прогоны.

24 прогона до мёрж-реквеста вообще не дошли: это те самые ранние остановки, про которые шла речь выше: фикс не доказан, значит и предлагать его не надо.

Ноль багов в бэклоге

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

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

С этого и начинается R&D.

В итоге наработки, сделанные ради багофиксов, дали выхлоп и для других задач. Агент уже
разрабатывает простые фичи — и часть из них разработчики апрувят без единого комментария. Опять-таки, в теории с такой задачей справляется и Cursor. Но он это делает с большим количеством ручных шагов, в постоянном диалоге с человеком — то есть это по-прежнему рутинная задача. Новизна наших экспериментов в том, что они интегрируют человеческую экспертизу и сводят вмешательство к минимуму.

4. Заключение

Агентный код — уже давно не новость. Агентный код, стабильно, массово и качественно закрывающий задачи с минимальным вмешательством человека — к этому отрасль пока только идёт. Эксперимент, который поставила команда ТестОпс, показывает дорожку к этой цели.

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

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

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