Trwałość LangGraph: punkty kontrolne umożliwiające wznowienie pracy i HITL
Wyjaśnienie trwałości LangGraph: jak punkty kontrolne przechowują stan dla każdego wątku, dzięki czemu możliwe są przerwy, zatwierdzenie działań przez człowieka, ponawianie prób oraz debugowanie poprzez podróż w czasie.
Źródła: LangGraph Persistence: The Feature That Makes Everything Else Possible
To przewodnictwo odbudowuje funkcjonalną ścieżkę dla: LangGraph Persistence – cechy, która umożliwia wszystko inne. Skup się na kontraktach, sprawdzaniach oraz kodzie, który można umieścić w repozytorium bez domyślania się intencji. Aby uzyskać ogólny obraz, zdefiniuj dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Wolę małe, testowalne jednostki niż rozbudowane skrypty. Gdy dany krok zawiedzie, błąd powinien wskazywać na konkretną odpowiedzialność.
Krótka podsumowanie tego, co dotychczas wiemy
Aby szybko podsumować to, co do tej pory wiemy, należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Traktuj tę fazę jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nazwij poszczególne elementy, zdefiniuj kryteria sukcesu i odrzuć przypadkowe, częściowe ukończenie zadań. Ustawiaj punkty kontrolne po kosztownych krokach, aby ponawiane próby nie powodowały ponownego obliczania tej samej operacji modelu.
Czym właściwie jest trwałość
Aby zrozumieć, czym właściwie jest wytrwałość, należy przed modyfikacją kodu zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia. Operatorzy powinni móc ponownie uruchomić dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy rejestrować czasy wykonywania i koszty obok wyników funkcjonalnych. Wczesna widoczność zapobiega nieoczekiwanym rachunkom w środowiskach współdzielonych. Ustawiaj punkty kontrolne po kosztownych krokach, aby ponawiane próby nie powodowały ponownego naliczania opłat za ten sam wywołanie modelu.
Jak to faktycznie działa: punkty kontrolne
Aby zrozumieć, jak to faktycznie działa: przed modyfikacją kodu należy zdefiniować wskaźniki punktów kontrolnych, dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia. Operatorzy powinni móc ponownie uruchomić dany krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Konfigurację należy przechowywać poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą audytować. Ustawiaj punkty kontrolne po kosztownych krokach, aby ponawiane próby nie powodowały ponownego obliczania tej samej operacji modelu. Aby zrozumieć, jak to faktycznie działa: przed modyfikacją kodu należy zdefiniować wskaźniki punktów kontrolnych, dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia. Operatorzy powinni móc ponownie uruchomić dany krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy preferować małe, testowalne jednostki nad rozbudowanymi skryptami. Gdy dany krok zawiedzie, błąd powinien wskazywać na konkretną odpowiedzialność.
Wątkowanie: oddzielanie wielu wykonań
Dla wątkowania: aby utrzymać oddzielność różnych wykonań, należy zdefiniować dane wejściowe, właściciela kroku oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Traktuj tę fazę jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nazwij pliki artefaktów, zdefiniuj sprawdzenia sukcesu i odrzuć ciche, częściowe ukończenie zadania. Trwałość przekształca demonstracje w systemy: wątki, udział ludzi w procesie oraz debugowanie typu „podróż w czasie” zależą od niej.
Kod (jest prostszy, niż się spodziewasz)
Aby zapisać kod (jest prostszy, niż się spodziewasz), należy najpierw zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić ten krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy rejestrować czas trwania i koszty obok wyników funkcjonalnych. Wczesna widoczność zapobiega nieoczekiwanym rachunkom w środowiskach współdzielonych. Trwałość przekształca demonstracje w pełne systemy: wątki, udział ludzi w procesie oraz debugowanie typu „podróż w czasie” zależą od niej.
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph
from langgraph.checkpoint.memory import InMemorySaver
from typing import TypedDict
llm = ChatOpenAI()
class JokeState(TypedDict):
topic: str
joke: str
explanation: str
def generate_joke(state: JokeState):
response = llm.invoke(f"Generate a joke on the topic: {state['topic']}")
return {"joke": response.content}
def generate_explanation(state: JokeState):
response = llm.invoke(f"Explain this joke: {state['joke']}")
return {"explanation": response.content}
# Build graph
graph = StateGraph(JokeState)
graph.add_node("generate_joke", generate_joke)
graph.add_node("generate_explanation", generate_explanation)
graph.add_edge("__start__", "generate_joke")
graph.add_edge("generate_joke", "generate_explanation")
graph.add_edge("generate_explanation", "__end__")
# Add persistence - this is the key line
checkpointer = InMemorySaver()
workflow = graph.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "1"}}
result = workflow.invoke(
{"topic": "pizza"},
config=config
)
# Get the final state
workflow.get_state(config)
# Get ALL intermediate states
workflow.get_state_history(config)
config_2 = {"configurable": {"thread_id": "2"}}
result_2 = workflow.invoke({"topic": "pasta"}, config=config_2)
Co można stworzyć za pomocą tego narzędzia
Aby określić, co można stworzyć przy użyciu tego narzędzia, należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Konfigurację należy przechowywać poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą audytować. Trwałość danych przekształca demonstracje w pełne systemy – wątki, udział ludzi w procesie oraz debugowanie typu „podróż w czasie” zależą od niej. Aby określić, co można stworzyć przy użyciu tego narzędzia, należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy preferować małe, testowalne jednostki nad rozbudowanymi skryptami. Gdy dany krok zawiedzie, błąd powinien wskazywać na konkretną odpowiedzialność.
1. Pamięć krótkoterminowa w chatbotach
1. Pamięć krótkoterminowa w chatbotach: zanim zmieni się kod, należy określić dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia. Operatorzy powinni móc ponownie uruchomić ten krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Traktuj tę fazę jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nazwij poszczególne elementy, zdefiniuj kryteria sukcesu i odrzuć ciche, częściowe ukończenie zadania. Utrzymuj stan grafu w formie prostych, spójnych struktur. Zagłębione elementy ukrywają informację o tym, który węzeł zapisał dane do którego pola, co utrudnia kontynuację pracy po przerwach.
2. Tolerancja na awarie
Dla celów tolerancji błędów: zdefiniuj dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Zapisuj czas trwania i koszty obok wyników funkcjonalnych. Wczesna widoczność zapobiega nieoczekiwanym rachunkom w środowiskach współdzielonych. Utrzymuj stan grafu w prostej formie i z określonym typem danych. Wkładki nawiasowe ukrywają informację o tym, który węzeł zapisał dane do którego pola, co utrudnia kontynuację pracy po przerwach.
import time
def step_two(state):
print("Step 2 running...")
time.sleep(30) # simulating a slow operation
return {"step_two": "done"}
workflow.invoke(None, config=config)
3. Człowiek w łańcuchu
Dla przypadku 3. Human-in-the-Loop należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić dany krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Konfigurację należy przechowywać poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą audytować. Stan grafu powinien być prosty i typowany. Zagłębione struktury danych ukrywają informację o tym, który węzeł zapisał dane w danym polu, co utrudnia kontynuację pracy po przerwach. Dla przypadku 3. Human-in-the-Loop należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić dany krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy preferować małe, łatwe do przetestowania jednostki nad rozbudowanymi skryptami. Gdy dany krok zawiedzie, błąd powinien wskazywać na konkretną odpowiedzialność.
4. Podróż w czasie
Dla celów podróży w czasie należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie wykonać dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Traktuj tę fazę jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nazwij poszczególne elementy, zdefiniuj kryteria sukcesu i odrzuć przypadkowe, częściowe ukończenie zadań. Ustaw punkty kontrolne po kosztownych krokach, aby ponawiane próby nie powodowały ponownego naliczania opłat za ten sam wywołanie modelu.
# Go back to a specific checkpoint and change the topic
workflow.update_state(
{"configurable": {"thread_id": "1", "checkpoint_id": "<checkpoint_id>"}},
{"topic": "samosa"}
)
# Re-run from that point
workflow.invoke(None, config={"configurable": {"thread_id": "1", "checkpoint_id": "<new_checkpoint_id>"}})
Bardziej szeroki kontekst
Dla celów bardziej szerokiego kontekstu należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie wykonać dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Zapisuj czasy wykonywania i koszty obok wyników funkcjonalnych. Wczesna widoczność informacji zapobiega niespodziewanym rachunkom w środowiskach współdzielonych. Ustaw punkty kontrolne po kosztownych krokach, aby ponawiane próby nie powodowały ponownego naliczania opłat za ten sam wywołanie modelu.
Ustrukturyzowane uczenie się poza blogami
W przypadku ustrukturyzowanego uczenia się poza blogami należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Konfigurację należy przechowywać poza kodem aplikacji. Pliki środowiskowe, magazyny poufnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą audytować. Ustawiaj punkty kontrolne po kosztownych krokach, aby ponawiane próby nie powodowały ponownego obliczania tej samej operacji modelu.
Kursy
Dla kursów należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu.
Lista kontrolna operacyjna
Dla listy kontrolnej operacyjnej należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu.
Należy razem udokumentować prawidłowy przebieg procesu oraz ścieżkę naprawczą. Próby ponownych wykonania i obsługa wiadomości błędnych stanowią część produktu.
Persistentność przekształca demonstracje w pełne systemy – od wątków po debugowanie z udziałem ludzi i możliwość cofania czasu, wszystko to zależy od niej.
Dodaj test dymny dla krytycznej ścieżki w CI z użyciem fixture’ów, jeśli pozwala budżet.
Należy preferować małe, testowalne jednostki zamiast rozbudowanych skryptów. Gdy jakiś krok zawiedzie, błąd powinien wskazywać na konkretną odpowiedzialność.
Persistentne rozwiązania przekształcają dema w pełne systemy: wątki, udział ludzi w procesie oraz debugowanie typu „podróż w czasie” zależą od nich.
Zanim przejdziemy do ulepszeń całego stacku, należy zamrozić wersje, utworzyć „złoty” zapis działań dla krytycznej ścieżki oraz potwierdzić kroki odwracające zmiany. Środowiska współdzielone wymagają ograniczeń szybkości, weryfikacji uprawnień oraz jasno określonego właściciela odpowiedzialnego za rotację haseł.