Что такое LangGraph и зачем он нужен?
Краткий тезис
LangGraph — это фреймворк для построения циклических графов вычислений в экосистеме LangChain. В отличие от линейных цепочек LangChain (DAG), LangGraph позволяет создавать агентов с петлями, ветвлениями и состоянием, общим для всех узлов. Он нужен там, где требуется сложное принятие решений: reasoning|мультишаговый reasoning, инструменты, обратная связь и динамическое изменение плана — то есть для production-grade агентов и RAG|Agentic RAG.
1. Термин: LangGraph
LangGraph — это библиотека, расширяющая LangChain для создания направленных графов с циклами (cyclic graphs). Она разработана компанией LangChain Inc. и тесно интегрирована с экосистемой LangChain (модели, инструменты, память). Основная идея: вы определяете узлы (nodes — функции/LLM вызовы) и рёбра (edges — связи), а LangGraph управляет потоком данных и состоянием. Ключевое отличие от обычного LangChain — возможность циклов: узел может передавать управление обратно предыдущему узлу или самому себе, что необходимо для итеративных процессов.
На практике LangGraph позволяет описывать поведение агента как граф: каждый шаг — это узел, а решение, куда перейти дальше, принимается либо статически, либо через conditional edges (edges|условные рёбра). Это даёт гибкость, недостижимую в линейных цепочках.
2. Отличие от LangChain: DAG vs Cyclic Graph
LangChain по умолчанию работает с DAG (Directed Acyclic Graph) — направленный ациклический граф. Вы задаёте последовательность шагов, и выполнение идёт вперёд без возврата. Для простых RAG (Retrieval-Augmented Generation) или цепочек «запрос → поиск → ответ» этого достаточно. Но для агентов, которые должны:
- вызывать инструменты,
- оценивать результаты,
- при необходимости переформулировать запрос или повторить действие,
нужны циклы. LangGraph разрешает циклы за счёт того, что граф может содержать обратные рёбра.
Сравнение
| Характеристика | LangChain | LangGraph |
|---|---|---|
| Тип графа | DAG (ациклический) | Направленный граф с циклами |
| Поток выполнения | Линейный, предсказуемый | Ветвистый, может зацикливаться |
| Состояние | Передаётся через цепочку | Единое состояние (shared state) |
| Гибкость принятия решений | Низкая (только статические маршруты) | Высокая (conditional edges) |
| Сложность разработки | Низкая | Средняя |
| Production-агенты | Ограниченно | Да, основное применение |
LangGraph не заменяет LangChain, а дополняет: он использует те же модели, промпты и инструменты, но даёт новый уровень контроля над логикой агента.
3. Ключевые концепции: Nodes, Edges, State
Разберём три базовых строительных блока LangGraph.
-
Nodes (узлы) — это вычислительные единицы. Каждый узел получает текущее состояние графа, выполняет некоторую логику (например, вызов LLM, запуск инструмента, проверку условия) и возвращает обновлённое состояние. Узлы могут быть:
-
Edges (рёбра) — это связи между узлами. Они определяют, в каком порядке выполняются узлы. Рёбра бывают:
- обычные: после узла A всегда идёт узел B;
- условные (conditional edges): после узла A решение, какой узел выполнить следующим, принимается на основе текущего состояния (например, если LLM вернул
tool_call, то идти к узлу инструмента, иначе к узлу ответа).
-
State (состояние) — это структура данных, которая передаётся по графу и доступна всем узлам. Оно обновляется по мере прохождения. Каждый узел может читать и изменять состояние. Состояние задаётся в виде TypedDict или Pydantic BaseModel. LangGraph автоматически обрабатывает обновления состояния (append, overwrite и т.д.).
4. Как работает State (Shared Memory между узлами)
State — сердце LangGraph. Это единое хранилище, к которому имеют доступ все узлы графа. Оно гарантирует, что информация не теряется между шагами.
Пример определения состояния:
from typing import TypedDict, List
class AgentState(TypedDict):
messages: List[dict] # история диалога
next_action: str # действие, которое нужно выполнить
result: str # результат последнего шага
В узлах вы можете читать и модифицировать поля состояния. LangGraph поддерживает разные режимы обновления:
- Overwrite — заменить поле новым значением.
- Append — добавить элемент в список (полезно для истории сообщений).
- Merge — слить словари.
Пример узла, добавляющего сообщение:
def call_model(state: AgentState) -> dict:
# state["messages"] – список сообщений
response = llm.invoke(state["messages"])
return {"messages": [response]} # append по умолчанию для списков
Благодаря единому состоянию граф может «помнить» предыдущие действия и результаты, что необходимо для циклов: например, агент может вызывать инструмент несколько раз, накапливая наблюдения в state.
5. Conditional Edges — ветвление в графе
Conditional Edges позволяют динамически выбирать следующий узел на основе текущего состояния. Это главный механизм для реализации принятия решений у агента.
Условное ребро задаётся функцией, которая принимает состояние и возвращает имя узла, к которому перейти.
Пример:
def router(state: AgentState) -> str:
last_message = state["messages"][-1]
if "tool_call" in last_message.get("additional_kwargs", {}):
return "call_tool"
else:
return "generate_answer"
graph.add_conditional_edges(
source="agent",
condition=router,
path_map={"call_tool": "execute_tool", "generate_answer": "final_output"}
)
Здесь после узла agent выполняется router, и в зависимости от того, запросил ли LLM вызов инструмента, граф идёт либо на execute_tool, либо на final_output. Из execute_tool может быть ребро обратно в agent (цикл). Таким образом, агент может вызывать несколько инструментов, пока не получит достаточно информации для ответа.
Conditional Edges превращают граф из статического конвейера в настоящую систему с логикой.
6. Пример: Agentic RAG на LangGraph
Agentic RAG (агентный RAG) — классический сценарий, где LangGraph оправдывает себя. Обычный RAG: запрос → поиск → ответ. Агентный RAG: агент решает, нужно ли уточнить запрос, искать ещё один источник, переписать запрос или сразу ответить.
Схема графа:
- Узел
analyze_query: LLM определяет намерение (уточнить, искать, ответить). - Условное ребро:
- Если «искать» → узел
retrieve(выполняет поиск по векторной БД). - Если «уточнить» → узел
clarify(запрос к пользователю). - Если «ответить» → узел
answer(генерация на основе имеющегося контекста).
- Если «искать» → узел
- Узел
retrieve: добавляет найденные чанки в state. - Условное ребро после
retrieve: если чанков достаточно →answer, иначе →analyze_query(цикл, возможно уточнить запрос). - Узел
answer: LLM генерирует финальный ответ, используя state.
Такой граф позволяет агенту самостоятельно решать, когда остановиться, и не тратить ресурсы на поиск, если ответ уже есть в истории.
7. Код: простой пример графа с ветвлением
Ниже — минимальный рабочий пример на основе LangGraph (предполагается, что используется Python 3.10+ и установлены langgraph, langchain-openai).
from langgraph.graph import StateGraph, END
from typing import TypedDict, List
# 1. Определяем состояние
class GraphState(TypedDict):
input: str
question: str
context: List[str]
output: str
# 2. Определяем узлы
def classify(state: GraphState) -> dict:
# Допустим, мы смотрим, есть ли слово "search" в вопросе
if "search" in state["input"].lower():
return {"question": state["input"], "context": []}
else:
return {"output": "No search needed, just answer directly."}
def retrieve(state: GraphState) -> dict:
# Заглушка поиска
docs = ["doc1 about search", "doc2 about query"]
return {"context": docs}
def generate(state: GraphState) -> dict:
context = "\n".join(state["context"])
# В реальности здесь вызов LLM
state["output"] = f"Answer based on: {context}"
return {"output": state["output"]}
# 3. Строим граф
builder = StateGraph(GraphState)
builder.add_node("classify", classify)
builder.add_node("retrieve", retrieve)
builder.add_node("generate", generate)
builder.set_entry_point("classify")
# Условное ребро из classify
def router(state: GraphState) -> str:
if "context" in state and not state["context"]:
return "retrieve"
else:
return "generate"
builder.add_conditional_edges("classify", router, {
"retrieve": "retrieve",
"generate": "generate"
})
# После retrieve идём в generate
builder.add_edge("retrieve", "generate")
builder.add_edge("generate", END)
# Компилируем
graph = builder.compile()
# Запуск
result = graph.invoke({"input": "I need to search something"})
print(result["output"])
Этот пример демонстрирует: узел classify решает, нужно ли искать, и направляет либо на retrieve, либо на generate. Обратите внимание, что после retrieve граф идёт в generate, а затем завершается. Для полноценного агента с циклами нужно добавить обратное ребро из generate в classify или другой узел.
8. Сравнение с альтернативами (AutoGen, CrewAI)
LangGraph — не единственная библиотека для построения агентов. Основные конкуренты:
| Критерий | LangGraph | AutoGen (Microsoft) | CrewAI |
|---|---|---|---|
| Модель вычислений | Граф с циклами и состоянием | Асинхронный обмен сообщениями между агентами | Иерархия ролей (Crew, Agent, Task) |
| Управление потоком | Программное (условные рёбра) | Через события и обратные вызовы | Статическое (задачи в порядке) |
| Состояние | Единое shared state | Каждый агент имеет своё состояние | State передаётся через задачи |
| Интеграция с LangChain | Родная | Слабая (можно через LLM) | Средняя (есть adapter) |
| Гибкость | Очень высокая | Высокая (много паттернов) | Средняя (ориентир на роли) |
| Порог входа | Средний | Высокий (асинхронность) | Низкий |
| Production readiness | Высокая (LangSmith, мониторинг) | Средняя | Средняя |
Когда выбирать LangGraph:
- Нужна тонкая настройка потока (conditional edges, циклы).
- Уже используете LangChain (легко интегрировать).
- Требуется единое состояние для всех шагов.
- Планируете использовать LangSmith для отладки и мониторинга.
Когда альтернативы:
- AutoGen — если нужно много агентов, общающихся друг с другом (multi-agent conversation).
- CrewAI — если хотите быстро прототипировать ролевую команду без программирования сложной логики.
9. Когда стоит использовать LangGraph, а когда нет
Используйте LangGraph, если:
- Ваш агент должен выполнять последовательность шагов с принятием решений (выбор инструмента, повторный поиск, уточнение).
- Нужны циклы: например, итеративное улучшение ответа или повторные попытки при ошибках.
- Требуется единое состояние, доступное на всех этапах.
- Вы строите production-систему и хотите мониторить выполнение графа через LangSmith.
- Нужна гибкость в маршрутизации (conditional edges).
Не используйте LangGraph, если:
- Ваша логика укладывается в линейную цепочку (простой RAG, один LLM вызов).
- Нужна простая координация нескольких независимых агентов (лучше AutoGen).
- У вас очень простой сценарий «запрос → ответ» — хватит обычного LangChain или даже прямого вызова API.
- Команда не знакома с графами и хочет минимизировать сложность.
10. Интеграция с LangChain и экосистема
LangGraph не работает изолированно. Он использует:
- LangChain — промпты, модели, инструменты, цепочки.
- LangSmith — отладка, трейсинг, мониторинг графа (вы можете видеть, как данные проходят по узлам).
- LangServe — деплой графа как REST API.
- LangHub (опционально) — шаблоны графов.
Пример интеграции: в узле можно использовать любой Runnable из LangChain:
from langchain.chains import LLMChain
from langchain.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([("system", "You are a helpful assistant."), ("user", "{input}")])
chain = prompt | llm
def node_function(state):
response = chain.invoke({"input": state["question"]})
return {"output": response}
LangGraph также поддерживает параллельные узлы и fan-out/fan-in (разветвление и схождение), что полезно для выполнения нескольких инструментов одновременно.
Пет-проект для закрепления
Задача: Реализовать агента, который отвечает на вопросы по документации LangGraph, используя Agentic RAG с двумя источниками (векторная БД и поиск в интернете). Агент должен решать, какой источник использовать, а при необходимости комбинировать.
Инструменты:
- Python 3.10+, langgraph, langchain-openai, chromadb (векторная БД), duckduckgo-search (инструмент поиска).
- LangSmith (опционально для отладки).
Шаги:
- Создать векторное хранилище с документацией LangGraph (например, 20 страниц).
- Определить граф с узлами:
router— LLM решает, нужна ли внешняя информация.vector_search— поиск в Chroma.web_search— поиск в DuckDuckGo.answer— генерация ответа на основе контекста.
- Добавить conditional edges для выбора источника.
- Реализовать цикл: если после поиска контекста недостаточно, идти в другой источник.
- Протестировать на вопросах из документации и общих вопросах о LangGraph.
Ожидаемый результат: Рабочий граф, который корректно выбирает источник и даёт ответ. Вы сможете визуализировать граф через graph.draw_mermaid() и отслеживать шаги в LangSmith. Проект покажет понимание state, conditional edges и циклов.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 41 | Что такое AI-агент и в чём его отличие от цепочки вызовов LLM? |
| 43 | Как работает AutoGen и для каких сценариев он подходит? |
| 44 | Что такое CrewAI и чем отличается от LangGraph? |
| 45 | Как реализовать Agentic RAG с помощью графов? |
| 30 | Что такое LangChain и какие у него основные компоненты? |
| 31 | В чём разница между Chain и Agent в LangChain? |
Навигация
- Предыдущий: 41
- Следующий: 43
- Индекс: 00. Индекс разборов