AI-агент для код-ревью на LangGraph: опыт, ошибки, код | AiManual
AiManual Logo Ai / Manual.
04 Май 2026 Гайд

AI-агент для код-ревью: как мы строили на LangGraph и что пошло не так

Реальный опыт построения AI-агента для автоматического код-ревью с LangGraph и GPT-4. Разбираем архитектуру, ошибки и решения. Практические советы и примеры код

В один прекрасный понедельник я понял, что больше не могу читать pull request'ы. Чужие - еще туда-сюда, но свои... Когда команда разрослась до 20 человек, уткнуться в 400 строк кода перед мержем стало рутиной. Мы хотели ускорить код-ревью, не теряя качества. Идея создать AI-агента витала в воздухе - на дворе 2026, LLM уже умеют писать неплохие ревью. Оставалось заставить их делать это стабильно, дешево и без галлюцинаций. Спойлер: не вышло с первого раза. Вообще не вышло. Но после трех итераций у нас получился агент, который прошел 1000+ PR. Рассказываю, как мы до этого дошли и где наступили на грабли.

Если вы еще не знакомы с архитектурой современных AI-агентов, советую прочитать мой материал "Как спроектировать современного AI-агента: от planner/executor до stateful memory" - там заложена база для понимания графов.

Почему LangGraph, а не просто промпт к ChatGPT?

Код-ревью - это не линейная задача. Сначала нужно понять контекст PR, потом проверить логику, затем стиль, потом безопасность, иногда запустить тесты. Простой вызов LLM с промптом "проверь код" дает много шума. Нужен четкий pipeline, где каждый этап может ветвиться, возвращаться назад, запускать инструменты. LangGraph (текущая версия 0.4.x) позволяет построить граф состояний - идеально для такого сценария.

Сейчас многие инженеры переходят от LangChain к нативным решениям - подробно разобрал в статье "Почему AI-инженеры бегут от LangChain к нативным архитектурам агентов". Но мы остались на LangGraph - на тот момент он давал достаточную гибкость и уже был обкатан в production кейсах, например как Vodafone и Fastweb автоматизировали поддержку клиентов.

Архитектура нашего агента

Мы разделили процесс на четыре узла в графе:

  1. Context Collector - загружает diff, связанные файлы, историю коммитов, комментарии.
  2. Static Analyzer - запускает линтеры (eslint, pylint) и type-checker.
  3. LLM Reviewer - основной узел: отправляет контекст в GPT-4 (мы использовали gpt-4o-mini для экономии, потом перешли на gpt-4o).
  4. Checklist Runner - проверяет, покрыты ли все пункты ревью (безопасность, производительность, архитектура).

Граф выглядел так: Collector -> Analyzer (ветвление) -> LLM Reviewer -> Runner -> (если не хватает -> возврат к LLM Reviewer с дополнительным контекстом). Вот упрощенный код на Python с LangGraph:

from langgraph.graph import StateGraph, END
from typing import Dict, List, TypedDict

class ReviewState(TypedDict):
    pr_diff: str
    analysis_results: Dict
    review_comments: List[str]
    checklist: Dict[str, bool]

def collector(state: ReviewState) -> ReviewState:
    # загружаем diff и метаданные
    state['pr_diff'] = fetch_diff(state['pr_id'])
    return state

def analyzer(state: ReviewState) -> ReviewState:
    # запускаем статический анализ
    state['analysis_results'] = run_linters(state['pr_diff'])
    return state

def llm_reviewer(state: ReviewState) -> ReviewState:
    prompt = build_review_prompt(state['pr_diff'], state['analysis_results'])
    response = call_gpt4o(prompt)
    state['review_comments'] = parse_response(response)
    return state

def checklist_runner(state: ReviewState) -> ReviewState:
    # проверяем coverage чеклиста
    state['checklist'] = check_coverage(state['review_comments'])
    if not all(state['checklist'].values()):
        # возвращаемся в llm_reviewer с недостающей информацией
        return {"next": "llm_reviewer", "context_hint": "...", **state}
    return {"next": END, **state}

