\n\n\n\n Gestione degli Errori dell'Agente: Una Guida Pratica Avanzata - AiDebug \n

Gestione degli Errori dell’Agente: Una Guida Pratica Avanzata

📖 11 min read2,081 wordsUpdated Apr 4, 2026

Introduzione: La Realtà Inevitabile degli Errori degli Agenti

Nel mondo degli agenti AI, l’esecuzione perfetta è un mito. Che il tuo agente stia navigando in un’applicazione web complessa, generando contenuti creativi o gestendo flussi di lavoro intricati, gli errori sono una parte inevitabile del processo. Le interruzioni di rete, i limiti di velocità delle API, le risposte malformate, i cambiamenti inattesi nell’interfaccia utente e anche sottili fraintendimenti delle istruzioni possono portare a fallimenti. Anche se i blocchi try-catch di base sono un buon inizio, la vera solidità nel design degli agenti richiede un approccio più sofisticato alla gestione degli errori. Questa guida avanzata esplorerà strategie pratiche e pattern architettonici per costruire agenti che non solo si riprendono con eleganza, ma che apprendono e si adattano dai loro errori.

Oltre ai Riprova di Base: Comprendere i Tipi di Errore e la Loro Gravità

Il primo passo verso una gestione avanzata degli errori è superare un generico “riprova tutto.” Non tutti gli errori sono uguali. Distinguere tra i diversi tipi di errore e la loro gravità consente strategie di recupero più intelligenti e contestualmente consapevoli.

Categorizzare gli Errori:

  • Errori Transitori: Problemi temporanei che è probabile si risolvano da soli con un breve ritardo e un tentativo di ripetizione (ad es., glitch di rete, sovraccarichi temporanei delle API, blocchi nel database).
  • Errori Persistenti: Problemi che difficilmente si risolvono con un semplice tentativo di ripetizione e richiedono un approccio diverso (ad es., chiavi API non valide, schemi di input errati, errori di logica fondamentali, permessi negati).
  • Errori Sistemici: Problemi profondi che indicano un difetto fondamentale nel design, nell’addestramento o nell’ambiente dell’agente (ad es., allucinazioni ricorrenti, incapacità di analizzare un componente critico, fallimenti continui su un tipo specifico di compito).
  • Errori di Sistema Esterni: Errori che originano da servizi di terze parti con cui l’agente interagisce, spesso richiedendo una gestione specifica in base alla documentazione del servizio esterno.

Livelli di Gravità:

  • Informativo: Problemi minori che non impediscono il completamento del compito, ma potrebbero indicare prestazioni subottimali.
  • Avvertenza: Problemi che potrebbero influire sulle prestazioni o indicare un potenziale problema, ma l’agente può continuare.
  • Errore: Un problema significativo che impedisce il completamento del passaggio o del sotto-compito attuale.
  • Critico: Un fallimento catastrofico che impedisce all’intero agente di completare il proprio obiettivo principale.

Meccanismi di Riprova Avanzati con Backoff e Jitter

I semplici tentativi di ripetizione possono spesso esacerbare i problemi, specialmente con errori transitori come i limiti di velocità delle API. Le strategie di riprova avanzate sono fondamentali.

Backoff Esponenziale:

Invece di riprovare immediatamente, attendi un tempo che aumenta esponenzialmente tra i tentativi. Questo dà al sistema il tempo di recuperare e previene ulteriori sovraccarichi.


import time
import random

def call_api_with_exponential_backoff(func, *args, max_retries=5, initial_delay=1, max_delay=60):
 for i in range(max_retries):
 try:
 return func(*args)
 except Exception as e:
 print(f"Tentativo {i+1} fallito: {e}")
 if i == max_retries - 1:
 raise
 delay = min(initial_delay * (2 ** i), max_delay)
 jitter = random.uniform(0, delay * 0.1) # Aggiungi fino al 10% di jitter
 print(f"Riprovo tra {delay + jitter:.2f} secondi...")
 time.sleep(delay + jitter)

# Esempio di utilizzo:
def problematic_api_call():
 if random.random() < 0.7: # 70% di probabilità di errore
 raise ConnectionError("Problema di rete simulato")
 return "Successo!"

