Как мерить свой пайплайн разработки на ИИ. Начинаю с ревьюера
В продолжение статьи "Доводим задачи 1С до боевой базы: конвейер из семи агентов, восемь задач, шесть промахов".
Разработка на 1С с ИИ многим уже более-менее знакома, и главный вопрос теперь другой: как мерить качество своего пайплайна. Как проверить, что правила, которые вы завели после ретроспективы, действительно работают? А может, их вообще не надо делать правилами, может, нужна машинная проверка.
Мерить надо с какого-то одного шага. Я начал с ревьюера: от него зависит, что дойдёт до человека, и его выход удобнее всего считать - это список находок. Сейчас в постоянном бенчмарке 4 задачи, причём задачи реальные, а не искусственно придуманные, то есть те, что выполнялись в рамках работы.
Если вы только начинаете и что-то в статье покажется сложным, в разделе 10 есть короткая инструкция, как попробовать то же самое у себя с одним агентом. Можно начать с неё, а потом прочитать статью целиком.
Сначала несколько слов, которые дальше будут встречаться постоянно:
- Кейс - то, что ревьюер получает на вход: код задачи и её документы, иногда с подложенными ошибками.
- Находка - одно замечание ревьюера или скрипта к коду: что не так, в каком месте и по какому правилу.
- Эталон - список находок, которые ревьюер должен выдать на кейсе.
- Бенчмарк - набор кейсов с эталонами, на котором я гоняю ревьюера.
- Прогон - одно ревью одного кейса. Круг - по одному прогону на каждый кейс набора.
- Разметка - мой разбор находок, которых в эталоне нет: каждая из них реальная, ложная или вне оценки.
- Редакция пайплайна - состояние правил, инструкций агентам и скриптов на момент замера. Правка переводит пайплайн из одной редакции в другую.
- Оркестратор - агент, который ведёт задачу и раздаёт работу остальным. Хук - скрипт, который среда агентов запускает сама на событие, например перед запуском ревьюера.
1. Свод правил и колонка "Проверки"
В прошлой статье я писал, что у каждого правила должен быть ответ, чем оно проверяется. Сейчас это колонка в своде. Стандарты и свои регламенты я веду в одной таблице. У каждого правила постоянный номер, и дальше все ссылаются на него: ревьюер в отчёте, скрипты в выводе. Агенты таблицу целиком не читают: из неё по колонке "Кому" собираются файлы правил для каждой роли, и хук пересобирает их при каждом изменении таблицы. Так агент не тратит контекст на правила, которые к его роли не относятся. Правила, которые целиком проверяет машина, убираются только из файлов, которые получают роли: агенту не нужно держать их в голове. В самом своде они остаются со своим номером, и по нему скрипт подписывает свои находки.
| № | Правило | Кому | Ловит | Проверки |
|---|---|---|---|---|
| std436 | Явные запросы и чтения из базы в цикле запрещены | analyst, architect, developer, reviewer, tester | скрипт, ревьюер | линтер: явное чтение из базы в цикле и вызов в цикле метода, который читает базу; чек-листы ревьюера и разработчика |
| К-087 | Откаты транзакций, отказы фоновых заданий и необработанные исключения пишутся в журнал регистрации | architect, developer, reviewer | скрипт, ревьюер | линтер: откат транзакции без записи в журнал |
| std441 | Ключевые слова в коде пишутся канонически, как в Синтакс-помощнике | developer, reviewer, tester | скрипт | линтер: НЕ и ИЛИ прописными в коде; диагностика BSL LS |
| К-021 | Вложенные циклы с поиском совпадений заменяются поиском по Соответствию | architect, developer, reviewer | ревьюер | - |
"Ловит" - кто по процессу может заметить нарушение. "Проверки" - чем именно правило проверяется: машинная проверка или пункт чек-листа. Прочерк значит, что правило держится только на том, что его кто-то прочитает.
На момент замеров у меня было 223 действующих правила. У 122 из них, то есть больше чем у половины, не было ни одной проверки. Машинная проверка была у 68, причём у 37 только она. Когда видишь такие числа, как раз и возникает вопрос, кто проверяет правило и стоит ли его вообще держать.
2. Что сдаёт ревьюер
Ревьюер пишет отчёт для человека, а в конце прикладывает машинный блок находок. Проза для меня, блок для скриптов.
{
"редакция": 1,
"вердикт": "вернуть",
"находки": [
{"id": "В-1", "level": "В", "class": "ИСКЛЮЧЕНИЯ", "rule": "К-087",
"file": "CommonModules/ОбменСБанкомСервер/Module.bsl",
"lines": "120-134",
"anchor": "цитата строки кода, по которой находка ищется после сдвига",
"summary": "обёртка фонового задания гасит исключение без записи в журнал",
"status": "новое"}
]
}
редакция - какое по счёту это ревью задачи, status - новая это находка или повторная с прошлого ревью. id - номер находки внутри отчёта, rule - номер правила из свода. level - уровень: К критично, В важно, Н незначительно. class - вид дефекта из общего классификатора: логика, запрос, исключения, права и так далее, чтобы промахи можно было считать по видам. anchor нужен, чтобы находка нашлась после того, как номера строк сдвинулись.
Одна находка - одна запись в блоке. Если одно правило нарушено в нескольких местах, это одна находка со списком строк. А разные дефекты в одну запись не склеиваются, даже если они одного класса.
3. Откуда кейсы
Кейсы для замеров я собираю двумя путями.
- Снимок реальной задачи перед ревью. Перед запуском ревьюера стартует хук, который сохраняет все изменения задачи: что добавлено, удалено и поправлено в коде и метаданных. Так сделано специально: после ревью код будет уже исправлен, а мне нужны реальные дефекты. Помимо изменений хук кладёт артефакты задачи: постановку, ТЗ, проектное решение, отчёт о реализации и сценарии тестирования. В таблицах ниже такие кейсы обозначены буквой D. D1 и D3 сняты с одной и той же задачи.
- Готовая задача с подложенными ошибками. Логика такая: беру итоговый код закрытой задачи, вношу в него ошибки и записываю, что ревьюер должен на них ответить. Ошибки я внёс сам, поэтому эту часть эталона знаю заранее. Таких задач у меня три, Б1-Б3, в каждую подложено шесть ошибок и одна ловушка: законный код, который похож на дефект. Если ревьюер отметил ловушку, это ложное срабатывание. Так видно, придирается ли ревьюер к нормальному коду.
Кейсов получается больше, чем задач: каждую задачу Б я гоняю в нескольких вариантах, которые отличаются тем, как ревьюер читает правила, например с инвентарём и без него. Что такое инвентарь, расскажу в разделе 7.
4. Как я готовлю эталоны
С эталоном сравнивается каждый прогон. Для двух видов кейсов эталоны готовятся по-разному.
- Для снимка реальной задачи часть находок известна из работы: задача прошла ревью и агентом, и мной, а мои возвраты оркестратор записывает в журнал задачи. Скрипт собирает из журнала и из исправленного после ревью кода то, что нашли и исправили. Но прогоны на снимке находят и то, что в работе пропустили оба ревью. Такие находки я размечаю и тоже добавляю в эталон.
- С подложенными ошибками сложнее. Беру итоговый код закрытой задачи, и ревьюер находит в нём больше, чем я подложил. Заметная часть - оформление и стандарты: метки вставок, комментарии в теле кода, длина и сложность методов, имена. Причины тут простые. Задачи делались в разное время, а свод правил с тех пор вырос: например, правило, что комментарии пишутся только в описании метода, появилось недавно, а задача Б2 сделана раньше. Ревьюер же проверяет код по сегодняшнему своду. Ну и что-то в работе просто не углядели, такое тоже бывает.
То есть, когда я беру кейс для замера, я сначала прогоняю обычное ревью и размечаю всё, что ревьюер нашёл сверх эталона. Это нужно, потому что правила за время проверок могли измениться. Каждую такую находку я отношу к одной из трёх групп:
- реальная - пополняет эталон;
- ложная - уходит в список ложных, и если ревьюер повторит её в следующих прогонах, это засчитается как ложное срабатывание;
- вне оценки - по делу, но на качество кода не влияет, например расхождение кода с документацией задачи.
Тут важно отметить, что машинные проверки тоже входят в счёт: я меряю этап ревью целиком, включая скрипты. Машинные проверки (скрипты, линтер) написаны так, что возвращают отчёт в том же виде, что и ревьюер, чтобы потом можно было объединить результаты. Раньше было не так. То, что ловят инструменты, ревьюер сам не ищет, а только приводит их отчёт. Если он не переносил эти находки в свой блок, в счёт они не попадали: ревьюер терял баллы, и полезная правка выглядела как ухудшение. А если ревьюер что-то стабильно пропускает, это видно по пропущенным находкам эталона, и тогда я ищу способ сделать эту проверку машинной.
5. Счёт
Скрипт сверяет машинный блок отчёта с эталоном по файлу и номеру правила из свода. На выходе по каждому кейсу четыре числа: сколько ожидаемых находок найдено, сколько найдено с заниженным уровнем, сколько пропущено и сколько лишних, то есть тех, которых в эталоне нет.
Например, задача Б2 с инвентарём, второй круг: из 24 ожидаемых найдено 18, ещё 2 найдены с заниженным уровнем, 4 пропущены, лишних ноль.
6. Насколько гуляет результат
Ревьюер - модель, и два одинаковых прогона дают разные отчёты. Поэтому прежде чем верить разнице между редакциями пайплайна, надо знать, насколько различаются прогоны одной и той же редакции.
| Набор | Редакция пайплайна | Два прогона одной редакции |
|---|---|---|
| Снимок D1, в эталоне 27 находок | ранняя | 48% и 37% |
| Снимок D3, в эталоне 33 находки | промежуточная | 58% и 55% |
| Задачи Б1-Б3, в эталоне 48 находок | поздняя, без инвентаря | 67% и 69% |
| Задачи Б1-Б3, в эталоне 48 находок | поздняя, с инвентарём | 77% и 75% |
Из цифр видно, что один и тот же запуск с одними и теми же правилами гуляет на 2-3 пункта. В самом начале, на ранней редакции, было 11 пунктов.
Что это значит на практике: если после правки результат вырос или упал на несколько пунктов в одном прогоне, это ещё ничего не говорит, столько же дают прогоны без всякой правки. То есть внимание я обращаю, когда разница больше 3 пунктов.
7. Пример: как я проверил одну правку
Покажу на одном примере, как всё это работает целиком: придумал правку, прогнал, сравнил с разбросом, принял решение.
Правка такая. Пункт чек-листа "проверить транзакции" легко отметить, ничего не проверив. Поэтому перед ревью теперь запускается скрипт, который проходит по строкам, добавленным или изменённым задачей, и по ключевым словам находит места четырёх видов:
- транзакции: НачатьТранзакцию, ЗафиксироватьТранзакцию, ОтменитьТранзакцию, БлокировкаДанных;
- запросы: текст с ВЫБРАТЬ, ГДЕ, СОЕДИНЕНИЕ или параметром &Имя, а также запросы в схемах СКД;
- циклы: Для Каждого, Для или Пока, если в теле цикла явно читаются данные, например запрос, ЗначенияРеквизитовОбъекта() и т.д.;
- исключения: Попытка, ВызватьИсключение, ЗаписьЖурналаРегистрации.
Я называю этот список инвентарём. По каждому месту ревьюер пишет вердикт: в порядке и почему, или номер своей находки. Пока хотя бы одно место без вердикта, отчёт не принимается. Решение остаётся за агентом, но пропустить место он не может.
Как мерил. Взял три задачи Б1-Б3 и прогнал ревью в двух вариантах: без инвентаря и с ним. Каждый вариант прогнал в два круга, чтобы видеть разброс. Всё остальное одинаковое: код, подложенные ошибки, эталон.
Сколько находок из эталона нашла стадия ревью, то есть ревьюер вместе с машинными проверками:
| Задача | Без инвентаря, круг 1 | Без инвентаря, круг 2 | С инвентарём, круг 1 | С инвентарём, круг 2 |
|---|---|---|---|---|
| Б1, в эталоне 8 находок | 7 | 7 | 8 | 8 |
| Б2, в эталоне 24 находки | 15 | 17 | 16 | 18 |
| Б3, в эталоне 16 находок | 10 | 9 | 13 | 10 |
| Итого из 48 | 32 (67%) | 33 (69%) | 37 (77%) | 36 (75%) |
Из отчёта стало видно, что правка с инвентарём сработала: в обоих кругах найдено больше, а ложных находок не прибавилось. Заодно разбор прогона показал ошибку в самом скрипте инвентаря, её я исправил.
8. Как я на самом деле принимаю правку
Формально всё просто: правку принимаю, если находок из эталона стало больше, а лишних не прибавилось. На деле я смотрю не только на процент. Ещё важно, сколько правка стоит в токенах и не сломает ли она что-то, чего замер не покажет.
Пример. Сейчас каждая роль читает только свои правила, включая общие для всех. Появилась идея разделить правила ещё и по темам задачи: транзакции, права, формы и так далее. Я сделал правку и прогнал замеры. Они должны были показать, что регресса нет: правила остались те же, я только разложил их мельче.
Первую попытку бенчмарк отклонил сам, результат упал. Во второй раз, на трёх задачах, процент вырос с 70% до 74%. И всё равно для ревьюера я правку отклонил. Причин две:
- Токенов меньше не стало: ревьюер тратит их в основном на код и документы задачи, а не на правила.
- Темы задачи определяет оркестратор, и есть риск, что он определит их неверно. Тогда ревьюер не увидит нужных правил, а он как раз страхует остальных.
Ревьюер по-прежнему читает файл своей роли целиком.
Бывало и наоборот: правку принимал, хотя процент упал.
| Правка | До | После | Почему принял |
|---|---|---|---|
| Машинная проверка прав: каждый новый объект задачи должен входить хотя бы в одну роль с включённым правом | 71% | 69% | дефект "общая команда без роли" до проверки ревьюер находил 1 раз из 5 прогонов, с проверкой нашёл сразу; падение на 2 пункта в пределах разброса |
| Машинные проверки переехали из моего скрипта в линтер bsl-ls-mcp (моя обёртка над BSL LS) и инструмент правки кода 1c-meta | 80% | 77% | я хотел, чтобы проверки жили в инструментах, которые развиваются отдельно. Всё, что ловили старые проверки, нашлось и после переноса, а падение на 3 пункта в пределах разброса |
9. Сколько это стоит
Ревьюер у меня работает на Claude Opus 5.5. Одно ревью задачи в работе занимает 10-25 минут и 50-100 вызовов инструментов. Время на разметку я не фиксировал.
Что касается 10-25 минут на ревью - да, это долго. Но в замере работает тот же ревьюер с теми же правилами и инструментами, что и в боевой задаче. Он читает правила своей роли, ТЗ и проектное решение, сам код задачи, смотрит вывод линтера и инструмента правки кода, выносит вердикт по каждому месту из инвентаря и пишет отчёт.
По деньгам: я работаю по подписке за 100 долларов в месяц, и при постоянной работе над задачами в инхаусе, когда весь цикл разработки ведут агенты, мне её хватает.
Ревью идёт на каждой задаче, поэтому сейчас я стараюсь сделать его дешевле. И тут в помощь сам бенчмарк: по нему видно, сколько токенов сэкономила правка и нет ли при этом регресса.
10. Как начать с простого
Всё это можно попробовать с одним ИИ-помощником. Нужен список правил, по которым он проверяет код. Если его нет, выпишите в текстовый файл всё, что вы обычно правите за ИИ на ревью, и давайте этот файл ИИ вместе с кодом.
- Возьмите две-три свои закрытые задачи целиком, со всеми изменёнными модулями, и скопируйте код. В копию внесите несколько ошибок, которые нарушают ваши правила. Берите не очевидные, а такие, какие вы реально находили на ревью. Добавьте один кусок нормального кода, похожий на ошибку, и запишите, что и где подложили. Чем ближе к живой задаче, тем лучше: на маленьком куске кода ИИ найдёт всё, и замер ничего не покажет.
- Дайте ИИ правила, код и попросите: проверь код по этим правилам и перечисли нарушения. Сверьте ответ со своим списком: сколько ошибок нашёл, сколько пропустил, сколько раз назвал ошибкой то, что ею не было.
- Повторите то же в новой сессии ещё 2-3 раза. Числа будут разными - это и есть разброс. Пока вы его не знаете, не верьте улучшению на одну-две найденные ошибки.
- Пройдите по своим правилам и для каждого ответьте: кто его проверяет. ИИ на глаз или что-то надёжное - скрипт, проверка синтаксиса, тест.
- Найдите правило, которое ИИ в одних повторах находит, а в других нет. Напишите скрипт, который ищет его нарушение в тексте модуля, или попросите ИИ написать его и сами протестируйте. Потом добавьте в запрос: перед проверкой запусти этот скрипт и включи его находки в ответ. Для этого нужен помощник, который умеет запускать команды на вашем компьютере.
- Снова сделайте 2-3 повтора. Если нарушение этого правила теперь находится каждый раз - поздравляю, оно больше не зависит от того, как модель отработала в этот раз.
11. Недостатки подхода
Эталон размечаю я один, второго разметчика нет: что считать реальной находкой, а что ложной, решаю я.
Правила я правлю и проверяю на одних и тех же задачах. Поэтому есть риск подогнать правила под эти задачи: на них всё работает, а на новой задаче уже нет. Защита от этого - новые кейсы. Хук снимает кейс при каждом запуске ревьюера, после разметки такой кейс можно добавить в набор, и правило, подогнанное под старые задачи, проверится на новых.
Кейс - замороженное состояние, а база и правила за это время двигаются, так что кейсы стареют. Тут как раз помогает разметка: если на старом кейсе появились новые находки по правилам, которых раньше не было, значит эталон устарел и его пора дополнить.
Что я вынес для себя
- У каждого правила должен быть ответ, чем оно проверяется. Если ответа нет, правило держится только на том, что его кто-то когда-то (не факт) прочитает.
- Прежде чем радоваться улучшению, я смотрю, насколько расходятся одинаковые прогоны.
- То, что ревьюер находит через раз, я переношу в машинную проверку. Тогда это правило перестаёт зависеть от того, как модель отработала в этот раз.
Делитесь мнениями, как вы измеряете свой пайплайн разработки. Правила-то скапливаются, и их надо как-то проверять. Очень интересно узнать опыт других по этой теме.
P.S. Пока писал статью, появились идеи по самому пайплайну. Например, темы задачи сейчас задаёт оркестратор по постановке, а их можно пересчитывать после проектного решения, когда уже известно, какие объекты затронуты. Или вопрос, нужна ли агентам проза в отчётах. Машинный блок уже есть, а что сделано, и так видно по diff в git. Без прозы токенов уйдёт меньше. И ещё есть мысль проверить, не слишком ли много у меня агентов: есть ощущение, что они читают лишнее, но без замеров разговор пока не предметный. Проверять всё это буду тем же бенчмарком. Начну с этапа ревью, там он уже есть, а для остальных этапов замер ещё предстоит придумать.
Вступайте в нашу телеграмм-группу Инфостарт
Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».
- 0% комиссии — оплата напрямую исполнителю;
- Исполнители любого масштаба — от отдельных специалистов до команд под проект;
- Прямой обмен контактами между заказчиком и исполнителем;
- Безопасная сделка — при необходимости;
- Рейтинги, кейсы и прозрачная система откликов.