System plików twojego agenta to decyzja: wyjaśnienie serwerów tła Deep Agents
Backendy LangChain Deep Agents decydują, które pliki przetrwają: pięć wbudowanych rozwiązań, routowanie przez CompositeBackend, pułapka przestrzeni nazw w magazynach i środowiskach testowych wielu użytkowników.
Źródła: LangChain Deep Agents: Your Agent’s Filesystem Is a Decision. Most People Make It by Accident. IV
Najstraszniejszy błąd w agentach nie powoduje awarii. Po cichu zapomina o tym, co wydarzyło się wczoraj, ujawnia pamięć kogoś innego lub zapisuje cenne informacje w przestrzeni tymczasowej, której utraty wcale nie chcieliśmy. W Deep Agents od LangChain miejscem, w którym decyduje się o tych wynikach, jest backend, a celem jest przestać traktować system plików jak coś magicznego i celowo określić, co zostanie, co zniknie oraz kto będzie mógł to zobaczyć.
Każdy agent w Deep Agents ma swój system plików, ale to ty decydujesz, gdzie on faktycznie będzie znajdował się: w pamięci, na dysku, w trwałym magazynie obejmującym różne wątki lub w połączeniu tych rozwiązań. Błędny wybór sprawia, że agent po cichu zapomina o wszystkim między rozmowami.
Błąd stojący za znikającymi notatkami
Załóżmy, że stworzyłeś agenta z włączonymi narzędziami systemu plików i zobaczyłeś, jak w trakcie rozmowy zapisuje notatki do pliku /memories/findings.md. Później odczytuje je ponownie w tej samej rozmowie. Wszystko w porządku, więc wypuszczasz produkt. Następnego dnia wraca użytkownik i wspomina o czymś, co agent miał rzekomo znać poprzedniego dnia, ale agent nic o tym nie wie. Notatki zniknęły. Żadnego błędu, żadnej linii z dziennika. Agent po prostu zaczął od nowa i o tym nie poinformował.
Nikt celowo nie pisze takiego błędu. Wynika on z domyślnej ustawienia: pliki agenta trafiają do przestrzeni tymczasowej dostępnej tylko dla danej rozmowy, która kończy się wraz z nią. Jeśli pominiesz decyzję o miejscu przechowywania plików, framework sam o to zadecyduje. Dla notatek tymczasowych to w porządku, ale dla przechowywania informacji to zła decyzja.
Prawa dostępu, pamięć, środowiska testowe oraz proces wdrażania opierają się wszystkie na tej jednej decyzji. Gdy backend jest prawidłowy, reszta funkcjonuje bez problemów, natomiast przy błędnym wyborze tracimy czas na poszukiwanie nieistniejących informacji przez całe popołudnia.
System plików jest wirtualny, a backend stanowi jego podstawę
Agent zawsze widzi zwykły system plików z standardowymi ścieżkami. Backend decyduje o tym, z czego składa się ten system plików, a można go zmieniać bez wpływu na zachowanie agenta.
Dostępne są sześć narzędzi do obsługi systemu plików: ls, read_file, write_file, edit_file, glob oraz grep. Dla modelu działają one na zwykłym systemie plików. Model zapisuje plik /workspace/plan.md, później go odczytuje i przeszukuje katalog /repo, jakby miał dysk – a to wszystko nie zmienia się niezależnie od tego, jaki backend jest używany.
Tło systemu to implementacja stojąca za tą abstrakcją. Odpowiada na pytania, o których agent nawet nie myśli. Jeśli model zapisuje plik /workspace/plan.md, dokąd trafiają te bajty? Do pamięci Pythona, która znika po zakończeniu procesu? Na rzeczywisty dysk? Do bazy danych, która przetrwa wszystkie rozmowy użytkownika? Każda odpowiedź odpowiada innemu tłumieniu systemu.
Wybór dokonuje się poprzez przekazanie odpowiedniego tłumienia systemu do funkcji create_deep_agent(model=..., backend=...). Jeśli nic nie przekazamy, stosuje się wartość domyślną, co tłumaczy znikanie notatek opisanych powyżej.
Jeden wskazówka, aby twój kod był zgodny z najnowszymi wersjami: przekaż agentowi gotową instancję, np. StateBackend(). Starsze przykłady używały otoczki lambda rt:, która jest obecnie przestarzała i niepotrzebna.
Pięć tłumień systemu, uporządkowanych według tego, co przetrwa
Prostym sposobem na rozróżnienie pięciu wbudowanych rozwiązań jest określenie ich czasu trwania, a nie mechanizmów wewnętrznych: jak długo pozostaje plik zapisany za pomocą każdego z nich?
StateBackend jest domyślnym rozwiązaniem i stanowi przestrzeń roboczą skojarzoną z wątkiem. Pliki znajdują się w stanie agenta i przetrwają przez cały przebieg rozmowy (jednego wątku), ale nie są dzielone pomiędzy wątkami. Jest to notatnik agenta, idealny do przechowywania informacji tymczasowych, które są odczytywane po dwóch krokach dalej i usuwane po zakończeniu rozmowy. Jest to odpowiedni domyślny wybór, a jednocześnie właśnie ten backend powoduje zaskoczenie związanе z znikającymi notatkami, gdy jest mylony z pamięcią.
StoreBackend to trwałe przechowywanie danych pomiędzy wątkami. Pliki trafiają do magazynu LangGraph i przetrwają różne rozmowy, co jest niezbędne dla długoterminowej pamięci. Problemem, i to poważnym, jest konieczność wskazania, czyje dane są przechowywane – o tym będzie mowa poniżej.
FilesystemBackend oraz LocalShellBackend wykorzystują rzeczywisty dysk komputera hosta. Czytają i zapisują rzeczywiste pliki w wybranym katalogu, a LocalShellBackend dodatkowo dostarcza agentowi narzędzie execute, które uruchamia polecenia w shellu bezpośrednio na twoim komputerze, bez żadnego sandboxu. Nadają się do lokalnego asystenta programistycznego, który tworzysz przy biurku, ale są niebezpieczne wszędzie indziej, dlatego dokumentacja wyraźnie ostrzega przed ich używaniem w uruchomionych agentach. Bezpośredni dostęp do hosta w połączeniu z dynamicznymi danymi od użytkownika stanowi zagrożenie bezpieczeństwa, które może w każdej chwili dojść do skutku.
ContextHubBackend to natywna, trwała opcja LangSmith. Pliki znajdują się w repozytorium LangSmith Hub i są zapisywane jako komitety, dzięki czemu uzyskujesz trwałość danych między różnymi uruchomieniami oraz rejestr każdej zmiany w systemie plików, bez konieczności utrzymywania własnego magazynu. Jest to rozwiązanie sensowne, gdy już pracujesz w ekosystemie LangSmith i chcesz zachować trwałość danych bez konfiguracji bazy danych.
Patrząc na to razem, opcje te znajdują się na jednej osi: nic nie przetrwa, rozmowa przetrwa, wszystko przetrwa lub wszystko przetrwa wraz z historią. Wybierz to, czego potrzebuje Twoja funkcjonalność.
CompositeBackend: różne ścieżki, różne okresy trwania
Idea, dzięki której backendy dobrze funkcjonują, polega na tym, że rzadko chce się jednej polityki przechowywania dla całego systemu plików. Pliki robocze powinny być tanie, szybkie i do wyrzucenia, natomiast pamięć powinna być trwała. To dwa różne backendy, a CompositeBackend umożliwia korzystanie z obu w ramach jednego systemu plików poprzez kierowanie ruchu na podstawie prefiksu ścieżki.
Ustaw dla niego jeden domyślny backend dla większości ścieżek oraz tabelę kierowania, która przekierowuje wybrane prefiksy. Zwykłym rozwiązaniem jest używanie pamięci tymczasowej do wszystkiego, z trwałym przechowywaniem za adresem /memories/:
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StateBackend, StoreBackend
from langgraph.store.memory import InMemoryStore
agent = create_deep_agent(
model="anthropic:claude-sonnet-4-6",
backend=CompositeBackend( # ①
default=StateBackend(), # ②
routes={ # ③
"/memories/": StoreBackend(namespace=lambda _rt: ("memories",)), # ④
},
),
store=InMemoryStore(), # passed to create_deep_agent, not to the backend ⑤
)
Przechodząc przez oznaczone numerami elementy w tym wykazie:
CompositeBackendprezentuje agentowi jeden system plików i przekazuje każdą operację do podsystemu backendu, którego ścieżka odpowiada podanej ścieżce.defaultbierze to, co nie zostało przypisane żadnej ścieżce. W tym przykładzie jest toStateBackenddostępny w zakresie wątku.routesłączy przedrostki ścieżek z backendami. Jeśli dwie ścieżki się pokrywają, decyduje ta dłuższa, więc/memories/projects/ma pierwszeństwo przed/memories/.- Zapisy pod
/memories/trafiają do trwałegoStoreBackend, którego funkcjanamespacezwraca tuple identyfikujący właściciela i określający zakres danych. - Struktura LangGraph
storejest przekazywana do funkcjicreate_deep_agent, a nie bezpośrednio do backendu – to szczegół konfiguracji, który ludzie przeoczają przy pierwszym przeglądaniu.
Skutkiem tego jest to, że wszystko, co zostanie napisane pod adresem /memories/, trafia do trwałego magazynu i przetrwa między rozmowami, natomiast wszystko inne – czy to plan w katalogu /workspace, plik tymczasowy czy zapisana treść – trafia do domyślnego StateBackend i znika po zakończeniu sesji. Agent ani o tym nie wie, ani go to nie obchodzi. On po prostu zapisuje ścieżki.
Strategia systemu plików dla prawdziwego agenta
Weźmy na przykład agenta do utrzymania kodu, np. o nazwie buggy-shop. Gdy pracuje nad repozytorium, pliki w tym repozytorium stanowią aktualny stan pracy. Są one ładowane, edytowane i testowane, i nie ma powodu, by przeniknęły do następnej rozmowy. To, czego agent się naucza w trakcie pracy, to inna sprawa – i właśnie to powinien zapamiętać przy następnej prośbie: niestabilne testy w jednym module, elementy współdzielone przez kilka zestawów testowych, błąd paginacji typu „off-by-one” znaleziony wcześniej.
Zatem buggy-shop wykorzystuje powyższe złożone rozwiązanie. Repozytorium znajduje się pod domyślnym StateBackend jako tymczasowa przestrzeń do zapisu na poziomie wątku, a gromadzone notatki są przechowywane pod adresem /memories/ w trwałym StoreBackend. To jeden agent z celową odpowiedzią na to, co powinno przetrwać po zakończeniu rozmowy. Rozdzielenie tymczasowego stanu roboczego od trwałej wiedzy jest właściwym rozwiązaniem, a routowanie za pomocą prefiksów ścieżek służy do jego realizacji.
Gdzie uruchamiany jest kod, a gdzie znajdują się pliki
Do tej pory mowa była wyłącznie o plikach, a wiele agentów w ogóle ich nie potrzebuje. Jednak agent, którego zadaniem jest udowodnienie poprawności naprawy poprzez uruchomienie pytest, potrzebuje shella, ponieważ wykonywanie poleceń nie jest operacją systemu plików. Taki backend typu sandbox to rozwiązuje: oprócz systemu plików udostępnia narzędzie execute do wykonywania poleceń, wszystko to w izolowanym kontenerze, dzięki czemu agent nie może uszkodzić hosta.
Agenty mogą być połączone z sandboxem na dwa sposoby. Pierwszy polega na umieszczeniu agenta wewnątrz sandboxu, który pracuje z jego systemem plików. Dzięki temu rozwój lokalny jest bliski środowisku produkcyjnemu, ale wymusza umieszczenie kluczy API w sandboxie. Drugi sposób traktuje sandbox jako narzędzie: agent pozostaje na twoim serwerze i łączy się z sandboxem wyłącznie w celu uruchomienia kodu. Dane uwierzytelniające pozostają na zewnątrz, możesz wyrzucić sandbox, a w przypadku awarii stan agenta pozostaje bezpieczny. Deep Agents radzi sobie z obiema opcjami, co stanowi wyraźny kontrast z narzędziami, które pozwalają tylko na pierwszą opcję.
Pamiętaj, że wykonywanie kodu i przechowywanie plików to dwa odrębne zagadnienia. Tło systemu plików odpowiada na jedno z nich, tło sandboxu na drugie, a wiele zaawansowanych agentów potrzebuje obu.
Konfiguracja, której nie można pominąć
Gdy StoreBackend obsługuje więcej niż jednego użytkownika, konieczna jest fabryka nazw przestrzeni. Jej brak nie powoduje awarii, ale prowadzi do wycieku pamięci. Backend musi wiedzieć, czyje pliki przechowuje, a dowiaduje się o tym dzięki nazwie przestrzeni – małej funkcji, która przyjmuje informacje o czasie wykonywania i zwraca tuple z nazwą właściciela. Dzielenie przestrzeni na użytkowników umożliwia każdej osobie oddzielne przechowywanie danych:
from deepagents.backends import StoreBackend
backend = StoreBackend(
namespace=lambda rt: (rt.server_info.user.identity,),
)
Wyłącz przestrzeń nazw oraz tradycyjne domyślne zakresy zarządzane przez asystenta, aby wszyscy użytkownicy tego samego agenta dzielili się jednym zasobem przechowywania i mogli czytać nawzajem to, co teoretycznie powinno być prywatne.
To błąd przechodzi wszystkie testy na twoim własnym komputerze, ponieważ jesteś jedynym użytkownikiem. Pojawia się jako incydent dotyczący prywatności w dniu, w którym loguje się druga osoba. Traktuj mechanizm tworzenia przestrzeni nazw jako obowiązkowy przy projektach wykraczających poza prosty lokalny prototyp dla jednego użytkownika. W dokumentacji jest napisane, że stanie się to wymogiem, więc napisz ten kod teraz i pominij migrację później.
W standardowym backendzie dostępna jest również mniejsza pułapka. StateBackend zapisuje cały stan agenta na każdym kroku, co umożliwia czyste wznowienie rozmowy. Kosztem jest to, że duże pliki przechowywane w stanie są serializowane raz po raz, krok po kroku, przez co powstają duże pliki tymczasowe, które po cichu obniżają wydajność i zużycie pamięci. Rozwiązaniem jest ten sam wzorzec kompozytowy: duże lub trwałe elementy należy wysyłać do magazynu, a StateBackend zachować wyłącznie do przechowywania małych, tymczasowych informacji.
Lista kontrolna na dzisiaj
- Dowiedz się, jaki backend używa twój agent. Bez argumentu
backend=w funkcjicreate_deep_agentużywaszStateBackend, a agent zapomina o wszystkim pomiędzy rozmowami. Sprawdź, czy to jest to, czego oczekiwałeś. - Decyduj, co powinno przetrwać po zakończeniu rozmowy. Wymień osobno pliki w trakcie edycji oraz trwałą wiedzę. Wszystko, co znajduje się w kolumnie trwałej, powinno trafić do
StoreBackend. - Wykorzystaj wzorzec kompozytowy. Stwórz
CompositeBackend, który domyślnie używaStateBackend, a ścieżkę/memories/kieruje doStoreBackend. Sam elementstorejest przekazywany do funkcjicreate_deep_agent, a nie do backendu. - Daj każdemu wieloosobowemu
StoreBackendfabrykę przestrzeni nazw. Powiąż ją z identyfikatorem użytkownika i zrób to przed tym, jak druga osoba się zaloguje. - Przeprowadzaj audyt dużych plików w stanie. Jeśli agent tworzy duże pliki, skieruj je poza
StateBackend, aby nie były ponownie serializowane na każdym kroku.
Backend staje się decyzją
Traktuj backend jako opcję, a od samego początku unikniesz problemu jedzenia po południu. Możesz opisać pięć wbudowanych rozwiązań pod kątem ich czasu trwania, wysyłać różne ścieżki do różnych okresów trwania za pomocą CompositeBackend, a także wiesz, że przechowywanie danych wielu użytkowników nie może obejść się bez fabryki nazw przestrzeni. Agent zaprojektowany w ten sposób ma plan dotyczący swoich plików: tymczasowe przechowywanie dla repozytorium roboczego oraz trwała pamięć dla tego, co jest przechowywane na stałe.
Trwałe przechowywanie, do którego agent sam może zapisać dane, powinno skłonić cię do zastanowienia, a to jest dobre. Agent, który potrafi zapisywać pliki, może zapisać niewłaściwy plik, usunąć właściwy lub uruchomić polecenie, którego wcale nie chciałeś. System plików to jak dawanie mu rąk. Kolejnym krokiem jest „smycz”, czyli deklaratywne uprawnienia, które ograniczają, jakie pliki może edytować, oraz ludzka akceptacja, która zmusza go do zatrzymania się i najpierw zapytania. „Smycz” jest przydatny tylko wtedy, gdy celowo ustaliłeś już, gdzie znajdują się pliki.