r3tam blog

Обзор возможностей LangChain/LangGraph

Обзор возможностей LangChain и LangGraph: строим мультиагентную систему

Современные LLM-приложения быстро эволюционируют от простых чат-ботов к сложным мультиагентным системам. Разработчики всё чаще сталкиваются с задачами, где один вызов модели не решает проблему: нужно ветвление логики, циклы, сохранение состояния между шагами и координация нескольких специализированных агентов. Здесь на сцену выходят LangChain и LangGraph — два фреймворка от одной команды, которые стали де-факто стандартом в мире агентного ИИ.

По данным репозитория на GitHub, LangGraph уже собрал более 34 тысяч звезд, а версия 1.2.5 вышла в июне 2026 года с поддержкой production-патчей для чекпоинтов. Фреймворк активно развивается: команда LangChain привлекла $125 млн инвестиций и делает ставку на наблюдаемость и платформенные решения.

В этой статье разберёмся, чем отличается LangChain от LangGraph, какие ключевые концепции предлагает графовый подход, и построим небольшой, но показательный пример: мультиагентную систему для сбора требований к задаче в формате опроса-интервью.

LangChain и LangGraph: в чём разница?

LangChain — это модульный фреймворк для создания LLM-приложений. Он предоставляет строительные блоки: промпты, модели, память, инструменты (tools), ретриверы, — и позволяет соединять их в линейные цепочки (chains). LangChain отлично подходит для RAG-пайплайнов, простых агентов с вызовом инструментов и типовых сценариев «запрос → обработка → ответ». Это идеальный выбор для быстрого прототипирования и MVP.

LangGraph — это надстройка над LangChain, реализующая графовый подход к оркестрации. Вместо линейных цепочек используется направленный граф: узлы (nodes) выполняют действия, а рёбра (edges) определяют переходы. Состояние (state) путешествует по графу, накапливая результаты. Это даёт три ключевых преимущества:

  • Циклы — агент может задавать уточняющие вопросы и возвращаться к предыдущим шагам;
  • Условные ветвления — выбор следующего действия на основе текущего состояния;
  • Встроенный контроль состояния — граф автоматически сохраняет чекпоинты после каждого узла.

Если провести параллель, то LangChain — это библиотека компонентов, а LangGraph — архитектурный фреймворк, который оркестрирует эти компоненты. Первый хорош для простых маршрутов, второй незаменим для систем с нетривиальной логикой.

Ключевые концепции LangGraph

StateGraph — граф состояний

В основе любой LangGraph-системы лежит StateGraph — класс, параметризованный схемой состояния. Состояние описывается через TypedDict с указанием, какие поля накапливаются, а какие перезаписываются.

from typing import Annotated, TypedDict
from langgraph.graph import StateGraph, add_messages

class InterviewState(TypedDict):
    messages: Annotated[list, add_messages]
    requirements: str
    current_step: str

Поле messages использует редьюсер add_messages — каждое новое сообщение добавляется к списку, а не заменяет его. Это важная деталь: благодаря редьюсерам LangGraph знает, как правильно объединять результаты работы разных узлов. Для скалярных полей вроде requirements используется стандартное перезаписывание — такой гибридный подход даёт гибкость без лишнего кода.

Узлы и рёбра

Узел (Node) — это обычная Python-функция, которая принимает состояние и возвращает словарь изменений. Узел может вызывать LLM, выполнять инструменты или просто трансформировать данные. Каждый узел делает ровно одну задачу: это соответствует принципу единственной ответственности из SOLID.

def interviewer_node(state: InterviewState) -> dict:
    """Задаёт вопрос пользователю на основе текущего состояния."""
    question = generate_question(state["messages"])
    return {"messages": [question]}

Рёбра (Edges) — определяют маршруты между узлами. В LangGraph есть два типа рёбер: обычные (всегда ведут в один узел) и условные (conditional edges), которые выбирают следующий узел на основе текущего состояния. Условная функция получает состояние и возвращает имя целевого узла:

