Перейти к основному содержимому

🦜🕸️LangGraph

⚡ Создание языковых агентов в виде графов ⚡

Обзор

LangGraph — это библиотека для создания приложений с сохранением состояния и множеством акторов, использующих модели изучения языков (LLMs), используемая для создания агентных и многоагентных рабочих процессов. В отличие от других фреймворков LLM, она предлагает следующие основные преимущества: циклы, управляемость и сохранение состояния. LangGraph позволяет определить потоки, включающие циклы, что необходимо для большинства агентных архитектур, отличая её от решений, основанных на DAG. Как фреймворк низкого уровня, он предоставляет детальный контроль как над потоком, так и над состоянием вашего приложения, что важно для создания надёжных агентов. Кроме того, LangGraph включает встроенное сохранение, позволяющее использовать расширенные функции участия человека и памяти.
LangGraph вдохновлён Pregel и Apache Beam. Публичный интерфейс заимствует идеи из NetworkX. LangGraph создан компанией LangChain Inc, разработчиками LangChain, но может использоваться без LangChain.

Платформа LangGraph — это инфраструктура для развёртывания агентов LangGraph. Это коммерческое решение для развёртывания агентных приложений в производственной среде, построенное на основе фреймворка с открытым исходным кодом LangGraph. Платформа LangGraph состоит из нескольких компонентов, которые работают вместе для поддержки разработки, развёртывания, отладки и мониторинга приложений LangGraph: LangGraph Server (API), LangGraph SDKs (клиенты для API), LangGraph CLI (инструмент командной строки для создания сервера), LangGraph Studio (интерфейс/отладчик).
Чтобы узнать больше о LangGraph, ознакомьтесь с нашим первым курсом LangChain Academy, Введение в LangGraph, доступным бесплатно здесь.

Ключевые особенности

  • Циклы и ветвление: Реализуйте циклы и условные конструкции в своих приложениях.

  • Сохранение состояния: Автоматически сохраняйте состояние после каждого шага в графе. Приостанавливайте и возобновляйте выполнение графа в любой момент для поддержки восстановления ошибок, рабочих процессов с участием человека, путешествий во времени и многого другого.

  • Человек в цикле: Прерывайте выполнение графа для одобрения или редактирования следующего действия, запланированного агентом.

  • Поддержка потоковой передачи: Передача выходных данных по мере их создания каждым узлом (включая потоковую передачу токенов).

  • Интеграция с LangChain: LangGraph бесшовно интегрируется с LangChain и LangSmith (но не требует их).

Платформа LangGraph

Платформа LangGraph — это коммерческое решение для развёртывания агентных приложений в производственной среде, построенное на основе фреймворка с открытым исходным кодом LangGraph. Вот некоторые распространённые проблемы, возникающие в сложных развёртываниях, которые решает платформа LangGraph:

  • Поддержка потоковой передачи: Сервер LangGraph предоставляет несколько режимов потоковой передачи, оптимизированных для различных потребностей приложений
  • Фоновое выполнение: Запускает агентов асинхронно в фоновом режиме
  • Поддержка долгоработающих агентов: Инфраструктура, способная обрабатывать долгоработающие процессы
  • Двойное текстирование: Обработка ситуации, когда вы получаете два сообщения от пользователя до того, как агент сможет ответить
  • Обработка всплесков: Очередь задач для обеспечения устойчивой обработки запросов без потерь, даже при высоких нагрузках

Установка

pip install -U langgraph

Пример

Одной из центральных концепций LangGraph является состояние. Каждое выполнение графа создает состояние, которое передается между узлами графа по мере их выполнения, и каждый узел обновляет это внутреннее состояние своим возвращаемым значением после выполнения. Способ, которым граф обновляет свое внутреннее состояние, определяется либо выбранным типом графа, либо пользовательской функцией.
Давайте рассмотрим простой пример агента, который может использовать инструмент поиска.

pip install langchain-anthropic
export ANTHROPIC_API_KEY=sk-...

При желании, мы можем настроить LangSmith для лучшего наблюдения.

export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=lsv2_sk_...
from typing import Annotated, Literal, TypedDict

from langchain_core.messages import HumanMessage
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import END, START, StateGraph, MessagesState
from langgraph.prebuilt import ToolNode


