Хто має керувати циклом агента: інструменти Runtime Harnesses проти складених фреймворків.
Чернетка рішень для створення агентів ШІ, яка починається з одного запитання: хто відповідає за цикл виконання, з порівнянням інструментів Node harnesses з LangGraph, CrewAI та LangChain.
Джерела: Who Owns the Agent Loop? Node.js Harnesses vs. Python Frameworks (LangGraph, CrewAI, LangChain)
Здалеку інструменти для створення AI-агентів здаються взаємозамінними. Кожен з них надає модель, спосіб виклику інструментів та певну систему, яка підтримує роботу моделі до завершення завдання, і за допомогою будь-кого з них можна створити функціонального агента протягом одного дня. Розбіжності між ними залишаються прихованими доти, доки ви не перейдете від демонстрації до реального використання, а потім виявляється, що вони фундаментальні: хто володіє циклом, який керує агентом? Одна група інструментів надає вам середовище виконання та відступає убік; інша — абстракції та просить вас їх скомпонувати. У цій статті чітко проводиться межа між цими двома філософіями, а потім наводиться спосіб визначення того, до якої сторони належить певний проект. Мета не в тому, щоб обрати переможця, а щоб зробити ці компроміси очевидними.
З одного боку знаходяться інструменти у вигляді фреймворків: середовища виконання, які контролюють увесь цикл — від надсилання запиту до отримання результатів та їх повторного оброблення. Ви не пишете цей цикл самостійно; ви лише налаштовуєте та розширюєте його. З іншого боку — фреймворки на кшталт LangGraph, CrewAI та LangChain, які надають чітко визначені елементи для будови систем: графи, структури та ланцюги обробки даних, вимагаючи від користувача самостійного поєднання елементів керування. Хоча це часто представляється як протиставлення Node та Python, мова є лише побічним ефектом. Справжньою змінною є те, наскільки багато елементів цієї системи ви хочете контролювати.
Дві визначення, які витримують тиск
Визначте два ключові терміни, перш ніж покладатися на них. Harness – це середовище виконання, яке бере на себе керування циклом агента. Воно виконує всі операції замість вас: надсилає запит, збирає інструменти, які потрібні моделі, запускає їх, повертає результати та повторює цей процес, поки модель не припинить просити щось нове. Крім того, воно зазвичай бере на себе складні завдання, пов’язані з обробкою контексту та пам’яті, стисненням історії операцій у міру заповнення бюджету токенів, повторними спробами та збереженням сеансу. Ви керуєте ним за допомогою налаштувань – системного запиту, вибору моделі, дозволених інструментів та обмежень – а також через хуки та точки розширення, ніколи не писуючи цикл вручну. Один із таких harness у цій галузі – Pi, середовище виконання на Node та TypeScript – узагальнює цю філософію як використання примітивів замість функцій: ви адаптуєте середовище до свого робочого процесу, а не змушуєте свій робочий процес підлаштовуватися під нього.
Фреймворк, або SDK, — це набір чітких абстракцій, які ви комбінуєте у коді. LangGraph пропонує граф з вузлами, з’єднаними ребрами. CrewAI пропонує групи агентів для рольових ігор. LangChain пропонує ланцюжки. У кожному з цих інструментів ви є автором логіки керування, а бібліотека лише надає елементи та дозволяє їх поєднувати. Справжня різниця полягає у наступному: у інструментах типу «harness» цикл є прихованим та належить середовищу виконання, тоді як у фреймворках цикл, структура графа чи розташування елементів є відкритими та належать саме вам.
Це спектр, а не простий вибір. На одному краю ви керуєте середовищем виконання за допомогою конфігурації та хуків, причому цикл належить саме йому; на іншому краю ви створюєте кожен вузол, ребро, групу елементів чи ланцюжок, і цикл буквально є вашим власним кодом.
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 — нативне створення контрольних точок після кожної зміни стану. Оскільки стан записується на диск у міру просування графа, виконання може продовжуватися після перезапуску, відновлюватися після збою, дозволяє повернутися до попередніх станів та здійснювати паузи під час виконання у очікуванні дій користувача. Недоліками є складний процес навчання та дизайн, який свідомо не підтримує розгортання без сервера. 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 у сфері обробки даних та машинного навчання є справжнім плюсом, оскільки агенти працюють поруч із бібліотеками та ноутбуками, у яких ви вже працюєте.
Аспекти, у яких насправді приймається рішення
Розташовані один поруч з іншим, ці підходи розходяться вздовж невеликої кількості осей, причому деякі з них у практиці несуть більшу частину навантаження.
Власність на цикл виконання є основною осею; все інше походить від неї. Фреймворк утримує цикл для себе, тоді як інструментарій передає його вам; 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 чи одного пакету проти його конкурента. Йшлося про те, чи має runtime керувати циклом агента, чи ви. Харнес відмовляється від свободи переписувати логіку заради швидкого створення функціонального агента та зміни його поведінки через налаштування, події та хуки, причому все це застосовується до циклу, який ви не створювали. Фреймворк жертвує швидкістю заради чітких команд, надійності та оркестрації, а також стягує постійні витрати на написання та підтримку логіки. Для більшості команд розумним початковим підходом є використання харнесу для прототипів, кодування агентів та будь-чого, що вбудоване в продукт, де ключовим фактором є швидкість; перехід на LangGraph досягається тоді, коли надійний стан стає обов’язковою вимогою, а до CrewAI — коли робота справді потребує команди спеціалістів. Оскільки ці інструменти постійно поглинають найкращі ідеї один одного, навичкою, яка залишається актуальною, є не володіння одним з них.
API, але рішення полягає у тому, щоб подивитися на агента перед собою та зрозуміти, хто має володіти його циклом.