Strona główna / Artykuły

Wyjaśnienie pamięci agenta: okno kontekstowe, RAG i stan trwały

Pamięć agenta składa się z trzech różnych elementów. Przyjrzyj się, w jaki sposób różnią się okno kontekstowe, mechanizm wyszukiwania informacji oraz stan trwały, jaki jest koszt każdego z nich i jak najpierw wybrać odpowiedni element.

Wydawca: REACTAPP.TOP10 min czytania2195 słów

Źródła: Agent Memory Explained

Niewiele słów powoduje tyle zamieszania w rozwoju agentów co „pamięć”. Gdy ktoś mówi, że jego agent ją ma, może to oznaczać, iż rozmowa jest długa, że podłączona jest baza danych wektorowych lub że fakty są zapisywane w pliku, który przetrwa restart. To trzy odrębne systemy o różnych kosztach, sposobach awarii i powodach istnienia.

Jedno zdanie rozwiązuje większość zamieszania. Model nie ma pamięci, ma tylko dane wejściowe, a to, co ludzie nazywają pamięcią, to po prostu zasada decydująca o tym, co trafi do tych danych wejściowych.

Model jest bezstanowy. Każda wywołanie API zaczyna się od zera, bez żadnej pamięci o twojej ostatniej wiadomości, ostatniej sesji czy nazwie, którą mu nadałeś. To, co wie podczas wywołania, to tylko treść żądania, i nic więcej.

Zatem istotne pytanie nie brzmi, czy agent ma pamięć. Chodzi o to, co zasługuje na miejsce w tym żądaniu i skąd pochodzi. Istnieją trzy odpowiedzi, a zadaniem jest je rozróżnienie.

Model bez stanu, aplikacja z stanem

Początkujący zakładają, że model prowadzi rozmowę. Tak nie jest. To aplikacja prowadzi rozmowę i ją odtwarza. Gdy wysyłasz czwarte wiadomość w czacie, twój kod przesyła wszystkie wiadomości – 1, 2, 3 i 4 – za każdym razem. Wrażenie ciągłości pochodzi z ponownego wysyłania historii, a model otrzymuje po prostu dłuższy wpis niż wcześniej.