def route_interview(state: InterviewState) -> str:
    if state["current_step"] == "done":
        return "summarize"
    return "ask_question"

Чекпоинтинг и наблюдаемость

Одна из сильнейших сторон LangGraph — автоматическое сохранение состояния после каждого узла (checkpointing). Это даёт восстановление после сбоев, возможность прервать и возобновить выполнение, а также просмотр истории состояний для отладки.

В разработке используется MemorySaver — всё хранится в оперативной памяти, что удобно для итераций. В продакшене рекомендуется AsyncPostgresSaver: он сохраняет чекпоинты в PostgreSQL и позволяет восстанавливать сессии после перезапуска сервера. Идентификация сессии происходит через thread_id в конфиге вызова.

Для observability (наблюдаемости) используется LangSmith — достаточно установить три переменные окружения, и каждый вызов графа появляется в дашборде: с задержками узлов, количеством токенов и полным логом I/O. Это критически важно для отладки мультиагентных систем: когда граф из пяти узлов выдаёт неверный ответ, без трейсов почти невозможно понять, на каком шаге произошла ошибка.

Паттерн «супервизор + специалисты»

В мультиагентных системах наиболее распространён паттерн с оркестратором (supervisor), который управляет работой специализированных агентов-воркеров. Каждый воркер имеет свою узкую задачу, свой промпт и свой набор инструментов. Такое разделение ответственности предотвращает раздувание контекста и делает систему предсказуемой.

Архитектура в общем виде выглядит так:

  1. Supervisor получает запрос, анализирует его и решает, какой воркер нужен;
  2. Воркер выполняет свою задачу (исследует, анализирует, трансформирует) и возвращает результат;
  3. Supervisor анализирует результат и либо завершает работу, либо отправляет задачу другому воркеру.

Все узлы общаются через единое состояние графа, что гарантирует согласованность данных на каждом шаге. LangGraph автоматически трассирует каждый вызов через LangSmith, позволяя видеть, какой узел сколько времени отработал и сколько токенов потратил.

Исследователи из LangChain отмечают, что иерархия агентов с изоляцией контекста — ключевой паттерн для production-систем. Субагенты запускаются со свежим окном контекста и не засоряют общую историю сообщений. Это напрямую влияет на качество работы: модель не отвлекается на информацию из других доменов.

Пример: мультиагентная система сбора требований

Давайте соберём практический пример — систему, которая проводит опрос-интервью для сбора требований к задаче. Это распространённый бизнес-сценарий: аналитик тратит часы на синхронные встречи, а затем вручную структурирует заметки. Наша система автоматизирует этот процесс.

Система будет состоять из трёх агентов:

  • Interviewer — задаёт вопросы, уточняет детали, адаптируется к ответам;
  • Analyst — анализирует ответы, выделяет функциональные и нефункциональные требования;
  • Summarizer — формирует итоговый документ с требованиями.

Шаг 1. Определяем состояние

from typing import Annotated, TypedDict
from langgraph.graph import StateGraph, END, add_messages

class RequirementState(TypedDict):
    messages: Annotated[list, add_messages]
    raw_answers: dict
    requirements: str
    phase: str
    question_count: int

Шаг 2. Создаём узлы-агенты

Interviewer двигается по сценарию: цель задачи, ожидаемый результат, сроки, критерии приёмки, стейкхолдеры. После каждого ответа он может задать уточняющий вопрос. Когда собрано достаточно данных — переключает фазу.

def interviewer(state: RequirementState) -> dict:
    if state["question_count"] >= 5:
        return {"phase": "analyzing"}
    question = generate_question(state["messages"])
    return {
        "messages": [question],
        "question_count": state["question_count"] + 1,
    }

Analyst получает сырые ответы и преобразует их в структурированный формат: выделяет требования, проверяет полноту, находит противоречия.

def analyst(state: RequirementState) -> dict:
    structured = analyze_requirements(state["messages"])
    return {"requirements": structured, "phase": "done"}

