Strona główna / Artykuły

Dlaczego agenty długoterminowe potrzebują harness: jak Deep Agents zarządza stanem i kontekstem.

Dlaczego agenci o długim horyzoncie czasowym potrzebują uprzęży: jak LangChain Deep Agents zwalcza degradację kontekstu za pomocą czterech filarów, middleware, backendów systemu plików oraz izolowanych podagentów.

Wydawca: REACTAPP.TOP12 min czytania2727 słów

Źródła: The Deep Agents Architecture, Part 1: Understanding the Agent Harness

Najprostszym projektem agenta jest pętla obejmująca wywołania narzędzi. Model czyta żądanie, wybiera narzędzie, je uruchamia, czyta wynik, ponownie dokonuje wyboru i kontynuuje, aż przestanie wywoływać narzędzia. LangChain otwarcie wskazuje, gdzie kończy się efektywność takiego projektu: takie agenty są „płytkie”, niezdolne do planowania i działania przy długich, złożonych zadaniach. Nie jest to wada charakterystyczna tylko dla jednego frameworku – jest to skutek braku jakiejkolwiek izolacji pomiędzy modelem a kontekstem, który stale się rozszerza.

Najlepszym dowodem na to, że sufit jest strukturalny, są prace trzech zespołów, które nigdy ze sobą nie współpracowały. Anthropic doszedł do tego rozwiązania podczas tworzenia Claude Code, OpenAI – dzięki Deep Research, a Butterfly Effect tak mocno na to nacisnął, że Manus został przebudowany cztery razy. We wszystkich przypadkach chodziło o te same cztery komponenty: narzędzie planowania, delegowanie zadań podagentom, dostęp do systemu plików oraz szczegółowy prompt. Niezależne starania różnych firm prowadzące do podobnej architektury wskazują, że to problem sam wymusił takie rozwiązanie, a nie czyjeś upodobania.

Prawdziwym mechanizmem jest degradacja kontekstu

Kuszące jest stwierdzenie, że długotrwałe działanie agentów kończy się niepowodzeniem z powodu wypełnienia się okna kontekstowego. To jednak nie jest prawdziwy mechanizm.

Model prowadzący długą rozmowę rozumuje gorzej niż ten sam model w krótkiej rozmowie, nawet jeśli większość jego okna pozostaje wolna. Ilość uwagi dostępna dla danego tokena zmniejsza się wraz ze wzrostem liczby otaczających go tokenów, a spadający stosunek sygnału do szumu negatywnie wpływa na jakość wyniku na długo przed wypełnieniem się okna. Efekt ten jest udokumentowany, a nie oparty na anegdotach: modele rzetelnie gorzej odzyskują informacje z środka długiego kontekstu, preferując tokeny znajdujące się na początku i na końcu.

To zmienia charakter rozwiązania. Gdyby problemem była niska pojemność, większe okno mogłoby to naprawić. Prawdziwym problemem jest rozcieńczenie informacji: model nadal jest zdolny do pracy, ale to, czego potrzebuje, jest pogrzebane pod wszystkim, co pojawiło się wcześniej. To problem higieny, a nie rozmiaru, a dodatkowa przestrzeń w oknie nic nie pomaga w poprawie tej higieny.

Luka, której ani LangChain, ani LangGraph nie zostały stworzone po to, by ją zamknąć

LangGraph koncentruje się na trwałej eksploatacji: punkty kontrolne, przepływ danych w czasie rzeczywistym, ludzka weryfikacja oraz przechowywanie danych pomiędzy wątkami. Jego rzeczywistym odpowiednikiem jest Temporal, a nie inne frameworki agentów. Zapewnia on, że agent przetrwa awarię, ale nic nie mówi o tym, czy agent nadal myśli jasno po czterech godzinach. LangChain znajduje się na wyższym poziomie i dostarcza abstrakcje: pętlę wywoływania narzędzi, strukturyzowany kontent oraz middleware do personalizacji. Mimo to domyślna pętla zbudowana na tych abstrakcjach jest właśnie tą powierzchowną, o której mowa wcześniej. Trwałość i abstrakcja są rzeczywiste i niezbędne, jednak żadna z nich nie odnosi się do tego, czy kontekst pracy modelu pozostaje czysty.

