Автоматический код-ревью на Claude через API

Ставим Claude в GitHub Actions так, чтобы он комментировал пул-реквесты, не дублировал списания при перезапуске джобы и не съедал бюджет на одном гигантском диффе.

4 мин · · Обновлено · разработка · код-ревью

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

Какой эндпоинт и какая модель

Для ревью кода нужен /v1/messages (Anthropic Messages API) — он даёт больше контроля над системным промптом, чем chat completions. Модель — claude-sonnet-5 для обычных PR, claude-opus-5-5 для критичных веток типа main или release/*, где важнее качество разбора, чем скорость. У обеих 1,000,000 токенов контекста, так что диф на пару тысяч строк проходит целиком, без нарезки на чанки.

bash
curl https://api.zukko.pro/v1/messages \
  -H "Authorization: Bearer fetch_ваш_ключ" \
  -H "Content-Type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
    "model": "claude-sonnet-5",
    "max_tokens": 2000,
    "system": "Ты код-ревьюер. Ищи баги, гонки, утечки ресурсов, проблемы безопасности. Не комментируй форматирование. Отвечай списком: файл, строка, проблема, предлагаемое исправление.",
    "messages": [{"role": "user", "content": "'"$(git diff origin/main... | jq -Rs .)"'"}]
  }'

Идемпотентность — обязательна в CI

GitHub Actions ретраит упавшие джобы. Без защиты повторный запуск джобы на том же коммите отправит тот же диф второй раз и спишет деньги дважды. Решение — заголовок Idempotency-Key, построенный из хеша PR и коммита:

bash
IDEMPOTENCY_KEY="review-$(git rev-parse HEAD)-$PR_NUMBER"

curl https://api.zukko.pro/v1/messages \
  -H "Authorization: Bearer fetch_ваш_ключ" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d @payload.json

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

Оценка стоимости до запуска

Диф в 50 файлов может занять сотни тысяч токенов, и на большом монорепо это ощутимая сумма при каждом PR. Перед отправкой на ревью стоит прогнать текст через бесплатный подсчёт:

bash
curl https://api.zukko.pro/v1/messages/count_tokens \
  -H "Authorization: Bearer fetch_ваш_ключ" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{"model": "claude-sonnet-5", "messages": [{"role": "user", "content": "..."}]}'

Если число выше разумного порога — например, 300 000 токенов — логичнее не гнать весь диф в модель, а урезать до изменённых функций плюс их вызывающего контекста, либо переключиться на ревью по файлам вместо ревью по PR целиком.

Контроль бюджета через ключ

CI-бот должен работать на отдельном API-ключе, а не на общем. У ключа в кабинете (https://zukko.pro/app/api-keys) можно выставить дневной или месячный лимит расхода в долларах и список разрешённых моделей. Практический смысл: если в пайплайн случайно попадёт рекурсивный вызов или кто-то поставит ревью на каждый push вместо каждого PR, ключ упрётся в key_daily_limit_reached (429) вместо того, чтобы списать весь баланс аккаунта за ночь.

bash
{"error": {"message": "Daily limit reached for this key", "code": "key_daily_limit_reached"}}

Обработка в скрипте — не падать с ошибкой, а мягко пропустить ревью и оставить комментарий «лимит на сегодня исчерпан, ревью пропущено» вместо того, чтобы красный крест в PR пугал разработчиков.

Что ловить в ответах сервера

Два заголовка ответа полезны для мониторинга бота: X-Zukko-Cost-Micros — цена конкретного вызова в микродолларах, X-Fetch-Balance-Cents — остаток баланса (несмотря на название, в микродолларах: 1 000 000 = $1; точный баланс в центах — GET /v1/balance, поле balance_cents). Оба заголовка приходят только в обычных, не потоковых ответах. Логируя первый по каждому PR, легко построить график «сколько стоит ревью в месяц» и сравнить с ценой человеко-часов ревьюера.

bash
import requests

resp = requests.post(
    "https://api.zukko.pro/v1/messages",
    headers={
        "Authorization": "Bearer fetch_ваш_ключ",
        "anthropic-version": "2023-06-01",
    },
    json=payload,
)
cost_usd = int(resp.headers.get("X-Zukko-Cost-Micros", 0)) / 1_000_000
print(f"Review cost: ${cost_usd:.4f}")

Кэш для повторных прогонов

Если пайплайн перезапускает ревью при каждом новом комментарии в PR, а диф с прошлого раза не изменился, есть смысл включить кэш ответов заголовком X-Zukko-Cache: true — без temperature: 0: claude-sonnet-5 и claude-opus-5-5 могут отклонить temperature, отличную от 1, с ошибкой 400. Запрос с полностью совпадающим телом от того же пользователя закэшируется до 7 суток, а повторный вызов вернётся мгновенно и бесплатно — в ответе будет X-Zukko-Cache: hit (такие попадания не пишутся в /v1/usage); «почти одинаковые» запросы в кэш не попадают. Это не подходит для ревью, где промпт каждый раз включает актуальный диф, но хорошо работает для повторного анализа неизменного файла, если бот вызывается по требованию несколько раз подряд.

Ошибки, которые встретятся в проде

503 model_disabled — модель в конфиге выключена в каталоге (это не 404 и не model_retired); замену берите из актуального списка GET https://api.zukko.pro/v1/models и логируйте факт для последующего апдейта пайплайна. 503 provider_unavailable — временная недоступность модели у провайдера, на оба случая в скрипте стоит держать фоллбэк на другую модель, например gpt-5.6-sol через /v1/chat/completions, чтобы ревью не блокировало мёрж полностью. 422 idempotency_conflict возникает, если тот же Idempotency-Key отправлен с другим телом запроса — обычно означает баг в генерации ключа (например, забыли включить номер PR), а не проблему сервера.

Итоговая связка: свой ключ на бота с лимитом, идемпотентность на каждый вызов, count_tokens перед большими диффами и логирование X-Zukko-Cost-Micros — этого достаточно, чтобы код-ревью на Claude работал в проде месяцами без сюрпризов в счёте.

zukko

Платите в 25×
меньше

за Claude Code & ChatGPT

Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.

Автоматический код-ревью через API Claude в CI/CD · Zukko