Главная / Статьи

Агент — это цикл While: где LangChain create_agent и LangGraph Fit

Агент для вызова инструментов в двадцати строках, четыре проблемы производства, с которыми сталкивается цикл, и когда переходить от LangChain create_agent к графам состояний LangGraph.

Издатель: REACTAPP.TOP8 мин чтения1816 слов

Источники: An Agent Is a While Loop

Использование инструментов привлекает много внимания, но тема, которая определяет, когда вызывать инструмент, что делать с результатом и когда прекращать работу, — это цикл, окружающий модель. Люди называют этот цикл оркестрацией, что придает ему более значимый оттенок, чем он есть на самом деле. Самый простой способ понять это — сначала рассмотреть всю структуру целиком, а затем выяснить, почему она недостаточна для промышленного использования.

Целый агент примерно из двадцати строк

Возьмем процесс вызова инструментов, при котором модель запрашивает инструмент, ваш код его выполняет, а результат возвращается модели, и обернем всё это циклом while. Сначала инициализируется диалог:

messages = [system_prompt, user_question]

А теперь сам цикл:

while True:
    reply = model(messages, tools=TOOLS)    if not reply.tool_calls:
        return reply.text                    # the model answered; done    for call in reply.tool_calls:
        result = TOOLS[call.name](**call.args)   # your code runs it
        messages.append(tool_result(call.id, result))

Это настоящий агент, а не его карикатура. Модель выбирает, какой инструмент использовать. Ваш код запускает его и добавляет полученный результат. Этот процесс повторяется до тех пор, пока модель не вернёт текст вместо запроса к инструменту.

Если кажется немного разочаровывающим, что всё так мало, именно это и предусмотрено. Всё, что предоставляет фреймворк, — это то, что вы добавляете после того, как этот цикл вступает в эксплуатацию.

Один запуск, шаг за шагом

Представьте агента для триажа, которому задали вопрос о причинах сбоев синхронизации с перевозчиками ночью из-за ошибки ERR_4412 с момента выпуска в декабре. Он находит ответ за три итерации, в рамках одного цикла без использования фреймворка.

Шаг 1. Модель пока не отвечает. Она отправляет запрос search_runbooks("ERR_4412"), и цикл его выполняет. Возвращается три результата, при этом в первом указано, что ошибка означает истёкший токен аутентификации перевозчика. Эта информация добавляется к сообщениям.

Шаг 2. Прочитав это, модель запрашивает get_release_notes("december"). Возвращается один документ: интервал обновления токена сокращен с 24 часов до 12. Данные добавлены снова.

Шаг 3. Имея оба результата, модель отвечает в простом текстовом формате без запроса к инструментам. Синхронизация, происходящая ночью, длится более двенадцати часов, поэтому токен истекает в середине её выполнения. Поскольку запросов к выполнению нет, цикл завершается.

Модель выбрала инструменты и запустила их. Список сообщений пополнился двумя результатами, больше ничего не произошло.

Что ломается после недели работы

В производственных условиях обычно появляются четыре проблемы, как правило, в таком порядке. Ни одна из них не связана с самой моделью. Все они касаются среды её выполнения.

Перезапуск. На втором ходу система застревает из-за медленного обработчика инцидентов на восемь минут, пока контейнер не перерабатывается. Поскольку messages хранились только в памяти, они исчезают, и процесс начинается заново с первого вопроса. Решение заключается в сохранении хода разговора после каждого шага и возобновлении работы на основе этого сохранения.

Опасный инструмент. В другой ночь на четвертом ходу модель приходит к выводу, что правильным решением является rollback_release("dec-2025"). Позволять модели делать это в одиночку в 3 часа ночи — это то, чего никто не хочет. Цикл должен остановиться, спросить у человека, подождать, возможно, до утра, а затем продолжить именно с того места, где он остановился. Простой цикл while не может ждать до утра.

Неверный поворот. На пятом шаге выбирается неэффективный запрос к поиску, а на шагах с 6 по 16 этот поиск повторяется с незначительными изменениями формулировок, причем за каждый такой шаг взимается плата. Простое рассмотрение списка из шестнадцати элементов не покажет, где произошла ошибка. Помогает наличие лимита на количество шагов и записи каждого шага, которую можно воспроизвести.

Как действовать дальше. Для ошибок аутентификации требуется определенная последовательность действий, а для ошибок данных — другая; когда два инструмента приходят к разному выводу, дело должно быть передано человеку. Модель способна хорошо выбирать инструмент, но не должна импровизировать с порядком выполнения действий при каждой работе. Все варианты развития событий должны быть зафиксированы, чтобы они вели себя одинаково каждый раз.

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