To można sprawdzić na podstawie sposobu połączenia poszczególnych komponentów. Deklarowane zależności Deep Agents to nazwy langchain, langchain-core, langchain-anthropic, langchain-google-genai oraz langsmith. LangGraph nie znajduje się na tej liście – pojawia się tam jedynie dlatego, że LangChain od niego zależy. Struktura jest ściśle zagnieżdżona: Deep Agents zależą od LangChain, a ten z kolei od LangGraph, a nie jest to połączenie trzech równorzędnych komponentów. Na poziomie wykonywania kodu ani frameworka nie istnieje żadna mechanizm kontrolujący czystość kontekstu, ponieważ warstwa, która powinna nią zarządzać, znajduje się powyżej obu tych poziomów.

Teza: problem stanu, a nie problem rozumowania

Złożone zadania należące do określonej, dobrze zdefiniowanej kategorii złożoności stanowią problem zarządzania stanem, a nie problem rozumowania.

Istnieje prosty sposób, aby określić, jaki rodzaj problemu dotyczy danej zadania. Wyobraźmy sobie wykwalifikowanego kolegę, który co dziesięć minut traci całą pamięć. Jeśli ten kolega nie poradzi sobie z zadaniem, chodziło w nim o zarządzanie stanem, a notatki, delegowanie zadań oraz procedury opracowane z myślą o utracie pamięci pomogą rozwiązać problem. Jeśli osoba, która cały czas miała problem w pamięci, nadal będzie miała trudności, notatki nic nie pomogą, ponieważ trudność polegała od początku na rozumowaniu.

Każdy mechanizm dodawany przez system zarządzania zadaniami – czy to system plików służący do przenoszenia obciążenia, podagenty zapewniające izolację, czy narzędzie planowania do śledzenia postępów – radzi sobie z przypadkiem amnezji. Żaden z nich nie sprawia, że poszczególne kroki rozumowania stają się inteligentniejsze. Zarządzanie stanem nie jest zatem konkurencyjnym wyjaśnieniem rozumowania; stanowi podstawę, która umożliwia rozumowaniu rozwój na wielu etapach bez konieczności jego resetowania przy każdym z nich. Model, który poprawnie rozumuje na każdym kroku, może nadal zawieść przy zadaniu składającym się z dwustu kroków, jeśli krok 150 nie jest w stanie poznać decyzji podjętej na kroku 12. Rozumowanie nie było błędne – po prostu nie miało ciągłości, z którą mogłoby pracować.

Ta podstawa sama w sobie nie jest wystarczająca, a udawanie, że tak jest, osłabi resztę argumentu. Trzymanie się nieskutecznej strategii, podczas gdy prawdziwa, kompletna historia jest łatwo dostępna, to czysty błąd w rozumowaniu, a doskonałe zarządzanie stanem go nie naprawia. Podobnie model, którego własna wersja wydarzeń odbiega od tego, co faktycznie się wydarzyło, ma problem z rozumowaniem, nawet gdy prawdziwe zapisy są nienaruszone i dostępne. Sprawiedliwa wersja tego twierdzenia brzmi następująco: w przypadku problemów, na które jest przeznaczony harness, czyli zadań, które ulegają degradacji, ponieważ przetrwają jeden czysty kontekst, zarządzanie stanem jest głównym narzędziem, natomiast jakość rozumowania to trudniejszy element, którego ramy nie mogą wpłynąć. Poniższe sekcje opisują, co zbudowały trzy zespoły, aby to narzędzie wykorzystać.

Cztery filary składające się z wielu elementów

Czym jest Deep Agents