try:
 result = call_api_with_exponential_backoff(problematic_api_call)
 print(result)
except Exception as e:
 print(f"Errore finale dopo più tentativi: {e}")

Jitter:

Aggiungere un piccolo ritardo casuale (jitter) al periodo di backoff previene un problema di “gregge di tuoni” in cui molti agenti riprovano esattamente negli stessi intervalli esponenziali, sovraccaricando potenzialmente un servizio recuperato simultaneamente.

Pattern del Circuit Breaker: Prevenire i Fallimenti a Cascata

Mentre i tentativi di ripetizione sono buoni per problemi transitori, tentare continuamente di ripetere rispetto a un servizio che fallisce in modo persistente è uno spreco e può portare a fallimenti a cascata. Il pattern del Circuit Breaker è progettato per questo scenario.

Come Funziona:

  1. Stato Chiuso: Il circuito è normale. Le chiamate al servizio procedono. Se si verificano un certo numero di fallimenti entro una soglia, il circuito si interrompe e diventa Aperto.
  2. Stato Aperto: Le chiamate al servizio falliscono immediatamente senza tentare di raggiungere il servizio reale. Dopo un timeout configurabile, il circuito passa a Mezzo Aperto.
  3. Stato Mezzo Aperto: Un numero limitato di chiamate è consentito al servizio per testare se si è ripreso. Se queste chiamate di prova hanno successo, il circuito torna a Chiuso. Se falliscono, torna a Aperto.

import time

class CircuitBreaker:
 def __init__(self, failure_threshold=3, recovery_timeout=10, half_open_test_count=1):
 self.failure_threshold = failure_threshold
 self.recovery_timeout = recovery_timeout
 self.half_open_test_count = half_open_test_count

 self.failures = 0
 self.last_failure_time = None
 self.state = "CHUSO" # CHIUSO, APERTO, MEZZO_APERTO
 self.successes_in_half_open = 0

 def __call__(self, func, *args, **kwargs):
 if self.state == "APERTO":
 if time.time() - self.last_failure_time > self.recovery_timeout:
 self.state = "MEZZO_APERTO"
 self.successes_in_half_open = 0
 print("Circuit Breaker: APERTO -> MEZZO_APERTO")
 else:
 raise CircuitBreakerOpenError("Il circuito è aperto, non si sta tentando la chiamata.")

 try:
 result = func(*args, **kwargs)
 self._on_success()
 return result
 except Exception as e:
 self._on_failure(e)
 raise

 def _on_success(self):
 if self.state == "CHUSO":
 self.failures = 0
 elif self.state == "MEZZO_APERTO":
 self.successes_in_half_open += 1
 if self.successes_in_half_open >= self.half_open_test_count:
 self.state = "CHUSO"
 self.failures = 0
 print("Circuit Breaker: MEZZO_APERTO -> CHIUSO")

 def _on_failure(self, error):
 if self.state == "CHUSO":
 self.failures += 1
 self.last_failure_time = time.time()
 if self.failures >= self.failure_threshold:
 self.state = "APERTO"
 print(f"Circuit Breaker: CHIUSO -> APERTO (fallimenti: {self.failures})")
 elif self.state == "MEZZO_APERTO":
 self.state = "APERTO"
 self.last_failure_time = time.time()
 print("Circuit Breaker: MEZZO_APERTO -> APERTO (test fallito)")

class CircuitBreakerOpenError(Exception):
 pass

# Esempio di utilizzo:
breaker = CircuitBreaker(failure_threshold=2, recovery_timeout=5)

def flaky_service():
 if random.random() < 0.8: # 80% di probabilità di errore
 raise ValueError("Errore del servizio difettoso")
 return "Servizio operativo!"

for i in range(10):
 try:
 print(f"Tentativo {i+1}:")
 result = breaker(flaky_service)
 print(f" {result}")
 except (ValueError, CircuitBreakerOpenError) as e:
 print(f" Errore: {e}")
 time.sleep(0.5)

Gestione degli Errori Semantici e Recupero Contestuale