graph = StateGraph(ReviewState)
graph.add_node("collector", collector)
graph.add_node("analyzer", analyzer)
graph.add_node("llm_reviewer", llm_reviewer)
graph.add_node("checklist_runner", checklist_runner)
graph.set_entry_point("collector")
graph.add_edge("collector", "analyzer")
graph.add_edge("analyzer", "llm_reviewer")
graph.add_conditional_edges("checklist_runner", lambda state: state.get("next", "end"))
# ... запуск через app = graph.compile()

Ошибка №1: не делайте так с самого начала. В первой версии мы пытались в одном узле LLM сделать всё. Граф превращался в спагетти, а стоимость вызовов росла экспоненциально из-за возвратов. Разделение на узлы - база, но упрощение графа до 3-4 узлов спасает.

Что пошло не так - три главных грабля

1 Галлюцинации и ложные срабатывания

Первая версия агента засыпала разработчиков комментариями. "Вы забыли обработать null", "этот метод не используется", "у вас утечка памяти". 90% из них были ложными. LLM высасывала из пальца проблемы, которых нет. Причина - слишком общий промпт. Мы просили "найти все возможные баги", а надо было "найди только реальные проблемы, используя следующие критерии".

Решение: перешли на structured output (Pydantic модели) и строгие критерии. Пример промпта: "Если ты не уверен на 80% - не пиши комментарий". И добавили верификацию через второй LLM-вызов, который оценивает уверенность. Это удвоило latency, но сократило ложные срабатывания в 5 раз.

2 Проблемы с длинным контекстом

PR на 2000+ строк - обычное дело. Даже с контекстным окном в 128k токенов мы упирались в лимит, если добавляли весь файл целиком. Приходилось сжимать diff, вырезать нерелевантные строки. Но сжатие теряло важные детали. Агент начинал предлагать неправильные рефакторинги.

Решение: разбили большой PR на логические блоки (по файлам или по изменениям), прогоняли каждый блок отдельно, потом агрегировали результаты. Это похоже на LangSmith подход с трейсингом - кстати, для отладки таких сценариев очень помогает визуализация, как в инструменте Codag.

3 Стоимость и производительность

Каждый PR обходился в $0.5-$1.5 на вызовы GPT-4o. Для команды из 20 человек с 10 PR в день - $2000 в месяц. Жирно. Мы оптимизировали: использовали gpt-4o-mini для предварительного анализа, а gpt-4o только для критических проверок. Добавили кэширование (одинаковые diff'ы часто повторяются). И запускали агента асинхронно через webhook'и, а не синхронно в pipeline.

Деплой агента на production - отдельная история. Мы использовали LangGraph Deploy CLI, чтобы развернуть агента одной командой с Docker и CI/CD. Если хотите запустить своего агента быстро - рекомендую посмотреть в ту сторону.

Результаты и статистика (актуально на май 2026)

После всех фиксов мы получили:

Метрика До оптимизации После
Ложные срабатывания ~20% <3%
Среднее время ответа 45 сек 15 сек (с кэшем - 2 сек)
Стоимость за PR $1.20 $0.35
Хороших ревью (приняты без изменений) - 68%
💡
Неочевидный совет: не пытайтесь заменить человека полностью. Агент отлично ловит поверхностные ошибки (опечатки, нарушения стиля, пропущенные тесты), но архитектурные решения доверьте людям. Мы сделали агента "советником", который оставляет комментарии с пометкой "AI: уверенность 90%". Разработчики быстрее их просматривают.

Итог: стоит ли овчинка выделки?

Да, если делать правильно. Мы сэкономили ~40% времени команды на ревью. Но это потребовало двух месяцев итераций. Основные уроки:

  • Не верьте LLM на слово - добавляйте верификацию.
  • Разбивайте граф на мелкие узлы, но не увлекайтесь.
  • Кэшируйте и используйте разные модели по цене.
  • Следите за latency - 10 секунд еще норма, 30 уже раздражает.
  • Логируйте все в трассировщик - мы использовали Daggr для визуализации графов, но есть и open-source аналоги.

Если вы только начинаете строить AI-агентов - присмотритесь к нативным подходам. Возможно, вам вообще не нужен LangGraph, а хватит простого if-else с вызовом API. Но для сложных сценариев вроде код-ревью графовая архитектура незаменима.

Кстати, мы выложили core-часть агента в open source - ссылка в комментариях. А пока, если хотите глубже погрузиться в тему stateful memory - читайте ту же статью. Там много пересечений.

Подписаться на канал