Deep Agents jest dostępny jako pakiet Python o otwartym kodzie źródłowym pod licencją MIT, instalowany za pomocą pip, obecnie w wersji 0.7.5. Istnieje również jego wersja w TypeScript, interfejs CLI o nazwie deepagents-code oraz wersja hostowana i zarządzana, która nadal znajduje się w fazie prywatnego beta. Nie jest konkurentem LangChain i LangGraph. Jak wcześniej wspomniano, polega bezpośrednio na LangChain, a do LangGraph ma dostęp wyłącznie przez ten kanał. Wywołanie funkcji create_deep_agent() skutkuje wywołaniem funkcji create_agent() z LangChain, która tworzy graf LangGraph, więc wszelka trwałość agenta pochodzi od tego, co przekazuje LangChain.

Cztery filary i technologie, które je podtrzymują

Zgodnie z LangChain, cztery elementy sprawiają, że agent jest zaawansowany: narzędzie planowania, delegowanie zadań podagentom, dostęp do systemu plików oraz dobrze sformułowany prompt. Harrison Chase zaproponował tę koncepcję po zauważeniu podobnej struktury w Claude Code, Deep Research i Manus. Jako model mentalny sprawdza się dobrze, ponieważ te cztery kategorie rzeczywiście wskazują na różnicę pomiędzy prostym algorytmem a agentem, który potrafi wykonać długie zadanie.

Cztery było jedynie wstępnym opisem, a nie ostatecznym planem. Funkcja create_deep_agent() łączy poszczególne umiejętności z coraz większego zbioru komponentów pośrednich, które można swobodnie łączyć ze sobą. Lista ta obejmuje pliki, podagenty, funkcje podsumowywania, modyfikację wywołań narzędzi, różne warianty buforowania zapytań powiązane z konkretnymi dostawcami, pamięć, umiejętności oraz zatwierdzenie przez człowieka, a także dowolne własne warstwy stworzone przez programistę. Te elementy określają, co agent potrafi robić. Zbiór komponentów pośrednich to sposób, w jaki te umiejętności są faktycznie realizowane, i cały czas się zmienia.

Tylko dwie części stosu są wymagane ze względów strukturalnych, i to ma większe znaczenie niż jakakolwiek liczba. FilesystemMiddleware jest konieczny, ponieważ egzekwuje zasady uprawnień kontrolujące dostęp do plików, co w komentarzu źródłowym określano jako gwarancja bezpieczeństwa. SubAgentMiddleware jest wymagany, ponieważ jest to to, co wykorzystuje narzędzie task. Wykluczenie któregokolwiek z nich powoduje błąd i nie prowadzi do cichego pogorszenia funkcjonowania. Wszystko inne jest opcjonalne i jest dodawane wyłącznie na żądanie.

Każdy, kto teraz buduje na tym fundamentach, powinien wiedzieć, że dwie z pierwotnych czterech kategorii stały się opcjonalne. Od wersji v0.7.0 narzędzie planowania (write_todos) nie jest częścią domyślnego zestawu, a podstawowy prompt systemu jest dostarczany pusty, zgodnie z powiadomieniem o wycofywaniu. Aby przywrócić każdą z tych funkcji, potrzebna jest jedna linia konfiguracji. Prosta funkcja create_deep_agent() zapewnia dostęp do plików i delegację subagentów od razu, natomiast planowanie oraz szczegółowy prompt są dostępne tylko na żądanie.

Profile wykorzystania – warstwa dostroju, której nie ma nowszy model

Pod warstwą middleware znajduje się oddzielny mechanizm dostosowywania zwany profilem harness. Profil dodaje dodatkowy tekst wyświetlany przez system oraz usuwa określone narzędzia, wybrane na podstawie dokładnego ciągu znaków modelu przekazanego agentowi. Nie należy go mylić z ProviderProfile, który określa sposób konstruowania obiektu modelu. HarnessProfile kształtuje zachowanie po jego utworzeniu.