Biorąc to pod uwagę, te trzy warstwy przestają być abstrakcyjne:

  • Okno kontekstowe to to, co znajduje się obecnie w żądaniu.
  • Retrieval (RAG) to sposób na znalezienie materiałów do umieszczenia w żądaniu.
  • Trwały stan to sposób, w jaki fakty przetrwają po zakończeniu żądania, aby można je było później ponownie dodać.
  • Kontener, mechanizm wyszukiwania i magazyn – to trzy różne elementy. Jeśli je ze sobą pomieszasz, skończysz tak, że baza wektorowa zostanie wykorzystana do rozwiązania problemu, który można byłoby załatwić plikiem preferences.json.

    Szczegół 1: okno, czyli biurko modelu

    Uważaj okno za wszystko, co model może obecnie zobaczyć. Komenda systemowa, historia działań, definicje narzędzi, wyniki ich działania, pobraane dokumenty oraz tekst odpowiedzi – wszystko to korzysta z tego samego budżetu tokenów.

    Pomyśl o biurku. Wszystko, co model musi rozważyć, musi znajdować się fizycznie na nim; szafka w innym pokoju nie liczy się w tym kontekście.

    Gdzie się wyróżnia: natychmiastowość. Wszystko, co znajduje się w oknie, jest w pełni widoczne bez konieczności dodatkowych poszukiwań, a model może analizować to wszystko jednocześnie. W przypadku zadania z ograniczoną, znaną ilością materiału wypełnienie okna to najprostsza i często również najodpowiedniejsza metoda.

    Koszty:

    1. Pieniądze i opóźnienia zależą od tego, co odsyłasz. Odsłanie 51. kroku długiej rozmowy oznacza odsłanie 50 kroków historii. Długi kontekst nie jest darmowy tylko dlatego, że mieści się w ramach dostępnej przestrzeni.
    2. Uwaga jest nierównomierna. Modele najskuteczniej wykorzystują początek i koniec długiego wpisu, a środek w mniejszym stopniu – to powszechnie obserwowany efekt „zagubienia w środku”. Informacja umieszczona w 60% długości bardzo długiego promptu jest obecna, ale może nigdy nie zostać skutecznie wykorzystana. Tylko to, co mieści się w ramach, nie oznacza, że zostanie przeczytane.
  • To się kończy. Gdy sesja się zakończy, okno znika. Okno kontekstowe nigdy nic nie przechowuje. To najczęstszy błąd początkujących: długa rozmowa wydaje się pamięcią, ale to tylko bufor, a nie magazyn.
  • Cała ta mechanika mieści się w kilku linijkach:

    messages = []
    
    def turn(user_text):
        messages.append(
            {"role": "user",
             "content": user_text}
        )
        # every call resends
        # the ENTIRE history
        reply = client.messages.create(
            model="claude-opus-5",
            max_tokens=1024,
            messages=messages,
        )
        messages.append(
            {"role": "assistant",
             "content": reply.content[0].text}
        )
        return reply
    
    # messages grows forever.
    # so does your bill.
    

    Ta lista jest tutaj „pamięcią”. Istnieje tylko wewnątrz twojego procesu, więc gdy proces się zakończy, lista również znika.

    Sama okno jest wystarczająca, gdy zadanie jest krótkie, potrzebne materiały są niewielkie i znane z góry, a nic nie musi przetrwać sesji. Podsumowujący narzędzie, recenzent jednego pliku lub jednorazowe wydobywanie informacji nie wymagają niczego więcej. Nie dodawaj tam zbędnych elementów.

    Szczegół 2: wyszukiwanie, biblioteka referencyjna

    Ranо czy późno materiały przestają mieścić się w ograniczeniach lub przestają być opłacalne do posiadania. Może to być 10 000 zgłoszeń obsługi klienta, wewnętrzna wiki lub baza kodu. Nie można umieszczać tego wszystkiego w każdej prośbie, i nie powinno się tego robić.

    Generowanie wzbogacone o wyszukiwanie dzieli pracę: najpierw przygotowuje się indeks, a następnie wyszukuje informacje w momencie pojawienia się pytania.

    Najpierw dzielisz dokumenty na fragmenty, przekształcasz każdy z nich w wektor embeddingowy (wektor liczbowy odzwierciedlający znaczenie) i przechowujesz go. Gdy pojawia się pytanie, również je przekształcasz w wektor embeddingowy, znajdujesz fragmenty, których wektory są najbliższe, i wklejasz je do okna kontekstowego obok pytania.

    # 1. AHEAD OF TIME (once, offline)
    for chunk in split(documents):
        store.add(
            text=chunk,
            vector=embed(chunk),
        )
    
    # 2. AT QUESTION TIME (every request)
    def answer(question):
        hits = store.search(
            embed(question), top_k=5
        )
        context = "\n\n".join(h.text for h in hits)
        return client.messages.create(
            model="claude-opus-5",
            max_tokens=1024,
            # retrieved chunks land in the same request
            messages=[{
                "role": "user",
                "content": f"{context}\n\nQ: {question}",
            }],
        )
    

    Zwróć uwagę na to, co tak naprawdę się stało. RAG nie dostarczył modelowi pamięci – dał jedynie strategię selekcji treści z okna. Pobrane fragmenty trafiają do tej samej prośby co wszystko inne, więc RAG stanowi jedynie kanał prowadzący do warstwy 1 i nie ma dla niego żadnej alternatywy.

    Mocne strony: skalowalność i świeżość. Miliony dokumentów mogą się zmieniać niezależnie od twojego kodu, podczas gdy model widzi tylko te nieliczne, które są istotne dla danego pytania.

    Koszty:

    1. Wynik jest ustalany wcześnie. Rozmiar fragmentów, model embeddingu oraz projekt indeksu są ustalone już podczas tworzenia indeksu. Fakt rozproszony między dwoma fragmentami lub sformułowany inaczej niż w pytaniu użytkownika po prostu nie zostanie znaleziony, a model odpowiada wtedy z pewnością na podstawie tego, co otrzymał. Dla obserwatora słaba wydajność wyszukiwania i halucynacje wyglądają identycznie.
  • Jest to głównie konto tylko do odczytu. Klasyczny RAG to karta biblioteczna, a nie notatnik. Agent wyszukuje informacje, ale nic nie zapisuje, a to, czego nauczy się podczas sesji, nie trafia do indeksu, chyba że sam stworzysz tę ścieżkę.
  • Istnieje dodatkowy krok. Wbudowanie zapytania i wyszukiwanie w indeksie powodują opóźnienie przy każdej prośbie.
  • Metoda pobierania danych nadaje się wtedy, gdy baza wiedzy jest duża, wspólna dla wszystkich użytkowników i rzadko się zmienia – na przykład dokumentacja, zasady, katalogi produktów i kod. Charakterystyczną cechą jest to, że jeden korpus służy wszystkim użytkownikom. Jeśli to, co chcesz zapamiętać, należy do konkretnego użytkownika i zmienia się w trakcie rozmowy, metoda pobierania danych nie jest odpowiednia, a potrzebny będzie poziom 3.

    Poziom 3: trwały stan, notatnik

    Stan trwały to to, co przetrwa po zakończeniu procesu. Nie chodzi o to, co agent wie o świecie, ale o to, czego się nauczył i co powinno przetrwać poza tą sesją: preferencje użytkownika, punkty kontrolne zadań, podjęte już decyzje, rzeczy wypróbowane i porzucone, streszczenia wcześniejszych wykonywań. Są to małe, konkretne, cenne fakty, które sam agent zapisał.

    Wzorzec składa się z cyklu odczytu, modyfikacji i zapisu, który odbywa się w ramach sesji:

    # session start: load
    with open("state.json") as f:
        state = json.load(f)
    
    system = (
        "Known about this user:\n"
        f"{json.dumps(state)}"
    )
    
    messages = []
    
    def turn(user_text):
        messages.append(
            {"role": "user",
             "content": user_text}
        )
        reply = client.messages.create(
            model="claude-opus-5",
            max_tokens=1024,
            system=system,   # state in
            messages=messages,
        )
        return reply
    
    # ... along the way ...
    state["prefers"] = "metric units"
    state["last_checkpoint"] = "step_4_done"
    
    # session end: save
    with open("state.json", "w") as f:
        json.dump(state, f)
    

    Koncepcyjnie nie potrzeba już niczego więcej. Plik JSON stanowi rzeczywistą implementację; podobnie jak wiersz w bazie danych, magazyn typu klucz-wartość lub folder z notatkami w formacie Markdown. Nośnik jest częścią nudną, a interesujące jest to, kto decyduje, co zapisać, a co ponownie załadować.

    Dobre podjęcie tej decyzji jest trudne i właśnie tutaj różnią się frameworki agentów. Najbardziej bezpośrednia obecna opcja powierza tę zadanie modelowi: dzięki narzędziu do przechowywania informacji od Anthropic, Claude otrzymuje niewielki zestaw poleceń do plików (przeglądanie, tworzenie, zastępowanie, wstawianie, usuwanie i przemienianie nazwy), które działają w izolowanym katalogu /memories, dzięki czemu agent wybiera to, co warto zapisać, i odczytuje te notatki na początku następnej sesji. Twoja własna aplikacja wykonuje operacje na dowolnym nośniku, który wybierzesz. Narzędzie działa po stronie klienta, co sprawia, że pliki pozostają pod twoją kontrolą.

    Mocne strony: ciągłość, personalizacja i długoterminowa praca. Agent, który podejmuje ponownie zadanie trwające kilka dni od miejsca przerwy, potrzebuje tego, podobnie jak ten, który nie powinien pytać o twoją strefę czasową dwukrotnie.

    Koszty:

    1. Ktoś musi zarządzać tymi danymi. Zapisz wszystko, a otrzymasz nieprzeszukiwalny zapis. Jeśli napiszesz za mało, ciągłość zostanie przerwana. Dobry, trwały stan wymaga dużego wysiłku przy jego tworzeniu.
    2. Przestarzałe dane są gorsze niż żadne. Preferencje zapisane osiem miesięcy temu i nigdy nie sprawdzane są stosowane z pełnym przekonaniem. Błędy przy pobieraniu danych powodują niejasność, natomiast przestarzały stan prowadzi do błędnego zachowania.
    3. Ładowanie nie jest bezkosztowe. Cokolwiek załadujesz, nadal zajmuje miejsce w oknie, więc warstwa 3 wpływa również na warstwę 1.

    Należy korzystać ze stanu trwałego, jeśli ten sam użytkownik lub zadanie wielokrotnie uruchamia agenta, a to, czego nauczył się wykonywanie n, powinno wpłynąć na wykonywanie n+1.

    Trzy podejścia obok siebie

    Koszty, zagubienie danych w trakcie przetwarzania
    Okno kontekstowe Pobieranie danych Stan trwały
    Rola Biurko Biblioteka Zeszyt notatek
    Zawiera Wszystko z tej prośby Duże, wspólne korpusy danych Małe fakty zapisane przez agenta
    Tworzone przez Plik aplikacji, przy każdym wywołaniu Zadanie indeksowania Agenta lub twój kod
    Przetrwa restart Nie Tak Tak
    Główne ryzykoPominięte fragmenty danych Przestarzałe fakty

    Najważniejszy wiersz to ten dotyczący restartów. To granica pomiędzy długą rozmową a agentem z pamięcią.

    Składanie odpowiedzi

    Prawdziwy agent wykorzystuje wszystkie trzy elementy, a przydatne jest obserwowanie, jak działają one w ramach jednej prośby. Od agenta obsługi klienta pytano, czy zamówienie kwalifikuje się do wydłużonego terminu zwrotu.

    1. Najpierw ładowany jest trwały stan: ten klient ma plan biznesowy i woli krótkie odpowiedzi. Dwa fakty, około czterdziestu tokenów.
    2. Pobieranie informacji odbywa się na podstawie pytania i przyciąga sekcję z polityką zwrotów dla planów biznesowych wraz z dwoma najnowszymi wyjątkami. Trzy fragmenty, około tysiąca dwustu tokenów.
    3. Okno kontekstowe obejmuje oba te elementy, a także czat na żywo i definicje narzędzi, dzięki czemu model może analizować całą sytuację razem.
  • Po udzieleniu odpowiedzi agent zapisuje jedną linię do trwałego stanu: w połowie sierpnia zadał pytanie dotyczące polityki zwrotów, więc w następnej sesji nie jest on traktowany jako osoba dzwoniąca po raz pierwszy.
  • Jedna prośba, trzy warstwy. Okno to miejsce, gdzie wszystko trafia, natomiast pobieranie informacji i trwały stan to dwa oddzielne sposoby dostępu do nich.

    To także powód, dla którego agentom działającym przez długi czas zazwyczaj zaleca się łączenie pamięci z edycją kontekstu: stare wyniki narzędzi są usuwane z okna, aby utrzymać je lekkie, a fakty, które warto zachować, zostały już zapisane, dzięki czemu okno pozostaje małe bez utraty wiedzy.

    Który naprawdę potrzebujesz?

    Początkujący niemal zawsze najpierw biorą bazę danych wektorową. To najbardziej widoczna część całego zestawu narzędzi, ale rzadko jest to rozwiązanie odpowiednie. Lepiej wspinać się po drabinie rozwoju.

    Zacznij od okna. Czy wszystko istotne mieści się w jednej prośbie po cenie, która Ci odpowiada? W takim razie przestań. Żaden indeks, żadna baza danych.

    Zintegruj stan trwały, gdy agent zostanie uruchomiony po raz drugi. Gdy sesja druga musi znać to, czego nauczyła się sesja pierwsza, potrzebny jest warstwa 3, a plik JSON stanowi prawdziwą odpowiedź. Zrób to przed wykonywaniem zapytań – jest to tańsze, prostsze i właśnie tego większość ludzi oczekuje, mówiąc, że chcą, aby agent ich pamiętał.

    Zastosuj mechanizm wyszukiwania, gdy korpus jest większy niż okno. Oznacza to dużą liczbę dokumentów, udostępnianych między użytkownikami i edytowanych oddzielnie od Twojego kodu. Jest to najcięższa warstwa do stworzenia i utrzymania w działaniu, więc wdrażaj ją tylko wtedy, gdy jest to konieczne.

    Jedna pułapka, której należy uniknąć: tworzenie pipeline RAG dla czegoś, co w rzeczywistości było plikiem z preferencjami, a potem zastanawianie się, dlaczego agent nadal zapomina imię kogoś. Proces wyszukiwania przeprowadza się w korpusie, który nigdy nie będzie zawierał informacji o tym, że dana osoba woli jednostki metryczne.

    Wersja skrócona

    • Okno kontekstowe: biurko. Natychmiast widoczne, drogie za każdy token i usuwane w momencie zakończenia sesji.
    • Wyszukiwanie: biblioteka. Ogromna, dostępna do wyszukiwania, odświeżana według ustalonego harmonogramu i stanowiąca jedynie materiał, który agent czyta.
    • Stan trwały: notatnik. Mały, wypełniany przez agenta i jedyny element, który przenosi naukę z dnia na dzień.

    Sam model nic nie przechowuje. Tzw. pamięć jest wyborem inżynieryjnym podejmowanym przy każdej prośbie: co dostanie miejsce w tym oknie i skąd powinno pochodzić?

    Dla dalszej lektury artykuły inżynieryjne Anthropic na temat skutecznego projektowania kontekstu oraz tworzenia efektywnych agentów, wraz z ich dokumentacją dotyczącą narzędzia do zarządzania pamięcią i edycji kontekstu, omawiają ten temat bardziej szczegółowo.