Punkty kontrolne i wznawianie zadań
Zapisuj stan agenta po każdym ukończonym kroku, aby po awarii można było wznowić długotrwałe zadanie od ostatniego pomyślnego punktu kontrolnego zamiast rozpoczynać je od początku.
Punkty kontrolne i wznawianie zadań to bezpłatna lekcja AI Engineering Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Engineering Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Problem z agentami działającymi przez długi czas
Agent wykonujący 50-etapowe zadanie badawcze może działać przez 30 minut. Jeśli zakończy działanie na 47. kroku z powodu przekroczenia limitu czasu API lub ponownego uruchomienia serwera, rozpoczęcie od początku zmarnuje całą wykonaną pracę i spowoduje zużycie dodatkowych tokenów. Tworzenie punktów kontrolnych zapisuje stan agenta po każdym ukończonym kroku, dzięki czemu zadanie można wznowić od ostatniego pomyślnie wykonanego miejsca, a nie od początku. Jest to niezbędne w przypadku każdego zadania agenta trwającego dłużej niż kilka minut.
Jaki stan agenta należy zapisywać
Stan agenta obejmuje: definicję zadania, ukończone kroki wraz z wywołaniami narzędzi i obserwacjami, indeks bieżącego kroku, wszelkie zgromadzone wyniki (zapisane pliki, zebrane dane) oraz metadane, takie jak czas rozpoczęcia i całkowite zużycie tokenów. Zapisuj wszystkie te informacje po ukończeniu każdego kroku. Stan musi być możliwy do serializacji — ze względu na przenośność preferuj JSON zamiast obiektów Pythona.
from dataclasses import dataclass, field
from typing import List, Any, Optional
@dataclass
class AgentStep:
step_index: int
thought: str
tool_name: str
tool_args: dict
observation: str
tokens_used: int
completed_at: str
@dataclass
class AgentCheckpoint:
task_id: str
task_description: str
status: str # 'running', 'completed', 'failed'
current_step: int
completed_steps: List[AgentStep] = field(default_factory=list)
accumulated_results: dict = field(default_factory=dict)
total_tokens: int = 0
final_answer: Optional[str] = NoneZapisywanie punktów kontrolnych po każdym kroku
Po każdym pomyślnym kroku zserializuj punkt kontrolny i zapisz go w trwałym magazynie. Użyj bazy danych lub magazynu obiektów (S3, Redis) zamiast lokalnego dysku, aby punkt kontrolny przetrwał ponowne uruchomienie serwera. Uwzględnij task_id w kluczu, aby można było pobrać punkt kontrolny konkretnego zadania. Zapisuj również wpis w dzienniku kroków dla każdego kroku, aby zachować ścieżkę audytu nawet w przypadku uszkodzenia pliku punktu kontrolnego.
import json
import redis
from dataclasses import asdict
redis_client = redis.Redis()
def save_checkpoint(checkpoint: AgentCheckpoint):
key = f'agent:checkpoint:{checkpoint.task_id}'
data = json.dumps(asdict(checkpoint), default=str)
redis_client.set(key, data, ex=86400) # 24h TTL
# Also append to step log
log_key = f'agent:log:{checkpoint.task_id}'
if checkpoint.completed_steps:
last = checkpoint.completed_steps[-1]
redis_client.rpush(log_key, json.dumps(asdict(last), default=str))
def load_checkpoint(task_id: str) -> AgentCheckpoint | None:
key = f'agent:checkpoint:{task_id}'
data = redis_client.get(key)
if data:
return AgentCheckpoint(**json.loads(data))
return NoneWznawianie z punktu kontrolnego
Podczas wznawiania zadania wczytaj punkt kontrolny i odtwórz historię wiadomości agenta na podstawie ukończonych kroków. Rozpocznij pętlę wykonywania od indeksu następnego nieukończonego kroku. Model otrzymuje wcześniejsze przemyślenia i obserwacje w historii wiadomości, dzięki czemu ma pełny kontekst wykonanych działań i może kontynuować bez powtarzania pracy.
async def resume_or_start(task_id: str, task_description: str) -> str:
checkpoint = load_checkpoint(task_id)
if checkpoint and checkpoint.status == 'running':
print(f'Resuming task {task_id} from step {checkpoint.current_step}')
messages = rebuild_history(checkpoint)
start_step = checkpoint.current_step
else:
print(f'Starting new task {task_id}')
checkpoint = AgentCheckpoint(task_id=task_id, task_description=task_description, status='running', current_step=0)
messages = [{'role': 'user', 'content': task_description}]
start_step = 0
save_checkpoint(checkpoint)
return await run_agent_from(checkpoint, messages, start_step)Odtwarzanie historii wiadomości na podstawie kroków
Kluczem do wznowienia jest wierne odtworzenie historii wiadomości na podstawie zapisanych kroków. Każdy ukończony krok odpowiada wiadomości asystenta (przemyślenie i wywołanie narzędzia) oraz wiadomości narzędzia (obserwacja). Odtwórz wszystkie ukończone kroki jako wiadomości przed kontynuowaniem, aby model miał taki sam kontekst, jak gdyby nigdy nie doszło do przerwania.
def rebuild_history(checkpoint: AgentCheckpoint) -> list:
messages = [{'role': 'user', 'content': checkpoint.task_description}]
for step in checkpoint.completed_steps:
# Reconstruct the agent's reasoning message
messages.append({
'role': 'assistant',
'content': f'Thought: {step.thought}\nAction: {step.tool_name}({step.tool_args})'
})
# Reconstruct the tool observation
messages.append({
'role': 'user',
'content': f'Observation: {step.observation}'
})
return messagesIdempotentne wywołania narzędzi
Jeśli krok został częściowo ukończony przed awarią (narzędzie zostało wywołane, ale obserwacja nie została zapisana), podczas wznawiania narzędzie może zostać wywołane ponownie. Projektuj narzędzia jako idempotentne: dwukrotne wywołanie ich z tymi samymi argumentami powinno dawać taki sam wynik jak jednokrotne wywołanie. W przypadku operacji zapisu (tworzenia plików, wysyłania wiadomości e-mail) używaj kluczy deduplikacji, aby zapobiegać wielokrotnym skutkom ubocznym nawet wtedy, gdy narzędzie zostanie wywołane kilka razy.
async def idempotent_write_file(content: str, path: str, task_id: str, step: int) -> str:
dedup_key = f'{task_id}:step_{step}:write:{path}'
if redis_client.exists(dedup_key):
return f'File {path} already written (dedup key present)'
with open(path, 'w') as f:
f.write(content)
redis_client.set(dedup_key, '1', ex=3600)
return f'Successfully wrote {len(content)} chars to {path}'Czyszczenie i przechowywanie punktów kontrolnych
Punkty kontrolne zajmują miejsce i nie powinny gromadzić się bezterminowo. Ustal zasady przechowywania: usuwaj ukończone punkty kontrolne po 24 godzinach, nieudane punkty kontrolne po 7 dniach (na potrzeby analizy po awarii), a działających punktów kontrolnych nigdy nie usuwaj automatycznie. Zaimplementuj zadanie czyszczenia w tle, uruchamiane co godzinę i usuwające wygasłe punkty kontrolne zgodnie z zasadami.
from datetime import datetime, timedelta
RETENTION = {
'completed': timedelta(hours=24),
'failed': timedelta(days=7),
'running': None # never auto-delete
}
def cleanup_expired_checkpoints():
now = datetime.utcnow()
for key in redis_client.scan_iter('agent:checkpoint:*'):
data = json.loads(redis_client.get(key))
status = data.get('status', 'running')
retention = RETENTION.get(status)
if retention is None:
continue
started = datetime.fromisoformat(data.get('started_at', str(now)))
if now - started > retention:
redis_client.delete(key)Tworzenie punktów kontrolnych w LangGraph
LangGraph ma natywną obsługę punktów kontrolnych za pośrednictwem klas MemorySaver i SqliteSaver. Dołącz checkpointer do grafu, a wykonanie każdego węzła będzie automatycznie zapisywane. Aby wznowić działanie, wywołaj graph.invoke z tym samym thread_id. LangGraph obsługuje odtwarzanie historii i śledzenie kroków, więc nie trzeba ręcznie implementować logiki punktów kontrolnych.
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.graph import StateGraph
# Create graph with persistent checkpointer
checkpointer = SqliteSaver.from_conn_string('/tmp/agent_state.db')
graph = StateGraph(AgentState)
graph.add_node('reason', reason_node)
graph.add_node('act', act_node)
# ... add edges ...
app = graph.compile(checkpointer=checkpointer)
# Run with thread_id - LangGraph auto-checkpoints
config = {'configurable': {'thread_id': 'task_abc123'}}
result = await app.ainvoke({'task': 'Research climate change'}, config)
# Resume same thread - LangGraph loads from checkpoint
result = await app.ainvoke({'task': 'Continue'}, config)Rozproszone punkty kontrolne dla agentów równoległych
Gdy wielu agentów pracuje równolegle nad podzadaniami, każdy agent potrzebuje własnej przestrzeni nazw punktów kontrolnych. Użyj hierarchicznej struktury kluczy: parent_task_id:sub_task_id. Punkt kontrolny agenta nadrzędnego zapisuje, które podzadania zostały ukończone, oraz ich wyniki. Po wznowieniu agent nadrzędny ponownie wykorzystuje wyniki ukończonych podzadań z punktu kontrolnego zamiast wykonywać je ponownie.
async def parallel_with_checkpoints(parent_id: str, subtasks: list) -> list:
results = []
for i, subtask in enumerate(subtasks):
sub_id = f'{parent_id}:sub_{i}'
# Check if subtask already completed
existing = load_checkpoint(sub_id)
if existing and existing.status == 'completed':
print(f'Sub-task {i} already done, using cached result')
results.append(existing.final_answer)
else:
result = await run_agent(sub_id, subtask)
results.append(result)
return resultsTestowanie wznawiania działania
Napisz testy integracyjne, które celowo przerywają działanie agenta w trakcie zadania, i sprawdź, czy wznowienie prowadzi do prawidłowego wyniku końcowego. Zasymuluj awarię, zgłaszając wyjątek przy określonym indeksie kroku. Po wznowieniu sprawdź, czy ponownie wykonywane są tylko kroki następujące po awarii, a nie kroki wcześniejsze. Przetestuj również, czy idempotentne wywołania narzędzi nie powodują powielonych skutków ubocznych podczas odtwarzania kroku.
import pytest
@pytest.mark.asyncio
async def test_resumption_from_step_3():
task_id = 'test_resume_001'
# Run until step 3, then crash
with pytest.raises(SimulatedCrash):
await run_agent_crashing_at(task_id, 'Research AI trends', crash_at_step=3)
checkpoint = load_checkpoint(task_id)
assert checkpoint.current_step == 3
assert len(checkpoint.completed_steps) == 3
# Resume and complete
result = await resume_or_start(task_id, 'Research AI trends')
assert result is not None
# Verify only steps 4+ were re-executed
assert checkpoint_step_was_not_replayed(task_id, step=0)Wersjonowanie punktów kontrolnych przy zmianach schematu
Po zmianie schematu AgentCheckpoint (dodaniu lub zmianie nazw pól) stare punkty kontrolne w magazynie stają się niezgodne. Rozwiąż ten problem za pomocą wersjonowania punktów kontrolnych: dodaj pole checkpoint_version i napisz funkcje migracji, które podczas wczytywania aktualizują stare punkty kontrolne do nowego schematu. Zapobiega to awariom podczas wznawiania agentów po wdrożeniu zmieniającym format punktu kontrolnego.
def load_and_migrate_checkpoint(task_id: str) -> AgentCheckpoint:
raw = json.loads(redis_client.get(f'agent:checkpoint:{task_id}'))
version = raw.get('checkpoint_version', '1.0')
if version == '1.0':
# Migrate: add new fields added in v2.0
raw['checkpoint_version'] = '2.0'
raw['accumulated_results'] = raw.get('accumulated_results', {})
raw['total_tokens'] = raw.get('total_tokens', 0)
return AgentCheckpoint(**raw)Szybkie sprawdzenie
Sprawdź swoją wiedzę na temat tworzenia punktów kontrolnych i wznawiania zadań przez agentów.
Podsumowanie lekcji
W tej lekcji poznano: serializowanie stanu agenta w punktach kontrolnych po każdym kroku, dzięki czemu długotrwałe zadania przetrwają awarie bez rozpoczynania od początku; odtwarzanie historii wiadomości na podstawie zapisanych kroków, które zapewnia modelowi pełny kontekst po wznowieniu; oraz idempotentne narzędzia z kluczami deduplikacji, które zapobiegają powielonym skutkom ubocznym podczas ponownego odtwarzania kroków. Następnie zaprojektujemy mechanizmy eskalacji z udziałem człowieka.
Często zadawane pytania
Czy lekcja „Punkty kontrolne i wznawianie zadań” jest bezpłatna?
Tak — pełny tekst „Punkty kontrolne i wznawianie zadań” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Engineering Academy, przejdź na CoddyKit PRO. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Punkty kontrolne i wznawianie zadań”?
Zapisuj stan agenta po każdym ukończonym kroku, aby po awarii można było wznowić długotrwałe zadanie od ostatniego pomyślnego punktu kontrolnego zamiast rozpoczynać je od początku. Ćwiczysz AI Engineering Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć AI Engineering Academy?
Nie wymagamy żadnego doświadczenia. AI Engineering Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Punkty kontrolne i wznawianie zadań”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji AI Engineering Academy?
Tak. Każda lekcja AI Engineering Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Klasyfikowanie trybów awarii agentów
- Samokorekta i refleksyjne promptowanie
- Punkty kontrolne i wznawianie zadań
- Eskalacja z udziałem człowieka