Содержание
- Что такое LangGraph и кто его делает
- Чем LangGraph отличается от LangChain
- Из чего собран граф: узлы, рёбра, состояние
- Зачем в LangGraph нужна компиляция графа
- Как поставить LangGraph и запустить первый граф
- Условные рёбра и циклы: чем граф сильнее цепочки
- Чекпойнты и треды: что LangGraph помнит между запусками
- Бесплатный ли LangGraph и за что берут деньги
- Где документация LangGraph и что такое команда langgraph
- Чек-лист перед первым графом
LangGraph: что это, как устроен и зачем компиляция
Черновик готовит редакция с помощью ИИ. За стандарт издания отвечает главный редактор — Валерий Курземнек.
Материал редакции Зерокодера. Числа сняты с API PyPI, GitHub и страницы тарифов LangChain 11 сентября 2026 года, поведение библиотеки измерено собственным прогоном на версии 1.2.11. Обновлено: сентябрь 2026.
LangGraph — это открытая Python-библиотека для сборки ИИ-агентов в виде графа: узлы делают работу, рёбра решают, что выполнять дальше, а состояние живёт отдельно от кода узлов. Её делает команда LangChain, лицензия MIT, актуальная версия 1.2.11 от 11 августа 2026 года по данным PyPI. Библиотека бесплатна целиком; деньги начинаются на облачной платформе LangSmith и у провайдера модели.
Главное:
- Ставится одной командой
pip install langgraph, требуется Python 3.10 или новее, зависимостей у пакета шесть. - Граф живёт в две фазы:
StateGraph— чертёж,.compile()— исполняемый объект. До компиляции у чертежа нет методаinvoke. - Компиляция проверяет связность графа: из семи битых графов она поймала три, ещё один перехватил
add_node, а три дожили до запуска. - Циклы и ветвление: узел возвращает управление назад, потолок числа проходов в версии 1.2.11 равен 10 007.
- Чекпойнтер и
thread_idдают память между запусками: повторный вызов в том же треде продолжил счёт с третьего прохода, новый тред начал с нуля.
Что такое LangGraph и кто его делает
LangGraph — библиотека оркестрации агентов от команды LangChain под лицензией MIT. Репозиторий langchain-ai/langgraph создан 9 августа 2023 года, написан на Python и по данным GitHub API на 11 сентября 2026 года набрал 41 442 звезды и 7 001 форк при описании «Build resilient agents.». Пакет на PyPI называется langgraph, версия 1.2.11 загружена 11 августа 2026 года, всего выпущено 276 версий.
Задача библиотеки описана её же аннотацией на PyPI: «Building stateful, multi-actor applications with LLMs» — приложения с состоянием и несколькими действующими лицами. Она нужна, когда модель должна сходить в инструмент, посмотреть на результат и при необходимости повторить круг. Зачем делить работу между несколькими агентами, разобрано в материале про многоагентные системы.
Своей модели у LangGraph нет: это каркас потока управления. Модель подключается отдельно, инструменты пишете вы, библиотека отвечает за порядок вызовов и за состояние.
Мини-вывод: LangGraph — открытый каркас для агентов с состоянием от авторов LangChain, версия 1.2.11, лицензия MIT.
Чем LangGraph отличается от LangChain
LangGraph и LangChain — разные пакеты одной команды. LangChain собирает линейный конвейер: компоненты соединяются оператором |, поток идёт слева направо. LangGraph описывает граф: узлы, рёбра и общее состояние, поток ветвится и возвращается назад. Компоненты LangChain и язык LCEL разобраны в соседнем материале — что такое LangChain.
| LangChain | LangGraph | |
|---|---|---|
| Модель потока | линейный конвейер, оператор \| |
граф: узлы, рёбра, условные переходы |
| Что хранится между шагами | контекст цепочки | явная схема состояния с редьюсерами |
| Циклы | нет | есть, это главный сценарий |
| Память между запусками | подключается компонентом | чекпойнтер и thread_id из коробки |
| Типичная задача | RAG, чат-бот, прототип | агент с инструментами, human-in-the-loop |
Связь между ними видна в метаданных пакета. Зависимости langgraph 1.2.11 по данным PyPI: langchain-core<2,>=1.4.7, langgraph-checkpoint<5.0.0,>=4.1.0, langgraph-prebuilt<1.2.0,>=1.1.0, langgraph-sdk<0.5.0,>=0.4.2, pydantic>=2.7.4, xxhash>=3.5.0. Пакета langchain в этом списке нет: LangGraph берёт только ядро langchain-core и работает без большого фреймворка. Обратное направление жёстче: пакет langchain версии 1.4.0 по данным PyPI требует langgraph<1.3.0,>=1.2.11 — большой фреймворк ставит граф себе в зависимости.
Мини-вывод: LangChain даёт конвейер, LangGraph даёт граф с состоянием; langgraph тянет langchain-core, но обходится без пакета langchain.
Из чего собран граф: узлы, рёбра, состояние
Граф в LangGraph собирается из трёх сущностей. Состояние — общая схема данных, чаще всего TypedDict. Узел — функция Python, которая принимает состояние и возвращает словарь с изменениями. Ребро — правило перехода: безусловное задаётся через add_edge, условное — через add_conditional_edges.
Ключевая деталь, на которой спотыкаются в первый день: узел возвращает не всё состояние, а только изменённые ключи, и способ их слияния задаёт редьюсер. Поле без редьюсера перезаписывается, поле с аннотацией Annotated[list[str], operator.add] — дополняется. Так журнал шагов копится сам, без ручного склеивания списков.
Отдельная сущность — объект Command из langgraph.types, который ищут по запросу «langgraph command» (терминальная команда langgraph — это другое, о ней ниже). Command позволяет узлу одним возвратом и обновить состояние, и указать следующий узел. В нашей проверке 11 сентября 2026 года узел вернул Command(goto="b", update={"n": s["n"]+10}) без единого объявленного ребра между узлами a и b, и граф отработал с результатом {'n': 11}. Поля объекта: graph, update, resume, goto, PARENT.
Начало и конец графа помечают служебные узлы START и END; имена зарезервированы, и свой узел с именем END падает прямо на add_node.
Мини-вывод: состояние — схема, узел — функция, ребро — правило перехода; редьюсер решает, как складываются возвращённые узлом данные.
Зачем в LangGraph нужна компиляция графа
Компиляция превращает чертёж в исполняемый объект и по дороге проверяет связность. Объект StateGraph — это описание: список узлов и рёбер, который можно дополнять, и запускать его нельзя. Вызов .compile() возвращает CompiledStateGraph — неизменяемый объект с методами invoke, stream и get_state. Граница видна дословно: в нашем прогоне на версии 1.2.11 попытка запустить чертёж дала AttributeError: 'StateGraph' object has no attribute 'invoke'.
Вторая работа компиляции — валидация. Чтобы понять её границы, редакция Зерокодера прогнала скриптом compile_matrix.py семь заведомо битых графов. Результат прогона 11 сентября 2026 года на langgraph 1.2.11:
| Что сломано | Где упало | Дословное сообщение |
|---|---|---|
| Ребро ведёт в несуществующий узел | .compile() |
ValueError: Found edge ending at unknown node `zz` |
Нет ребра из START |
.compile() |
ValueError: Graph must have an entrypoint: add at least one edge from START to another node |
| Условное ребро ведёт в несуществующий узел | .compile() |
ValueError: At 'a' node, 'condition' branch found unknown target 'zz' |
Имя узла совпадает со служебным END |
add_node, до компиляции |
ValueError: Node `__end__` is reserved. |
| Узел объявлен, входящих рёбер нет | нигде | компиляция и запуск прошли молча |
| Узел пишет ключ вне схемы состояния | нигде | ключ выброшен молча, результат {'n': 0} |
| Цикл без условия выхода | invoke() |
GraphRecursionError: Recursion limit of 10007 reached without hitting a stop condition |
Практический вывод: компиляция ловит ошибки адресации — ссылки на несуществующие узлы и отсутствие входа. Ошибки данных проходят насквозь: недостижимый узел и лишний ключ в возврате узла молча игнорируются. Это стоит помнить, когда узел «отработал», а в состоянии пусто.
Мини-вывод: .compile() даёт исполняемый объект и проверяет адресацию узлов; корректность данных остаётся на вас.
Как поставить LangGraph и запустить первый граф
Установка занимает одну команду: pip install langgraph. Поле requires_python в метаданных пакета — >=3.10; наш прогон сделан на Python 3.14.3, langgraph 1.2.11, langgraph-checkpoint 4.2.0. Механика pip, если это ваш первый пакет, — в заметке про установку пакета Python.
Первый граф собирается в четыре шага: описать состояние через TypedDict, добавить узлы методом add_node, связать их рёбрами от START, скомпилировать вызовом .compile() и запустить invoke. Ровно это делает langgraph_probe.py — датчик редакции Зерокодера: минимальный граф с циклом и чекпойнтером, который копируется целиком и работает без ключей к модели.
"""langgraph_probe.py — минимальный граф на LangGraph без единого вызова LLM.
Датчик редакции Зерокодера, прогон 11.09.2026.
Установка и запуск: pip install langgraph && python langgraph_probe.py
"""
import operator
import platform
from importlib.metadata import version
from typing import Annotated, TypedDict
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import END, START, StateGraph
class State(TypedDict):
queue: list[str] # что осталось сделать
log: Annotated[list[str], operator.add] # редьюсер: списки складываются
passes: int # счётчик проходов по циклу
def split(state: State) -> dict: # узел: разобрать задачу на шаги
return {"queue": ["сбор", "проверка", "отчёт"], "log": ["план готов"]}
def step(state: State) -> dict: # узел: выполнить один шаг
task, *rest = state["queue"]
return {"queue": rest, "log": [f"шаг: {task}"], "passes": state["passes"] + 1}
def more(state: State) -> str: # условное ребро: цикл или выход
return "step" if state["queue"] else END
builder = StateGraph(State)
builder.add_node("split", split)
builder.add_node("step", step)
builder.add_edge(START, "split")
builder.add_edge("split", "step")
builder.add_conditional_edges("step", more, ["step", END])
print("1. до .compile() объект:", type(builder).__name__, "| invoke есть:", hasattr(builder, "invoke"))
try:
builder.invoke({"queue": [], "log": [], "passes": 0})
except Exception as e:
print(" ошибка:", f"{type(e).__name__}: {e}")
broken = StateGraph(State) # тот же граф с опечаткой в имени узла
broken.add_node("split", split)
broken.add_edge(START, "split")
broken.add_edge("split", "srep")
try:
broken.compile()
except Exception as e:
print("2. compile() поймал:", f"{type(e).__name__}: {e}")
graph = builder.compile(checkpointer=InMemorySaver())
print("3. после .compile() объект:", type(graph).__name__, "| invoke есть:", hasattr(graph, "invoke"))
cfg = {"configurable": {"thread_id": "demo-1"}}
out = graph.invoke({"queue": [], "log": [], "passes": 0}, cfg)
print("4. итог:", out["passes"], "прохода по узлу step |", out["log"])
history = list(graph.get_state_history(cfg))
print("5. чекпойнтов в треде demo-1:", len(history), "| следующий узел:", graph.get_state(cfg).next)
out2 = graph.invoke({"queue": ["добивка"]}, cfg) # тот же тред: состояние на месте
fresh = {"configurable": {"thread_id": "demo-2"}}
out3 = graph.invoke({"queue": [], "log": [], "passes": 0}, fresh) # новый тред: с нуля
print("6. повтор в треде demo-1:", out2["passes"], "проходов,", len(out2["log"]), "записей в журнале")
print("7. чистый тред demo-2: ", out3["passes"], "проходов,", len(out3["log"]), "записей в журнале")
print("8. версии: langgraph", version("langgraph"),
"| langgraph-checkpoint", version("langgraph-checkpoint"),
"| python", platform.python_version())
Дословный вывод прогона 11 сентября 2026 года:
1. до .compile() объект: StateGraph | invoke есть: False
ошибка: AttributeError: 'StateGraph' object has no attribute 'invoke'
2. compile() поймал: ValueError: Found edge ending at unknown node `srep`
3. после .compile() объект: CompiledStateGraph | invoke есть: True
4. итог: 3 прохода по узлу step | ['план готов', 'шаг: сбор', 'шаг: проверка', 'шаг: отчёт']
5. чекпойнтов в треде demo-1: 6 | следующий узел: ()
6. повтор в треде demo-1: 6 проходов, 8 записей в журнале
7. чистый тред demo-2: 3 проходов, 4 записей в журнале
8. версии: langgraph 1.2.11 | langgraph-checkpoint 4.2.0 | python 3.14.3
Мини-вывод: рабочий граф с циклом, ветвлением и памятью помещается в 73 строки и запускается без единого ключа к модели.
Условные рёбра и циклы: чем граф сильнее цепочки
Условное ребро — это функция, которая смотрит на состояние и возвращает имя следующего узла. В скрипте выше эту роль играет more: пока очередь непуста, она возвращает "step" и граф идёт на новый круг; когда очередь опустела, возвращает END. Именно поэтому строка вывода читается как «3 прохода по узлу step» — число проходов задали данные состояния, при том что вызов узла в коде написан один раз.
Линейная цепочка так не умеет: шаги выстроены заранее, вернуться назад поток не может. Циклы дают агенту три вещи: повтор при неудачной попытке, вызов инструмента с последующей проверкой результата и остановку по условию готовности.
У цикла есть предохранитель. Граф без условия выхода в нашем замере упал сообщением GraphRecursionError: Recursion limit of 10007 reached without hitting a stop condition. Дефолт прочитан в коде установленной версии — файл langgraph/_internal/_config.py, строка 32: DEFAULT_RECURSION_LIMIT = int(getenv("LANGGRAPH_DEFAULT_RECURSION_LIMIT", "10007")). Значение переопределяется переменной окружения либо ключом конфигурации в конкретном вызове: с {"recursion_limit": 5} тот же граф остановился на пятом проходе. В учебных материалах рунета recursion_limit показывают ручным значением 25 или 1000 — это осознанное занижение потолка: дефолт версии 1.2.11 больше первого примерно в 400 раз, второго — в 10.
Мини-вывод: цикл в графе управляется данными состояния, а страховкой служит лимит проходов, который лучше выставлять руками под задачу.
Чекпойнты и треды: что LangGraph помнит между запусками
Персистентность в LangGraph включается одним аргументом: graph = builder.compile(checkpointer=InMemorySaver()). После этого каждый шаг графа сохраняется как чекпойнт, а ключом хранения служит thread_id из конфигурации вызова. В нашем прогоне один проход по графу из двух узлов оставил 6 чекпойнтов в треде demo-1, и посмотреть их можно методом get_state_history.
Смысл виден из двух последних строк вывода. Повторный вызов с тем же thread_id показал 6 проходов и 8 записей — состояние первого запуска никуда не делось. Вызов с новым thread_id вернул 3 прохода и 4 записи, то есть начал с чистого листа. Один скомпилированный граф обслуживает много независимых диалогов, и разделяет их thread_id.
InMemorySaver держит всё в оперативной памяти и умирает вместе с процессом — он для разработки; для прода есть чекпойнтеры поверх SQLite и Postgres. На чекпойнтах держится human-in-the-loop: состояние сохранено на каждом шаге, поэтому выполнение можно остановить перед узлом, показать человеку и продолжить с той же точки.
Мини-вывод: чекпойнтер плюс thread_id дают памяти агента адрес, и без них граф каждый раз стартует с нуля.
Бесплатный ли LangGraph и за что берут деньги
Сама библиотека бесплатна полностью: пакет langgraph опубликован на PyPI под лицензией MIT, исходники открыты. Ограничений по числу графов, запусков или коммерческому использованию в лицензии нет. Платными становятся две соседние вещи: вызовы модели у провайдера и облачная платформа, если агент переезжает в прод.
Тарифы платформы сняты со страницы langchain.com/pricing 11 сентября 2026 года; сама страница озаглавлена «LangSmith Plans and Pricing».
| План | Цена | Что внутри |
|---|---|---|
| Developer | 0 долларов за место | до 5 000 базовых трасс в месяц, 1 место, поддержка сообществом |
| Plus | 39 долларов за место в месяц | до 10 000 трасс, один бесплатный Serverless-деплой размера Small, неограниченные места |
| Enterprise | цена по договорённости | self-hosted и гибридное размещение, SSO, SLA |
Расход сверх включённого меряется двумя единицами: LCU (LangChain Compute Units) по 1,50 доллара — за вычисления, LSU (LangChain Storage Units) по 1,00 доллара — за трассы и хранение. Актуальные значения стоит сверять на официальной странице тарифов.
Итог: писать агентов на LangGraph и запускать их на своём сервере можно бесплатно и бессрочно, деньги появляются на наблюдаемости, хостинге и командной работе. Безкодовый путь для сравнения — обзор n8n.
Мини-вывод: библиотека под MIT бесплатна вся, платный слой — платформа LangSmith от 39 долларов за место плюс LCU и LSU по расходу.
Где документация LangGraph и что такое команда langgraph
Официальных адресов у документации LangGraph три, и в выдаче они перемешаны со старыми. Домашней страницей в метаданных пакета 1.2.11 указан docs.langchain.com/oss/python/langgraph/overview — это основная документация, там же разделы про графовый API, персистентность и режим «thinking in LangGraph». API-справочник по классам и методам живёт отдельно: reference.langchain.com/python/langgraph. Список изменений по версиям — на странице релизов github.com/langchain-ai/langgraph/releases. Старый адрес langchain-ai.github.io/langgraph на 11 сентября 2026 года ещё отвечает, и ссылки на него стоят на четырёх страницах топа Яндекса — код оттуда сверяйте с текущей версией.
Точек входа четыре: Graph API — синтаксис узлов, рёбер и редьюсеров; Persistence — чекпойнтеры и треды; раздел прерываний — human-in-the-loop; релизы — что изменилось между вашей версией и текущей.
Под запросом «langgraph command» скрываются две разные вещи. Первая — объект Command из langgraph.types, разобранный выше. Вторая — терминальная команда langgraph с подкомандами вроде langgraph dev. Она приезжает отдельно: в каталоге langgraph-1.2.11.dist-info файла entry_points.txt нет, консольную программу pip install langgraph не ставит. Команда живёт в пакете langgraph-cli версии 0.4.31 под MIT, с описанием «CLI for interacting with LangGraph API».
Мини-вывод: документация — на docs.langchain.com, справочник API — на reference.langchain.com, а терминальная команда langgraph ставится отдельным пакетом langgraph-cli.
Чек-лист перед первым графом
- Начните со схемы состояния: решите, какие поля перезаписываются, а какие копятся редьюсером.
- Компилируйте один раз при старте приложения и переиспользуйте объект
CompiledStateGraph. - Не рассчитывайте, что компиляция поймает ошибку в данных: лишний ключ в возврате узла исчезает молча.
- Ставьте
recursion_limitпод задачу: дефолт 10 007 проходов диагностирует зацикливание слишком поздно. - Включайте чекпойнтер сразу и задавайте
thread_id: без него граф не помнит ничего между вызовами. - Прогоните
langgraph_probe.pyна своей машине и сверьте вывод с нашим: это десять секунд и полная картина жизненного цикла графа.
Соседние библиотеки для тех же задач перечислены в подборке ИИ-фреймворков для Python.