Преобразуйте шаги в блоки

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

  • Каждый блок заканчивается сохранением состояния в надёжное хранилище, так что после перезапуска процесс продолжается с последнего завершённого блока, а не с начала.
  • Любой блок может приостановить весь процесс и ждать человека столько, сколько потребуется, при этом состояние безопасно сохраняется.
  • Поскольку шаги являются дискретными, вы можете вернуться к любому этапу и продолжить работу с другими входными данными, чтобы выяснить, где произошла ошибка.
  • Разветвления находятся в блоке классификации: здесь есть путь аутентификации, путь обработки данных и механизм эскалации. Вы нарисовали эти три стрелки. Модель по-прежнему выбирает инструменты в рамках определенного пути, но сам путь уже находится в вашем распоряжении.

    Сама модель не изменилась. Цикл приобрел вид, который можно увидеть, сохранить и прервать.

    Что такое LangChain

    LangChain — это библиотека на Python с аналогом на JavaScript, предоставляющая готовые компоненты для приложений на основе больших языковых моделей, чтобы не переписывать одни и те же элементы каждый раз. Важны три вида таких компонентов.

    Обертки моделей. Один интерфейс для всех поставщиков. ChatAnthropic, ChatOpenAI и ChatGoogleGenerativeAI предоставляют одинаковый метод .invoke(messages), поэтому смена поставщика требует лишь одной правки, а не переписывания кода запроса.

    Интеграции. Адаптеры для компонентов, окружающих модель: хранилища векторов, загрузчики документов, механизмы поиска, модели эмбеддингов и несколько сотен внешних инструментов. Именно здесь заключается основная экономия усилий, поскольку иначе каждый адаптер пришлось бы писать и обслуживать самостоятельно.

    Цикл агента в виде пакета. Начиная с версии 1.0 в октябре 2025 года основным методом является create_agent, который импортируется следующим образом:

    from langchain.agents import create_agent
    

    и используется так:

    agent = create_agent(
        model="anthropic:claude-sonnet-4-6",
        tools=[search_runbooks, get_release_notes],
        system_prompt="You are the on-call triage assistant.",
    )
    result = agent.invoke({"messages": [("user", "Why is ERR_4412 failing?")]})
    

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

    create_agent не предусматривает возможности управления внутренней логикой цикла: нет возможности просматривать состояние между шагами, нет паузы на весь день, и сбой процесса приводит к прерыванию всей работы.

    Что такое LangGraph

    Под create_agent находится LangGraph — другая библиотека от того же разработчика. LangChain предоставляет готовый цикл, тогда как LangGraph даёт компоненты для создания собственного цикла в форме графа, показанного выше. Необходимо определить три элемента.

    Состояние. Объект, созданный с помощью функции типа «тип», который передается по цепочке обработки. Для сортировки он содержит полученные сообщения, классификацию ошибок, предложенное решение и информацию о том, было ли оно одобрено.

    Узлы. Обычные функции на Python, которые принимают состояние и возвращают обновления. Функции classify, search_runbooks, propose_fix и ask_human — это просто функции.

    Рёбра. Порядок выполнения. Простые рёбра представляют собой фиксированные стрелки, а условные рёбра — функции, которые считывают состояние и выбирают следующий узел; таким образом там реализуется разделение на аутентификацию и обработку данных.

    Импорт:

    from langgraph.graph import StateGraph
    

    И граф, построенный из этих элементов:

    g = StateGraph(TriageState)
    g.add_node("classify", classify)
    g.add_node("search", search_runbooks)
    g.add_node("propose", propose_fix)
    g.add_node("approve", ask_human)
    g.add_edge("classify", "search")
    g.add_conditional_edges("search", route_by_error_type)
    g.add_edge("propose", "approve")app = g.compile(checkpointer=PostgresSaver(...))
    

    Одна строка ближе к концу несет в себе ключевую информацию: это указатель на место для сохранения состояния, который передается при компиляции графа. Благодаря ему состояние записывается после каждого узла. Возможности возобновления работы, функция interrupt() для ожидания действий пользователя и возврата к определенному узлу для повторного выполнения последующих действий — все это реализуется с помощью этого аргумента, а не вручную написанного кода. Ваш вклад заключается в рамках и стрелках.

    Такова разница между ними. LangChain предоставляет цикл, внутреннюю структуру которого невозможно увидеть. LangGraph требует от вас самостоятельно нарисовать этот цикл, но взамен вы можете его изучать, сохранять, останавливать и повторно запускать. Четыре вышеупомянутых ограничения — это моменты, когда невозможность увидеть внутреннюю структуру уже становится неприемлемой.

    Одна стек-структура, две возможности

    Поскольку create_agent основан на графе LangGraph, выбор между LangChain и LangGraph не является вопросом фреймворка. Это вопрос того, какой путь использовать, причем эти два инструмента работают вместе. Агент, созданный с помощью create_agent, сам по себе представляет собой граф, что позволяет разместить его в качестве отдельного узла внутри более крупного StateGraph. Больший граф отвечает за классификацию, утверждение действий и сохранение данных, в то время как внутренний агент обрабатывает те этапы, где модель должна принимать решения.

    Используйте create_agent, когда сегодня нужен рабочий агент, и его внутренняя структура не имеет значения. Многим агентам никогда не требуется что-то большее. Обратитесь к StateGraph, когда необходимо отслеживать состояние в процессе выполнения, дожидаться человека, восстанавливаться после сбоя или перематывать процесс — это те же четыре проблемы, что и ранее.

    SDK-инструменты от Anthropic и OpenAI находятся рядом с этой стек-архитектурой, а не внутри неё. Они выполняют один и тот же цикл, связанный с одной семьёй моделей, и при использовании только одного поставщика возникает минимум проблем. Цена этого — портативность.

    Советы для команды

    Сначала напишите двадцатистрочный цикл, даже если он в итоге будет удалён. На это уйдёт час, а после этого станет ясно, что делает за вас любая фреймворк-среда, и это единственный способ отлаживать её.

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

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