LangGraph

Фреймворк для оркестрации агентов, реализующий паттерн State Machine: агент описывается как граф из узлов (функции) и рёбер (переходы) поверх единого типизированного состояния. Это hub-заметка — отдельные концепции вынесены в atomic.

Суть

Главный тезис: агенты — это графы, а не цепочки. Линейная цепочка вход → LLM → tool → выход не умеет ветвиться, циклиться, прерываться и восстанавливаться — а агенту это нужно. LangGraph даёт три примитива, из которых собирается любая логика:

  • State — единое строго типизированное состояние, передаваемое между узлами (LangGraph State, правила обновления — LangGraph Reducers).
  • Node (узел) — Python-функция, принимающая state и возвращающая его частичное обновление.
  • Edge (ребро) — переход между узлами, обычный или условный (функция-маршрутизатор) (LangGraph Nodes and Edges).

Граф собирается через StateGraph(...) builder и переводится в исполняемый объект вызовом .compile().

Зачем это нужно

LangChain-цепочки — это API для прототипирования; для production нужны контроль потока, циклы, retry, воспроизводимость и human-in-the-loop. LangGraph закрывает именно это (подробно — LangGraph vs LangChain). Показательно: высокоуровневая create_agent() из LangChain сама написана на LangGraph.

Карта концепций (atomic)

Ядро State Machine:

  • LangGraph State — типизированное неизменяемое состояние, схема данных графа
  • LangGraph Reducers — как обновления состояния мержатся (Annotated[type, reducer], add_messages)
  • LangGraph Nodes and Edges — узлы, обычные/условные рёбра, builder, compile

Production-механизмы:

Практические паттерны:

Учебная последовательность

State → Reducers → Nodes/Edges → (собрать минимальный LangGraph ReAct Loop) → Checkpointers → HITL → Time-Travel → Reliability → практические паттерны (Intent Router, MCP-as-Node).

Когда граф, а когда что-то другое

Против PydanticAI и голого цикла — критерий разобран в Agent Architecture: граф покупают, когда поток перестал быть линейным и вы уже начали писать ветвления, лимиты циклов и персистентность руками. PydanticAI решает соседнюю задачу (типобезопасность на границе с моделью) и ставится рядом, а не вместо.

Против MCP/A2A-кооперации развилка проходит по границе процесса и организации. Граф — это оркестрация внутри одного приложения: общее состояние, общий рантайм, полная трассировка. Как только участники — отдельные системы с собственным жизненным циклом, а тем более чужие команды, общее состояние становится невозможным, и нужен протокол с явными контрактами: MCP для подключения инструментов, Agent2Agent Protocol для общения агентов между платформами.

Формулировка, по которой удобно решать: если можно передать объект состояния — это граф; если приходится передавать сообщение с контрактом — это протокол. Гибрид нормален: узел графа вызывает удалённого агента по A2A, оставаясь для остальной системы обычным шагом (LangGraph MCP as Node — тот же приём для MCP).

Связано с

  • Mastra — те же механизмы в TypeScript под другими именами; таблица соответствия там

  • Agent vs Workflow — «граф vs цепочка» = «агент vs workflow»

  • ReAct — ReAct-цикл реализуется как граф; create_agent() написан на LangGraph

  • Agent Architecture — LangGraph как способ собрать агентный цикл

  • PydanticAI — другой фреймворк для агентов (типизация вывода vs оркестрация графа)

  • Orchestrator Adapter — как не растащить вызовы фреймворка по всему приложению

Альтернативный взгляд: граф как actor model

Основная линия заметки объясняет LangGraph со стороны разработчика: цепочки не умеют ветвиться и восстанавливаться, граф умеет. Это взгляд «чем удобнее». Другой заход — со стороны вычислительной модели: LangGraph силён не потому, что удобнее, а потому что стоит на Pregel Model, системе Google для распределённой обработки графов, и на модели акторов из объектных языков.

Что меняется от смены рамки:

  • Узел — не «функция в пайплайне», а актор. Своё изолированное состояние, общение только сообщениями, никакой общей изменяемой памяти. Разработчику с фоном C# или Java эта модель знакома напрямую — она не изобретена для LLM, а заимствована.
  • Параллелизм становится первичным, а не побочным. Дело не в том, что граф «умеет ветвиться», а в том, что внутри супершага узлы физически не видят промежуточных результатов друг друга. Гонка за общее поле исключена конструктивно, а не дисциплиной разработчика.
  • Компиляция — это верификация. .compile() не просто собирает исполняемый объект: он строит граф зависимостей, ловит недостижимые узлы и проверяет рёбра до запуска. Скомпилированный граф неизменяем, поэтому потокобезопасен и держит тысячи независимых сессий.
  • Восстановление — следствие модели, а не фича. Состояние согласовано на границе супершага, значит эта граница и есть естественная точка сохранения.

Отсюда же следует граница применимости, которой нет в «продуктовом» взгляде: LangGraph окупается там, где логика выразима явно — аудит, кредитный скоринг, комплаенс. На задачах, где важна не траектория, а реплика (диалоговый ассистент «поговорить»), граф вырождается в бесконечность сценариев, и проект тонет в ветках.