Каму павінна належаць ця агентская структура: інструменты часу выконання протыва фрэймворкаў, складзеных з частак
Канва прымкнення для стварэння агентаў AI, якая базуецца на адной пытанні: хто контролюе цикл выконання, з’яўляецца параболіка межы між рашэннямі Node harnesses і LangGraph, CrewAI, а таксама LangChain.
Крыніцы: Who Owns the Agent Loop? Node.js Harnesses vs. Python Frameworks (LangGraph, CrewAI, LangChain)
З даляйшыні інструменты для стварэння агентаў AI выглядаюць як можна заменільныя. Кожны з іх дае вам модель, спосаб вызываць інструменты та апаратуру, якая падтрымлеўвае работу моделі пакуль не будзе завершана задача, і за дапамой любога з іх можна стварыць функцыйнальнага агента за адно popołudnie. Разлікі межы імі застаюцца непазначанымі пакуль вы не перейдзеце да рэальнай експлуатацыі, а потым выявляецца, што гэты разлік є фундаментальным: хто володзіць цыклам, який керуе агентам? Одна група інструментоў дае вам среду выконання і адступае у бок; іншая дае вам абстракцыі і прасіць вас самых іх склadaць. У этай статыце чыста паказаны разлік межы гэтымі двумя філасофіямі, а пасля даўаецца спосаб выбраць, да каго належы конкрэтны проект. Мета не ў тым, каб выбраць пераможца, а ў тым, каб чыткаа паказаць асоцыяцыі межы гэтымі падходамі.
З адной стороны знаходзяцца фреймваркі з абсалютным контролем: среды выконання, якіе керуюць усім циклам – адправка запита, выконанне інструментаў, прыем рэзультатаў і ўтварэнне новага циклу. Вы не пішаце гэты цыкл; вы яго настраюеце і расширяеце. З іншай стороны знаходзяцца фреймваркі такія як 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 цыклу, ня ёсць галузі на кшталт «якщо модэль вернула запыткі да інструментаў, выконайце іх і зноў вызваніце модэль», а таксама ня ёсць системы для лічэння токенаў, якая б вялікала, калі трэба скompактаваць історыю. Сам працоўны час адмаўляецца за ўсё гэта. Усё, што вы падалі, — это модэль, інструменты, якія ўона можа выкарыстоўваць, і запытка системы, пасля чаго адбываецца адна вызовка методу 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 — гэта старейшы і найбольшы з трох. Ён пачаўся як спосаб прыяеднання калэкаў LLM, а пазней ператворыўся на масштабную платформу: больш чым тыяча інтеграцый, інтерфейс, які не зважае на тое, який модель стоіць за ўсім гэтым, а таксама продукт-брат для стварэння можлівасцяў аналізу. Для агентаў ён з’являецца як цыкл вызову інструментаў у стыле 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 — гэта сама гілка, якую супрацоўнік запускае за вас пасля адзінаго вызову 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 у сфере дадзеных і машыннага навучэння ёст справжнім падтрымкам, таму што агент працуе разам з бібліятэкамі і нотабукамі, у яых вы вядома працуеце.
Разміры, дзе насправды выбіраецца рашэнне
Размешаныя адна ля другой, гэтыя падходы розьдзеляюцца па некалькі вачэй, і калькі з іх у практыцы несу большую частку навантажэння.
Адміністрацыя циклу выконання ёсць галоўная вачэй; усё інша выводзіцца з яе. Фреймворк утримвае цыкл для сябе, а іншы фреймворкы перадаюць яго вам; LangGraph адпавядае за частку, калі вы пішаце все самі, Pi — за частку, калі система пішаце все сама, а рэшта распадзяюцца между імі.
Стойкасць ў LangGraph — гэта яго рашучая перадчыннае. Зберагачэнне пункта контролю пасля кожной перамены дозволяе вам восстанавіць роботу працэса та пераглядзець яго ход з практычна жаданым накладам зусиль. Функцыя зберагачэння сесій, яку прымае харчувальная система, а таксама типы памяці, якія надае CrewAI, є корыстнымі, але жадна з іх не дастае можлівасці стойка пераглядзець стан працэса. Калі працэс завершваецца на паўпацёку, граф, зберажаны як пункт контролю, продовжае роботу з апошней перамены, тады як журнал сесій зазвычай дозволяе толькі пазнейшае аналізаванне таго, што выйшла наступнай.
Кераванне калькамі агентаў у CrewAI здзейснюецца за дапамогою ролей, а ў LangGraph — за допамогою падграфаў. Харчувальная система досягае таго ж рэзультата іншымі спосабамі — чыраз падагентаў, расширэнняў або логікі коордынацыі, яку вы задаеце.
Экасператы ягоўскага сераўісу зазвычай прымяюць рашэнні тыхама. Выбор Python прыводзіць агента ў сферу работы з дадзеннямі та машынным навучэнням, дзе ён работае разам з вашымі табламі дадзенняў, кодам для навчання та нотбукамі. Выбор TypeScript прыводзіць яго да вебу та периферыйных систем, дзе ён работае разам з вашымі API, спяльнымі типамі фронтэнду та там, дзе вы размешчаеце безсерверныя функцыі. Гравітацыйны тлак гэтых напрамоў часта пераважае будзь-які адзін функцыонал, паказаны ў табелі порэвання, і важна чыста сказаць, у якім свете вже працуе ваша команда.
Паштоўны аспект, які робіць порэванне чыстым: патэрн «Node з інструментамі» та «Python з фрамворкамі» — это тэндэнцыя, а не закон. Існуюць рантаймы у стылі інструментаў, напісаныя на Python, а таксама бібліятэкі для оркестрацыі у стылі графа, напісаныя на TypeScript. Рашайце на адной з гэтых парадыгм та са уважнам разлікам да вашых рэальных абмежэнняў, а не на адной з мов, паказаных на пакете.
Адзін заводак, падыяўшы з кожной стороны
Уявіце сабе адзін, звычайны завадак: агента, які за дапамой інструмента запошчае ўтрымак і пасля стварае яго апূরк. Тое, з чым кожны парадыгма прыводзіць вас з самага пачатку, — гэта тая частка, якая раскрывае сутнасць завадку. Пад такім падходам весь работа складаецца практычна з налаштаванняў. Пасля таго, як вы заявілі прыемнік пошуку і запит системы, вы выдаеце адзін кал, пасля чаго система берае на сябе кераванне: запускае модель, стварае умовы для пошуку, выкананняго яго, вяртае рэзультаты, дазволяе модэлі склаць апূрк і сама завершае работу. Такі падход фактычна запытае вас пра адно: якія можлівасці должен маты гэты агент?
У такой парадыгме, як LangGraph, вся робота заключаецца практычна ў структурах. Вы задаеце об’ект стану, вузел, які запускае модель, вузел, які выканаўляе інструмент, а таксама умовную грань, якая выбірае між поверненням да моделі і завершэнням. Пакуль граф не будзе цалком створаны, ніч не выканаўляецца. LangGraph запитаў вас пра іншае: што самэй ёсць поток контролю?
Суть, якую трэба запам’ятаваць, — гэта тое, што жадна з версій не ўскладнена; задача ў обох варыянтах є простая. Тое, што іх розлічвае, — не столькі зусилля, сколькі тое, што трэба адначасова выявіць заздалегідь, і тое, што залишаецца можна пераглядзець або вярнуць пазней. Фреймворк усуне першы вопыт і за короткі час дае вам аднароднага агента. Фреймворк не дозволяе яму зникнуць і компенсуюць гэта можлівасцю стежыць за прагамом, працуючы з кожным переходам окрема, калі система паслужыць сабе не так у некальконадзеяны час. Гэты баланс стае ўсё значнейшым, калі система расте. Калі вы ствараеце асистента для кодавання або агента, які знаходзится ўнутрь прыемлена, важлівым є пытанне пра можлівасці, і фреймворк становіць сабой вялікую дапамогу. Калі ж вы ствараеце рабочы процес, які трэба запускаць праўільна дзеякі час, вытрымліваць збоі і працаваць пад час аудытов, важлівым є пытанне пра контрольны прагам, і кожная дапамога з явнага графа прадаецца сама сабою.
Падэнне інструмента да лупа, які ў вас ёсць
Гіюрістыкі выленяюцца непасляўна з усьму вышэйзначанага. Выберыце harness, калі прыоритэтам являецца высока скорасць агента, калі вбудованыя выбары среды выконання вам вже падходзяць, калі метай являецца агент для кодавання чыстаінструментаў для разработчыкаў, або калі вы розмешчаеце агента ў прыкладнэй программе на TypeScript і хочаце бяспекі типоў чыстаінтыму супакоўванню з фронтэндам. Выберыце LangGraph, калі непрыемна ўтрачаць прыгодзены прогрэс, тое ў разлічных давга трываючых чыстаінцыклічных рабочых процесах, якія павінны перазстаяць крахам, трэбае точнае кераванне станам чыстаікожным пераходам, чыстаіколі трэбае націснуць на чалавека. Выберыце CrewAI, калі проблема сапраўды можа быць разбітая на набор спецыялістскіх ролей і вы хочаце швайна стварыць мульті-агентны паяў. Выберыце LangChain, калі шырокая лінія доступных інтеграцый для традыцыйнага адно-агентскага потока вызывання інструментаў пераважае ўсія іншыя прычыны, пашчотна ў вядомай базе коду на Python. І, зноў.
З адчуванням грані ёсць мягкія: трэба выбраць парадыгму і абмежэння, а не тое, якія мовы будуць надрукаваны на бляшцы.Вопыт з самага пачатку быў пра цікл
Суперэт ніколі не быў справжнім прымусам между Node і Python, або між адним пакетам і яго канкурэнтам. Што насправды было пад пытанням — чы гэты рантайм должен кераваць цыклам агента, чы вы. Харнес пасвячае свободу перапісвання логіки ў борг за шырокі час стварэння працюючага агента і можлівасць змяні його поведзення за дапамою налаштавань, запускаеў і хуків, прычаму якія застосоўваюцца да цыклу, які вы ніколі не стваралі. Фрэймворк пасвячае шырокі час зарады ясных каманд, стойкасці і возможнасці коордынацыі, а таксуе вас за постаўленню тымпу напісвання і падтрымкі логіки. Для большасці команд разумны пачатковы варыянт — гэта харнес для протатаяў, напісвання агентаў і всьога, што ўбудовваецца ў продукт, калі ключоўым прынцыпам є шырокасц дзеяння; пераход на LangGraph, калі стойкае становішча становіцца абавязковай патрабоўкай; і выбір CrewAI, калі робота справдзіва ўключае команду спецыялістаў. Паколькі гэтыя інструменты продовжваюць прабіраць у сабе лепшыя ідеі, навык, які застаецца, — гэта не вольнае володзенне адним з іх.
API, але неабяжна падзейка — глядзець на агента, які стоіць пры вас, і з’ясаваць, хто павінен кераваць яго цыклам.