Summarizer формирует финальный документ.

Шаг 3. Собираем граф

graph = StateGraph(RequirementState)

graph.add_node("interviewer", interviewer)
graph.add_node("analyst", analyst)
graph.add_node("summarizer", summarizer)

graph.set_entry_point("interviewer")

graph.add_conditional_edges(
    "interviewer",
    lambda s: s["phase"],
    {"analyzing": "analyst", "interviewing": "interviewer"},
)

graph.add_edge("analyst", "summarizer")
graph.add_edge("summarizer", END)

app = graph.compile()

Шаг 4. Запускаем с чекпоинтером

Добавим сохранение состояния, чтобы система могла продолжить диалог после перезапуска:

from langgraph.checkpoint.memory import MemorySaver

app = graph.compile(checkpointer=MemorySaver())

result = app.invoke(
    {"messages": [], "phase": "interviewing", "question_count": 0},
    config={"configurable": {"thread_id": "req-001"}},
)
print(result["requirements"])

В продакшене MemorySaver заменяется на AsyncPostgresSaver, а thread_id привязывается к идентификатору пользователя или сессии. Это позволяет безопасно прерывать и возобновлять интервью в любое время — пользователь может ответить на пять вопросов, закрыть браузер и вернуться через день ровно на тот же шаг.

Что дальше?

К описанной архитектуре легко добавить:

  • Human-in-the-loop — прерывание перед критическими шагами (например, перед финализацией документа);
  • Инструменты — подключение поиска по базе знаний компании через MCP-сервер;
  • Параллельные воркеры — одновременный опрос нескольких стейкхолдеров;
  • LangSmith-трейсинг — мониторинг каждого интервью для улучшения промптов.

Заключение

LangChain и LangGraph — это не конкуренты, а взаимодополняющие инструменты. LangChain даёт быстрый старт и отлично подходит для линейных сценариев. LangGraph включается тогда, когда задача требует ветвлений, циклов и координации нескольких агентов.

Мультиагентный подход особенно эффективен в сценариях, где разные части задачи требуют разных компетенций: сбор информации, анализ и оформление результата. Паттерн «супервизор + специалисты» масштабируется практически на любую предметную область — от интервью по сбору требований до автоматизации техподдержки или анализа данных.

Если вы только начинаете — попробуйте LangChain. Как только почувствуете, что линейной логики не хватает, переходите на LangGraph. Графовый фреймворк даёт тот уровень контроля и предсказуемости, который необходим в продакшен-системах, а растущее сообщество и инструменты вроде LangSmith делают разработку и отладку по-настоящему комфортной.

FAQ

Чем LangGraph лучше обычного LangChain-агента?

LangGraph даёт явный контроль над потоком выполнения: вы точно знаете, в каком порядке вызываются узлы, и можете добавлять условные переходы, циклы и паузы. В обычном LangChain-агенте логика спрятана внутри цикла LLM.

Нужно ли знать LangChain, чтобы работать с LangGraph?

Желательно. LangGraph использует компоненты LangChain (модели, инструменты, промпты), но графовая логика работает как отдельный слой. Если вы знакомы с LangChain, порог входа будет минимальным.

Какой чекпоинтер выбрать для продакшена?

Для разработки — MemorySaver, для продакшена — AsyncPostgresSaver. Он обеспечивает сохранение состояния между перезапусками и горизонтальное масштабирование. SQLite не рекомендуется из-за проблем с конкурентным доступом.

Сколько агентов можно объединить в один граф?

Формальных ограничений нет, но на практике три-семь агентов — золотая середина. Большее количество увеличивает сложность отладки и стоимость вызовов LLM. При необходимости агентов можно группировать иерархически.

Поддерживает ли LangGraph стриминг?

Да, метод stream() позволяет получать промежуточные результаты по мере выполнения узлов. Это особенно удобно для UI с индикацией прогресса: пользователь видит, что система работает, а не просто ждёт ответа.