Per gli agenti AI, gli errori non sono spesso solo eccezioni tecniche; possono essere fraintendimenti semantici o fallimenti nel raggiungere un obiettivo previsto. Una gestione avanzata degli errori implica la comprensione del significato dell'errore nel contesto operativo dell'agente.

Esempio: Agente di Web Scraping

Considera un agente progettato per estrarre i prezzi dei prodotti da un sito di e-commerce.

  • Errore Tecnico: requests.exceptions.ConnectionError (transitorio, riprova con backoff).
  • Errore Semantico 1: XPath per il prezzo non trovato. Questo non è un errore tecnico; la pagina si è caricata, ma l'elemento atteso non è presente.
    • Strategia di Recupero: Prova percorsi XPath alternativi, utilizza OCR su uno screenshot, segnala per revisione umana, o annota che il prezzo non è disponibile.
  • Errore Semantico 2: Il prezzo estratto è "Esaurito" o "N/D". L'estrazione ha funzionato, ma il valore non è un prezzo valido.
    • Strategia di Recupero: Contrassegna come non disponibile, prova a trovare la data di riassortimento, informa che il prodotto è esaurito.
  • Errore Semantico 3: L'agente viene reindirizzato a una pagina di login invece che alla pagina del prodotto.
    • Strategia di Recupero: Prova a effettuare il login (se le credenziali sono disponibili), o segnala come elaborabile a causa di un requisito di autenticazione.

Implementare la Gestione degli Errori Semantici:

Questo implica spesso un sistema gerarchico di gestione degli errori:

  1. Gestori a Basso Livello (Tecnici): Catturano eccezioni specifiche (ad es., requests.exceptions, errori di parsing JSON) e applicano ripetizioni, backoff o circuit breaker.
  2. Gestori di Medio Livello (Specifici per Componente): All'interno di un componente specifico (ad es., una classe `Scraper`, un modulo `APICaller`), gestiscono errori rilevanti per il funzionamento di quel componente. Questo potrebbe comportare l'interpretazione dei codici di errore dalle risposte delle API (ad es., HTTP 404, 429) e la loro traduzione in tipi di errore interni più significativi.
  3. Gestori ad Alto Livello (Obiettivo dell'Agente): A livello di orchestrazione dell'agente, valutano se l'obiettivo generale è stato raggiunto. Se non lo è, analizzano gli errori accumulati e decidono su una strategia di recupero olistica (ad es., provare uno strumento diverso, riformulare il prompt, chiedere chiarimenti, escalare a un umano).

Autocorrezione e Apprendimento dagli Errori

Gli agenti più avanzati non si limitano a gestire errori; apprendono da essi.

Regolazioni Dinamiche del Prompt:

Se un agente alimentato da LLM non riesce costantemente a raggiungere un sottobiettivo a causa di una cattiva interpretazione, modifica il prompt in modo dinamico. Ad esempio, se cerca frequentemente di accedere a strumenti inesistenti:

  • Prompt Originale: "Usa gli strumenti disponibili per rispondere alla domanda dell'utente."
  • Dopo un Errore (ToolNotFound): "Hai accesso ai seguenti strumenti: [elenco degli strumenti effettivamente disponibili]. Usa solo questi strumenti per rispondere alla domanda dell'utente."
  • Dopo un Errore (IncorrectToolParameters): "Quando usi lo strumento 'search', ricorda che il parametro 'query' è obbligatorio e deve essere una stringa."

Aggiornamenti della Base di Conoscenza:

Quando un agente incontra un errore di sistema esterno persistente (ad es., un sito web specifico restituisce sempre un 403), registralo in una base di conoscenza permanente. Agenti futuri possono interrogare questa base di conoscenza prima di tentare la stessa azione.


class ErrorKnowledgeBase:
 def __init__(self):
 self.problematic_endpoints = {}

 def record_failure(self, endpoint_url, error_type, timestamp, message):
 if endpoint_url not in self.problematic_endpoints:
 self.problematic_endpoints[endpoint_url] = []
 self.problematic_endpoints[endpoint_url].append({
 "error_type": error_type,
 "timestamp": timestamp,
 "message": message
 })
 # Logica semplice: Se un endpoint fallisce ripetutamente, marcarlo come 'non affidabile'
 if len(self.problematic_endpoints[endpoint_url]) > 5 and \
 all(time.time() - f["timestamp"] < 3600 for f in self.problematic_endpoints[endpoint_url][-5:]):
 print(f"Attenzione: {endpoint_url} sembra non affidabile. Considera alternative.")

 def is_endpoint_unreliable(self, endpoint_url, recent_threshold=3600):
 # Controlla se un endpoint ha avuto fallimenti recenti e ripetuti
 failures = self.problematic_endpoints.get(endpoint_url, [])
 recent_failures = [f for f in failures if time.time() - f["timestamp"] < recent_threshold]
 return len(recent_failures) > 5 # Soglia di esempio

# Utilizzo in un agente:
kb = ErrorKnowledgeBase()

def make_api_call(url):
 if kb.is_endpoint_unreliable(url):
 print(f"Saltando {url} a causa di un'affidabilità nota non soddisfacente.")
 raise Exception("Endpoint considerato non affidabile.")
 try:
 # ... chiamata API effettiva ...
 if random.random() < 0.6: # Simula un fallimento
 raise requests.exceptions.HTTPError(f"403 Forbidden da {url}")
 return "Dati da " + url
 except Exception as e:
 kb.record_failure(url, type(e).__name__, time.time(), str(e))
 raise

import requests

for _ in range(10):
 try:
 print(make_api_call("http://example.com/sensitive_api"))
 except Exception as e:
 print(f"Errore catturato: {e}")
 time.sleep(0.1)

Feedback Umano nel Loop:

Per errori critici o irreversibili, escalare a un umano è spesso la migliore strategia. L'agente dovrebbe fornire tutto il contesto rilevante:

  • Che cosa stava cercando di fare l'agente?
  • Quale passaggio ha fallito?
  • Quale era il messaggio di errore/stack trace esatto?
  • Quali tentativi di recupero sono stati effettuati?
  • Quali dati hanno portato all'errore?

La risoluzione dell'umano (ad es., fornire un input corretto, aggiornare uno strumento, modificare la logica dell'agente) può poi essere reinserita nella base di conoscenza o nel codice dell'agente per future iterazioni.

