Strona główna / Artykuły

Kto powinien posiadać agent loop: narzędzia runtime vs frameworki składane

Ramy decyzyjne do tworzenia agentów AI opierają się na jednym pytaniu: kto kontroluje pętlę wykonywania, przy czym porównuje się narzędzia typu Node harness z LangGraph, CrewAI i LangChain.

Wydawca: REACTAPP.TOP13 min czytania2927 słów

Źródła: Who Owns the Agent Loop? Node.js Harnesses vs. Python Frameworks (LangGraph, CrewAI, LangChain)

Z daleka narzędzia do tworzenia agentów AI wyglądają na wzajemnie zamienne. Każde z nich dostarcza model, sposób wywoływania narzędzi oraz mechanizmy utrzymujące model w działaniu aż do zakończenia zadania, i przy użyciu dowolnego z nich można stworzyć funkcjonalnego agenta w ciągu popołudnia. Różnice pomiędzy nimi pozostają ukryte, dopóki nie przejdziemy od demonstracji do fazy produkcji, a wtedy okazuje się, że są one fundamentalne: kto posiada pętlę sterującą agentem? Jedna grupa narzędzi dostarcza środowisko wykonawcze i odchodzi na bok; druga dostarcza abstrakcje i prosi o ich skomponowanie. Ten artykuł wyraźnie rozróżnia te dwie filozofie, a następnie pokazuje, jak zdecydować, po której stronie powinien znajdować się dany projekt. Celem nie jest wyłonienie zwycięzcy, lecz ujawnienie zalet i wad każdego podejścia.

Z jednej strony znajdują się narzędzia typu harness: środowiska wykonawcze, które obsługują cały cykl wysyłania zapytań, wykonywania narzędzi, przekazywania wyników i ich powtarzania. Nie musisz sam pisać tego cyklu – wystarczy go skonfigurować i rozszerzyć. Z drugiej strony znajdują się frameworki takie jak LangGraph, CrewAI i LangChain, które dostarczają konkretne elementy budulcowe w postaci grafów, zespołów i łańcuchów, oczekując jednak od użytkownika samodzielnego połączenia elementów sterowania. Chociaż często przedstawia się to jako konfrontacja Node z Pythonem, język jest tu efektem ubocznym. Prawdziwą zmienną jest to, w jakim stopniu chcesz kontrolować cały mechanizm.

Dwie definicje, które wytrzymują presję

Zanim zaczniesz od nich korzystać, określ dokładnie te dwa słowa. Harness to środowisko wykonawcze, które przejmuje kontrolę nad pętlą agenta. Wykonuje ono ten cykl za ciebie: wysyła prompt, zbiera żądane przez model narzędzia, je uruchamia, zwraca wyniki i powtarza to, dopóki model nie przestanie o nic prosić. Ponadto ma tendencję do obejmowania pod sobą powiązanych z tym trudnych problemów, takich jak obsługa kontekstu i pamięci, kompresja historii w miarę wypełniania się budżetu tokenów, próby ponowne oraz utrzymywanie sesji. Kierujesz je za pomocą konfiguracji – czyli promptu systemowego, wyboru modelu, dozwolonych narzędzi oraz ograniczeń – a także za pomocą hooków i punktów rozszerzeń, nigdy nie pisząc ręcznie pętli. Jednym z takich rozwiązań jest Pi, środowisko Node i TypeScript, które podsumowuje tę filozofię jako „prymitywy zamiast funkcji”: dostosowujesz środowisko do swojego procesu pracy, zamiast próbować dostosować swój proces do niego.

Framework, czyli SDK, to zamiast tego zestaw wyraźnych abstrakcji, które komponujesz w kodzie. LangGraph oferuje graf węzłów połączonych krawędziami. CrewAI oferuje zespoły agentów odgrywających role. LangChain oferuje łańcuchy. We wszystkich tych narzędziach to ty jesteś twórcą struktury sterowania, a biblioteka dostarcza jedynie elementy i umożliwia ich połączenie. Rzeczywiście istotna różnica polega na tym, że w takim rozwiązaniu pętla jest ukryta i należy do środowiska wykonawczego, natomiast w frameworku pętla, struktura grafu lub układ zespołów jest jawna i należy do ciebie.

