Почему CodeRabbit — это роскошь, которую вы не обязаны терпеть
CodeRabbit — отличный сервис. Пока не посмотришь на ценник. $15-25 за пользователя в месяц — для команды из 10 человек это $1500-2500 в год. И это за ревью, которое часто сводится к «уберите магические числа» и «добавьте типы». Проблема не в том, что он плох. Проблема в том, что вы переплачиваете за то, что можно сделать самому за вечер.
Мы — команда DevOps-инженеров, которые устали платить за SaaS, где 80% функционала — это обёртка вокруг API GPT/Claude. Решили собрать свой пайплайн. Результат: полноценный AI-ревьюер на базе DeepSeek V4 в связке с GitHub Actions. Цена — в 6 раз дешевле CodeRabbit. Ниже — полный рецепт. Без воды.
Дисклеймер: вы не получите «кнопку — всё сделано». Но получите архитектуру, которую сможете кастомизировать под себя. И будете контролировать каждый байт.
Архитектура решения: меньше магии, больше контроля
В основе — три компонента: триггер (GitHub Actions), анализатор (скрипт на Python) и LLM (DeepSeek V4 через OpenRouter). Весь пайплайн работает в облаке бесплатно (в рамках лимитов GitHub Actions) или на вашем сервере за $10-20 в месяц. CodeRabbit же тянет тысячи долларов.
Шаг 1: Создаем GitHub Actions workflow
Триггер — событие pull_request. Не советую ставить на все PR подряд — быстро сожжете квоты. Лучше повесить на label, например ai-review, или только на PR в main. Ниже — оптимизированный workflow.
Как НЕ надо делать
Новички часто копируют примеры с pull_request_target — это дыра в безопасности. Злоумышленник может подсунуть вредоносный код напрямую в воркфлоу. Всегда используйте pull_request и checkout с ref: ${{ github.event.pull_request.head.sha }}.
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
branches: [main]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Get diff
run: |
git fetch origin ${{ github.base_ref }} --depth=1
git diff origin/${{ github.base_ref }}...HEAD > diff.txt
- name: AI review
env:
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
run: |
python review.py diff.txt
Зачем fetch-depth: 0? Без этого git diff не увидит базовую ветку. Ещё один подвох: если PR содержит бинарные файлы, diff раздует токены — лучше исключить из диффа через .gitattributes.
Шаг 2: Скрипт ревьюера — превращаем diff в замечания
Пишем на Python (3.11+). Основная логика: прочитать дифф, разбить на chunks, отправить в LLM, распарсить ответ, оставить комментарий. Важный момент: не отправляйте весь файл целиком — у DeepSeek V4 контекст 200K токенов, но зачем тратить деньги? Отправляйте только изменённые строки с окружающим контекстом в 5-10 строк.
import os, sys, requests, json
def get_diff(path):
with open(path) as f:
return f.read()
def review_chunk(chunk: str) -> str:
prompt = f"""You are a senior code reviewer. Analyze this diff and list only issues with severity:
HIGH / MEDIUM / LOW. Be concise. Provide suggestion if possible.
Diff:
{chunk}
Return JSON array of objects: {{"file": "...", "line": 42, "issue": "...", "severity": "...", "suggestion": "..."}}"""
response = requests.post(
"https://openrouter.ai/api/v1/chat/completions",
headers={
"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}",
"Content-Type": "application/json"
},
json={
"model": "deepseek/deepseek-v4",
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.2,
"max_tokens": 2000
}
)
return response.json()["choices"][0]["message"]["content"]
def main():
diff = get_diff(sys.argv[1])
# Разбиваем на части по 5000 токенов (упрощённо)
chunks = [diff[i:i+5000] for i in range(0, len(diff), 5000)]
all_findings = []
for chunk in chunks:
result = review_chunk(chunk)
try:
all_findings.extend(json.loads(result))
except:
print("Failed to parse LLM response")
# Сохраняем для следующего шага
with open("findings.json", "w") as f:
json.dump(all_findings, f)
if __name__ == "__main__":
main()
Да-да, парсинг JSON из LLM — это лотерея. Но с temperature=0.2 и правильным промптом DeepSeek V4 выдаёт валидный JSON в 95% случаев. Если хотите надёжности — оборачивайте в retry с fallback на повторный запрос.
Промпты инжиниринг: мы пробовали разные варианты. Лучше всего работает ролевка: «Senior code reviewer with 15 years experience». И просите формат JSON — так проще парсить.
Шаг 3: Отправляем комментарии в PR
GitHub API позволяет оставлять review с замечаниями на конкретные строки. Нам нужно получить pull_number и commit_id. В GitHub Actions эти переменные доступны через контекст github. Добавляем шаг в workflow:
- name: Post review
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python post_review.py findings.json ${{ github.event.pull_request.number }} ${{ github.event.pull_request.head.sha }}
А вот скрипт post_review.py:
import sys, json, os
from github import Github
def main():
with open(sys.argv[1]) as f:
findings = json.load(f)
pr_number = int(sys.argv[2])
commit_id = sys.argv[3]
g = Github(os.environ["GITHUB_TOKEN"])
repo = g.get_repo(os.environ["GITHUB_REPOSITORY"])
pr = repo.get_pull(pr_number)
comments = []
for f in findings[:10]: # ограничим 10, чтобы не спамить
comments.append({
"path": f["file"],
"body": f"**{f['severity']}**: {f['issue']}\n\n*{f.get('suggestion','')}*",
"line": f["line"]
})
pr.create_review(
commit_id=commit_id,
body="AI review results:",
event="COMMENT",
comments=comments
)
if __name__ == "__main__":
main()
Стоит заметить: GitHub API не позволяет прикреплять комментарии к строкам, которые не были изменены в PR. Если LLM укажет строку вне diff, API вернёт ошибку. Просто фильтруйте замечания по наличию в диффе.
Шаг 4: Сравниваем стоимость — CodeRabbit vs наше решение
Посчитаем для команды из 10 разработчиков, которые делают 50 PR в месяц, каждый в среднем 20 файлов по 30 строк изменений. CodeRabbit Pro — $25/мес/чел = $3000/год.
| Параметр | CodeRabbit | Open-source (DeepSeek V4) |
|---|---|---|
| Стоимость/мес (10 чел) | $250 | $12-20 (токены DeepSeek + GitHub Actions) |
| Годовая экономия | — | ~$2760 (экономия 92%) |
| Задержка на PR | 30-60 сек | 60-120 сек (зависит от OpenRouter) |
| Качество ревью | Высокое (фин-тюнинг) | Высокое (DeepSeek V4 ≈ Opus) |
Да, наше решение чуть медленнее — из-за сетевых задержек OpenRouter. Но если поставить локальную LLM через Ollama (как в Open Cowork), то скорость сравняется. А цена упадёт до стоимости электричества.
Нюансы, которые превратят вашу жизнь в ад
Не хочу, чтобы вы думали, что всё гладко. Вот грабли, на которые мы наступили.
- Токены контекста. Если diff большой (>2000 строк), DeepSeek V4 может не уложиться в лимит. Решение: разбивать на файлы и обрабатывать независимо. Или увеличить контекст через опцию
max_context. - Галлюцинации. Модель иногда «видит» проблемы, которых нет. Например, может рекомендовать заменить
intнаfloatв местах, где это сломает логику. Добавьте в промпт: «Only report real issues. If unsure, skip.». - Дубликаты. Если два раза вызвать ревью на один и тот же коммит — получите два одинаковых комментария. Решение: проверять через API, есть ли уже комментарий от бота на этой строке.
- Безопасность API-ключа. Храните ключ OpenRouter в секретах GitHub, никогда не светите в логах. Используйте
maskв действии. - Лимиты GitHub API. Если у вас много замечаний (>30), API может вернуть ошибку. Лучше отправлять пачками по 10 через
create_reviewсevent: "COMMENT".
Если хотите полноценную замену CodeRabbit с дашбордом и историей — гляньте проект GitHub Copilot SDK, он позволяет создавать точно таких же агентов, но с готовыми шаблонами.
Что дальше: эволюция или смерть SaaS?
Через два-три года CodeRabbit либо станет бесплатным, либо умрёт. Open-source альтернативы уже сейчас дают 90% функционала за 10% цены. От нейрошизы до продакшена — тренд очевиден.
Совет, который вы не просили: не пытайтесь заменить человека. AI-ревью — это первый фильтр. Он отсекает глупые ошибки, но не может оценить архитектуру. Используйте наш пайплайн как стартовую площадку, а не как панацею. И перестаньте платить за то, что можно собрать самому.