Aby dokonać wyszukiwania, konieczny jest dokładny zarejestrowany klucz provider:model; w przypadku jego braku jako rozwiązanie awaryjne stosuje się wpis dotyczący całego dostawcy. Od razu dostępne są profile dla rodziny Codex od OpenAI oraz dla trzech generacji Claude: Haiku 4.5, Sonnet 4.6 i Opus 4.7. Profil Sonnet 4.6 to coś więcej niż zwykłe porady dotyczące formułowania zapytań – ściśle follows wydane przez Anthropic wytyczne, o czym świadczą nawet komentarze w kodzie; ponadto celowo pomija ostrzeżenia skierowane do użytkowników Opus dotyczące nadmiernego entuzjazmu i intensywnego wykorzystania podagentów, które uznano za nieważne w przypadku Sonnet.

Model wydany jedną generację po zarejestrowanych nie otrzymuje nic. Po sprawdzeniu w rozwiązcy claude-sonnet-5, anthropic:claude-sonnet-5 oraz zwykły anthropic nie zwracają żadnego profilu. Ponieważ nie ma dokładnego dopasowania, a także nie ma ogólnej pozycji Anthropic do użycia jako alternatywy (istnieją tylko profile przypisane do poszczególnych wersji), nic się nie zgadza. Brak dopasowania jest całkowicie niesłyszalny. Zgodnie z własną dokumentacją rozwiązcy, jedyną linią w dzienniku jest nota debugowa wyświetlana, gdy używana jest alternatywa na poziomie dostawcy. Jeśli oba wyszukiwania zawiodą, w ogóle nie jest zapisywany żaden dziennik.

Agent zbudowany na najnowszym modelu z szybko rozwijającej się rodziny otrzymuje więc dokładnie takie ustawienia jak agent zbudowany na modelu, o którym framework nigdy nie słyszał, czyli żadne, przy czym nie ma żadnych oznak wskazujących, że tak się stało.

Jak to działa

Jedna abstrakcja pod wszystkim

Cały ten middleware opiera się na jednym mechanizmie należącym do LangChain, a nie do Deep Agents. LangChain 1.0 dodał punkty, w których kod może być wykonywany przy rozpoczęciu lub zakończeniu działania, przed i po każdej wywołaniu modelu oraz przy każdym wywołaniu narzędzia, co umożliwia pojedynczemu agentowi obsługę zupełnie różnych konstrukcji. Hooki typu before uruchamiają się w kolejności, w jakiej zostały zarejestrowane, natomiast hooki typu after – w odwrotnej kolejności, co odzwierciedla sposób, w jaki frameworki internetowe układają swój middleware. Hooki typu wrapping mogą spróbować ponownie wykonać wywołanie, dostarczyć jego treść z pamięci cache lub całkowicie je przechwycić. Konfiguracje typu Supervisor, zespoły agentów, pętle refleksji oraz deep agents okazują się być w rzeczywistości jedną pętlą z przyłączonymi różnymi hookami, a create_deep_agent() to po prostu jedna z możliwych selekcji tych elementów.

Pliki: dwa backendy, dwie gwarancje

Pliki mogą znajdować się w dwóch miejscach, a to, który z nich jest aktywny, decyduje o tym, co dokładnie oznacza operacja zapisu przez agenta.

  • StateBackend, który jest domyślny, przechowuje pliki w kanale stanu LangGraph: dane grafu z własnymi zasadami łączenia, a nie na dysku. Działa jedynie podczas uruchamiania grafu w czasie rzeczywistym. To, czy zawartość przetrwa pojedyncze wezwanie, zależy od obecności zarówno wskaźnika stanu, jak i stabilnego identyfikatora wątku. Bez nich każde wezwanie rozpoczyna się od zera, co tłumaczy, dlaczego prosta funkcja create_deep_agent() zawsze pokazuje pusty katalog roboczy.
  • FilesystemBackend dokonuje rzeczywistych odczytów i zapisów na dysku, co niesie ze sobą zarówno trwałe przechowywanie, jak i prawdziwe zagrożenia. W jego dokumentacji jasno stwierdza się, że nadaje się do lokalnego rozwoju oraz procesów CI/CD, ale nie nadaje się do zastosowań dostępnych dla użytkowników, ponieważ agent może odczytać wszystko, do czego ma dostęp, włączając tajne dane, a dokonane przez niego zmiany nie da się odwrócić. Przy domyślnym ustawieniu virtual_mode=True dostęp ograniczony jest do katalogu głównego, przechodzenie po ścieżkach zarówno w formie surowej, jak i po rozwiązaniu jest odrzucane, a prawdziwe ścieżki bezwzględne są ukryte w wynikach działania agenta, dzięki czemu lokalizacja katalogu głównego nie zostaje ujawniona.