To raczej spektrum niż prosty wybór. Na jednym biegunie kierujesz środowiskiem wykonawczym za pomocą konfiguracji i hooków, podczas gdy ono kontroluje cykl; na drugim biegunie sam tworzysz każdy węzeł, krawędź, zespół lub łańcuch, a pętla jest dosłownie twoim własnym kodem.

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

Pamiętaj o tej osi, ponieważ wszystko poniżej zależy od tego, gdzie znajduje się narzędzie na niej.

Podejście oparte na harnessie: przekazanie pętli środowisku uruchomieniowemu

Pi to minimalny harness napisany w Node, zorganizowany jako warstwowa sterta, z której korzystasz tylko z tej warstwy, która jest ci potrzebna. U dołu znajduje się abstrakcja dostawcy obejmująca wiele dostawców modeli; pośrodku znajduje się pętla agenta i mechanizm wysyłania zadań do narzędzi; powyżej znajduje się pełne środowisko uruchomieniowe z funkcją przechowywania sesji; a na wierzchu znajduje się interfejs użytkownika terminalowego. Dla większości inżynierów interesującą jest środkowa warstwa, czyli pętla. Uruchomienie agenta oznacza opisanie tego, jak powinien funkcjonować, a następnie przekazanie mu kontroli. Mały agent badawczy zbudowany na API sesji wygląda w ten sposób.

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.");

Zwróć uwagę na to, czego brakuje. Nie ma pętli while, żadnego rozgałęzienia typu „jeśli model zwróci wywołania narzędzi, uruchom je i ponownie wezwij model” oraz żadnego mechanizmu liczenia tokenów służącego do określenia, kiedy historia powinna zostać skompresowana. System operacyjny zajmuje się tym we wszystkich aspektach. Jedyne, co dostarczyłeś, to model, narzędzia, których może używać, oraz komenda systemowa, a następnie pojedyncze wezwanie do metody prompt. Narzędzie niestandardowe to również mały, zdefiniowany obiekt: nazwa, opis skierowany do modelu, schemat parametrów oraz funkcja wykonywania.

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: {} };
  },
};

Na razie wydaje się to raczej udogodnieniem, niczym więcej. Problemy pojawiają się w momencie, gdy trzeba zmienić zachowanie pętli, ponieważ nie można przepisać pętli, której nie posiada się. Zamiast tego dostęp do niej uzyskuje się poprzez wydarzenia i haczyki. Subskrypcja na wydarzenia umożliwia obserwację jej działania i interwencję, na przykład poprzez liczenie rund oraz zatrzymanie jej po osiągnięciu określonego limitu.

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
  }
});

Ta sama struktura może być wykorzystana do ograniczenia kosztów: gromadzi się koszt każdej wiadomości w momencie jej przybycia, a działanie jest przerywane po przekroczeniu określonego progu. System kontynuuje wykonywanie pętli, podczas gdy użytkownik obserwuje jej przebieg i decyduje, kiedy ją zatrzymać. Haczyki pozwalają posunąć się o krok dalej, umożliwiając przechwytywanie decyzji przed ich realizacją, niewidocznie dla modelu. Haczyk typu „przed wywołaniem narzędzia” może sprawdzić planowane wywołanie i je odrzucić.

session.agent.beforeToolCall = async (ctx) => {
  if (ctx.toolCall.name === "bash" && isDangerous(ctx.args.command)) {
    return { block: true, reason: "blocked by policy" };
  }
  return undefined; // allow
};

Model „poprosił” o uruchomienie polecenia w shellu; hook cicho odmówił i podał powód, a pętla kontynuowała pracę, jakby nic się nie stało. Zmieniłeś zachowanie agenta w oparciu o pętlę, której sam nie stworzyłeś. To właśnie oferuje taki framework – rezygnujesz z możliwości przepisania struktury sterowania, a w zamian szybko otrzymujesz działającego agenta, solidne domyślne rozwiązania dla naprawdę trudnych aspektów, takich jak zarządzanie kontekstem, kompresja i trwałość danych, oraz uporządkowany zestaw elementów do modyfikacji zachowania, takich jak konfiguracja, zdarzenia i hooki. Jest to doskonała oferta, szczególnie gdy poglądy środowiska wykonawczego pokrywają się z twoimi.

