Strona główna / Artykuły

Siedem cichych sposobów, w których zanieczyszczenie kontekstu psuje agenty LangGraph

Stary stan, ogromne zbiory narzędzi, duplikowane fragmenty RAG, podsumowania o niskiej jakości oraz przeciążone zestawy narzędzi: siedem awarii, które nie powodują błędów, wraz z rozwiązaniami i sposobami ich wykrywania.

Wydawca: REACTAPP.TOP9 min czytania2174 słów

Źródła: 7 Ways Context Pollution Quietly Killed My LangGraph Agents

Załóżmy agenta LangGraph, który informuje użytkownika, że jego projekt jest „w dobrym kierunku pod względem terminu dostawy w lipcu”. Kalendarz pokazuje sierpień. Logi nie wykazują żadnych wyjątków ani nieudanych prób użycia narzędzi, a nic nie jest oznaczone na czerwono. Według wszystkich wskaźników monitorowanych przez zespół agent funkcjonuje prawidłowo, a przez trzy dni potajemnie wprowadza ludzi w błąd, zanim ktoś to zauważy.

Incydenty takie jak ten mają wspólną, niewidzialną przyczynę – zanieczyszczenie kontekstu – a wszystkie siedem opisanych poniżej przypadków ma do niego związek. Każdy z nich nie spowodował żadnych błędów. Są to scenariusze fikcyjne i ilustracyjne, w których nazwy firm oraz liczby są wymyślone, ale mechanizmy leżące u ich podstaw to te, z którymi regularnie borykają się twórcy agentów.

Zanieczyszczenie kontekstu w dwie minuty

Gdy agent rozumuje, nie widzi twojej aplikacji. Widzi tylko jeden długi blok tekstu, okno kontekstowe, i nic więcej. Wyobraź to sobie jako biurko: wszystko, co znajduje się na biurku, jest dostępne do użycia, a wszystko poza nim nie istnieje dla agenta, nawet jeśli znajduje się w twojej bazie danych zaledwie kilka metrów dalej.

Każdorazowo na tym biurku pojawia się kilka elementów:

  • prośba systemu, czyli instrukcje
  • definicje narzędzi, czyli to, do czego agent może się odwołać
  • historia rozmowy, czyli wszystko, co zostało powiedziane do tej pory
  • wyniki narzędzi, czyli wszystko, co zwróciły twoje API
  • stan, który to informacje przechowywane i przekazywane przez węzły LangGraph
  • dokumenty pobrane, jeśli używasz RAG

Początkujący rzadko o tym słyszą: biurko ma ograniczoną pojemność, a uwaga nie jest na nim rozkładana równomiernie. Treść może technicznie istnieć, ale w praktyce być ignorowana. Może też być przestarzała, powtarzalna lub sprzeczna, a model nie będzie miał nic przeciwko. Rozważa takie „śmieci” bez żadnych komentarzy.

Zanieczyszczenie kontekstem to błędny, przestarzały, nadmierny lub sprzeczny materiał znajdujący się w systemie i pogarszający jego działanie, bez wywoływania żadnego błędu. W tym właśnie tkwi niebezpieczeństwo. Awaria może sprawić, że stracisz dostęp o 2 w nocy, podczas gdy zanieczyszczenie po prostu sprawia, że twój agent będzie działał błędnie przez cały czas w 20% przypadków.

Każdy z poniższych przypadków ma objaw, przyczynę podstawową, rozwiązanie oraz sposób na wykrycie go w twoim systemie – ponieważ awaria, której nie możesz wykryć, na pewno się powtórzy.

Przypadek 1: agent uwięziony w lipcu

Symptom. Weźmy przykład sztucznego narzędzia analitycznego o nazwie Meridian Energy. To narzędzie stale podawało „bieżącego” miesiąca dane, które w rzeczywistości należały do poprzedniego miesiąca. Użytkownicy obwiniali pipeline danych, co nie stanowiło problemu.

Przyczyna podstawowa. Na początku sesji programista przechowywał aktywny okres raportowania w stanie LangGraph dokładnie raz (klucz o nazwie current_period zawierający wartość 2026-07), a punkt kontrolny go zapisał. Później użytkownicy wznowili stare sesje, co jest popularną funkcją, a zapisany punkt kontrolny przywrócił dane z lipca. Narzędzie nie miało żadnych problemów – kierowało się swoim kontekstem, a w tym kontekście widniał lipiec.

Rozwiązanie. Wprowadź zasadę, która zmienia podejście do projektowania stanu: nigdy nie przechowuj tego, co można tanio obliczyć na nowo. Data bieżąca, okres bieżący oraz wszystko oznaczone jako „dzisiaj” powinno być obliczane od nowa na początku każdego uruchomienia, a nie przechowywane. Przeniesienie zmiennej current_period z stanu zapisywanego w punktach kontrolnych do małego węzła, który oblicza ją przy każdym wywołaniu, wymaga jednej linijki kodu i eliminuje całą klasę błędów.

