Промпт-инжиниринг для Code Review — как настроить ИИ-агента
Как настроить ИИ-агента, чтобы он реально "ловил" архитектурные нарушения, а не только опечатки. В этой статье я покажу, как настроить промпт (prompt) так, чтобы агент реально ловил архитектурные нарушения: с формализацией правил, парными примерами «правильно / неправильно» и защитой от ложных срабатываний на пограничных случаях (edge cases). Заодно разберём, как встроить такое ревью в pipeline, а не держать его как разовую ручную проверку.
Введение
Стандартный ИИ-ревьюер (AI reviewer) хорошо справляется с тем, что легко формализовать: опечатки, несоблюдение code style, забытые using, неймспейсы не по конвенции. Это удобно, но это далеко не то, ради чего вы на самом деле хотите видеть ИИ-агента в процессе ревью (code review).
Настоящая проблема — архитектурные нарушения. "Разработчик добавил прямой вызов репозитория (repository) из контроллера, минуя слой сервисов". Или "протащил бизнес-логику в DTO". Или "сделал так, что модуль из Vertical Slice стал зависеть от внутренностей соседнего модуля напрямую, а не через контракт". Формально код рабочий, тесты зелёные, стиль соблюдён — а архитектура уже начала "разъезжаться".
Почему стандартный ИИ-ревьюер это пропускает? Дело не в том, что модель «не умеет». Дело в том, что без явного контекста о правилах вашего конкретного проекта агент оценивает код по общим, усреднённым представлениям о «хорошем коде» — а не по тем границам, которые Вы для себя определили. Он не знает, что в вашем проекте слой Application не должен ссылаться на Infrastructure напрямую, если вы ему это не сказали.
Как агент «видит» код при ревью
Прежде чем формализовать правила, полезно понять, с чем именно работает агент, когда вы просите его сделать ревью (review) кода. Это не «понимание» в человеческом смысле — это анализ текста в пределах контекстного окна (context window), и у него есть свои ограничения.
Что агент видит. Если вы передаёте агенту diff (изменения в pull request) или конкретный файл, он анализирует именно этот текст — построчно (!), с учётом синтаксиса языка. Он хорошо распознаёт локальные паттерны: сигнатуры методов, обращения к зависимостям, структуру классов. Если в diff явно виден вызов _repository.GetAsync() внутри контроллера — агент это заметит, потому что это буквально написано в тексте, который он "читает".
Что агент додумывает. Проблема начинается там, где нарушение не локально, а системно. Например: правило гласит, что слой Application не должен зависеть от Infrastructure. Агент видит только изменённый файл — а не всю структуру проекта, не остальные классы, не то, как устроены остальные 200 файлов в решении. Если в diff нет прямого import'а из Infrastructure, агент физически не может проверить архитектурное правило — он не видит общей картины, если вы её не предоставили.
Здесь агент начинает «додумывать» — то есть опираться на усреднённые представления о том, как «обычно» строятся приложения на C#/.NET, а не на ваши конкретные решения. Иногда это совпадает с вашей архитектурой, иногда — нет. Именно поэтому агент может пропустить нарушение (не увидел контекст) или, наоборот, придраться к тому, что у вас осознанно сделано иначе (принял отклонение от «усреднённого» за ошибку).
Практический вывод. Есть два рабочих подхода, и они не взаимоисключающие:
- Расширить контекст — передавать агенту не только diff, но и структуру проекта, ключевые интерфейсы, схему зависимостей между слоями. Чем больше релевантного контекста, тем меньше додумывания.
- Формализовать правила заранее — не полагаться на то, что агент сам выведет архитектурные принципы из кода, а явно прописать их в системном промпте или в файле правил проекта. Это снимает саму необходимость догадываться.
Второй подход масштабируется лучше: контекст проекта растёт с каждым спринтом, а хорошо сформулированные правила остаются стабильными и работают на любом diff, даже небольшом. Именно этому посвящён следующий раздел.
Структура промпта под архитектурные правила
Формализация правил — это перевод того, что у вас «в голове» как архитектор, в текст, который агент воспринимает буквально, без интерпретаций и без права на «показалось». Здесь работает простое правило: если вы не написали правило явно — для агента его не существует.
Что входит в формализацию. Архитектурные правила проекта обычно распадаются на три группы:
- Правила слоёв (layers) — какой слой на какой имеет право ссылаться. Например: Presentation → Application → Domain, и никогда наоборот. Domain не знает про Infrastructure.
- Правила зависимостей (dependencies) — как модули должны взаимодействовать между собой: через интерфейсы, через события (events), через явные контракты — а не через прямые обращения к внутренним классам.
- Правила паттернов (patterns) — какой архитектурный стиль принят в проекте и что из него следует. Если у вас Clean-архитектура — Domain не должен знать о существовании EntityFramework. Если Vertical Slice — каждый слайс (slice) самодостаточен и не тянет за собой соседние.
Как это ложится в промпт. Общая структура правила в системном промпте (или в файле вроде CLAUDE.md) выглядит так: сначала называете принцип, потом формулируете его как проверяемое условие, а не как абстрактный лозунг. Разница принципиальна:
- Плохо: «Соблюдайте SOLID» — это ничего не даёт агенту, потому что SOLID — это пять разных принципов, и агент не знает, на какие сигналы в коде реагировать.
- Хорошо: «Если класс отвечает за загрузку данных, их валидацию и отправку уведомлений одновременно — это нарушение принципа единственной ответственности (Single Responsibility Principle, SRP). Такой класс нужно разделить».
Второй вариант превращает абстрактный принцип в конкретный, узнаваемый признак нарушения — то, что агент реально может сопоставить с кодом перед собой.
Три архитектурных стиля — три разных набора правил. Если в проекте используется конкретная архитектура, её тоже стоит формализовать отдельно, а не полагаться на то, что агент «и так знает, что такое Clean-архитектура»:
- Clean-архитектура — правило про направление зависимостей: внутренние слои (Domain, Application) не должны знать о внешних (Infrastructure, Presentation). Проверяемый признак нарушения: import или прямое обращение из Domain/Application в сторону Infrastructure.
- Vertical Slice-архитектура — правило про границы функционала (feature): один слайс (slice) не должен напрямую обращаться к внутренним классам другого слайса, только через общий контракт. Проверяемый признак: прямой вызов класса из чужой папки-слайса вместо использования публичного интерфейса.
- SOLID — правило про размер и ответственность класса, про то, что абстракции не должны зависеть от деталей реализации (Dependency Inversion) и так далее — каждый из пяти принципов лучше формализовать как отдельное проверяемое условие, а не одной строкой.

