Gestione degli errori nelle catene di prompt
Convalidi gli output intermedi e recuperi dagli errori della catena
Gestione degli errori nelle catene di prompt è una lezione AI Prompt Engineering gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Prompt Engineering, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Prompt Engineering include 4 lezioni in totale.
Perché le catene falliscono
Le catene di prompt introducono nuove modalità di errore che non si verificano nei sistemi basati su un singolo prompt. Ogni passaggio può fallire a modo proprio e gli errori si propagano: un output errato del Passaggio 2 compromette tutti i passaggi successivi.
Modalità di errore comuni:
- Il modello restituisce JSON non valido che non può essere analizzato
- Il modello fraintende il compito e produce un output semanticamente errato
- I limiti di frequenza o i timeout dell'API causano errori nei passaggi
- La finestra di contesto viene superata in una catena lunga
- Il modello allucina dati che i passaggi successivi trattano come fatti
Convalidare l'output dopo ogni passaggio
La prima linea di difesa consiste nel convalidare immediatamente l'output dopo ogni passaggio, prima di trasferirlo a quello successivo. Non dia mai per scontato che il modello abbia restituito ciò che gli è stato richiesto.
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)Logica di retry per errori temporanei
Gli errori dell'API, come limiti di frequenza, timeout ed errori del server, sono temporanei. Implementi una logica di retry con exponential backoff per gli errori a livello di rete:
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}')Convalida semantica
Alcuni errori sono strutturalmente validi ma semanticamente errati: il modello restituisce JSON valido, ma con valori non corretti. Utilizzi un passaggio di convalida leggero per verificare la correttezza semantica:
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))Prompt di fallback
Quando un passaggio non supera la convalida dopo i retry, un prompt di fallback può produrre un output più semplice ma utilizzabile, invece di interrompere l'intera catena:
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)Circuit breaker
Un circuit breaker impedisce a una catena che sta fallendo di sprecare chiamate API. Dopo N errori consecutivi, apre il circuito e restituisce immediatamente un errore senza effettuare ulteriori chiamate 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.')Checkpointing delle catene lunghe
Per le catene con molti passaggi o con passaggi costosi, utilizzi il checkpointing per salvare i risultati intermedi. Se un passaggio avanzato fallisce, riprenda dal checkpoint invece di ricominciare dal Passaggio 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.')Degradazione controllata
Quando un passaggio della catena fallisce e non può essere recuperato, la degradazione controllata consente di proseguire con dati parziali invece di interrompere completamente l'esecuzione:
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.')Registrazione strutturata degli errori
Registri gli errori con un contesto sufficiente per diagnosticare quale passaggio è fallito, quale fosse l'input e che cosa abbia restituito il modello:
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.')Testare gli scenari di errore
Testi esplicitamente la gestione degli errori inserendo errori simulati. Utilizzi oggetti mock per simulare errori dell'API e output non validi:
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.')Monitorare lo stato della catena in produzione
In produzione, monitori le metriche sullo stato della catena per rilevare i problemi prima che se ne accorgano gli utenti:
- Tasso di successo del passaggio: percentuale di esecuzioni in cui ogni passaggio riesce al primo tentativo
- Tasso di attivazione del fallback: con quale frequenza viene utilizzato il prompt di fallback?
- Tasso di degradazione: quale frazione delle esecuzioni della catena si completa in modalità degradata?
- Latenza del passaggio: monitori la latenza p50/p95 per passaggio; una fase lenta indica problemi di complessità del prompt
- Tasso di errori di convalida: un tasso elevato indica che il prompt deve essere perfezionato
Verifica rapida
Qual è lo scopo di un circuit breaker in una catena di prompt?
Gestione degli errori nelle catene — Punti chiave
Una solida gestione degli errori è ciò che distingue le catene prototipali dai sistemi pronti per la produzione:
- Convalidi l'output di ogni passaggio prima di trasferirlo a valle: non dia mai per scontato che il modello abbia restituito dati corretti
- Ripeta i tentativi per gli errori temporanei dell'API utilizzando un exponential backoff: i limiti di frequenza e i timeout sono recuperabili
- Utilizzi prompt di fallback per ottenere un output più semplice quando il prompt principale non supera la convalida semantica
- I circuit breaker impediscono lo spreco di chiamate API dopo errori ripetuti
- Crei checkpoint per i passaggi costosi, così le catene lunghe possono riprendere dopo l'errore di un passaggio avanzato
- La degradazione controllata mantiene operativa la pipeline con dati parziali invece di interromperla
- Monitori in produzione il tasso di successo dei passaggi, il tasso di fallback e il tasso di degradazione
Domande Frequenti
La lezione «Gestione degli errori nelle catene di prompt» è gratuita?
Sì — il testo completo di «Gestione degli errori nelle catene di prompt» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Prompt Engineering, passa a CoddyKit PRO. Il corso AI Prompt Engineering include 4 lezioni in totale.
Cosa imparerò in «Gestione degli errori nelle catene di prompt»?
Convalidi gli output intermedi e recuperi dagli errori della catena Eserciti AI Prompt Engineering con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare AI Prompt Engineering?
Non è richiesta alcuna esperienza precedente. AI Prompt Engineering su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Gestione degli errori nelle catene di prompt»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione AI Prompt Engineering?
Sì. Ogni lezione AI Prompt Engineering include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Che cos'è il prompt chaining?
- Schemi da output a input
- Catene di trasformazione sequenziali
- Gestione degli errori nelle catene di prompt