Rozpoznawanie. W fazie rozwoju wydrukuj słownik stanu na początku każdego uruchomienia i sprawdź dla każdej klucza, czy może być przestarzały oraz czy zauważyłbyś to. Każdy klucz zawierający datę, status lub słowo „bieżący” jest podejrzany.

Przypadek 2: wydruk narzędzia zawierającego 8 000 tokenów, który przytłoczył instrukcje

Symptom. Agent zaczął ignorować zasady formatowania. Wskazówki systemu nakazywały otwieranie każdej odpowiedzi tabelą podsumowującą. Tak się działo, dopóki czasami tak nie było. Losowo, co jest najgorszym rodzajem błędu.

Powód podstawowy. Jeden z narzędzi, zwany API, umieszczał surową odpowiedź w formacie JSON w kontekście. W przypadku małych zapytań liczyło to 400 tokenów, natomiast przy dużych wartość ta przekraczała 8 000 tokenów – były to zagnieżdżone struktury JSON pełne identyfikatorów, metadanych, pól typu null oraz informacji o paginacji, których nikt nie potrzebował. Dzięki temu instrukcje traciły na znaczeniu, stając się ledwo słyszalnym głosem w hałaśliwym otoczeniu. Uwaga skupiała się na tym „murze” danych JSON, przez co zasady formatowania faktycznie znikały.

Nieprzyjemnym dodatkiem jest to, że problem występował tylko przy dużych odpowiedziach. Małe zapytania funkcjonowały poprawnie, więc nawiązanie związku pomiędzy „niewłaściwym zachowaniem agenta” a „zadaniem przez użytkownika zbyt skomplikowanego pytania” zajęło tygodnie.

Rozwiązanie. Narzędzia powinny zwracać fakty, a nie całe zbiory danych. Należy dodać wewnątrz każdego narzędzia prostą warstwę transformacji, która wybierze niezbędne pola, sformatuje je w sposób zwięzły i usunie resztę. Jedno takie narzędzie zmniejszyło liczbę tokenów z około 8 000 do około 300.

# Before: return response.json()          # 8,000 tokens of noise
# After:
def summarize_response(data: dict) -> dict:
    return {
        "project": data["name"],
        "status": data["status"],
        "owner": data["owner"]["displayName"],
        "due": data["dueDate"],
        "open_risks": len(data.get("risks", [])),
    }                                       # ~300 tokens of signal

Rozpoznawanie. Rejestruj liczbę tokenów w wynikach każdego narzędzia. Narzędzie, które może zwrócić więcej tokenów niż całe polecenie systemowe, stanowi źródło zanieczyszczeń czekające na długą odpowiedź.

Przypadek 3: korekta, która nigdy nie zastąpiła oryginału

Symptom. Użytkownik prosi o informacje dotyczące regionu Północny Wschód, a następnie natychmiast się poprawia i pyta o Północny Zachód. Agent odpowiada prawidłowo na temat Północnego Zachodu. Po czterech rozmowach, bez żadnego pytania, wraca do tematu Północnego Wschodu.

Prawdziwa przyczyna. Historia rozmów jest domyślnie dostępna tylko do dodawania nowych wpisów, więc błędne polecenie oraz korekta pozostają trwale w kontekście. Zazwyczaj model prawidłowo uwzględnia nowsze wiadomości, ale nie zawsze tak jest, a w miarę rozwoju rozmowy obie wersje stają się równie prawdopodobne. Agent nie przypominał sobie błędnie – po prostu przypomniał sobie zbyt wiele informacji.

Rozwiązanie. Istnieją dwie opcje.

  1. Rozwiąż problem, nie gromadź błędów. Gdy zauważysz korektę, nie dodawaj jej po prostu na końcu. Zapisz nową wartość w strukturyzowanym bloku faktów w stanie (region ustawiony na Northwest), który agent traktuje jako autorytatywny, dzięki czemu nie trzeba za każdym razem ponownie dyskutować o chaotycznym transkrypcie.
  2. Zmień historię rozmowy. LangGraph umożliwia skracanie lub przepisywanie wiadomości przed ich ponownym włączeniem do kontekstu, więc przestarzałe instrukcje można po prostu usunąć.

Ogólnie rzecz biorąc, historia rozmowy to zapis działań, a nie baza wiedzy. Nie zmuszaj agenta do wydobywania prawdy z transkrypcji za każdym razem.

Detekcja. Dodaj do swoich testów przypadek samokorekty: użytkownik mówi X, potem poprawia to na Y, a następnie zadaje pięć dodatkowych pytań. Jeśli agent kiedykolwiek wróci do X, masz ten błąd.