Jeden system uprawnień obejmuje oba przypadki. Reguła łączy operację z wzorem ścieżki oraz jednym z trzech trybów: zezwól, odrzuć lub przerwij. Usuwanie jest dokładnie sprawdzane pod kątem reguł zapisu. Recydywne usuwanie zostaje odrzucone, jeśli jakakolwiek reguła odrzucająca pokrywa się z czymkolwiek poniżej celu, a nie tylko z pierwszą dopasowaną regułą, więc wczesne, szerokie zezwolenie nie może upoważnić do usunięcia czegoś, co miało być chronione przez późniejszą, węższą regułę.

Podagenty, zaprojektowane tak, by być izolowanymi

Do podagentów dostęp uzyskuje się w taki sam sposób jak do dowolnego narzędzia. Gdy model wywołuje task, uruchamia się zupełnie oddzielny agent, wyposażony we własny model, narzędzia i middleware. Ustawienia jego modelu przyjmują albo ciąg znaków provider:model, albo już utworzony obiekt modelu, więc umieszczenie podagenta na innym dostawcy niż jego rodzic wymaga tylko jednej linijki konfiguracji, a nie dużego wysiłku inżynieryjnego.

Pamięć nie jest przenoszona automatycznie. Podagent nie posiada pola pamięci, a logika przekazywania uprawnień nie ma odpowiednika dla pamięci. Izolowany, czysty kontekst jest celowy, co oznacza, że wszystko, co zostanie zapisane za pośrednictwem pamięci nadzorcy, jest niewidoczne dla podagenta, chyba że otrzyma on własny middleware pamięciowy.

Pamięć może być zmieniana, nie jest autorytatywna

Plik pamięci, po załadowaniu, nie trafia bezpośrednio do komendy systemowej. Przychodzi on w formie wyraźnie oznaczonego bloku, w którym znajdują się instrukcje dodane przez framework, informujące model, że treść stanowi materiał referencyjny, który może być przestarzały lub stworzony przez kogoś innego, a nie jest to polecenie do bezwarunkowego wykonania. Oznacza to niższy poziom zaufania w porównaniu z komendą systemową i odpowiada zamierzonemu przeznaczeniu: jest to coś, co agent czyta i modyfikuje podczas pracy, a nie zbiór zasad, który programista pisze raz i oczekuje, że będzie miał pełną władzę.

Szczegóły skalowania

Stan rozmowy opiera się na spersonalizowanym reduktorze o nazwie DeltaChannel, zaprojektowanym tak, aby przechowywanie punktów kontrolnych rosło liniowo, O(N), w miarę wydłużania się rozmowy, zamiast kwadratowo, O(N²).

Zachowanie na krańcach

Zanim zdecydujemy, w jakim stopniu polegać na podagentach w środowisku produkcyjnym, należy zrozumieć dwie okoliczności, obie sprawdzone w obecnym repozytorium.

Jedna awaria, wiele strat (problem nr 694). Jeśli kilka podagentów działa równocześnie, a jeden wywoła wyjątek, pozostałe są anulowane wraz z nim. Zestaw operacji udaje się lub zawodzi jako całość, bez żadnej izolacji pomiędzy jego elementami. Raport pochodzi z stycznia 2026 roku i dotyczy wczesnej wersji oprogramowania; problem został już zamknięty, chociaż zmiany w kodzie, które go naprawiły, nie można zlokalizować ani w kodzie podagentów, ani w warstwie wykonywania narzędzi LangChain. Mimo to należy brać to pod uwagę: traktować każdą rozproszoną grupę niezależnych zadań jako taką, która może ulec częściowemu awarii, dopóki nie zostanie potwierdzone inaczej w rzeczywistej wersji wdrożonej oprogramowania.

