Agenci LangGraph w końcu otrzymali niezawodny sposób na zachowanie stanu po tygodniach cichej utraty danych. Po trzech nieudanych próbach z checkpointingiem — SQLite, surowym Object Storage i wadliwej wersji każdego z nich — autor zdecydował się na wzorzec aktualizacji atomowej, który zapobiega restartowaniu agentów od zera przy każdym przychodzącym żądaniu.
Dlaczego checkpointing jest ważny dla LangGraph
LangGraph pozwala programistom łączyć wywołania LLM w reużywalne „agentów”, którzy potrafią pamiętać, co wydarzyło się wcześniej w rozmowie. Agenci ci dzielą żądanie użytkownika na podzadania, przechowują wyniki pośrednie i wznawiają pracę w miejscu, w którym przerwali, przy kolejnym wywołaniu. Jeśli zapisany stan zniknie, agent musi przeliczyć wszystko od nowa, co marnuje moc obliczeniową, zwiększa opóźnienia i pogarsza doświadczenie użytkownika. W przypadku bota produkcyjnego obsługującego wiadomości na Telegramie, utrata danych wymazała tygodnie historii rozmów.
Pierwsza poprawka: SQLite saver
Wbudowany SqliteSaver działa dobrze, gdy agent jest uruchamiany przez pojedynczą instancję. Zapisuje on każdy checkpoint jako obiekt JSON w lokalnym pliku SQLite. Problemy zaczęły się, gdy programista dodał nowe pole do typu AgentState i ponownie wdrożył aplikację. Istniejące checkpointy, utworzone przed zmianą schematu, nie posiadały nowego pola. Ponieważ SqliteSaver nigdy nie przeprowadza migracji, LangGraph wczytywał niekompletny JSON, odrzucał brakujące dane, a agent restartował się od początku.
Kluczowa kwestia: Przechowywanie w SQLite to narzędzie demonstracyjne, a nie rozwiązanie produkcyjne, gdy wymagana jest ewolucja schematu.
Druga poprawka: Object storage
Aby uzyskać kontrolę nad formatem serializacji, autor napisał własny saver, który przesyłał checkpoint JSON do Oracle Cloud Object Storage. Ten ruch zapewnił elastyczność w ręcznym wersjonowaniu schematu, ale wprowadził nowy tryb awarii. Gdy dwa żądania uderzały w ten sam wątek rozmowy jednocześnie, oba próbowały nadpisać ten sam obiekt. Usługi Object Storage są zoptymalizowane pod wzorce „zapisz raz, czytaj wiele”; nie zapewniają one semantyki atomowego nadpisywania. Wyścig (race condition) powodował powstawanie błędnych lub uciętych plików JSON, a agent ponownie tracił kontekst.
Kluczowa kwestia: Zwykłe nadpisywanie w Object Storage nie jest bezpieczne, gdy wiele procesów może jednocześnie operować na tym samym kluczu.
Trzecia poprawka: Aktualizacje atomowe z wersjonowaniem
Ostateczny, stabilny projekt łączy dwa pomysły: jawne numery wersji oraz warunkowe zapisy oparte na ETag obiektu (identyfikatora sumy kontrolnej usługi składowania).
- Odczytaj aktualny checkpoint i przechwyć jego ETag.
- Zwiększ pole wersji wewnątrz opakowania (envelope) checkpointu.
- Zapisz zaktualizowany checkpoint, używając żądania warunkowego, które zakończy się sukcesem tylko wtedy, gdy ETag będzie zgodny z tym odczytanym wcześniej.
- Powtórz całą pętlę odczyt-inkrementacja-zapis, jeśli zapis warunkowy się nie powiedzie, ponieważ inny proces zmienił obiekt.
Ponieważ zapis powodzi się tylko wtedy, gdy żaden inny proces nie zmodyfikował pliku, tylko jeden proces może zatwierdzić nowy stan w danym czasie. Pole wersji ułatwia również wykrywanie nieaktualnych checkpointów i ich migrację w przód przy zmianie schematu.
Wzorzec ten działa z usługami Object Storage, które obsługują warunkowe zapisy oparte na ETag.
Lekcje dla inżynierów AI
- Używaj SQLite tylko do prototypów. Agenci produkcyjni potrzebują magazynu, który poradzi sobie ze zmianami schematu i współbieżnym zapisem.
- Samodzielnie planuj migracje schematu. Słowniki typowane (TypedDicts) opisują kształt danych do analizy statycznej, ale nie wymuszają struktury w czasie wykonywania (runtime).
- Traktuj stan jako zasób współdzielony. Błędy współbieżności objawiają się jako cicha utrata danych; trudniej je debugować niż jawne wyjątki.
- Korzystaj z prymitywów chmurowych. Warunkowe zapisy oparte na ETag zapewniają tanie blokowanie optymistyczne bez konieczności używania oddzielnej usługi blokowania (lock service).
- Loguj każdy krok. Ciche awarie — takie jak brakujące pole, które LangGraph ignoruje — są najtrudniejsze do wykrycia.
Co dalej z checkpointingiem w LangGraph?
Dla zespołów, które już napotkały te same przeszkody, przepis na aktualizację atomową oferuje szybką i niskokosztową poprawkę. Pokazuje on, że niezawodny pipeline produkcyjny nie wymaga ciężkiego magazynu stanu — wystarczy staranne zarządzanie współbieżnością i wersjonowaniem.
Wniosek: Proste, wersjonowane opakowanie plus zapisy warunkowe zmieniają niestabilny system w niezawodny, pozwalając inżynierom AI skupić się na logice agentów, a nie na niekończącym się debugowaniu utraty danych.