Przypadek 4: RAG zwróciło jedną odpowiedź pięć razy i ukryło prawdziwą

Symptom. Ludzie zadawali pytania, na które dokumentacja dawała jasną odpowiedź, a agent dostarczał niejasne, częściowe odpowiedzi. Logi pobierania pokazywały sześć pobraanych fragmentów, więc wydawało się, że mechanizm wyszukiwania dobrze funkcjonuje.

Prawdziwa przyczyna. W korpusie znajdowały się niemal identyczne treści: jeden akapit z polityki został wklejony do pięciu różnych dokumentów, co często robią rzeczywiste firmy. Szukanie podobieństw działało perfekcyjnie i zwracało pięć niemal identycznych fragmentów, które wyparły ten jedyny inny fragment zawierający prawdziwą odpowiedź. Pięć miejsc przy stole przypadło temu samemu „gościowi”.

Rozwiązanie. Usuń duplikaty w momencie pobierania danych. Nawet prosta metoda dobrze działa: pobierz 20 kandydatów, odrzuć te zbyt podobne do już wybranych i zachowaj najbardziej zróżnicowany zestaw. Jeśli twój magazyn wektorów obsługuje maksymalną marginalną istotność, to ta sama koncepcja jest już w nim wbudowana. Jakość odpowiedzi na pytania polityczne poprawiła się natychmiast bez konieczności zmiany modelu czy promptu.

Detekcja. Zapisuj pobrane fragmenty i przeglądaj je co tydzień. Jeśli jakaś zdanie regularnie pojawia się dwa razy w jednej grupie, płacisz za redundancję kosztem istotności.

Przypadek 5: streszczenie, które pominęło jedną ważną zasadę

Symptom. Aby utrzymać niskie koszty długich sesji, zespół wprowadził funkcję stopniowego podsumowywania: co N ruchów starsze wiadomości są kompresowane. To standardowe rozwiązanie powszechnie zalecane. Następnie jeden z pracowników zaczął umieszczać wstępne zapiski w oficjalnych raportach, mimo że użytkownik jedenastu ruchów wcześniej poprosił o „wykluczenie wersji roboczych”.

Prawdziwa przyczyna. Narzędzie do podsumowywania zachowywało się jak taki narzędzie. Zachowywało istotne elementy (użytkownik chciał raporty regionalne i mówił o układzie) oraz odrzucało to, co uznawało za mniej ważne. Tymczasem prośba o „wykluczenie wersji roboczych” nie była sprawą drugorzędną – była to restrykcja, a restrykcje nie podlegają parafrazowaniu: albo pozostają nienaruszone, albo znikają. Ze wszystkich siedmiu błędów ten wymaga największej troski, ponieważ funkcja podsumowywania wydaje się bezsprzecznie korzystna podczas demonstracji.

Rozwiązanie. Przed jakąkolwiek kompresją należy podzielić pamięć na dwa oddzielne obszary.

  • Opowieść może być podsumowywana w sposób agresywny.
  • Ograniczenia, decyzje i preferencje są umieszczane na uporządkowanej liście, która nigdy nie jest podsumowywana. Pozycje są dodawane lub wyraźnie usuwane, a cała lista jest włączana do kontekstu przy każdej turze.

Polecenie dotyczące podsumowania może to jasno określić: przed podsumowaniem należy dosłownie skopiować każdą instrukcję, ograniczenie i decyzję do listy ograniczeń.

Rozpoznawanie. W fazie rozwoju należy porównywać listę ograniczeń przed i po każdym procesie podsumowywania. Ograniczenie, które istnieje tylko w ramach akapitu podsumowawczego, oznacza, że narzędzie kompresujące niszczy twoje zasady.

Przypadek 6: ślad wywołań, który sprawił, że agent stracił zaufanie do działającego narzędzia

Symptom. Jeden narzędzie raz krótko wykazało problem z czasem reakcji, ale od razu zadziałało przy następnej próbie połączenia. Mimo to przez resztę sesji agent ciągle informował użytkowników, że nie może uzyskać dostępu do systemu raportowania, chociaż faktycznie udawało mu się z nim połączyć. Naturalnie użytkownicy wierzyli tym stwierdzeniom bardziej niż danym.

Powód podstawowy. Pełny ślad błędu, TimeoutError po upływie 30 sekund, został dodany do historii i nigdy nie zniknął. Dla modelu stwierdzenie „system raportowania jest niewiarygodny” stało się ustalonym faktem, a każda późniejsza odpowiedź była przez to wpływana. Jeden chwilowy błąd przekształcił się w trwałe zanieczyszczenie danych.