Błędy trafiające do niewłaściwego odbiorcy (#947). Po tym, jak narzędzie zgłasza błąd, model powinien zwykle spróbować ponownie lub dokonać korekty. Jednak w niektórych zaobserwowanych przypadkach kierował on bezpośrednio wiadomość do końcowego użytkownika, używając tekstu przeznaczonego wyłącznie do wewnętrznych korekt, w rezultacie czego dana osoba widziała komunikat o błędzie, który w ogóle nie był dla niej przeznaczony. Problem pozostawał nierozwiązany podczas sprawdzenia. W oddzielnym raporcie zaznaczono, że po późniejszym aktualizacji frameworka określone wyjątki narzędziowe całkowicie zatrzymują działanie agenta, zamiast przekazać błąd modelowi jako wiadomość nadającą się do ponownej próby, podczas gdy wcześniejsze wersje kontynuowały swoją pracę pomimo tych wyjątków.

Dwa starsze raporty z tej samej dziedziny zostały zamknięte: subagenty ignorujące limit rekurencji nadrzędnego agenta (#1698) oraz problemy z kontynuowaniem pracy przy decyzjach o edycji i odrzuceniu (#554). Poprawka dotycząca limitu rekurencji została również sprawdzona w stosunku do zainstalowanego źródła. Pokazują one, jak szybko problemy w tym obszarze zazwyczaj są rozwiązywane, więc nie stanowią powodu do zmiany projektu.

Praktyczne alternatywy

Dziś istnieją trzy praktyczne opcje na stworzenie agenta o długim horyzoncie działania: Deep Agents, Claude Agent SDK oraz OpenAI Agents SDK. Każda z nich odzwierciedla inne podejście dotyczące tego, co zespół kontroluje, a co udostępnia innym, i żadna z nich nie jest niekompletną wersją pozostałych.

Jedna z różnic jest bardziej mylona niż jakakolwiek inna – chodzi o różnicę pomiędzy przekazaniem obowiązków a podagentem. Dwie z trzech opcji wykorzystują ten sam podstawowy wzorzec, do którego niezależnie dochodzą dwa zespoły. Trzecia to zupełnie inny mechanizm.

Wybór spośród nich sprowadza się do dwóch pytań, a nie do rankingu, przy czym żadne z nich nie ma uniwersalnej odpowiedzi. Każde z nich można odpowiedzieć tylko w kontekście rzeczywistych ograniczeń konkretnego systemu.

Co wynieść z tego

Agenty o długim horyzoncie czasowym potrzebują ram wspierających, ponieważ gromadzący się kontekst osłabia sygnał na długo przed wypełnieniem okna czasowego. Deep Agents buduje te ramy w oparciu o strukturę składającą się z czterech filarów, wspieraną zmieniającą się kolekcją komponentów pośrednich, systemem plików, podagentami oraz pamięcią, z których każdy zapewnia określone gwarancje i ma określone braki. Znajomość tych braków – takich jak brak informacji w profilach nowszych modeli, pamięć niewidoczna dla podagentów, awarie równoległe typu „wszystko albo nic” oraz błędy narzędzi przenikające do użytkowników – jest tym, co odróżnia świadome korzystanie z tej ramy od polegania na jej ustawieniach domyślnych.

Literatura pokrewna

  • Zarządzane Głębokie Agenty Beta: Sposób oparty na katalogach do wdrażania agentów LangChain — Zarządzane Głębokie Agenty LangChain przekształca folder projektu w hostowanego agenta. Co robi każdy plik, jak działa mda deploy oraz jakie są braki w wersji beta, które warto znać.
  • Budowanie Skutecznych Agentów AI: Wzory architektury i frameworki — Przewodnik po architekturze agentów, od pętli wykorzystujących narzędzia, ReAct i routerów po zarządzanie pamięcią, zasobami bezpieczeństwa, zatwierdzeniem przez człowieka i możliwościami obserwacji, wraz z krok po kroku sekwencją budowy.