ИИ-рецензент кода проверяет изменения в Git на наличие ошибок и уязвимостей безопасности перед отправкой (push) или слиянием (merge). Рецензенты, которые читают только diff, вынуждены гадать, как выглядит остальная часть файла и действительно ли предполагаемая ошибка приводит к сбою во время выполнения, а предлагаемые ими исправления возвращаются в виде текста, который вы копируете вручную.
Рецензент из этого руководства использует Responses API от OpenRouter для запуска измененных файлов в песочнице Linux, подтверждения каждой ошибки путем запуска проверки и возврата исправлений в виде патчей, которые можно применить с помощью флага. На четырех протестированных нами моделях при изменении с пятью ошибками рецензирование занимает от одной до четырех минут и стоит от $0.015 до $0.045.
Кратко
- Скрипт на Python из 233 строк (review.py), который использует только стандартную библиотеку и работает локально, в хуке pre-push Git или в GitHub Actions.
- Загружает измененные файлы, тестовые файлы и diff в размещенную песочницу Linux, чтобы модель могла воспроизвести ошибки и предложить патчи V4A.
- Каждая находка возвращается с отметкой «подтверждено» (с указанием команды и вывода, которые это доказывают) или «неподтверждено» (если ошибку не удается продемонстрировать с помощью быстрого скрипта).
- В наших бенчмарках при изменении с пятью ошибками модель Claude Haiku 5.5 тратила в среднем $0.0153 на рецензирование, а Claude Sonnet 5.5 исправила все пять ошибок во всех трех запусках за $0.0447 и 60 секунд.
- Записывает полный отчет в .git/ai-review.json и завершает работу с кодом 1, если находка достигает вашего порога критичности (--block-at, по умолчанию high).
Как работает рецензент
Скрипт объединяет три серверных инструмента OpenRouter и один инструмент-функцию в Responses API:
- Files API (бета-версия): загружает полное содержимое каждого измененного файла после внесения изменений, любые дополнительные файлы контекста, которые вы передаете (например, тесты), и Git diff. Предоставление модели полных файлов вместе с diff избавляет ее от необходимости гадать об импортах, хелперах или типах за пределами блоков diff.
- Серверный инструмент Shell (openrouter:shell, бета-версия): копирует загруженные файлы в домашний каталог размещенного контейнера Linux перед выполнением первой команды, поэтому любая модель может запускать Python и SQLite для вашего кода. Без песочницы модель может только строить предположения о поведении во время выполнения. Инструмент Shell работает в Responses и Messages APIs; Chat Completions возвращает 400.
- Серверный инструмент Apply Patch (openrouter:apply_patch, бета-версия): позволяет модели предлагать изменения файлов в виде патчей V4A (create_file, update_file, delete_file). OpenRouter проверяет синтаксис патча и автоматически отправляет ошибки синтаксиса обратно модели, оставляя фактическую запись файла вашему приложению.
- Инструмент-функция submit_review: инструмент со строгой схемой JSON, который модель вызывает по завершении. Мы используем инструмент-функцию для финального ответа, поскольку text.format с json_schema не поддерживался принудительно для anthropic/claude-haiku-5.5, когда в запросе присутствовали инструменты, и ответы возвращались в виде текста или блоков Markdown с лишними полями.
Каждое рецензирование следует этому процессу:
- Скрипт получает diff и имена измененных файлов из Git, добавляет любые файлы --context и загружает их в /api/v1/files (до 19 файлов плюс diff, что соответствует лимиту в 20 файлов на запрос).
- Загруженные файлы появляются в домашнем каталоге песочницы как ~/<последние 8 символов ID файла>-<имя файла> (например, ~/NR6q4V8w-orders.py), поэтому скрипт помещает точные команды cp для восстановления дерева репозитория в ~/repo в инструкции промпта.
- Скрипт инициирует вызов /api/v1/responses. Модель читает файлы, пишет и запускает короткие проверки в песочнице для подтверждения предполагаемых ошибок и предлагает патчи.
- Когда модель отправляет патч, ответ приостанавливается, чтобы скрипт мог применить его к временной копии на вашей машине и сообщить модели, применился ли он. Патч не применяется в песочнице, поэтому проверки модели выполнялись на оригинальном коде. Запустите тесты после --apply, чтобы убедиться, что исправление работает.
- Когда модель вызывает submit_review, скрипт удаляет загруженные файлы, записывает отчет в .git/ai-review.json, опционально копирует пропатченные файлы в ваше рабочее дерево с помощью --apply и завершает работу с кодом 0, 1 или 2.
Перед началом работы
Вам понадобятся Python 3.11 или новее, Git и ключ API OpenRouter из руководства по быстрому старту, экспортированный в вашей оболочке:
Files API, инструмент Shell и инструмент Apply Patch работают на глобальной конечной точке (https://openrouter.ai/api/v1); региональные конечные точки возвращают 403. Ваши измененные файлы и файлы контекста загружаются в ваше рабочее пространство OpenRouter и отправляются провайдеру модели, поэтому запускайте скрипт только для кода, которым вам разрешено делиться, и не допускайте попадания секретов в эти файлы.
Шаг 1. Сохраните рецензент
Сохраните следующий скрипт как review.py в корне вашего репозитория:
Несколько решений в review.py обусловлены проблемами, с которыми мы столкнулись при его создании:
- respond() передает каждый запрос в потоковом режиме ("stream": True) и считывает данные до response.completed. Длинные запросы без потоковой передачи в наших тестах обрывались, а потоковая передача с одной повторной попыткой через 5 секунд справляется с периодическими обрывами соединения или ограничениями частоты запросов провайдера.
- call() устанавливает собственный User-Agent, так как стандартный user agent для urllib в Python получал ошибку 403 от Files API.
- Список инструментов использует {"type": "openrouter:apply_patch"}. Нативный формат {"type": "apply_patch"} для моделей Claude приводил к тому, что каждый патч не проходил валидацию с ошибкой expected object, received string.
- Каждый патч применяется немедленно к временному рабочему каталогу, и скрипт сообщает Applied patch to ... или Patch failed: .... Когда мы просто отвечали «сохранено» без применения патча, модели отправляли один и тот же патч по два-три раза.
- API возвращает вызовы патчей с типом "type": "openrouter:apply_patch", но повторная отправка этого типа во входных данных следующего шага приводила к ошибке 400, как только появлялось два патча. Возврат вызовов патчей в виде элементов apply_patch_call работает корректно.
- Пути патчей поступают от модели, поэтому inside() разрешает каждый из них и отклоняет любые пути за пределами репозитория до того, как что-либо будет записано. Имена файлов передаются в команды настройки песочницы с экранированием оболочки, поэтому имя файла не может запустить команды. Загрузки и временная копия очищаются, даже если рецензирование прерывается на середине.
- Каждый блок (hunk) размещается по своей строке @@, сопоставляется как целая строка и никогда не размещается перед предыдущим блоком, поэтому фрагмент, который встречается в файле дважды, изменяется в правильном месте. Если указанная строка не существует, патч не проходит проверку, у модели запрашивается исправленный вариант, и ничего не записывается. Sonnet 5.5 отправляла диапазоны строк унифицированного diff вроде @@ -5,10 +5,13 @@ при каждом запуске. В таких случаях старый номер строки определяет положение блока при наличии текста с учетом более ранних блоков. Если номер указан неверно, блок применяется только тогда, когда его текст встречается в файле один раз, в противном случае патч отклоняется.
- Отчет содержит полные ответы модели, включая вывод оболочки и фрагменты вашего кода, поэтому по умолчанию он сохраняется в папке .git, куда не будет зафиксирован (committed).
- Код выхода 0 означает, что ни одна находка не достигла порога --block-at (по умолчанию high) или рецензировать было нечего, 1 означает наличие хотя бы одной блокирующей находки, а 2 означает, что рецензирование не завершилось за восемь ходов или в изменении более 19 файлов.
Шаг 2. Попробуйте на примере репозитория
Создайте небольшой репозиторий, в котором существующие тесты проходят даже после того, как изменение вносит пять ошибок.
Сохраните app/orders.py:
Сохраните tests/test_orders.py:
Сохраните это изменение с пятью ошибками как buggy.diff:
Закоммитьте исходные файлы, примените buggy.diff и запустите существующие тесты:
Два существующих теста по-прежнему проходят успешно, так как они не охватывают изменения, внесенные в diff. (В нашей песочнице были установлены Python 3.11.14 и SQLite 3.50.4; unittest работает «из коробки», в то время как pytest не установлен.)
Теперь запустите рецензент, используя файл теста в качестве контекста:
Вот вывод одного из наших запусков anthropic/claude-haiku-5.5:
Скрипт завершает работу с кодом 1, так как обнаружены три проблемы уровня HIGH. Четыре находки были подтверждены путем запуска кода в песочнице, а их команды и результаты сохранены в поле evidence в файле .git/ai-review.json. Удаленный вызов conn.close() не подтвержден, так как CPython закрывает локальное соединение при возврате из функции. Передайте флаг --apply, чтобы скопировать исправленный app/orders.py в ваше рабочее дерево.
Шаг 3. Проверка перед каждым пушем
Сохраните этот скрипт как .git/hooks/pre-push и выполните команду chmod +x .git/hooks/pre-push:
Когда мы закоммитили review.py в ветку main, поверх него закоммитили изменение с пятью багами и выполнили git push origin main, хук запустил рецензирование (5 находок, 1 патч, $0.0117, 96 с), завершился с кодом 1, а Git вывел ошибку: failed to push some refs. Удаленная ветка осталась на предыдущем коммите.
Обратите внимание на три нюанса:
- Хук сравнивает рабочее дерево с базовой веткой, поэтому перед пушем закоммитьте или спрячьте (stash) несвязанные изменения.
- Команда git push --no-verify пропускает локальные хуки, поэтому также запускайте рецензент в CI.
- В ОС Windows используйте полный путь к Python, если python3 отсутствует в переменной PATH для Git Bash.
Шаг 4. Запуск в GitHub Actions
Сохраните этот воркфлоу как .github/workflows/ai-review.yml. Мы не запускали этот воркфлоу в GitHub Actions; мы запускали те же команды git show и review.py локально.
Запуск git show "$BASE:review.py" использует копию скрипта из базовой ветки, чтобы пул-реквест не мог изменить проверяющий его рецензент, а python -I исключает файлы пул-реквеста из путей импорта Python. Пул-реквесты из форков пропускают этот шаг, а timeout-minutes: 15 покрывает время проверки, составляющее от одной до четырех минут.
Добавьте OPENROUTER_API_KEY в разделе Settings, Secrets and variables, Actions и сделайте рецензирование обязательной проверкой статуса в вашем правиле защиты ветки. О более широкой настройке блокировки CI см. в руководстве по блокировке пул-реквестов на основе оценок LLM в CI.
Какую модель вам следует использовать?
Мы провели по три проверки на каждую модель для приведенного выше примера с настройками по умолчанию и параметром --context tests/test_orders.py. Счётчик «Bugs found» (Найдено багов) отображает пять внедренных багов по результатам завершенных проверок. «Confirmed by running code» (Подтверждено запуском кода) означает, что модель выполнила проверку в песочнице, которая выявила баг. «Patch fixed all five» (Патч исправил все пять) означает, что мы применили принятые патчи к чистой копии, и наши собственные тесты успешно прошли для всех пяти багов.
Примечания по результатам запусков:
- Каждая модель во всех завершенных проверках подтвердила уязвимость SQL-инъекции, ошибку на единицу в пагинации, некорректное сравнение через is not и проглоченную ошибку повторной попытки.
- Удаленный вызов conn.close() разделил модели. Haiku и Sonnet отметили его как неподтвержденный (Haiku пропустила его один раз), а Sonnet также добавила низкоприоритетное неподтвержденное примечание в одном из запусков о том, что format_total принимает центы, в то время как apply_discount производит округление так, будто итоговые суммы указаны в долларах. GPT-6 Luna ни разу не сообщила об отсутствующем conn.close(), хотя один из ее патчей все равно исправил это. DeepSeek подтвердила его в обоих завершенных запусках, один раз подсчитав открытые дескрипторы файлов в /proc/self/fd до и после 300 вызовов.
- Одна из проверок DeepSeek потратила все восемь шагов на повторную отправку патчей без вызова submit_review, завершившись с кодом 2 и стоимостью $0.034.
- Два из трех патчей Haiku изменили total_revenue так, чтобы использовать индексы кортежей (order[2]) вместо исправления is not (после замечания о том, что get_orders возвращает кортежи, в то время как total_revenue ожидает словари), что ломает вызывающий код, передающий словари. Всегда читайте патчи перед их применением.
Начните с anthropic/claude-haiku-5.5, если вам нужна минимальная стоимость проверки ($0.0153), или используйте anthropic/claude-sonnet-5.5 (--model anthropic/claude-sonnet-5.5), если вам нужны более быстрые проверки (60 с) и патчи, которые исправили все пять багов в каждом запуске ($0.0447). Вы можете сравнить больше моделей в каталоге моделей и протестировать их на собственном репозитории с помощью Ori Eval.
Стоимость
Счет за каждую проверку включает токены модели плюс время работы контейнера-песочницы из расчета $0.0001 за секунду. API файлов не тарифицируется отдельно (до 100 МиБ на файл и 10 ГиБ на рабочее пространство, при этом файлы рабочего пространства не имеют срока действия).
Тарификация песочницы длится от первой команды оболочки до завершения ответа, поэтому время «размышления» модели во время проверки также учитывается. В результате более медленная модель обходится дороже с точки зрения песочницы, даже если ее токены стоят дешево: время работы песочницы составляло от $0.0074 до $0.0159 для каждой проверки Haiku по сравнению с $0.0047–$0.0068 для более быстрых запусков Sonnet.
Рецензент на базе Chat Completions, использующий только diff, стоит около $0.001 за проверку на Haiku 5.5. Запуск кода в песочнице стоит в 10–40 раз дороже, но взамен находки сопровождаются доказательствами воспроизведения и проверенными патчами.
Ограничения
- Измененные файлы, файлы контекста и diff загружаются в ваше рабочее пространство OpenRouter и отправляются провайдеру модели. Проверяйте только тот код, которым вам разрешено делиться, и не допускайте попадания секретов в эти файлы. Скрипт удаляет свои загрузки по завершении работы.
- Каждый запрос принимает до 20 идентификаторов файлов в инструменте Shell (19 измененных и контекстных файлов плюс diff). Проверяйте более крупные изменения группами.
- Песочница по умолчанию не имеет исходящего доступа в интернет (таймаут каждой команды по умолчанию составляет 2 минуты, максимум — 5), поэтому команды pip install завершаются ошибкой, как и проверки, выполняющие импорт сторонних пакетов, если вы не добавите в белый список такие хосты, как pypi.org и files.pythonhosted.org с помощью network_policy.
- API файлов, инструмент Shell и инструмент Apply Patch находятся в статусе бета-версии только для глобальной эндпоинт-точки, и их поведение может измениться.
- Модели по-прежнему могут пропускать баги, преувеличивать степень их серьезности или писать нерабочие патчи. Рассматривайте подтвержденные находки как веские сигналы, а неподтвержденные — как заметки для человека.
Часто задаваемые вопросы
Что происходит с файлами, которые загружает рецензент?
Скрипт загружает измененные файлы, любые переданные вами файлы контекста и diff Git в ваше рабочее пространство OpenRouter с помощью API файлов, и провайдер модели получает их содержимое во время проверки. Загруженные файлы невозможно скачать обратно, и они могут использоваться только внутри песочниц оболочки OpenRouter. Скрипт удаляет каждый загруженный файл в блоке finally перед выходом.
Сколько стоит ревью кода с помощью ИИ на OpenRouter?
В ходе наших тестов из трех запусков для изменения с пятью багами стоимость проверки в среднем составила $0.0153 с Claude Haiku 5.5, $0.0167 с GPT-6 Luna, $0.0307 с DeepSeek V4.1 Flash и $0.0447 с Claude Sonnet 5.5. Эта сумма включает токены модели и время работы песочницы Linux по цене $0.0001 за секунду от первой команды оболочки до завершения ответа.
Может ли песочница устанавливать сторонние пакеты Python?
По умолчанию песочница не имеет исходящего доступа в интернет, поэтому модель не может запустить pip install. Она все еще может читать код, который импортирует сторонние пакеты, и предлагать патчи, однако любая проверка, выполняющая импорт таких пакетов, завершится ошибкой. Если вашим проверкам требуются пакеты из PyPI, вы можете настроить network_policy для инструмента shell, чтобы добавить в белый список такие хосты, как pypi.org и files.pythonhosted.org.
Рецензент редактирует мое рабочее дерево автоматически?
Нет. Когда модель предлагает исправление с помощью инструмента apply_patch, скрипт применяет его к временной копии ваших файлов и сообщает модели, прошло ли применение успешно. Ваше рабочее дерево обновляется только в том случае, если вы передаете флаг —apply.
Дальнейшие действия
- Руководство по автоматическому ревью кода
- Руководство по инструменту Shell и руководство по контейнерам
- Руководство по инструменту Apply Patch
- Руководство по Files API
- Документация Ori Eval
- Как настроить проверку пулл-реквестов на основе оценок LLM в CI












