Не согласны с замечанием: как поспорить, чтобы бот ответил
Бот оставил замечание на строке кода в вашем MR/PR, а у вас есть причина, по которой его тут быть не должно: лог внутренний, код старый, приём сделан намеренно. Можно закрыть тред и идти дальше — так делают часто, и именно так ревью превращается в формальность. А можно возразить в треде и получить ответ.
Вот пример — тред в публичном MR нашего учебного репозитория на GitLab. Разработчик возражает: логи не покидают кластер, хранятся 14 дней, доступ к ним только у дежурных, а поддержка ищет счёт по почте, потому что номера клиент не знает. Бот отвечает, что понимает довод про закрытый контур, но правило команды рассчитано как раз на такой случай: чтобы найти запись, достаточно идентификатора. Искать счёт по почте поддержка должна в базе или через отдельный механизм, а не в логах, — и замечание бот оставляет в силе.
Ответ бота — это один вызов модели, несколько центов. Согласны вы с ним или нет — отдельный вопрос; суть в том, что это довод, на который можно ответить, а не вердикт, который остаётся только закрыть.
Ответы по умолчанию выключены. Их включает reply.enabled: true в .reviewgate/config.yml той ветки, из которой сделан MR/PR, или REPLY_ENABLED=true у самого бота — тогда на всю инсталляцию (явное reply.enabled: false в репозитории это гасит). И вебхук должен доставлять события комментариев. Обе ловушки разобраны ниже.
Где бот отвечает сам, а где его нужно позвать
В своих тредах бот отвечает на любую реплику, звать его не нужно. Свои — это треды его замечаний на строках кода, а в GitLab ещё и тред под сводкой.
В ленте PR на GitHub (вкладка Conversation) всё иначе: сводка там — обычный комментарий в общей ленте, и каждая следующая реплика для бота — отдельный разговор. Здесь, как и в чужом треде, бота нужно позвать по имени, а ответ придёт новым комментарием в ленту.
В чужом треде — разговоре двух людей или на строке, которой бот не касался, — бота нужно позвать по имени, то есть упомянуть учётку, от которой он пишет: в GitLab это пользователь, чьим токеном работает бот, в GitHub — имя приложения (у нас @reviewgate-bot). Упоминание срабатывает в любом месте текста, даже внутри цитаты или блока кода. Без упоминания у бота нет оснований считать, что разговор его касается, а инструмент ревью, который влезает в каждое обсуждение без приглашения, за день становится шумом.
Свой тред бот узнаёт технически: первый комментарий в нём написан учёткой бота и несёт его скрытый маркер. Человек, процитировавший замечание, «своего» треда не открывает — иначе беседу начинал бы каждый процитированный комментарий ревью. По той же учётке бот узнаёт и свои реплики и на них не отвечает. Поэтому, если бот работает с личным токеном человека (в GitLab — токен пользователя, в GitHub — PAT), этот человек поговорить с ботом не сможет: его реплики бот считает своими.
# .reviewgate/config.yml — in the BRANCH the pull request comes from
reply:
enabled: true # off by default
max_replies_per_thread: 3 # the only brake on a long argumentТри ответа на тред по умолчанию (reply.max_replies_per_thread). Лимит проверяется до вызова модели: дойдя до него, бот замолкает. В ленте PR на GitHub лимит общий на весь PR. Других тормозов между двумя ботами нет: чужих ботов код не распознаёт, и если в том же треде отвечает ассистент коллеги, разговор остановит только лимит.
Пока лимит не исчерпан, каждая реплика в треде бота — платный вызов модели, даже «спасибо». Промолчать модель решает сама, уже получив всё на вход, так что молчание тоже оплачено: в треде от него следа нет, в лимит оно не идёт, а в логе бота остаётся строка ↩ The model stayed silent. На наших прогонах с sonnet-5 по нынешней цене первый ответ в треде стоил 3–4 цента, следующие — от половины цента до полутора: вход шёл из кэша Anthropic.
В закрытых тредах бот не отвечает никогда — откройте тред заново, если ответ нужен. А вот черновики бота не останавливают: пропуск ревью черновика к разговорам не относится, и вопрос в черновике получит ответ — как и в закрытом или уже смерженном MR.
Правка комментария не считается. Бот реагирует на новые комментарии, а не на правки. Переписанное на месте возражение для него невидимо; чтобы спросить снова, напишите новый комментарий.
Кто отвечает: первая модель в generators вашей схемы, без судьи. Поэтому ответ хорош настолько, насколько хорош генератор, а не весь ансамбль. Модель для ответов можно сменить ключом reply.model — вендор при этом остаётся прежним; для другого вендора есть reply.backend.
Почему бот молчит
1. Ответы выключены, пока вы их не включите. reply.enabled по умолчанию выключен, и REPLY_ENABLED у бота тоже. Если ключ есть в репозитории, решает он: reply.enabled: true включает ответы даже при REPLY_ENABLED=false у бота, а reply.enabled: false выключает их даже при REPLY_ENABLED=true. Ключа в репозитории нет — действует REPLY_ENABLED, и в нём считается только буквальное true: 1 или yes ответы не включат. У команды, которая ответы не включала, бот делает ревью и не отвечает никогда — это не поломка, а дефолт продукта.
2. Конфиг читается из ветки MR, а не из целевой. Политика для MR берётся из той ветки, откуда он сделан. Если включить ответы в main, бот в открытых MR будет молчать, пока их ветки не подхватят изменение, — например, пока в них не вольют main. Учтите две вещи: такое вливание — новый платный прогон ревью, а старые комментарии бот заново не разбирает, так что возражение придётся написать ещё раз. Ветки не нужны, если включить ответы у бота через REPLY_ENABLED=true: это действует сразу во всех MR, где в ветке нет ключа reply.enabled.
3. Вебхук не доставляет комментарии. То, что ревью приходит, про ответы ничего не доказывает: они едут на других событиях. В GitLab хуку нужны Comments (note_events) вдобавок к событиям MR. В GitHub ответ в треде замечания приходит событием «Pull request review comment», а реплика в ленте PR — событием «Issue comment», которому нужно право приложения «Issues: read». Если этих событий в настройках нет, об этом никто не сообщит: ни GitLab с GitHub, ни бот ошибки не покажут — комментарий просто не дойдёт до бота, и тот промолчит.
4. Модель решила промолчать. На реплику вроде «поправил» или «спасибо» отвечать нечего, и модель может промолчать сама. В треде ничего не появится, а в логе бота будет строка ↩ The model stayed silent.
docker compose logs --since 10m app | grep -i reply). Строки Queued a reply нет — событие не пришло: дело в вебхуке. Она есть, а за ней ⏭ Reply skipped: — бот получил комментарий и отказался, причина написана в той же строке: ответы выключены, тред закрыт, лимит исчерпан, тред чужой без упоминания. ↩ The model stayed silent — бот спросил модель, и она решила промолчать.Спор целиком
Замечание на строке, где почта попадает в лог, — тред целиком на GitLab:
🟠 no-customer-data-in-logs 📐 — В лог пишется e-mail клиента (command.customerEmail).
Правило команды запрещает класть данные клиента в логи — для поиска записи достаточно
идентификатора счёта.Возражение — настоящий довод, а не «ты неправ»:
Не соглашусь. Наши логи не покидают кластер: они идут в самостоятельно поднятый сборщик
внутри той же сети, хранятся 14 дней, доступ только у дежурной команды. Почта — это то, по
чему поддержка ищет, когда клиент звонит про пропавший счёт: номера счёта клиент не знает.
С учётом этого, замечание всё ещё стоит того, чтобы его чинить?Ответ бота, через двадцать секунд:
Понимаю аргумент про закрытый контур и удобство поиска, но правило команды здесь как
раз рассчитано на этот случай: «идентификатора достаточно, чтобы найти запись в базе».
Если поддержке для звонка по email нужно найти счёт, это должно решаться поиском по
email в БД (или отдельным индексом/API поддержки), а не хранением email в логах — логи
не место для PII даже при ограниченном доступе и retention в 14 дней (audit/comfort с
будущими требованиями compliance, ротацией доступа дежурных и т.д.). Оставляю
замечание в силе: предлагаю логировать invoice.id (и, при желании, invoice.customerId,
если он есть), а поиск по email вынести в отдельный, контролируемый механизм.Бот признал довод, а не повторил замечание. Он показал, на что правило рассчитано: «идентификатора достаточно, чтобы найти запись в базе» — ровно тот случай, о котором возражение. И он ответил на исходную потребность, предложив другой способ решить задачу поддержки, а не просто отказав. При этом он не сдался: инструмент, который соглашается с последним возразившим, — не ревьюер, а зеркало.
Оба разговора публичны — PR на GitHub и MR на GitLab нашего учебного репозитория. Русский — отдельный прогон, а не перевод, и к той же позиции бот пришёл сам.
Дайте ему факт, которого у него быть не может
Если факт касается не одной строки, а всего MR или проекта, положите его туда, где его увидит каждый прогон: в описание MR — его читают и ревью, и ответы — или в dont_flag в конфиге.
В конце спора у вас развилка. Если бот был неправ, потому что ваше правило написано без исключения, — поправьте правило: одна фраза в его тексте снимает весь класс таких замечаний. Если бот был прав, но чинить сейчас вы не будете, — это решение, а не спор: напишите в треде почему и закройте его. В GitLab это одно действие — галочка «Resolve thread» под ответом, и бот отвечать не станет; в GitHub закрытие — отдельная кнопка, и бот может успеть ответить.
Лимит — это достоинство. Спор с инструментом ревью на десять сообщений — пустая трата и времени, и денег: если за три ответа бота вопрос не решился, разногласие не про строку, а про правило. Решайте его в конфиге.
Когда что-то пошло не так
Бот ответил общими словами. Обычно это значит, что в возражении не было нового факта. Напишите то, чего бот видеть не может: ограничение, договорённость команды, план на этот код.
Бот согласился, а замечание осталось. Так и должно быть: ответ ничего не меняет ни в сводке, ни в гейте. Раз замечание неверное, закройте тред, а если его породило ваше правило — поправьте правило, иначе в следующем MR оно может вернуться.
Тред затих после трёх ответов. Это лимит. Откройте новый тред и позовите бота по имени — у нового треда лимит свой (кроме ленты PR на GitHub: там он общий на весь PR). А лучше закрепите решение в конфиге, тогда оно подействует и на следующие MR.
Бот ответил в черновике, где вы этого не ждали. Пропуск черновиков касается только ревью, а не разговоров: на вопрос в черновике бот ответит, и стоит этот ответ столько же, сколько любой другой.
Дальше
- Правила, которые линтер проверить не может— куда должен приводить выигранный спор
- Кто ищет, а кто судит— ответ приходит от генератора, без судьи
- Справочник конфига— reply: включение, лимит и модель ответов