Rozwiązanie. Traktuj błędy jako zdarzenia, a nie jako fakty. Po udanym ponownym próbie zastąp informację o błędzie w historii jednozdaniową notatką, np. "(tymczasowy błąd, rozwiązany przy ponownej próbie)". Jeśli narzędzie naprawdę przestanie działać, podaj agentowi czysty, uporządkowany status, a nie ślad wywołania. Ślady wywołania powinny znajdować się w dziennikach, a nie w kontekście.

Detekcja. Szukaj w aktualnych kontekstach ciągów znaków Error, Traceback i Exception. Każde takie zdarzenie to albo trwający incydent, albo pozostałość, która zakłóca późniejsze operacje, i obie te sytuacje należy usunąć.

Przypadek 7: dwadzieścia pięć narzędzi w jednym oknie

Objaw. W ciągu kilku miesięcy narzędzie stało się wolniejsze i droższe, co było przewidywalne ze względu na dodawanie coraz więcej funkcji. Zaczęło również wybierać niewłaściwe narzędzie do prostych zapytań, na przykład zwracać eksport danych zgodności, gdy ktoś chciał tylko podsumowanie projektu. Nic nie było zepsute; po prostu wszystko stało się bardziej skomplikowane.

Przyczyna podstawowa. Zbiór narzędzi liczył już 25 elementów, z których każdy miał długą opis oraz schemat JSON, co łącznie zajmowało około 12 000 tokenów, zanim użytkownik wpisał choćby jedno słowo. Przy każdej próbie model rozważał 25 opcji, z których wiele miało pokrywające się opisy, takie jak „pobierz dane projektu”, „uzyskaj szczegóły projektu” i „zdobądź informacje o projekcie”. Wybór narzędzia to zadanie wymagające rozumowania, a zespół zamienił je w test z wielokrotnym wyborem, w którym było 25 podobnych odpowiedzi.

Rozwiązanie. Składa się z dwóch części.

  1. Agresywne przycinanie. Łącz narzędzia, które się pokrywają – w tym przypadku zmniejszając liczbę z 25 do 11 – i przepisuj opis każdego narzędzia wokół jednego pytania: kiedy model powinien wybrać to narzędzie zamiast sąsiadujących?
  2. Ładowanie narzędzi w zależności od kontekstu. Nie każdy węzeł potrzebuje wszystkich narzędzi. W LangGraph łatwo jest przypisać do każdego węzła tylko odpowiedni podzbiór narzędzi, dzięki czemu węzeł generujący raport może mieć do dyspozycji cztery narzędzia, a nie 25.

Koszt tokenów na rundę spadł o około jedną trzecią, a przypadki wyboru niewłaściwego narzędzia niemal zniknęły.

Detekcja. Policz tylko tokeny w definicjach narzędzi. Jeśli ich liczba przekracza długość promptu systemowego oraz średniej rozmowy, twój agent zdaje egzamin z otwartą książką, w którym większość stron jest błędna.

Co łączy te siedem elementów

Po ponownym przeczytaniu siedmiu punktów wyłania się jeden wzorzec. Żaden komponent nie był uszkodzony i niczego nie brakowało. Zanieczyszczenie polegało konsekwentnie na nadmiarze, przestarzałym lub powtarzającym się treściach.

Dlatego zwykłe debugowanie tutaj nie działa. Inżynierowie są szkoleni do poszukiwania braków: nieprzeprowadzonej weryfikacji wartości null, nierozwiązanej wyjątku. Zanieczyszczenie kontekstem wymaga odwrotnej umiejętności – nauki szukania tego, czego nie powinno tam być.

Jeśli masz zachować jedną nawyk, to niech będzie to czytanie surowego kontekstu twojego agenta. Pomiń logi i panel śledzenia i przeczytaj rzeczywisty tekst, który model otrzymuje podczas prawdziwej pracy w środowisku produkcyjnym. To najcenniejsza godzina dostępna dla każdego, kto buduje agenty, a prawie nikt jej nie wykorzystuje.

List kontrolny na sześćdziesiąt sekund przed wdrożeniem

  1. Czy jakaś wartość stanu to data, status lub coś o nazwie „obecny”? (Przypadek 1)
  • Czy narzędzie może zwrócić więcej tokenów niż sam prompt systemu? (Przypadek 2)
  • Gdy użytkownik poprawia swój własny tekst, czy stara wersja może kiedykolwiek zwyciężyć? (Przypadek 3)
  • Czy pobrane fragmenty kiedykolwiek się powtarzają? (Przypadek 4)
  • Czy ograniczenie może przetrwać dosłownie dzięki twojemu narzędziu do streszczania? (Przypadek 5)
  • Czy ślady błędów pozostają w historii po naprawie? (Przypadek 6)
  • Ile tokenów wykorzystują definicje narzędzi przed tym, jak użytkownik coś wpisze? (Przypadek 7)
  • Siedem pytań. Każda odpowiedź typu „prawdopodobnie, ale nie jestem pewien” to incydent czekający na datę.

    Literatura pokrewna