Obsługa błędów w łańcuchach promptów
Weryfikuj wyniki pośrednie i odzyskuj sprawność po awariach łańcucha.
Obsługa błędów w łańcuchach promptów to bezpłatna lekcja AI Prompt Engineering na CoddyKit. To lekcja 4 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 Prompt Engineering, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Prompt Engineering zawiera 4 lekcji w sumie.
Dlaczego łańcuchy zawodzą
Łańcuchy promptów wprowadzają nowe rodzaje awarii, które nie występują w systemach opartych na pojedynczych promptach. Każdy krok może zawieść na swój sposób, a awarie się kumulują — błędny wynik kroku 2 zniekształca każdy kolejny krok.
Częste rodzaje awarii:
- Model zwraca nieprawidłowy JSON, którego nie można sparsować
- Model błędnie rozumie zadanie i generuje wynik niepoprawny pod względem znaczeniowym
- Limity zapytań lub przekroczenia limitu czasu API powodują awarie kroków
- W długim łańcuchu zostaje przekroczone okno kontekstu
- Model halucynuje dane, które kolejne kroki traktują jako fakty
Walidacja wyniku po każdym kroku
Pierwszą linią obrony jest natychmiastowa walidacja wyniku po każdym kroku, przed przekazaniem go do następnego. Nigdy nie należy zakładać, że model zwrócił dokładnie to, o co go poproszono.
import json
def validate_json_output(raw_text, required_fields):
'Parse and validate that required fields are present in model output.'
try:
data = json.loads(raw_text.strip())
except json.JSONDecodeError as e:
raise ValueError(f'Invalid JSON: {e}. Raw: {raw_text[:200]}')
missing = [f for f in required_fields if f not in data]
if missing:
raise ValueError(f'Missing required fields: {missing}. Got: {list(data.keys())}')
return data
# Usage after a chain step
raw = '{"sentiment": "positive", "priority": "high"}'
validated = validate_json_output(raw, required_fields=['sentiment', 'priority'])
print('Valid:', validated)Logika ponawiania dla przejściowych awarii
Awarie API (limity zapytań, przekroczenia limitu czasu, błędy serwera) mają charakter przejściowy. W przypadku awarii na poziomie sieci należy zaimplementować mechanizm ponawiania z wykładniczym zwiększaniem opóźnienia:
import anthropic, time
client = anthropic.Anthropic(api_key='YOUR_API_KEY')
def call_with_retry(prompt, max_retries=3, base_delay=1.0):
last_error = None
for attempt in range(max_retries):
try:
r = client.messages.create(
model='claude-opus-4-5', max_tokens=500,
messages=[{'role': 'user', 'content': prompt}]
)
return r.content[0].text
except anthropic.RateLimitError as e:
wait = base_delay * (2 ** attempt)
print(f'Rate limited. Waiting {wait}s before retry {attempt+1}/{max_retries}...')
time.sleep(wait)
last_error = e
except anthropic.APIError as e:
last_error = e
if attempt < max_retries - 1:
time.sleep(base_delay)
raise RuntimeError(f'All retries exhausted: {last_error}')Walidacja semantyczna
Niektóre awarie mają poprawną strukturę, ale błędne znaczenie — model zwraca prawidłowy JSON, lecz z niepoprawnymi wartościami. Należy użyć lekkiego kroku walidacji do sprawdzenia poprawności semantycznej:
def semantic_validate(data, schema_rules):
'Apply semantic validation rules to parsed output.'
errors = []
for field, rules in schema_rules.items():
value = data.get(field)
if rules.get('required') and value is None:
errors.append(f'{field} is required but missing')
continue
if 'allowed_values' in rules and value not in rules['allowed_values']:
errors.append(f'{field} must be one of {rules["allowed_values"]}, got: {value}')
if 'min_length' in rules and isinstance(value, list) and len(value) < rules['min_length']:
errors.append(f'{field} must have at least {rules["min_length"]} items, got {len(value)}')
if errors:
raise ValueError('Semantic validation failed: ' + '; '.join(errors))
return data
rules = {'sentiment': {'allowed_values': ['positive', 'negative', 'mixed']}, 'issues': {'min_length': 1}}
data = {'sentiment': 'positive', 'issues': ['login bug']}
print(semantic_validate(data, rules))Zapasowe prompty
Gdy krok nie przejdzie walidacji po ponowieniach, zapasowy prompt może wygenerować prostszy, ale użyteczny wynik zamiast doprowadzić do awarii całego łańcucha:
import json
def call_with_fallback(primary_prompt, fallback_prompt, required_fields):
# Try primary prompt
try:
raw = call_with_retry(primary_prompt)
return validate_json_output(raw, required_fields)
except (ValueError, RuntimeError) as e:
print(f'Primary prompt failed: {e}. Trying fallback...')
# Try simpler fallback prompt
try:
raw = call_with_retry(fallback_prompt)
return validate_json_output(raw, required_fields)
except (ValueError, RuntimeError) as e:
print(f'Fallback also failed: {e}. Returning safe default.')
# Return safe default — chain continues with minimal data
return {field: None for field in required_fields}
# Usage
primary = 'Analyze this review. Return JSON with 10 fields: {...}'
fallback = 'Classify this review. Return JSON: {"sentiment": "positive|negative|neutral"}'
result = call_with_fallback(primary, fallback, ['sentiment'])
print(result)Mechanizmy circuit breaker
Mechanizm circuit breaker zapobiega marnowaniu wywołań API przez zawodny łańcuch. Po N kolejnych awariach otwiera obwód i natychmiast zwraca błąd bez wykonywania kolejnych wywołań API:
class CircuitBreaker:
def __init__(self, failure_threshold=3, recovery_timeout=60):
self.failure_count = 0
self.threshold = failure_threshold
self.state = 'closed' # closed = normal, open = blocking
self.opened_at = None
def call(self, fn, *args, **kwargs):
import time
if self.state == 'open':
elapsed = time.time() - self.opened_at
if elapsed > 60: # recovery_timeout
self.state = 'half-open'
else:
raise RuntimeError('Circuit open — skipping API call')
try:
result = fn(*args, **kwargs)
self.failure_count = 0
self.state = 'closed'
return result
except Exception as e:
self.failure_count += 1
if self.failure_count >= self.threshold:
self.state = 'open'
self.opened_at = time.time()
print(f'Circuit opened after {self.failure_count} failures.')
raise e
cb = CircuitBreaker(failure_threshold=3)
print('Circuit breaker initialized.')Punkty kontrolne w długich łańcuchach
W przypadku łańcuchów zawierających wiele kroków lub kosztownych kroków należy używać punktów kontrolnych do zapisywania wyników pośrednich. Jeśli późny krok zakończy się niepowodzeniem, można wznowić działanie od punktu kontrolnego zamiast rozpoczynać wszystko od kroku 1:
import json, os
CHECKPOINT_DIR = '/tmp/chain_checkpoints'
os.makedirs(CHECKPOINT_DIR, exist_ok=True)
def save_checkpoint(run_id, step_id, data):
path = os.path.join(CHECKPOINT_DIR, f'{run_id}_step{step_id}.json')
with open(path, 'w') as f:
json.dump(data, f)
print(f'Checkpoint saved: step {step_id}')
def load_checkpoint(run_id, step_id):
path = os.path.join(CHECKPOINT_DIR, f'{run_id}_step{step_id}.json')
if os.path.exists(path):
with open(path) as f:
return json.load(f)
return None
def run_with_checkpoints(run_id, input_data):
step1 = load_checkpoint(run_id, 1) or json.loads(call(f'Step 1 processing: {input_data}'))
save_checkpoint(run_id, 1, step1)
step2 = load_checkpoint(run_id, 2) or json.loads(call(f'Step 2 processing: {step1}'))
save_checkpoint(run_id, 2, step2)
return step2
print('Checkpointing system defined.')Kontrolowane ograniczanie funkcjonalności
Gdy krok łańcucha zawiedzie i nie można go odzyskać, kontrolowane ograniczanie funkcjonalności pozwala kontynuować działanie łańcucha z częściowymi danymi zamiast doprowadzać do całkowitej awarii:
def process_with_degradation(tickets):
results = []
for ticket in tickets:
try:
# Full chain: extract -> classify -> respond
extracted = json.loads(call(f'Extract issue from ticket. Return JSON: {{"issue": str}}\n\n{ticket}'))
classified = json.loads(call(f'Classify priority. Return JSON: {{"priority": str}}\n\n{extracted["issue"]}'))
response = call(f'Draft response for {classified["priority"]} priority: {extracted["issue"]}')
results.append({'ticket': ticket, 'response': response, 'degraded': False})
except Exception as e:
print(f'Chain failed for ticket, using fallback: {e}')
# Fallback: simple direct response without classification
simple_response = call(f'Respond to this support ticket:\n{ticket}')
results.append({'ticket': ticket, 'response': simple_response, 'degraded': True})
return results
print('Graceful degradation pipeline defined.')Strukturalne rejestrowanie błędów
Błędy należy rejestrować z wystarczającym kontekstem, aby można było ustalić, który krok zawiódł, jakie było wejście i co zwrócił model:
import logging, traceback
from datetime import datetime
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('chain')
def logged_step(step_name, prompt, validator=None):
start = datetime.utcnow()
try:
raw = call_with_retry(prompt)
result = validator(raw) if validator else raw
logger.info(f'[{step_name}] SUCCESS in {(datetime.utcnow()-start).total_seconds():.2f}s')
return result
except Exception as e:
logger.error(f'[{step_name}] FAILED after {(datetime.utcnow()-start).total_seconds():.2f}s')
logger.error(f'[{step_name}] PROMPT: {prompt[:200]}')
logger.error(f'[{step_name}] ERROR: {traceback.format_exc()}')
raise
print('Structured error logging defined.')Testowanie scenariuszy błędów
Obsługę błędów należy testować jawnie, celowo wywołując awarie. Do symulowania błędów API i nieprawidłowych wyników należy używać obiektów mock:
from unittest.mock import patch, MagicMock
def test_fallback_on_json_error():
with patch('__main__.call') as mock_call:
# First call returns malformed JSON, fallback returns valid JSON
mock_call.side_effect = [
'This is not JSON at all',
'{"sentiment": "positive"}'
]
result = call_with_fallback(
primary_prompt='Analyze review with 10 fields',
fallback_prompt='Just classify sentiment as JSON',
required_fields=['sentiment']
)
assert result['sentiment'] == 'positive'
print('PASS: fallback activated correctly on JSON parse error')
def test_circuit_breaker_opens():
cb = CircuitBreaker(failure_threshold=2)
for i in range(2):
try:
cb.call(lambda: (_ for _ in ()).throw(RuntimeError('API fail')))
except RuntimeError:
pass
assert cb.state == 'open'
print('PASS: circuit breaker opened after 2 failures')
print('Error handling tests defined.')Monitorowanie kondycji łańcucha na produkcji
W środowisku produkcyjnym należy śledzić metryki kondycji łańcucha, aby wykrywać pogorszenie działania, zanim zauważą je użytkownicy:
- Współczynnik powodzenia kroku: Odsetek uruchomień, w których każdy krok kończy się powodzeniem przy pierwszej próbie
- Współczynnik aktywacji zapasowego promptu: Jak często używany jest zapasowy prompt?
- Współczynnik działania w trybie ograniczonym: Jaka część uruchomień łańcucha kończy się w trybie ograniczonym?
- Opóźnienie kroku: Należy śledzić opóźnienia p50/p95 dla każdego kroku — powolny krok wskazuje na problemy ze złożonością promptu
- Współczynnik niepowodzeń walidacji: Wysoka wartość sygnalizuje, że prompt wymaga udoskonalenia
Szybkie sprawdzenie
Jaki jest cel mechanizmu circuit breaker w łańcuchu promptów?
Obsługa błędów w łańcuchach — najważniejsze informacje
Solidna obsługa błędów odróżnia łańcuchy prototypowe od systemów produkcyjnych:
- Waliduj wynik każdego kroku przed przekazaniem go dalej — nigdy nie zakładaj, że model zwrócił prawidłowe dane
- Ponawiaj przejściowe awarie API z wykładniczym zwiększaniem opóźnienia — limity zapytań i przekroczenia limitu czasu można obsłużyć
- Używaj zapasowych promptów, aby uzyskać prostszy wynik, gdy główny prompt nie przejdzie walidacji semantycznej
- Mechanizmy circuit breaker zapobiegają marnowaniu wywołań API po wielokrotnych awariach
- Twórz punkty kontrolne dla kosztownych kroków, aby długie łańcuchy można było wznowić po awarii późnego kroku
- Kontrolowane ograniczanie funkcjonalności pozwala utrzymać działanie potoku z częściowymi danymi zamiast doprowadzać do awarii
- W środowisku produkcyjnym śledź współczynnik powodzenia kroków, współczynnik użycia zapasowego promptu i współczynnik działania w trybie ograniczonym
Ucz się AI Prompt Engineering dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 53
- Lekcje
- 199
Często zadawane pytania
Czy lekcja „Obsługa błędów w łańcuchach promptów” jest bezpłatna?
Tak — pełny tekst „Obsługa błędów w łańcuchach promptów” 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 Prompt Engineering, przejdź na CoddyKit PRO. Kurs AI Prompt Engineering zawiera 4 lekcji w sumie.
Co nauczysz się w „Obsługa błędów w łańcuchach promptów”?
Weryfikuj wyniki pośrednie i odzyskuj sprawność po awariach łańcucha. Ćwiczysz AI Prompt Engineering 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 Prompt Engineering?
Nie wymagamy żadnego doświadczenia. AI Prompt Engineering 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 4 z 4.
Ile czasu zajmuje lekcja „Obsługa błędów w łańcuchach promptów”?
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 Prompt Engineering?
Tak. Każda lekcja AI Prompt Engineering 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
- Czym jest łańcuch promptów?
- Wzorce „wynik jako dane wejściowe”
- Łańcuchy sekwencyjnych transformacji
- Obsługa błędów w łańcuchach promptów