Osservabilità e Monitoraggio per la Gestione degli Errori

Anche la migliore gestione degli errori è inutile se non sai che sta funzionando (o fallendo). Un'osservabilità solida è fondamentale.

  • Logging Strutturato: Registra gli errori con formati coerenti (JSON è eccellente). Includi timestamp, ID agente, ID attività, tipo di errore, gravità, stack trace e variabili contestuali rilevanti.
  • Metrica e Avvisi: Traccia la frequenza di diversi tipi di errore. Imposta avvisi per errori critici, tassi di errore elevati o periodi prolungati di attivazione del circuito di interruzione.
  • Tracciamento: Per agenti complessi e multi-step, il tracciamento distribuito può aiutare a visualizzare il flusso e individuare dove si verificano i fallimenti tra i diversi componenti o servizi.
  • Dashboard: Crea dashboard per visualizzare le tendenze degli errori, i tassi di recupero e la salute complessiva dei tuoi agenti.

Conclusione: Costruire Agenti Resilienti e Intelligenti

Una gestione degli errori avanzata trasforma un agente da uno script fragile a un'entità resiliente e intelligente. Comprendendo i tipi di errore, implementando schemi di retry e circuit breaker sofisticati, abbracciando una gestione degli errori semantica e costruendo meccanismi per l'auto-correzione e l'apprendimento, possiamo creare agenti che navigano con grazia tra le complessità del mondo reale. Questo approccio proattivo non solo migliora l'affidabilità dei tuoi sistemi AI, ma riduce anche il carico operativo e migliora l'esperienza complessiva dell'utente, spianando la strada a un'AI veramente autonoma e affidabile.

🕒 Published:

✍️
Written by Jake Chen

AI technology writer and researcher.

Learn more →
Browse Topics: ci-cd | debugging | error-handling | qa | testing
Scroll to Top