# Определяем инструменты, которые агент может использовать
@tool
def search(query: str):
"""Вызов для поиска в интернете."""
# Это заглушка, но не говорите об этом LLM...
if "sf" in query.lower() or "san francisco" in query.lower():
return "60 градусов и туманно."
return "90 градусов и солнечно."


tools = [search]

tool_node = ToolNode(tools)

```python
model = ChatAnthropic(model="claude-3-5-sonnet-20240620", temperature=0).bind_tools(tools)

# Определяем функцию, которая определяет, продолжать или нет
def should_continue(state: MessagesState) -> Literal["tools", END]:
messages = state['messages']
last_message = messages[-1]
# Если LLM вызывает инструмент, тогда мы направляем на узел "tools"
if last_message.tool_calls:
return "tools"
# В противном случае мы останавливаемся (отвечаем пользователю)
return END


# Определяем функцию, которая вызывает модель
def call_model(state: MessagesState):
messages = state['messages']
response = model.invoke(messages)
# Мы возвращаем список, потому что это будет добавлено к существующему списку
return {"messages": [response]}


# Определяем новый граф
workflow = StateGraph(MessagesState)

# Определяем два узла, между которыми будем циклировать
workflow.add_node("agent", call_model)
workflow.add_node("tools", tool_node)

# Устанавливаем точку входа как `agent`
# Это означает, что этот узел вызывается первым
workflow.add_edge(START, "agent")

# Теперь добавляем условное ребро
workflow.add_conditional_edges(
# Сначала мы определяем стартовый узел. Мы используем `agent`.
# Это означает, что это ребра, которые будут использованы после вызова узла `agent`.
"agent",
# Далее, мы передаем функцию, которая определяет, какой узел будет вызван следующим.
should_continue,
)

# Теперь добавляем обычное ребро от `tools` к `agent`.
# Это означает, что после вызова `tools` узел `agent` вызывается следующим.
workflow.add_edge("tools", 'agent')
# Инициализируем память для сохранения состояния между запусками графа
checkpointer = MemorySaver()

# Наконец, мы его компилируем!
# Это компилирует его в LangChain Runnable,
# что означает, что вы можете использовать его, как и любой другой runnable.
# Обратите внимание, что при компиляции графа мы (опционально) передаем память
app = workflow.compile(checkpointer=checkpointer)

Использование Runnable

final_state = app.invoke(
{"messages": [HumanMessage(content="какая погода в сан-франциско")]},
config={"configurable": {"thread_id": 42}}
)
final_state["messages"][-1].content
"На основе результатов поиска, могу сказать, что текущая погода в Сан-Франциско:\n\nТемпература: 60 градусов по Фаренгейту\nУсловия: Туманно\n\nСан-Франциско известен своими микроклиматами и частыми туманами, особенно летом. Температура 60°F (около 15,5°C) является вполне типичной для города, где круглый год держатся мягкие температуры. Туман, часто называемый местными жителями \"Карл Туман\", является характерной чертой погоды Сан-Франциско, особенно по утрам и вечерам.\n\nЕсть ли ещё что-то, что вы хотели бы узнать о погоде в Сан-Франциско или любом другом месте?"

Теперь, когда мы передаем тот же "thread_id", контекст беседы сохраняется через сохранённое состояние (то есть сохранённый список сообщений)

final_state = app.invoke(
{"messages": [HumanMessage(content="а как насчёт погоды в нью-йорке")]},
config={"configurable": {"thread_id": 42}}
)
final_state["messages"][-1].content

"На основе результатов поиска, я могу сообщить вам, что в настоящее время погода в Нью-Йорке следующая:\n\nТемпература: 90 градусов по Фаренгейту (примерно 32,2 градуса Цельсия)\nУсловия: Солнечно\n\nЭта погода значительно отличается от того, что мы только что видели в Сан-Франциско. В Нью-Йорке сейчас значительно теплее. Вот несколько моментов, на которые стоит обратить внимание:\n\n1. Температура 90°F довольно высокая, типичная для летней погоды в Нью-Йорке.\n2. Солнечные условия предполагают ясное небо, что отлично для активного отдыха на свежем воздухе, но также означает, что может ощущаться ещё жарче из-за прямых солнечных лучей.\n3. Такая погода в Нью-Йорке часто сопровождается высокой влажностью, что может сделать её ещё более жаркой, чем это предполагает фактическая температура.\n\nИнтересно наблюдать резкий контраст между мягкой, туманной погодой в Сан-Франциско и жаркими, солнечными условиями в Нью-Йорке. Это различие иллюстрирует, насколько разнообразной может быть погода в разных частях Соединённых Штатов даже в один и тот же день.\n\nЕсть ли что-то ещё, что вы хотели бы узнать о погоде в Нью-Йорке или в каком-либо другом месте?"

Пошаговый разбор

  1. Инициализируйте модель и инструменты.
    • мы используем ChatAnthropic в качестве нашего LLM. ПРИМЕЧАНИЕ: нам нужно убедиться, что модель знает, что у неё есть эти инструменты для вызова. Мы можем сделать это, преобразовав инструменты LangChain в формат для вызова инструментов OpenAI с использованием метода .bind_tools().

    • мы определяем инструменты, которые хотим использовать - в нашем случае это инструмент поиска. Создать свои собственные инструменты очень просто - см. документацию здесь.

  2. Инициализация графа с состоянием.
    • мы инициализируем граф (StateGraph), передавая схему состояния (в нашем случае MessagesState)
    • MessagesState - это предварительно созданная схема состояния, которая имеет один атрибут — список объектов Message из LangChain, а также логику для объединения обновлений от каждого узла в состояние
  3. Details

    Определение узлов графа.
    Существует два основных узла, которые нам нужны:

    • Узел agent: отвечает за принятие решения о том, какие действия (если они вообще нужны) предпринять.
    • Узел tools, который вызывает инструменты: если агент решает предпринять действие, этот узел выполнит это действие.
  4. Details

    Определение точки входа и ребер графа.
    Сначала нам нужно установить точку входа для выполнения графа - узел agent.
    Затем мы определяем одно обычное и одно условное ребро. Условное ребро означает, что пункт назначения зависит от содержимого состояния графа (MessageState). В нашем случае пункт назначения неизвестен, пока агент (LLM) не примет решение.

    • Условное ребро: после вызова агента мы должны либо:

    • a. Запустить инструменты, если агент сказал действовать, ИЛИ

    • b. Завершить (ответить пользователю), если агент не попросил запустить инструменты

    • Обычное ребро: после вызова инструментов граф всегда должен возвращаться к агенту, чтобы решить, что делать дальше

  5. Скомпилируйте граф.
    • При компиляции графа мы превращаем его в Runnable LangChain, который автоматически позволяет вызывать .invoke(), .stream() и .batch() с вашими входными данными
    • Мы также можем по желанию передать объект контрольной точки для сохранения состояния между запусками графа, что позволяет использовать память, рабочие процессы с участием человека, путешествия во времени и многое другое. В нашем случае мы используем MemorySaver — простой контрольный пункт в памяти
  6. Выполните граф.
    1. LangGraph добавляет входящее сообщение во внутреннее состояние, затем передает состояние в узел точки входа, "agent".
    2. Узел "agent" выполняется, вызывая модель чата.
    3. Модель чата возвращает AIMessage. LangGraph добавляет это в состояние.
    4. Граф повторяет следующие шаги, пока нет больше tool_calls на AIMessage:
    • Если у AIMessage есть tool_calls, выполняется узел "tools"
    • Узел "agent" выполняется снова и возвращает AIMessage
    1. Выполнение продолжается до специального значения END и выводит финальное состояние.

    2. В результате мы получаем список всех наших сообщений чата в качестве вывода.

Документация

  • Учебные пособия: Узнайте, как создавать с помощью LangGraph через пошаговые примеры.
  • Руководства по выполнению задач: Выполняйте конкретные задачи в LangGraph, от потоковой передачи до добавления памяти и сохранения, до общих шаблонов проектирования (ветвление, подграфы и т.д.). Эти руководства помогут скопировать и запустить конкретный код.
  • Концептуальные руководства: Подробные объяснения ключевых концепций и принципов LangGraph, таких как узлы, рёбра, состояния и многое другое.
  • Справочник API: Ознакомьтесь с важными классами и методами, простыми примерами использования API графов и контрольных точек, более высокоуровневыми готовыми компонентами и многим другим.
  • Платформа LangGraph: Платформа LangGraph — это коммерческое решение для развертывания агентных приложений в производственной среде, построенное на основе фреймворка с открытым исходным кодом LangGraph.

Участие

Для получения дополнительной информации о том, как внести вклад, смотрите здесь.