Podejście oparte na frameworku: sam skomponuj strukturę sterowania

Ekosystem Pythona postawił na coś przeciwnego. Zamiast środowiska wykonawczego, które kontroluje pętlę, dostarcza abstrakcje, które sam musisz połączyć, przy czym trzy z nich dominują.

LangChain: tworzenie łańcucha

LangChain jest najstarszą i najszerszej funkcjonalnością z tych trzech. Zaczęło się jako narzędzie do łączenia ze sobą wywołań modeli językowych, a potem rozwinęło się w rozbudowaną platformę: ponad tysiąc integracji, interfejs niezależny od tego, jaki model się za nim kryje, oraz produkt pokrewny do celów monitorowania. Dla agentów oferuje pętlę wywołań narzędzi w stylu ReAct, a jego model kompozycji łączy jeden komponent z następnym. Jego największą zaletą jest zasięg – jeśli istnieje jakiś model, magazyn wektorów lub narzędzie zewnętrzne, prawdopodobnie istnieje też dla niego integracja LangChain. Jego wadą jest cena tej uniwersalności. Głębokie warstwy abstrakcji mogą przekształcić proces debugowania w trudne zadanie, a ponieważ domyślnie nie posiada stanu, trwałość musi być dodawana oddzielnie, a nie jest czymś naturalnym. LangChain nadaje się do standardowych, w dużej mierze liniowych procesów pracy z jednym agentem, gdzie najważniejsza jest szeroka gama integracji.

LangGraph: modelowanie maszyny stanowej

LangGraph stawia wyraźną, zdecydowaną tezę: systemy agentów to maszyny stanowe, a każdą transformację należy kontrolować. Agent jest modelowany jako graf, którego węzłami są kroki, a krawędziami transformacje – wszystko to działa w ramach typizowanego obiektu stanu.

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"}]})

Gdzie standardowe narzędzia ukrywają strukturę pętli, LangGraph przekształca ją w element, który sam tworzysz. Warunkowe rozgałęzienia są wyraźnie określone.

graph.add_conditional_edges("call_model", should_continue, {"tools": "tools", "end": END})

To właśnie ta funkcja continue-or-stop jest tym elementem, który system LangGraph uruchamia za Ciebie po każdym wezwaniu interfejsu. Oto ona – dostępna do zobaczenia, testowania i wersjonowania. Nagrodą za tę dodatkową szczegółowość jest wyjątkowa cecha LangGrapha, jaką jest natywny mechanizm tworzenia punktów kontrolnych przy każdej zmianie stanu. Ponieważ stan jest zapisywany na dysku w miarę postępów grafu, możliwe jest kontynuowanie wykonywania zadań po ponownym uruchomieniu, przywrócenie stanu po awarii, możliwość cofnięcia się i sprawdzenia wcześniejszych stanów oraz pauzy, które czekają na decyzję użytkownika w trakcie wykonywania zadań. Wadami są stroma krzywa uczenia się oraz projektowanie celowo nieprzyjazne wdrożeniu bez serwera. LangGraph nadaje się do długotrwałych, cyklicznych procesów wymagających odporności na awarie i posiadających stan.

CrewAI: budowanie zespołu

CrewAI zakłada, że większość problemów z wieloma agentami można łatwo podzielić na zespoły specjalistów. Każdy agent jest określony rolą, celem oraz historią, a następnie łączy się je w zespół i przydziela mu zadania.

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"})

Model mentalny jest intuicyjny, a droga do stworzenia działającego systemu wielu agentów jest krótka, dlatego cieszy się dużym zainteresowaniem wśród przedsiębiorstw. Z drugiej strony abstrakcja ról i celów jest wygodna dopóki nic się nie psuje, po czym staje się „czarną skrzynką”; delegowanie zadań między agentami może prowadzić do błędnego cyklu; ponadto trzeba samodzielnie wprowadzić mechanizmy kontrolne, by uniknąć niekontrolowanych kosztów. CrewAI nadaje się do systemów wielu agentów opartych na rolach oraz do szybkiego projektowania prototypów. Wszystko to łączy fakt, że to właśnie my tworzymy przepływ sterowania. To zarówno wada – większe koszty, więcej kodu, więcej decyzji, trudniejsza konserwacja – jak i zaleta, ponieważ możemy obserwować, testować i modyfikować każdą transycję. Siła przyciągania Pythona w dziedzinie danych i uczenia maszynowego stanowi tu prawdziwy atut, ponieważ agent działa obok bibliotek i notatek, z którymi już pracujemy.

