1. Основы LangGraph: переход от линейных цепочек LangChain к циклическим графам
Основы LangGraph: переход от линейных цепочек LangChain к циклическим графам
Представьте, что вы строите маршрут для автономного автомобиля. В классическом программировании и ранних версиях LLM-приложений этот маршрут напоминал рельсы: точка А, точка Б, точка В. Но что, если на пути возник завал? Или датчик показал, что топлива не хватит до конца пути? В жесткой линейной логике система либо выдаст ошибку, либо совершит фатальное действие. Настоящий интеллект начинается там, где появляется возможность вернуться назад, переосмыслить результат и попробовать снова. Именно этот качественный скачок — от жестких цепочек (Chains) к гибким графам (Graphs) — совершает индустрия с приходом LangGraph.
Ограничения линейной парадигмы LangChain Expression Language (LCEL)
Долгое время стандартом разработки был LangChain с его мощным, но направленным синтаксисом LCEL. Мы привыкли соединять компоненты оператором |, создавая элегантные конвейеры: prompt | model | parser. Эта модель идеально подходит для задач, где путь данных предсказуем. Например, классический RAG-пайплайн: получили вопрос, извлекли документы, сгенерировали ответ.
Однако реальные бизнес-задачи редко бывают линейными. Рассмотрим процесс написания кода агентом. Агент пишет функцию, запускает тесты, видит ошибку. В линейной цепочке у него нет штатного механизма «вернуться в начало и исправить код на основе ошибки». Нам приходится либо городить бесконечные циклы while внутри Python-кода, теряя контроль над состоянием и прозрачность логики, либо смириться с тем, что агент «одноразовый».
Проблема линейных цепочек в том, что они являются ациклическими направленными графами (DAG), где данные текут только в одну сторону. Как только нам требуется итеративность — возможность повторения шага до достижения условия — DAG перестает справляться. LangGraph был создан, чтобы решить именно эту проблему, привнеся циклы в архитектуру LLM-приложений.
Философия LangGraph: State Machine как основа агента
LangGraph — это не просто надстройка над LangChain, это переосмысление того, как должен работать агент. В основе библиотеки лежит концепция конечного автомата (State Machine).
В традиционном LangChain состояние (state) часто передается неявно или теряется между вызовами. В LangGraph состояние является «первогражданином». Весь граф — это процесс трансформации одного общего объекта состояния. Каждый узел графа получает текущее состояние, вносит в него изменения и передает дальше.
Это дает нам три фундаментальных преимущества:
Анатомия графа: Узлы, Ребра и Состояние
Чтобы профессионально работать с LangGraph, нужно декомпозировать приложение на три базовых элемента.
1. State (Состояние)
Это схема данных, которая описывает все, что агент «знает» в данный момент. Обычно это типизированный словарь (TypedDict). Важно понимать, что состояние в LangGraph — это не просто переменная, а структура, поддерживающая операторы обновления. Например, список сообщений может не перезаписываться полностью, а дополняться новыми ответами модели.2. Nodes (Узлы)
Узел — это обычная Python-функция (синхронная или асинхронная), которая принимает текущее состояние и возвращает обновленные поля этого состояния. > Узел в LangGraph — это изолированная единица логики. Он не должен знать о существовании других узлов. Его задача: «взять данные — вызвать LLM или инструмент — вернуть результат».3. Edges (Ребра)
Ребра определяют путь движения данных. Они бывают двух типов:Сравнение архитектур: Цепочка vs Граф
Рассмотрим задачу: агент должен ответить на вопрос, используя поиск в Google, но только если он сам не знает ответа.
В LangChain (LCEL) это выглядело бы как сложная логика внутри RunnableBranch или кастомная функция, которая внутри себя вызывает другие цепочки. Это трудно отлаживать, так как визуально это все еще «черный ящик».
В LangGraph мы проектируем это как систему переходов:
Здесь появляется цикл: Oracle -> Search -> Oracle. В линейной системе это вызвало бы рекурсию или бесконечный цикл без понятного выхода. В LangGraph это штатный режим работы.
Математическая модель переходов
Хотя LangGraph скрывает сложность под капотом, полезно понимать, как вычисляется следующий шаг. Если обозначить состояние графа в момент времени как , а функцию узла как , то переход выглядит так:
Здесь оператор (reducer) определяет, как новые данные объединяются со старыми. Это критически важно для работы с историей чата: мы не хотим, чтобы каждое новое сообщение стирало предыдущее, мы хотим их суммировать.
Проектирование первого циклического агента
Давайте разберем логику создания агента, который умеет пользоваться калькулятором и проверять свои вычисления. Это классический пример, где циклы спасают от галлюцинаций.
Шаг 1: Определение состояния
Нам нужно хранить историю сообщений. ИспользуемAnnotated и функцию add_messages из LangGraph, которая реализует логику аппенда (добавления в список).Шаг 2: Создание узлов
Нам понадобятся два узла:assistant: вызывает LLM с поддержкой инструментов (tools).tools: выполняет вычисления, если LLM этого потребовала.Шаг 3: Логика маршрутизации
Это «мозг» графа. После работы ассистента нам нужно проверить полеtool_calls в сообщении. Если оно не пустое — направляем поток в узел инструментов. Если пустое — завершаем работу (специальный узел END).Этот паттерн называется ReAct (Reasoning + Acting). В LangChain он часто реализован как готовый класс AgentExecutor, но в LangGraph мы собираем его «руками», что дает нам полный контроль над каждым вдохом и выдохом агента.
Почему State Management меняет правила игры
В профессиональной разработке главная проблема — это не «как заставить LLM ответить», а «как не потерять контекст в сложной сессии».
В LangGraph состояние иммутабельно (неизменяемо) в рамках одного шага. Когда узел возвращает значение, LangGraph создает новую версию состояния. Это позволяет реализовать концепцию Time Travel (путешествие во времени). Мы можем в любой момент откатить состояние графа на три шага назад, изменить одно сообщение и запустить выполнение по новой ветке.
Для сложных бизнес-процессов (например, оформление кредита или многоэтапное планирование путешествия) это означает, что мы можем сохранять прогресс пользователя. Если сервер упал в середине цикла, наличие Checkpointer (механизма сохранения состояния) позволит восстановить работу ровно с того узла, где произошел сбой.
Циклы и проблема зацикливания
Свобода использования циклов несет в себе риск: агент может войти в бесконечную петлю. Например, если LLM раз за разом вызывает инструмент с одними и теми же неверными параметрами, а инструмент возвращает ошибку.
В LangGraph эта проблема решается на двух уровнях:
Практический кейс: Корректор текстов
Рассмотрим более глубокий пример — агент для корректуры текста.
Линейный подход: Текст -> LLM-корректор -> Результат.
Циклический подход в LangGraph:
Такая архитектура на порядок повышает качество текста, так как модель "Critic" может быть настроена на более строгий системный промпт, чем "Writer". В линейной схеме одна и та же модель должна и созидать, и критиковать одновременно, что часто приводит к снижению качества из-за когнитивной перегрузки контекстного окна.
Интеграция с экосистемой LangChain
Важно понимать, что LangGraph не заменяет LangChain. Внутри узлов вы по-прежнему используете:
LangGraph выступает в роли «дирижера» или «оркестратора», который берет эти компоненты и выстраивает их в сложную топологию. Если LangChain — это отдельные инструменты в мастерской, то LangGraph — это чертеж и конвейер, по которому движется изделие.
Граничные случаи и когда НЕ использовать LangGraph
Как профессор педагогики, я должен предостеречь от избыточного усложнения. Если ваша задача решается простым преобразованием «вход -> выход», использование LangGraph будет неоправданным оверхедом.
Когда LangGraph избыточен:
Когда LangGraph необходим:
Эволюция мышления разработчика
Переход к LangGraph требует смены ментальной модели. Вместо того чтобы думать «какую функцию вызвать следующей», вы начинаете думать «как выглядит состояние моей системы и какие правила переводят её из одного состояния в другое».
Это приближает разработку ИИ-агентов к классической программной инженерии и проектированию систем. Мы перестаем надеяться на то, что «умная модель сама разберется», и начинаем выстраивать надежные каркасы, внутри которых модель может проявлять свою гибкость, не выходя за рамки дозволенного.
В следующих главах мы детально разберем, как именно проектировать State, чтобы он не превратился в «свалку» глобальных переменных, и как использовать Checkpoints для создания по-настоящему отказоустойчивых систем. Но фундамент заложен здесь: агент — это граф, работа которого направляется состоянием и управляется циклами.