LangGraph: граф состояний для AI-агентов

LangGraph — фреймворк от LangChain для создания агентов с контролем потока через графы состояний. Узлы = агенты/инструменты, Рёбра = переходы, Conditional Edges = ветвления и циклы. Полный контроль над каждым шагом агента.

📖 Документация ⭐ GitHub (11K+) 🦜 LangChain

🏗️ Архитектура LangGraph: граф состояний

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

📦 State (TypedDict) messages: list next_step: str iterations: int research_notes: str 👤 User Input START node 🔀 Router conditional edge 🔬 Researcher LLM → notes ✍️ Writer notes → answer 🛠️ Tools Web search, DB 🏁 END 🔄 Loop: conditional edge → back to Router

Архитектура LangGraph-агента: State хранит общие данные, Router направляет поток через conditional edges, Researcher/Writer/Tools — узлы-обработчики. Цикл обеспечивает итеративное улучшение ответа.

📊 LangGraph vs CrewAI vs AutoGen

Характеристика LangGraph CrewAI AutoGen
ПарадигмаState GraphRole-playConversation
Контроль потока✅ Полный⚡ Частичный⚡ Через диалог
Циклы✅ Нативные❌ Нет✅ Через HITL
Checkpointing✅ MemorySaver❌ Нет⚡ Ограниченный
Сложность старта⚡ Средняя✅ Низкая⚡ Средняя
Продакшен✅ LangSmith⚡ Экспорт⚡ AutoGen Studio

⚡ Шаг 1: Установка и базовые импорты

LangGraph — библиотека Python, устанавливается через pip. Зависит от langchain и langchain-openai для LLM-обёрток.

# Установка LangGraph
pip install langgraph langchain langchain-openai

# Ключевые импорты
from typing import TypedDict, Annotated, Literal
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI

🧩 Шаг 2: Определение состояния (State)

State — это TypedDict, который передаётся между узлами графа. Каждый узел читает из State и возвращает частичное обновление. LangGraph автоматически мёрджит возвращаемые словари. Поле с Annotated[list, "add"] добавляет элементы в список вместо перезаписи.

class AgentState(TypedDict):
    """Состояние — общий словарь, передаваемый между узлами."""
    messages: Annotated[list, "add"]  # "add" = append, не перезапись
    next_step: str
    research_notes: str
    iterations: int

# Инициализация LLM
llm = ChatOpenAI(model="gpt-4o", temperature=0)

🔬 Шаг 3: Узлы графа — Researcher и Writer

Каждый узел — это функция, принимающая State и возвращающая dict с обновлениями. Researcher анализирует запрос и генерирует заметки. Writer пишет финальный ответ. Router определяет, к какому узлу идти дальше.

def researcher_node(state: AgentState) -> dict:
    """Узел-исследователь: анализирует запрос и генерирует заметки."""
    last_msg = state["messages"][-1]
    prompt = f"Исследуй тему: {last_msg}. Дай 3 ключевых факта."
    response = llm.invoke(prompt)
    return {
        "research_notes": response.content,
        "messages": [response],
        "iterations": state.get("iterations", 0) + 1
    }

def writer_node(state: AgentState) -> dict:
    """Узел-райтер: пишет ответ на основе research_notes."""
    prompt = f"Напиши краткий ответ на русском на основе заметок: {state['research_notes']}"
    response = llm.invoke(prompt)
    return {"messages": [response]}

🔀 Шаг 4: Conditional Edges — Router и циклы

Router — ключевой элемент LangGraph. Это функция, которая возвращает строку-имя следующего узла. Через неё реализуются циклы (researcher → writer → researcher → writer → ...), ветвления (если нужен поиск → tools, иначе → writer) и условия остановки (достигнут лимит итераций → END).

def router(state: AgentState) -> Literal["researcher", "writer", "tools", "END"]:
    """Определяем следующий шаг на основе состояния."""
    iters = state.get("iterations", 0)

    if iters >= 3:                          # Лимит итераций — стоп
        return "END"
    if state.get("need_search"):       # Нужен поиск → tools
        return "tools"
    if not state.get("research_notes"): # Нет заметок → исследовать
        return "researcher"
    return "writer"                      # Есть заметки → писать ответ

💡 Совет: Router с Literal-аннотацией даёт полную type-safety. LangGraph проверит, что все возвращаемые строки соответствуют реальным узлам графа — опечатка в имени узла = ошибка при компиляции графа, а не в runtime.

🏗️ Шаг 5: Сборка графа и запуск

Финальный шаг: создаём StateGraph, добавляем узлы, настраиваем рёбра. add_conditional_edges подключает Router. compile() проверяет граф и возвращает готовый к вызову объект.

# Создаём граф
builder = StateGraph(AgentState)

# Добавляем узлы
builder.add_node("researcher", researcher_node)
builder.add_node("writer", writer_node)

# Точка входа
builder.set_entry_point("researcher")

# Conditional edges: router определяет следующий шаг
builder.add_conditional_edges("researcher", router, {
    "writer": "writer",
    "researcher": "researcher",
    "END": END
})

# Обычное ребро (без условий)
builder.add_edge("writer", END)

# Компилируем и запускаем
graph = builder.compile()
result = graph.invoke({
    "messages": ["Объясни принцип attention в трансформерах"],
    "iterations": 0
})
print(result["messages"][-1].content)

💾 Шаг 6: Checkpointing и Memory (продакшен)

Для продакшена критично персистентное состояние. MemorySaver сохраняет состояние после каждого шага. Это даёт: возобновление прерванных цепочек, Human-in-the-Loop (пауза → человек проверяет → продолжить), отладку (посмотреть состояние на любом шаге), streaming промежуточных результатов.

3x
Среднее число итераций
до качественного ответа
100%
Детерминированный поток
против вероятностного у CrewAI
<50ms
Оверхед графа
на каждом шаге
# MemorySaver — персистентность между вызовами
memory = MemorySaver()
graph = builder.compile(checkpointer=memory)

# Запуск с thread_id — состояние сохраняется между вызовами
config = {"configurable": {"thread_id": "user-001"}}
graph.invoke(initial_state, config)

# Human-in-the-Loop: прервать перед опасным действием
graph.invoke(state, config, interrupt_before=["tools"])

# Возобновить после проверки человеком
graph.invoke(None, config)  # продолжит с прерванного шага

🎯 Когда использовать LangGraph

✅ Идеально для

  • Сложной логики с циклами и ветвлениями
  • Продакшен-агентов с мониторингом
  • Human-in-the-Loop сценариев
  • Мульти-шаговых RAG с рефлексией
  • Когда нужен полный контроль над потоком

❌ Избыточно для

  • Простого чат-бота с одной LLM
  • Быстрого прототипа на вечер
  • Когда CrewAI/AutoGen уже решают задачу
  • Линейных цепочек без ветвлений

🚀 Готовы строить production-агентов на LangGraph?

Начните с туториала выше — скопируйте код и запустите за 5 минут.

📖 Официальные туториалы LangGraph