Kryteria, na podstawie których dokonuje się wyboru

Rozmieszczone obok siebie, te podejścia różnią się pod względem niewielkiej liczby osi, przy czym kilka z nich w praktyce odpowiada za większość obciążenia.

Własność pętli wykonywania stanowi główną oś; wszystko inne pochodzi od niej. Framework trzyma pętlę dla siebie, natomiast biblioteka umożliwia jej wykorzystanie przez użytkownika – LangGraph reprezentuje scenariusz, w którym użytkownik pisze wszystko, Pi reprezentuje scenariusz, w którym system wykonywania pisze wszystko, a pozostałe rozkładają się pomiędzy te dwa przypadki.

Trwałość jest decydującą zaletą LangGraph. Przechowywanie punktu kontrolnego przy każdej transycji umożliwia szybką odzyskanie funkcjonowania i odtworzenie procesu praktycznie bez dodatkowego wysiłku. Funkcja utrzymywania sesji dostępna w narzędziach typu harness oraz rodzaje pamięci oferowane przez CrewAI są przydatne, ale żadna z nich nie zapewnia możliwości trwałego odtwarzania stanu systemu. Gdy proces zostanie zatrzymany w trakcie działania, graf zapisany w punkcie kontrolnym może być kontynuowany od ostatniej transycji, natomiast zapis sesji zazwyczaj pozwala jedynie później rekonstruować to, co się wydarzyło.

Koordynacja kilku agentów jest wbudowana w CrewAI poprzez role, a w LangGraph za pomocą podgrafów. Narzędzia typu harness osiągają ten efekt innymi sposobami – poprzez podagenty, rozszerzenia lub logikę koordynacji dostarczoną przez użytkownika.

Ekosystem języków programowania ma tendencję do decydowania w sposób dyskretny. Wybór Pythona wprowadza agenta do świata danych i sztucznej inteligencji, obok tabel danych, kodu treningowego oraz notatek. Wybór TypeScript skierowuje go w stronę sieci i urządzeń periferii, obok interfejsów API, wspólnych typów frontendu oraz miejsc, gdzie implementuje się funkcje serverless. Ten „siłowy wpływ” często przeważa nad jakąkolwiek pojedynczą cechą przedstawioną w tabelach porównawczych, dlatego warto szczerze ocenić, w jakim środowisku już funkcjonuje zespół.

Ostatnia uwaga zapewnia uczciwość w porównaniu: wzorzec Node-with-harness, Python-with-framework to tendencja, a nie reguła. Istnieją środowiska wykonawcze w stylu harness napisane w Pythonie, a biblioteki do orkiestracji w stylu grafu napisane w TypeScript. Decyzję należy podejmować na podstawie paradygmatu i rzeczywistych ograniczeń, a nie na podstawie języka oznaczonego na paczce.

Jedno zadanie, widziane z każdej strony

Załóżmy sobie proste, zwyczajne zadanie: agent, który pobiera coś za pomocą narzędzia, a następnie tworzy jego streszczenie. To, z czym każdy paradygmat konfrontuje nas od razu, to jego kluczowa część. W tym podejściu praca polega niemal wyłącznie na konfiguracji. Po określeniu narzędzia do wyszukiwania i instrukcji dla systemu wystarczy wykonać jedno wezwanie – wtedy silnik wykonawczy przejmuje kontrolę, uruchamia model, sprawdza, czy ten chce przeprowadzić wyszukiwanie, wykonuje je, zwraca wyniki, umożliwia modelowi stworzenie streszczenia i samodzielnie zakończa proces. To podejście w praktyce pyta nas o jedno: jakie funkcje powinien mieć ten agent?

W ramach takiej struktury jak LangGraph praca polega niemal wyłącznie na tworzeniu struktury. Określasz obiekt stanu, węzeł, który wywołuje model, węzeł, który uruchamia narzędzie, oraz krawędź warunkową, która decyduje, czy powrócić do modelu, czy zakończyć proces. Dopóki ta struktura nie zostanie w pełni narysowana, nic się w ogóle nie wykonywa. LangGraph prosił o coś innego: czym dokładnie jest przepływ sterowania?

