Почему код-ревью — идеальная задача для AI-агента
Представьте типичный пулл-реквест в вашей команде. Три файла изменений, 400 строк кода. Коллега занят, вы устали, а дедлайн горит. Вы пролистываете diff, ставите пару комментариев про форматирование и сливаете. Знакомо? Это не лень — это человеческая природа.
AI-агент для код-ревью не устает. Не пропускает edge cases из-за усталости. Не забывает проверить обработку ошибок в 3 часа ночи. Но главное — он последователен. Одинаковые стандарты для всех разработчиков, всегда.
Ключевое отличие от обычных статических анализаторов: AI понимает контекст. Он видит не просто синтаксические ошибки, а архитектурные проблемы, нарушение паттернов проекта, логические противоречия. Как в статье "Рабочий процесс создателя Claude Code", контекст решает всё.
Что мы строим и почему именно на Claude Agent SDK
Не LangChain, не LlamaIndex. Claude Agent SDK — это не просто обёртка вокруг API. Это фреймворк, который заставляет агента думать как разработчик. Потому что создавали его разработчики для разработчиков.
Наш агент будет делать три вещи:
- Анализировать изменения в коде (diff, новые файлы)
- Проверять соответствие стандартам проекта
- Генерировать структурированный отчёт с приоритизацией проблем
- Предлагать конкретные исправления (не просто "здесь ошибка", а "замени эту строку на эту")
Предупреждение: не пытайтесь сделать агента, который заменяет человеческое ревью полностью. Он дополняет, а не заменяет. Человек смотрит на "что", AI — на "как".
1Подготовка: устанавливаем и настраиваем SDK
Начнем с боли — установки. Claude Agent SDK пока не в PyPI официально (Anthropic, когда уже?), поэтому клонируем репозиторий:
git clone https://github.com/anthropics/anthropic-sdk-python.git
cd anthropic-sdk-python
pip install -e .Теперь нужен API ключ. Не храните его в коде. Не в .env файле рядом с проектом. Используйте секреты вашей платформы или, на худой конец, системные переменные:
export ANTHROPIC_API_KEY='your-key-here'Проверяем, что всё работает:
import anthropic
client = anthropic.Anthropic()
print(client.models.list())2Архитектура агента: почему один большой агент — плохая идея
Типичная ошибка: создать монстра-агента, который делает всё. Анализирует код, проверяет стиль, ищет уязвимости, пишет комментарии. Получается неповоротливое чудовище, которое путается в собственных мыслях.
Вместо этого используем сабагентов. Каждый отвечает за свою область:
| Сабагент | Задача | Модель |
|---|---|---|
| Анализатор архитектуры | Проверка паттернов, зависимостей | Claude 3.5 Sonnet |
| Проверка стиля | Linting, форматирование, naming | Claude 3 Haiku |
| Поиск уязвимостей | Безопасность, инъекции, race conditions | Claude 3.5 Sonnet |
Как в статье "Как правильно использовать суб-агентов", разделение обязанностей — ключ к стабильной работе.
3Инструменты агента: что он умеет делать с кодом
Инструменты в Claude Agent SDK — это функции, которые агент может вызывать. Не просто анализ текста, а реальные действия с кодом.
Создаем базовый набор инструментов:
from anthropic import Anthropic
from typing import List, Dict, Any
import ast
import subprocess
import json
class CodeReviewTools:
def __init__(self):
self.client = Anthropic()
def analyze_syntax(self, code: str, language: str = "python") -> Dict[str, Any]:
"""Проверяет синтаксис кода"""
try:
if language == "python":
ast.parse(code)
return {"status": "valid", "errors": []}
except SyntaxError as e:
return {
"status": "invalid",
"errors": [{"line": e.lineno, "message": str(e)}]
}
return {"status": "valid", "errors": []}
def get_file_diff(self, filepath: str, base_commit: str = "HEAD~1") -> str:
"""Получает diff файла между коммитами"""
try:
result = subprocess.run(
["git", "diff", base_commit, "HEAD", "--", filepath],
capture_output=True,
text=True,
check=True
)
return result.stdout
except subprocess.CalledProcessError:
return ""
def check_code_style(self, code: str, rules: Dict[str, Any]) -> List[Dict]:
"""Проверяет код на соответствие стилю"""
# Здесь можно интегрировать с black, flake8, pylint
# Для примера — простая проверка длины строк
violations = []
lines = code.split('\n')
for i, line in enumerate(lines, 1):
if len(line) > rules.get("max_line_length", 79):
violations.append({
"line": i,
"rule": "line-too-long",
"message": f"Line {i} exceeds {rules['max_line_length']} characters"
})
return violationsНо инструменты — это только половина дела. Нужно научить агента ими пользоваться.
4Системный промпт: как заставить агента думать как senior разработчик
Промпт — это не инструкция. Это личность агента. Вы не пишете "проанализируй код", вы создаете опытного код-ревьюера.
SYSTEM_PROMPT = """
Ты — старший разработчик с 10-летним опытом. Твоя задача — проводить код-ревью.
Твои принципы:
1. Будь конструктивным, а не критичным
2. Сначала ищи архитектурные проблемы, потом — синтаксические
3. Предлагай конкретные исправления, а не просто указывай на проблемы
4. Учитывай контекст проекта (мы используем Python 3.11+, async/await, типизованные аннотации)
5. Обращай внимание на безопасность и производительность
Формат отчёта:
- Критические проблемы (блокируют мерж)
- Важные проблемы (нужно исправить)
- Рекомендации (можно исправить позже)
- Положительные моменты (что сделано хорошо)
Для каждого пункта указывай:
1. Описание проблемы
2. Местоположение (файл:строка)
3. Почему это проблема
4. Конкретное исправление (если возможно)
5. Альтернативные решения
"""Обратите внимание: промпт задаёт не только поведение, но и структуру вывода. Это критически важно.
5Собираем агента: от инструментов к автономной работе
Теперь собираем всё вместе. Главная фишка Claude Agent SDK — агент сам решает, какие инструменты использовать и в каком порядке.
from anthropic import Anthropic
from typing import List, Dict
import json
class CodeReviewAgent:
def __init__(self, api_key: str):
self.client = Anthropic(api_key=api_key)
self.tools = CodeReviewTools()
def review_pull_request(self, pr_data: Dict) -> Dict:
"""Основной метод для ревью пулл-реквеста"""
# 1. Собираем контекст
context = self._build_context(pr_data)
# 2. Создаем сообщение для Claude
messages = [
{
"role": "user",
"content": f"Проведи код-ревью для этих изменений:\n\n{context}"
}
]
# 3. Запускаем агента с инструментами
response = self.client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=4000,
system=SYSTEM_PROMPT,
messages=messages,
tools=[
{
"name": "analyze_syntax",
"description": "Проверяет синтаксис кода на ошибки",
"input_schema": {
"type": "object",
"properties": {
"code": {"type": "string"},
"language": {"type": "string", "default": "python"}
},
"required": ["code"]
}
},
{
"name": "get_file_diff",
"description": "Получает diff файла между коммитами",
"input_schema": {
"type": "object",
"properties": {
"filepath": {"type": "string"},
"base_commit": {"type": "string", "default": "HEAD~1"}
},
"required": ["filepath"]
}
}
]
)
# 4. Обрабатываем ответ агента
return self._parse_response(response)
def _build_context(self, pr_data: Dict) -> str:
"""Строит контекст для агента"""
context_parts = []
# Добавляем описание PR
context_parts.append(f"PR Title: {pr_data.get('title', 'No title')}")
context_parts.append(f"Description: {pr_data.get('description', 'No description')}")
# Добавляем информацию о файлах
for file in pr_data.get("files", []):
diff = self.tools.get_file_diff(file["path"])
if diff:
context_parts.append(f"\n--- File: {file['path']} ---\n{diff}")
return "\n\n".join(context_parts)
def _parse_response(self, response) -> Dict:
"""Парсит структурированный ответ агента"""
# Claude может возвращать структурированные данные в text или tool_use
# Здесь нужно преобразовать в удобный формат
result = {
"critical_issues": [],
"important_issues": [],
"recommendations": [],
"positives": []
}
# Парсинг ответа...
return resultУправление контекстом: самая сложная часть
Claude имеет ограничение на контекст (200K токенов для Sonnet). Это много, но не бесконечно. Если дать агенту весь код проекта + все зависимости + историю коммитов — контекст закончится.
Решение в фильтрации. Не весь код одинаково важен:
- Измененные файлы — обязательно
- Зависимые модули — выборочно
- Тесты для измененных файлов — если есть
- Конфигурационные файлы проекта — только если они влияют на изменения
Как в статье "Production-ready AI-агент с нуля", без умного управления контекстом агент будет теряться в деталях.
6Структурированный вывод: как превратить поток сознания в полезный отчёт
Claude может генерировать JSON. Используйте это. Заставьте агента возвращать данные в предсказуемом формате.
# В системном промпте добавляем:
"""
ВСЕГДА возвращай ответ в следующем JSON формате:
{
"summary": "Краткое резюме ревью",
"issues": [
{
"type": "critical|important|recommendation",
"file": "путь/к/файлу.py",
"line": 42,
"description": "Описание проблемы",
"suggestion": "Конкретное исправление",
"reason": "Почему это важно"
}
],
"score": 85,
"can_merge": true
}
"""Но будьте осторожны: иногда Claude "галлюцинирует" JSON. Всегда валидируйте ответ:
import json
def parse_agent_response(text: str) -> Dict:
"""Пытается найти JSON в ответе агента"""
# Ищем JSON между и
import re
json_match = re.search(r'\n(.*?)\n', text, re.DOTALL)
if json_match:
try:
return json.loads(json_match.group(1))
except json.JSONDecodeError:
pass
# Если не нашли, пробуем парсить весь текст как JSON
try:
return json.loads(text)
except json.JSONDecodeError:
# Fallback: парсим текстовый ответ
return {"raw_response": text}Интеграция в CI/CD: когда агент становится частью процесса
Самый простой способ — GitHub Actions. Но не делайте блокирующие проверки на старте. Агент должен помогать, а не тормозить.
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
code-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # Нужно для diff
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install anthropic
# другие зависимости
- name: Run AI Code Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: python code_review_agent.py --pr-number ${{ github.event.pull_request.number }}
- name: Post review as comment
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const review = JSON.parse(fs.readFileSync('review_results.json', 'utf8'));
let comment = `## 🤖 AI Code Review\n\n`;
comment += `**Score:** ${review.score}/100\n`;
comment += `**Can merge:** ${review.can_merge ? '✅ Yes' : '❌ No'}\n\n`;
if (review.issues.length > 0) {
comment += '### Issues found:\n\n';
review.issues.forEach(issue => {
const emoji = issue.type === 'critical' ? '🔴' : issue.type === 'important' ? '🟡' : '🔵';
comment += `${emoji} **${issue.type.toUpperCase()}** in ${issue.file}:${issue.line}\n`;
comment += `${issue.description}\n\n`;
comment += `**Suggestion:** ${issue.suggestion}\n\n`;
});
}
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});Не делайте агента обязательным для мержа. Сначала пусть работает как советник. Разработчики должны привыкнуть к нему, понять его ценность. Только потом можно делать блокирующие проверки для критических проблем.
Ошибки, которые совершают все (и как их избежать)
Я видел десятки попыток создать AI-агента для код-ревью. Вот что ломается чаще всего:
| Ошибка | Последствие | Решение |
|---|---|---|
| Слишком много контекста | Агент теряется, пропускает важное | Фильтруйте, давайте только релевантные части |
| Нет структуры вывода | Непонятные комментарии, которые нужно парсить вручную | Заставляйте возвращать JSON с четкой схемой |
| Игнорирование false positives | Разработчики перестают доверять агенту | Добавляйте механизм обратной связи, обучайте на ошибках |
| Попытка заменить всех | Агент не справляется, команда отвергает его | Начинайте с конкретных, узких задач |
Что дальше? От код-ревью к автономному рефакторингу
Когда агент станет надежным в код-ревью, можно идти дальше. Но осторожно.
Следующий шаг — автоматическое исправление мелких проблем. Не просите агента переписать весь код. Начните с:
- Автоисправление форматирования (black, isort)
- Переименование переменных по стандартам
- Добавление отсутствующих type hints
- Упрощение сложных выражений
Но всегда с подтверждением. Никогда не позволяйте агенту коммитить изменения без человеческого review. Как показано в статье "AI-агенты генерируют код быстрее, чем вы успеваете его проверить", скорость без контроля ведет к хаосу.
Самый интересный сценарий — когда агент начинает учиться на ваших исправлениях. Он видит, какие его предложения вы принимаете, какие отвергаете, и адаптируется. Это уже не просто инструмент, это коллега-стажер, который с каждым днем становится умнее.
Только не называйте его "Junior Dev". Он обидится.