Такая формализация уже неплохо работает сама по себе. Но по-настоящему точность ревью вырастает, когда к каждому правилу прикладывается пара конкретных примеров — именно об этом следующий подпункт.
Правильные примеры
Формулировка правила в виде текста — это только половина работы. Вторая половина — показать агенту, как выглядит код, который этому правилу соответствует. Дело в том, что языковая модель гораздо надёжнее распознаёт паттерны по образцу, чем по абстрактному описанию: одно дело прочитать «Domain не должен зависеть от Infrastructure», и совсем другое — увидеть перед собой класс, который наглядно это демонстрирует.
Правильный пример в промпте — это не абстрактный псевдокод, а реальный фрагмент из Вашего проекта (или максимально похожий на него), который агент может использовать как эталон (reference). Например, для правила Dependency Inversion из SOLID:
// Правильно: Domain знает только об абстракции
public interface IOrderRepository
{
Task<Order> GetByIdAsync(Guid id);
}
public class OrderService
{
private readonly IOrderRepository _repository;
public OrderService(IOrderRepository repository)
{
_repository = repository;
}
}
Здесь важно не просто вставить код, а коротко объяснить почему именно так — не полагаться на то, что агент сам выведет причину из синтаксиса:
Класс
OrderServiceзависит от интерфейсаIOrderRepository, а не от конкретной реализации. Это позволяет подменить реализацию (например, для тестов) без измененияOrderService. Domain-слой не содержит ссылок на конкретную технологию хранения данных.
Такое сопровождение — код плюс объяснение принципа, который он иллюстрирует — резко повышает точность: агент начинает узнавать не просто «этот код выглядит похоже», а «этот код соответствует правилу X по причине Y», и переносит эту логику на код, который видит впервые.
Для Vertical Slice-архитектуры правильный пример будет выглядеть иначе — например, слайс, который обращается к соседнему слайсу строго через его публичный интерфейс, а не через внутренние классы. Число примеров стоит держать небольшим: два-три на каждое ключевое правило обычно достаточно, чтобы задать паттерн, не раздувая промпт.
Неправильные примеры
Правильный пример показывает агенту, к чему стремиться. Неправильный — показывает, что именно нужно ловить, и это не менее важно: без контрастного примера агент может распознать «хороший» паттерн, но не всегда уверенно отличит нарушение от осознанного исключения.
Ключевое правило для неправильных примеров то же, что и для правильных: реальный код, а не абстракция, плюс явное объяснение, в чём именно нарушение. Продолжая пример с Dependency Inversion:
// Неправильно: Domain напрямую зависит от Infrastructure
public class OrderService
{
private readonly SqlOrderRepository _repository;
public OrderService()
{
_repository = new SqlOrderRepository(new SqlConnection("..."));
}
}
И сопровождение, которое объясняет агенту, на какие именно сигналы в коде реагировать:
Класс
OrderServiceнапрямую создаётSqlOrderRepositoryи работает со строкой подключения. Это нарушение сразу по двум признакам: (1) зависимость от конкретной реализации, а не от абстракции, и (2) знание о деталях инфраструктуры (SQL-подключение) внутри Domain-слоя. Такой код нельзя протестировать без реальной базы данных.
Здесь стоит держать в уме одну вещь: неправильный пример должен быть похож на правдоподобный код, который реально может написать разработчик по невнимательности, а не на карикатуру. Если пример нарушения слишком надуманный, агент учится ловить только надуманные нарушения — а реальные, более тонкие случаи (когда зависимость от Infrastructure спрятана на два уровня глубже) проходят мимо.
Полезная практика — брать неправильные примеры не из головы, а из истории реальных pull request'ов вашего проекта: находите места, где ревью человека когда-то поймало архитектурное нарушение, и превращаете это в пример для промпта. Это одновременно и более реалистично, и экономит время на придумывание.
Пара «правильно/неправильно» для одного и того же правила — это минимальная единица, которая работает. Для ключевых правил (нарушение слоёв, границы слайсов, размер класса) таких пар обычно 2–3 на правило; для менее критичных — достаточно одной.
Практическая настройка
Теория формализации правил имеет смысл только тогда, когда превращается в файл, который реально подключается к агенту. Разберём это на примере CLAUDE.md — файла, который Claude Code читает автоматически при работе с проектом, и того же подхода, применимого к системному промпту в любом другом инструменте.
Структура файла правил. Файл не должен быть монолитным текстом на пять страниц — агент работает с ним лучше, если правила разбиты на секции по темам. Условная структура:
# Архитектурные правила проекта
## Слои и зависимости
- Domain не должен ссылаться на Infrastructure
- Application не должен содержать прямых обращений к Entity Framework
- [правильный пример]
- [неправильный пример]
## Vertical Slice
- Слайсы обращаются друг к другу только через публичные контракты в папке Contracts
- [правильный пример]
- [неправильный пример]
## SOLID
- Класс с более чем одной причиной для изменения — нарушение SRP
- [правильный пример]
- [неправильный пример]
Что важно на этом этапе. Файл правил — это не разовая инструкция, а живой документ. На практике полезны три вещи:
- Явная команда на использование правил при ревью. Мало просто иметь файл — стоит явно прописать в промпте для ревью: «Перед оценкой pull request проверь код на соответствие правилам из раздела «Архитектурные правила проекта». Без этой связки агент может прочитать файл, но не применить его именно к задаче ревью.
- Ссылка на конкретный diff, а не на всё решение целиком. Как обсуждали в разделе 2, агент лучше работает с ограниченным контекстом. Передавайте diff plus минимально необходимый контекст (затронутые интерфейсы, соседние классы), а не весь репозиторий — это и экономит токены, и снижает шум.
- Формат ответа агента. Полезно заранее задать структуру, в которой агент должен вернуть замечания — например, «правило → строка кода → объяснение → предлагаемое исправление». Это превращает вывод агента в то, что можно сразу использовать в комментарии к pull request, а не в свободный текст, который придётся переформатировать вручную.
Пример команды для ревью:
Проверь изменения в этом diff на соответствие правилам из CLAUDE.md, раздел "Архитектурные правила проекта".
Для каждого найденного нарушения укажи:
1) какое правило нарушено,
2) в какой строке,
3) почему это нарушение (со ссылкой на правильный/неправильный
пример из файла правил),
4) как исправить.
Если нарушений нет — явно напиши "Архитектурных нарушений не найдено".
Последняя строка не случайна: без явного требования агент иногда генерирует замечания просто потому, что «предполагается» их найти — а точность ревью держится в том числе на умении агента сказать «всё в порядке».
Пограничные случаи
Даже с хорошо формализованными правилами и примерами агент периодически ошибается — не потому что правила плохие, а потому что реальный код часто балансирует на грани между «нарушением» и «осознанным исключением». Разберём три ситуации, где это проявляется чаще всего, и заодно закроем оставшиеся принципы — DRY, KISS и YAGNI.
Ложное срабатывание на DRY (Don't Repeat Yourself). Агент видит два похожих блока кода в разных местах — и сразу считает это нарушением принципа DRY, предлагая вынести общий код в отдельный метод или базовый класс. Проблема в том, что не всякое повторение — ошибка. Иногда два похожих фрагмента кода описывают концептуально разные вещи, которые просто сейчас выглядят одинаково, а через полгода начнут расходиться (правило часто формулируют как «дублирование поведения — плохо, дублирование совпадения — нормально»). Слепое обобщение по формальному сходству текста создаёт хрупкую абстракцию, которая усложняет код при следующем изменении.
Чтобы снизить ложные срабатывания, стоит явно прописать в промпте условие: «Предлагай устранение дублирования только если оба фрагмента кода относятся к одному и тому же бизнес-понятию (domain concept) и с высокой вероятностью будут изменяться синхронно. Если это просто внешнее текстовое сходство — не считай это нарушением».
Ложное срабатывание на KISS (Keep It Simple, Stupid). Здесь агент может спутать необходимую сложность с избыточной. Например, паттерн Strategy с тремя реализациями интерфейса иногда выглядит как «слишком много абстракции для простой задачи» — особенно если агент видит только один из трёх сценариев использования в переданном diff. Это тот самый случай из раздела 2, когда ограниченный контекст приводит к неверному выводу: агенту не хватает картины целиком, чтобы оценить, оправдана ли абстракция.
Практический выход — явно указывать в правиле: «Не предлагай упрощение архитектурного паттерна, если не видишь всех точек его использования в проекте. При сомнении — задай уточняющий вопрос вместо того, чтобы предлагать правку».
Ложное срабатывание на YAGNI (You Aren't Gonna Need It). Обратная ситуация: агент видит интерфейс с одной-единственной реализацией и предлагает его убрать как «преждевременную абстракцию» — избыточную гибкость, которая пока не нужна. Иногда это справедливое замечание. Но иногда единственная реализация — это осознанная точка расширения (например, для будущей интеграции, которая уже запланирована), и её удаление создаст больше работы при следующем изменении, чем экономит сейчас.
Здесь помогает простое правило-подсказка: если в кодовой базе или в комментариях есть явный маркер («TODO: вторая реализация появится после интеграции с X»), агент должен считать абстракцию оправданной и не поднимать её как нарушение YAGNI.
Общий принцип для всех трёх случаев. Ложные срабатывания на DRY, KISS и YAGNI объединяет одно: агент судит по формальному сходству с паттерном нарушения, а не по намерению (intent), стоящему за кодом. Полностью убрать это нельзя — но можно снизить частоту, если в промпте явно прописать не только «что считать нарушением», но и «что не считать нарушением», с конкретными условиями-исключениями. Чем более явные и проверяемые эти условия — тем меньше агент опирается на догадки.
Смежный паттерн: встраивание в pipeline
Ручной запуск ревью через чат — удобно для экспериментов с промптом, но не масштабируется на команду. Следующий логичный шаг — встроить архитектурное ревью в pipeline, чтобы оно срабатывало автоматически, а не по желанию конкретного разработчика вспомнить и запустить его руками.
Вариант 1: pre-commit hook. Самый быстрый способ получить обратную связь — до того, как код вообще попал в pull request. Hook перехватывает git commit, собирает diff изменённых файлов и прогоняет его через агента с тем же промптом и файлом правил, что описаны в разделе "Практическая настройка". Плюс — мгновенная обратная связь прямо на машине разработчика. Минус — это точка, которую легко обойти (git commit --no-verify), поэтому полагаться на неё как на единственный барьер не стоит.
Вариант 2: бот в pull request. Более надёжный вариант — интеграция на уровне CI/CD: при открытии или обновлении pull request автоматически запускается задача, которая забирает diff, прогоняет его через агента и публикует замечания как комментарии в самом PR — там же, где обычно оставляют замечания живые ревьюеры. Это не заменяет человека, а снимает с него рутинную часть: явные архитектурные нарушения агент находит раньше, чем человек вообще открыл вкладку с diff.
Общая механика для обоих вариантов:
1. Получить diff (изменённые файлы относительно base-ветки)
2. Загрузить файл правил (CLAUDE.md) как часть системного промпта
3. Отправить agent'у: diff + правила + формат ответа
4. Распарсить ответ по заданному формату
5. Опубликовать замечания (комментарий в PR / вывод в консоль)
6. Если найдены критичные нарушения — пометить проверку как failed

Важный нюанс — что считать блокирующим. Не все находки агента должны останавливать merge. Стоит разделить замечания на уровни серьёзности прямо в промпте — например, «критично» (прямое нарушение слоёв, зависимость Domain от Infrastructure) и «на рассмотрение» (потенциальное нарушение DRY/KISS, где нужен человеческий взгляд). Критичные — блокируют pipeline. Остальные — публикуются как комментарии, но не мешают работе.
Это разделение снижает риск того, что команда начнёт игнорировать бота из-за частых ложных срабатываний (см. раздел "Пограничные случаи") — если каждое замечание агента блокирует merge, разработчики быстро научатся добавлять --no-verify и обходить проверку целиком, что сводит на нет весь смысл настройки.
Как отслеживать эффективность ревью со временем
Настроить агента один раз — это только начало. Без обратной связи по метрикам сложно понять, работает ли ревью на самом деле или просто создаёт видимость контроля. Разберём, на что стоит смотреть.
Метрика 1: количество пойманных нарушений. Базовый показатель — сколько замечаний агент реально сгенерировал за период (неделю, спринт) и сколько из них были признаны валидными разработчиком или человеком-ревьюером. Если агент стабильно находит 2–3 архитектурных нарушения в неделю на команду — это рабочий сигнал того, что промпт настроен разумно. Если находок ноль на протяжении месяца — вопрос не в том, что код идеален, а в том, что правила либо слишком мягкие, либо не покрывают то, что реально происходит в коде.
Метрика 2: доля ложных срабатываний. Не менее важный показатель — сколько замечаний агента разработчики отклонили как неверные (тот самый DRY/KISS/YAGNI из раздела "Пограничные случаи"). Здесь полезна простая практика: если разработчик не согласен с замечанием, он оставляет короткую пометку прямо в PR («ложное срабатывание: DRY») — а вы периодически собираете эти пометки и используете как материал для правки промпта. Растущая доля отклонённых замечаний — прямой сигнал, что пора возвращаться к разделу "Пограничные случаи" и уточнять условия-исключения.
Метрика 3: время до обнаружения нарушения. Если ревью встроено в pipeline (раздел "Смежный паттерн: встраивание в pipeline"), полезно сравнивать: сколько нарушений находит агент на этапе PR — против того, сколько всплывает позже, уже в проде или на этапе рефакторинга. Рост доли нарушений, пойманных на раннем этапе, — это и есть измеримый эффект от всей настройки, а не просто ощущение «стало удобнее».
Практический подход без сложной инфраструктуры. Не обязательно строить дашборд с самого начала. Достаточно завести простую таблицу (или файл в репозитории) с колонками: дата, найденное нарушение, правило, статус (подтверждено / отклонено как ложное). Раз в месяц — беглый просмотр: какие правила дают больше всего ложных срабатываний, какие правила ни разу не сработали (возможно, они избыточны или сформулированы нечётко), какие нарушения регулярно повторяются (возможно, стоит не только ловить их ревью, но и обсудить с командой, почему они вообще возникают).
Главный вывод этого раздела: файл правил — не «настроил и забыл». Это документ, который стоит пересматривать по мере того, как накапливаются данные о том, где агент был прав, а где ошибся.
Заключение
Разница между «ИИ-агент ловит опечатки» и «ИИ-агент ловит архитектурные нарушения» — это разница не в возможностях модели, а в том, сколько контекста о правилах вашего конкретного проекта вы ей передали. Агент не выводит архитектуру самостоятельно — он применяет то, что явно сформулировано, и делает это тем точнее, чем конкретнее и проверяемее правила.
Ключевые шаги, которые мы прошли:
- Формализовать правила как проверяемые условия, а не абстрактные лозунги — «соблюдайте SOLID» не работает, а «класс с несколькими причинами для изменения — нарушение SRP» работает.
- Подкрепить каждое правило парой примеров — правильным и неправильным, из реального кода проекта, с объяснением причины, а не голым кодом.
- Учесть пограничные случаи заранее — DRY, KISS и YAGNI чаще других принципов дают ложные срабатывания, потому что агент судит по формальному сходству, а не по намерению.
- Встроить ревью в pipeline, разделив замечания на критичные и на рассмотрение, чтобы не приучить команду обходить проверку.
- Отслеживать метрики со временем — количество находок, долю ложных срабатываний, скорость обнаружения — и регулярно возвращаться к файлу правил с этими данными.
Файл правил, который получится в итоге, — это не разовая настройка, а живой артефакт проекта, который растёт вместе с кодовой базой. И, пожалуй, главное: такой подход не заменяет ревью человека, а снимает с него рутинную, формализуемую часть — оставляя место для того, что действительно требует опыта и понимания контекста, которого у агента нет и не будет.