Kluczową kwestią do zrozumienia jest to, że żadna z wersji nie jest trudna; zadanie jest proste w obu przypadkach. To, co je różni, to nie wysiłek, lecz to, co musisz zdecydować z góry oraz to, co pozostaje możliwe do sprawdzenia lub odzyskania później. Narzędzie pomocnicze sprawia, że pierwsze pytanie znika i szybko daje ci uporządkowanego agenta. Framework natomiast nie pozwala mu zniknąć i kompensuje to dostarczając możliwość śledzenia przebiegu działań krok po kroku, gdy system zachowuje się niewłaściwie o nieodpowiedniej porze. Ta różnica staje się coraz ważniejsza w miarę rozwoju systemu. Gdy budujesz asystenta do programowania lub agenta wewnątrz aplikacji, kluczowe jest pytanie dotyczące możliwości działania, a narzędzie pomocnicze stanowi ulgę. Gdy natomiast tworzysz proces pracy, który musi działać przez długi czas, wytrzymywać awarie i przetrwać audyty, istotne jest pytanie dotyczące przebiegu kontroli, a każda dodatkowa linia wyraźnego grafu sprawdza się sama w sobie.

Dostosowanie narzędzia do pętli, którą posiadasz

Heurystyki wynikają bezpośrednio z powyższych kryteriów. Wybierz harness, gdy priorytetem jest szybkość działania agenta, gdy wbudowane opcje środowiska już Ci odpowiadają, gdy celem jest agent do programowania lub narzędzi deweloperskich, albo gdy umieszczasz agenta w aplikacji TypeScript i chcesz bezpieczeństwa typów oraz bliskości do interfejsu użytkownika. Wybierz LangGraph, gdy utrata postępów jest nie do przyjęcia – nadaje się on do długotrwałych lub cyklicznych procesów, które muszą przetrwać awarie, wymagają dokładnej kontroli nad stanem i każdą transycją lub muszą być wstrzymywane ze względu na człowieka. Wybierz CrewAI, gdy problem rzeczywiście można rozłożyć na zestaw specjalistycznych ról i chcesz szybko stworzyć pipeline wielu agentów. Wybierz LangChain, gdy zakres dostępnych integracji dla konwencjonalnego przepływu z jednym agentem i wywoływaniem narzędzi przewyższa wszystkie inne kwestie, szczególnie w istniejącej bazie kodu Python. I ponownie

Z uczuciem granice stają się płynne: należy zdecydować o paradygmacie i ograniczeniach, a nie o tym, jakim językiem jest wydrukowana puszka.

Pytaniem od samego początku była pętla

Debata nigdy nie dotyczyła faktycznie Node kontra Python albo jednego pakietu kontra jego konkurenta. Chodziło o to, czy środowisko wykonawcze powinno kontrolować pętlę agenta, czy my. Narzędzie typu harness poświęca wolność do przepisania logiki w zamian za szybkie stworzenie działającego agenta oraz możliwość modyfikacji jego zachowania za pomocą konfiguracji, zdarzeń i hooków – wszystko to w ramach pętli, której sami nie stworzyliśmy. Framework natomiast poświęca szybkość na rzecz wyraźnych poleceń, trwałości i możliwości orkiestracji, a także nalicza stałe koszty związane z pisaniem i utrzymywaniem logiki. Dla większości zespołów rozsądnym punktem wyjścia jest użycie harness do prototypów, tworzenia agentów oraz wszelkich elementów wbudowanych w produkt, gdzie kluczowa jest szybkość realizacji zadań; należy przejść na LangGraph, gdy trwałość stanu stanie się bezwzględnym wymogiem, a zwrócić się do CrewAI, gdy praca naprawdę wymaga zespołu specjalistów. Ponieważ te narzędzia ciągle przyswajają najlepsze pomysły jednych od drugich, umiejętnością, która pozostaje niezmienna, nie jest biegłość w jednym z nich.

API, ale decyzja polega na spojrzeniu na agenta stojącego przed tobą i ustaleniu, kto powinien posiadać jego pętlę.

Literatura pokrewna