Кто должен владеть циклом агентов: инструменты времени выполнения против составных фреймворков
Параметры принятия решений при создании ИИ-агентов основаны на одном вопросе: у кого находится цикл выполнения, при этом сравниваются инструменты Node harness с LangGraph, CrewAI и LangChain.
Источники: Who Owns the Agent Loop? Node.js Harnesses vs. Python Frameworks (LangGraph, CrewAI, LangChain)
Издалека инструменты для создания ИИ-агентов кажутся взаимозаменяемыми. Каждый из них предоставляет модель, способ вызова инструментов и механизмы, обеспечивающие работу модели до завершения задачи, и с помощью любого из них можно создать функционирующего агента за один день. Различия между ними остаются скрытыми до тех пор, пока вы не перейдете от демо-версии к реальному использованию, после чего оказывается, что различия фундаментальны: кому принадлежит цикл, управляющий агентом? Одна группа инструментов предоставляет среду выполнения и отходит в сторону; другая — абстракции и просит вас собрать их самостоятельно. В этой статье четко проводится граница между этими двумя философиями, а затем предлагается способ определить, к какой стороне относится конкретный проект. Цель не в том, чтобы выбрать победителя, а в том, чтобы ясно показать существующие компромиссы.
С одной стороны находятся фреймворки вида harness: среды выполнения, которые полностью контролируют процессы отправки запросов, выполнения инструментов, обработки результатов и повторения циклов. Вы не пишете этот цикл с нуля; вы лишь настраиваете и расширяете его. С другой стороны — фреймворки вроде LangGraph, CrewAI и LangChain, которые предоставляют готовые элементы для построения структур, графов и цепочек обработки данных, но требуют от вас самостоятельной настройки потока управления. Хотя это часто представляется как противопоставление Node и Python, язык программирования здесь играет второстепенную роль. Настоящим фактором является степень контроля над всеми компонентами системы.
Две концепции, выдерживающие нагрузку
Сначала определитесь с двумя ключевыми словами, прежде чем полагаться на них. Harness — это среда выполнения, которая берет на себя управление циклом агента. Она выполняет этот цикл за вас: отправляет запрос, собирает необходимые инструменты, которые требует модель, запускает их, возвращает результаты и повторяет процесс до тех пор, пока модель не прекратит запрашивать что-либо. Кроме того, она обычно берет на себя решение связанных сложных проблем, включая обработку контекста и памяти, сжатие истории по мере заполнения лимита токенов, повторные попытки и сохранение сессии. Вы управляете ею с помощью настроек — системного промпта, выбора модели, разрешенных инструментов и ограничений, а также с помощью хуков и точек расширения, никогда не пиша цикл вручную. Одна из сред выполнения в этой области, Pi — среда на Node и TypeScript — отражает эту философию через примитивы вместо функций: вы адаптируете среду выполнения под свой рабочий процесс, а не пытаетесь приспособить свой рабочий процесс к ней.
Фреймворк, или SDK, представляет собой набор явных абстракций, которые вы комбинируете в коде. LangGraph предлагает граф узлов, соединённых рёбрами. CrewAI предоставляет группы агентов для ролевых игр. LangChain предлагает цепочки. Во всех этих случаях вы являетесь создателем логики управления, а библиотека лишь поставляет компоненты и позволяет вам их соединять. На самом деле важное различие заключается в следующем: в инструментах типа «харнесс» цикл скрыт и принадлежит среде выполнения, тогда как в фреймворках цикл, структура графа или расположение элементов являются открытыми и принадлежат вам.
Это спектр, а не переключатель. В одном крайнем случае вы управляете средой выполнения с помощью настроек и хуков, при этом цикл принадлежит ей; в другом крайнем случае вы сами создаёте каждый узел, ребро, группу элементов или цепочку, причём цикл буквально является вашим собственным кодом.
HARNESS you → [config + hooks] → ╔═══ runtime owns the loop ═══╗ → result
║ prompt → tools → results → ↺║
FRAMEWORK you author every node / edge / crew / chain → the loop IS your code
Не забывайте об этой оси, ведь всё, что находится ниже, зависит от того, где расположен инструмент на ней.
Подход с харнесом: передача цикла в руки рантайма
Pi — это минимальный харнес, разработанный специально для Node и организованный в виде многоуровневой стек-структуры; вы используете только тот уровень, который вам нужен. В нижней части находится абстракция-провайдер, объединяющая множество провайдеров моделей; в средней части — цикл агента и механизм развертывания инструментов; выше — полноценный рантайм с функцией сохранения сессий; а на поверхности — интерфейс терминала. Для большинства инженеров интересным является средний уровень, а именно цикл. Запуск агента подразумевает описание того, каким он должен быть, после чего управление передаётся ему. Пример небольшого исследовательского агента, построенного на API сессий, приведён ниже.
import { getModel } from "@mariozechner/pi-ai";
import { createAgentSession, SessionManager } from "@mariozechner/pi-coding-agent";
const { session } = await createAgentSession({
model: getModel("anthropic", "claude-sonnet-4-6"),
cwd: process.cwd(),
sessionManager: SessionManager.open("./session.jsonl"),
tools: ["read", "grep", "bash", "search_docs"], // built-ins + your custom tools, by name
customTools: [searchDocsTool], // your typed tools
});
// You steer the runtime with config, not loop code:
session.agent.state.systemPrompt = "You are a research assistant. Always cite sources.";
// A single call runs the entire prompt → tools → results → repeat cycle:
await session.prompt("Summarize the latest on agent frameworks and cite sources.");
Обратите внимание на отсутствующие элементы. Здесь нет цикла while, нет ветки кода вида «если модель вернула запросы к инструментам, выполните их и снова вызовите модель», а также нет механизма учёта токенов для определения момента сжатия истории. Всё это обрабатывается во время выполнения программы. Всё, что было предоставлено, — это модель, инструменты, которые она может использовать, и системное сообщение, за которым следует один вызов метода prompt. Пользовательский инструмент также представляет собой небольшой объект с указанием имени, описания, предназначенного для модели, схемы параметров и функции выполнения.
const searchDocsTool = {
name: "search_docs",
description: "Search the internal docs and return matching passages.",
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
async execute(_id: string, params: { query: string }, signal?: AbortSignal) {
const passages = await search(params.query, signal);
return { content: [{ type: "text", text: passages }], details: {} };
},
};
Пока что это кажется просто удобством без каких-либо серьезных преимуществ. Проблемы начинаются в тот момент, когда требуется изменить поведение цикла, поскольку нельзя переписать цикл, который не принадлежит вам. Вместо этого приходится взаимодействовать с ним через события и хуки. Подписка на события позволяет наблюдать за его работой и вмешиваться, например, подсчитывая количество итераций и останавливая процесс при достижении определенного лимита.
let turns = 0;
const MAX_TURNS = 20;
const unsubscribe = session.agent.subscribe((event) => {
if (event.type === "turn_end" && ++turns >= MAX_TURNS) {
session.agent.abort(); // pull the cord on a loop you didn't write
}
});
Аналогичный подход используется для ограничения затрат: накапливаются затраты на обработку каждого сообщения по мере его поступления, и процесс прерывается при превышении установленного порога. Система продолжает запускать цикл, пока вы наблюдаете за его работой и решаете, когда остановить его. Хуки позволяют зашагнуть дальше, интерцептируя решения до того, как они вступят в силу, при этом модель об этом не узнает. Хук перед вызовом инструмента может проверить ожидающийся вызов и отклонить его.
session.agent.beforeToolCall = async (ctx) => {
if (ctx.toolCall.name === "bash" && isDangerous(ctx.args.command)) {
return { block: true, reason: "blocked by policy" };
}
return undefined; // allow
};
Модель «попросила» выполнить команду оболочки; хук тихо отказался и указал причину, после чего цикл продолжил работу, как будто ничего не произошло. Вы изменили поведение агента в соответствии с циклом, который сами не создавали. В этом и заключается компромисс, который предлагает такой фреймворк: вы отказываетесь от возможности переписать логику управления, но взамен быстро получаете рабочего агента с надежными стандартными решениями для наиболее сложных аспектов, таких как управление контекстом, сжатие данных и сохранение информации, а также четко организованные механизмы для настройки, обработки событий и использования хуков для изменения поведения. Это отличная сделка тогда, когда подходы движка выполнения совпадают с вашими.
Подход фреймворка: самостоятельное формирование логики управления
Экосистема Python пошла противоположным путем. Вместо движка выполнения, который контролирует циклы, она предоставляет абстракции, которые пользователь сам собирает, причем три из них доминируют в этой среде.
LangChain: составление цепочки
LangChain является старейшим и наиболее масштабным из трех. Изначально он был создан для объединения вызовов больших языковых моделей, но со временем превратился в обширную платформу: более тысячи интеграций, интерфейс, не зависящий от того, какая модель находится за ним, а также сопутствующий продукт для мониторинга. Для агентов он предоставляет цикл вызова инструментов в стиле ReAct, а его схема композиции позволяет соединять один компонент с другим. Его главное преимущество — широта охвата: если существует какая-либо модель, хранилище векторов или внешний инструмент, скорее всего, существует и соответствующая интеграция LangChain. Его недостаток — цена такой универсальности. Глубокие слои абстракции могут превратить отладку в сложную задачу, а поскольку он по умолчанию не имеет состояния, надежность требует дополнительной настройки, а не является врожденной чертой. LangChain подходит для стандартных, в основном линейных рабочих процессов с одним агентом, где наибольшую ценность представляет широта интеграций.
LangGraph: моделирование автомата состояний
LangGraph выдвигает четкое и убедительное утверждение: системы агентов представляют собой автоматы состояний, и необходимо контролировать каждую переходную операцию. Агент моделируется как граф, узлы которого — это шаги, а ребра — переходы; вся структура работает с типизированным объектом состояния.
from langgraph.graph import StateGraph, MessagesState, START, END
def call_model(state: MessagesState):
return {"messages": [llm.invoke(state["messages"])]}
graph = StateGraph(MessagesState)
graph.add_node(call_model)
graph.add_edge(START, "call_model")
graph.add_edge("call_model", END)
app = graph.compile()
app.invoke({"messages": [{"role": "user", "content": "hi"}]})
Там, где другие инструменты скрывают логику циклов, LangGraph превращает ее в элемент, созданный пользователем. Условные разветвления описываются явно.
graph.add_conditional_edges("call_model", should_continue, {"tools": "tools", "end": END})
Эта функция continue-or-stop — именно тот ветвленийный участок, который библиотека LangGraph обрабатывает за вас после каждого вызова функции prompt. Теперь она находится в вашем распоряжении: видимая, подлежащая тестированию и версионированию. Платой за дополнительную подробность является выдающаяся особенность LangGraph — встроенная функция сохранения состояния при каждой смене состояния графа. Поскольку состояние записывается на диск по мере развития графа, выполнение программы сохраняется после перезапуска, позволяет восстанавливаться после сбоев, предоставляет возможность возврата к более ранним состояниям и паузы во время работы в ожидании действий пользователя. Недостатками являются сложность освоения и архитектура, умышленно не подходящая для развертывания без серверов. LangGraph идеально подходит для длительных, циклических, устойчивых к сбоям и имеющих состояние рабочих процессов.
CrewAI: формирование команды
CrewAI считает, что большинство проблем с несколькими агентами можно легко разделить на команды специалистов. Каждый агент определяется ролью, целью и предысторией, после чего их объединяют в команду и поручают задания.
from crewai import Agent, Task, Crew
researcher = Agent(role="Researcher", goal="Find key insights on {topic}",
backstory="A meticulous analyst.")
writer = Agent(role="Writer", goal="Summarize the research clearly",
backstory="A concise technical writer.")
research = Task(description="Research {topic}.", expected_output="3 insights.", agent=researcher)
summary = Task(description="Write a summary.", expected_output="A short summary.", agent=writer)
Crew(agents=[researcher, writer], tasks=[research, summary]).kickoff(inputs={"topic": "agent frameworks"})
Ментальная модель такова, что путь к рабочей системе с несколькими агентами кажется коротким, поэтому она пользуется широким распространением в крупных предприятиях. С другой стороны, абстракция ролей и целей удобна до тех пор, пока всё работает нормально; как только возникают проблемы, она превращается в «черный ящик»; передача заданий между агентами может привести к замкнутым циклам; к тому же необходимо самостоятельно вводить механизмы контроля для предотвращения несанкционированных расходов. CrewAI подходит для систем с несколькими агентами, основанных на ролях, и для быстрой разработки прототипов. Общим элементом, связывающим все эти аспекты, является возможность написания собственного алгоритма управления. Это одновременно и недостаток — больше кода, больше решений, больше работы по обслуживанию — и преимущество, поскольку можно наблюдать, тестировать и изменять каждый этап перехода. Преимущество использования Python в области данных и машинного обучения заключается в том, что агенты работают рядом с библиотеками и ноутбуками, в которых вы уже работаете.
Факторы, на основании которых делается выбор
Расположенные рядом друг с другом, эти подходы разделяются по небольшому числу осей, причем несколько из них в практике несут наибольшую нагрузку.
Владение циклом выполнения является основной осью; всё остальное происходит от нее. Фреймворк сохраняет контроль над циклом для себя, в то время как инструменты типа harness передают его пользователю; LangGraph отвечает за часть, где пользователь пишет всё сам, Pi — за часть, где всё записывается во время выполнения, а остальные распределяются между ними.
Надежность является решающим преимуществом LangGraph. Сохранение чекпоинта при каждой транзиции позволяет восстанавливать работу и перезапускать процесс практически без дополнительных усилий. Возможности сохранения сессии, предоставляемые инструментами типа harness, и виды памяти, которые использует CrewAI, тоже ценны, но ни один из них не заменяет возможность надежного воспроизведения состояния. Когда процесс прерывается на полпути выполнения, чекпоинт позволяет продолжить с последней транзиции, в то время как лог сессии обычно позволяет лишь в последующем восстановить ход событий.
В CrewAI организация работы нескольких агентов реализуется через роли, а в LangGraph — через подграфы. Инструменты типа harness достигают аналогичных результатов с помощью других методов: подагентов, расширений или логики координации, которую вы предоставляете.
Экосистема языков вокруг обычно определяется более-менее автоматически. Выбор Python приводит разработчика в сферу работы с данными и машинным обучением, где он работает вместе с данными в формате DataFrame, кодом обучения и ноутбуками. Выбор TypeScript направляет его к веб-технологиям и периферийным устройствам, где он используется вместе с API, общими типами фронтенда и там, где развертываются безсерверные функции. Это влияние часто превосходит значение любой отдельной функции, указанной в сравнительных таблицах, поэтому важно честно оценить, в какой среде уже работает ваша команда.
Ещё один момент, сохраняющий честность сравнения: подход «Node с инструментальным пакетом» и «Python с фреймворком» — это скорее тенденция, чем правило. Существуют среды выполнения в стиле инструментальных пакетов, написанные на Python, а также библиотеки оркестрации в стиле графов, написанные на TypeScript. Решение следует принимать исходя из выбранной парадигмы и реальных ограничений, а не на основе названия языка, указанного в пакете.
Одна задача, рассмотренная с разных сторон
Представьте себе обычную задачу: агент, который с помощью инструмента получает какую-то информацию, а затем составляет её краткое изложение. То, с чем сталкивается пользователь в каждой парадигме, — это ключевая часть процесса. В рамках данной парадигмы почти вся работа сводится к настройкам: после указания инструмента поиска и системного запроса отправляется один вызов, после чего движок выполнения берёт управление на себя, запускает модель, проверяет, нужен ли поиск, выполняет его, возвращает результаты, позволяет модели составить краткое изложение и самостоятельно завершает работу. Фактически этот механизм задаёт один вопрос: какими способностями должен обладать этот агент?
В рамках такой структуры, как LangGraph, вся работа сводится практически исключительно к формированию структуры. Вы указываете объект состояния, узел, вызывающий модель, узел, выполняющий инструмент, а также условную рёбро, определяющую, возвращаться ли к модели или завершать процесс. Пока эта структура не будет полностью сформирована, ничего не выполняется. LangGraph требует от вас другого: что именно представляет собой поток управления?
Суть заключается в том, что ни одна из версий не является сложной; задача в обоих случаях проста. Разница между ними не в усилиях, а в том, что необходимо решить заранее и что останется подлежащим проверке или восстановлению позже. Фреймворк делает первый вопрос нерелевантным и позволяет быстро получить функционального агента. Фреймворк же не позволяет этому вопросу исчезнуть и компенсирует это возможностью отслеживать ход выполнения пошагово, когда система начинает работать некорректно в неподходящее время. Эта замена становится всё более значимой по мере роста системы. Когда вы создаете помощника-программиста или агента внутри приложения, важен вопрос о функциональных возможностях, и фреймворк служит удобством. Когда же вы создаёте рабочий процесс, который должен работать долго, выдерживать сбои и пройти аудиты, важен вопрос о контроле потока выполнения, и каждая дополнительная строка в явном графе окупается сама собой.
Подбор инструмента под имеющийся у вас крюк
Эти гибкие подходы вытекают непосредственно из всего сказанного выше. Выбирайте harness, когда приоритетом является высокая скорость работы агента, когда встроенные в среду варианты уже соответствуют вашим требованиям, когда целью является агент для программирования или разработки инструментов, или когда вы внедряете агента в приложение на TypeScript и хотите гарантий типобезопасности и близости к фронтенду. Выбирайте LangGraph, когда недопустимо терять прогресс, что актуально для долгоживущих или циклических рабочих процессов, которые должны функционировать даже после сбоев, требующих точного контроля над состоянием и каждым переходом, или когда необходимо приостановить работу из-за участия человека. Выбирайте CrewAI, когда задача действительно можно разделить на набор специализированных ролей, и вы хотите быстро создать многоагентную систему. Выбирайте LangChain, когда количество доступных интеграций для традиционного одноагентного подхода с вызовом инструментов превосходит все остальные факторы, особенно в уже существующей базе кода на Python. И ещё раз
С учётом эмоций границы становятся размытыми: нужно определить парадигму и ограничения, а не то, на каком языке написано название на банке.Вопрос всегда заключался в цикле
Дискуссия никогда не касалась противостояния Node и Python или одного пакета против его конкурента. Речь шла о том, должен ли рантайм управлять циклом агента или вы сами. Инструменты типа харнеса жертвуют свободой переписывания логики в обмен на быстрое создание рабочего агента и возможность изменять его поведение с помощью настроек, событий и хуков, причем все это применяется к циклу, который вы сами не писали. Фреймворки жертвуют скоростью ради четких команд, надежности и возможностей оркестрации, взимая с вас постоянные затраты на написание и поддержку логики. Для большинства команд разумным стартовым вариантом является использование харнеса для прототипов, создания агентов и любых компонентов, встроенных в продукт, где ключевым фактором является скорость; затем можно перейти к LangGraph, когда надежное хранение данных становится обязательным требованием, а к CrewAI — когда работа действительно требует участия команды специалистов. Поскольку эти инструменты постоянно заимствуют лучшие идеи друг у друга, навыком, который остается востребованным, является не владение одним из них.
API, но решение заключается в том, чтобы посмотреть на агента перед собой и понять, кто должен управлять его циклом.