\n\n\n\n Gestione degli errori degli agenti: Una guida avanzata per sistemi IA performanti - AiDebug \n

Gestione degli errori degli agenti: Una guida avanzata per sistemi IA performanti

📖 7 min read1,226 wordsUpdated Apr 4, 2026

Introduzione : La Realtà Ineludibile degli Errori nell’IA Agente

Man mano che gli agenti IA diventano sempre più sofisticati e autonomi, la loro capacità di navigare in ambienti reali complessi è fondamentale. Tuttavia, il percorso verso un funzionamento fluido è raramente privo di ostacoli. Gli errori – sia che provengano da input utente ambigui, risposte inaspettate dai sistemi esterni, allucinazioni del modello o difetti logici nel ragionamento stesso dell’agente – rappresentano una realtà inevitabile. Un agente IA veramente solido non è quello che non incontra mai errori, ma quello che è in grado di rilevarli, diagnosticarli e riprendersi con facilità, riducendo così le perturbazioni e massimizzando il completamento dei compiti.

Questa guida avanzata va oltre i semplici blocchi try-except, esplorando strategie sofisticate ed esempi pratici per costruire meccanismi di gestione degli errori per agenti resilienti. Affronteremo la prevenzione proattiva, il recupero reattivo e l’apprendimento continuo, dotandovi degli strumenti necessari per progettare agenti che non siano solo intelligenti, ma anche notevolmente solidi.

Comprendere lo Spazio degli Errori dell’Agente

Prima di poter gestire gli errori in modo efficace, dobbiamo categorizarli. Gli errori dell’agente si suddividono spesso in diverse categorie chiave :

  • Errori di Input : Prompt utente malformati, ambigui, contraddittori o fuori portata.
  • Errori di Strumento/API : Indisponibilità del servizio esterno, parametri API errati, limitazioni di tasso, formati di dati inaspettati, fallimenti di autenticazione.
  • Errori di Ragionamento/Logica : L’agente che fraintende il suo scopo, allucinazione di fatti, bloccandosi in loop, non riuscendo a trovare uno strumento appropriato o prendendo decisioni errate basate sul suo stato interno.
  • Errori Contestuali : L’agente che perde il filo della cronologia della conversazione, fraintendendo scambi precedenti o fallendo nell’incorporare informazioni esterne pertinenti.
  • Errori di Risorse : Carestia di memoria, superamento dei limiti di token per i LLM, o problemi di timeout.
  • Errori di Sicurezza/Allineamento : Generazione di contenuti dannosi, parziali o inappropriati; tentativi di azioni vietate.

Prevenzione Proattiva degli Errori : Costruire la Resilienza Fin dall’Inizio

Il miglior errore è quello che non si verifica mai. Le strategie proattive mirano a ridurre la probabilità di errori attraverso la progettazione e la validazione.

1. Validazione e Pulizia degli Input Solidi

Anche prima che un agente inizi a elaborare, valida e pulisci l’input utente. Non si tratta solo di prevenire attacchi di iniezione; si tratta di assicurarsi che l’input sia in un formato utilizzabile e nei parametri attesi.

Esempio (Python/Pydantic per input strutturati) :

from pydantic import BaseModel, Field, ValidationError
from typing import Optional

class CreateTaskInput(BaseModel):
 title: str = Field(..., min_length=5, max_length=100, description="Titolo breve per il compito")
 description: Optional[str] = Field(None, max_length=500, description="Descrizione dettagliata del compito")
 due_date: Optional[str] = Field(None, pattern=r"^\d{4}-\d{2}-\d{2}$", description="Data di scadenza del compito nel formato AAAA-MM-GG")
 priority: str = Field("medium", pattern=r"^(low|medium|high)$", description="Priorità del compito")

def process_task_creation(raw_input: dict):
 try:
 task_data = CreateTaskInput(**raw_input)
 # L'agente procede alla creazione del compito utilizzando task_data.title, ecc.
 print(f"Compito convalidato e pronto: {task_data.title}")
 return {"status": "success", "data": task_data.dict()}
 except ValidationError as e:
 error_details = []
 for error in e.errors():
 field = ".".join(map(str, error['loc']))
 error_details.append(f"Campo '{field}': {error['msg']}")
 print(f"Errore di validazione dell'input: {'; '.join(error_details)}")
 return {"status": "error", "message": f"Input invalido fornito. Dettagli: {'; '.join(error_details)}"}

# Casi di test
process_task_creation({"title": "Corto", "due_date": "2023-13-01"})
process_task_creation({"title": "Pianificare la riunione di avvio del progetto", "description": "Scrivere l'ordine del giorno e invitare i principali interessati.", "due_date": "2023-11-15", "priority": "high"})

Spiegazione : Pydantic consente di definire schemi rigorosi per gli input attesi. Se l’input grezzo non rispetta questi criteri, viene sollevata una ValidationError, fornendo messaggi di errore chiari e strutturati che possono essere trasmessi all’utente o utilizzati per la registrazione interna.

2. Progettazione Difensiva degli Strumenti con Pre/Post-Condizioni

Ogni strumento che un agente può utilizzare deve essere progettato in modo difensivo. Ciò include la definizione di precondizioni chiare (ciò che deve essere vero prima che lo strumento venga chiamato) e di post-condizioni (ciò che dovrebbe essere vero dopo che lo strumento ha eseguito con successo).

Esempio (Python con verifiche esplicite) :

class InventoryManager:
 def __init__(self, stock: dict):
 self.stock = stock

 def get_item_quantity(self, item_name: str) -> int:
 return self.stock.get(item_name, 0)

 def update_item_quantity(self, item_name: str, quantity_change: int) -> dict:
 # Pre-condizione: L'oggetto deve esistere se quantity_change è negativo
 if quantity_change < 0 and self.get_item_quantity(item_name) + quantity_change < 0:
 raise ValueError(f"Stock insufficiente per {item_name}. Impossibile ridurre di {abs(quantity_change)}.")
 
 # Pre-condizione: Il cambiamento di quantità deve essere non nullo
 if quantity_change == 0:
 return {"status": "no_change", "message": f"Nessun cambiamento di quantità richiesto per {item_name}."}

 initial_quantity = self.get_item_quantity(item_name)
 self.stock[item_name] = initial_quantity + quantity_change
 
 # Post-condizione: La quantità dovrebbe essere cambiata come previsto
 if self.stock[item_name] != initial_quantity + quantity_change:
 raise RuntimeError(f"Fallimento nell'aggiornamento della quantità per {item_name}. Atteso {initial_quantity + quantity_change}, ottenuto {self.stock[item_name]}")

 return {"status": "success", "item": item_name, "new_quantity": self.stock[item_name]}

inventory = InventoryManager({"apple": 10, "banana": 5})

try:
 print(inventory.update_item_quantity("apple", -12)) # Dovrebbe sollevare un errore
except ValueError as e:
 print(f"Errore: {e}")

try:
 print(inventory.update_item_quantity("banana", 3))
except Exception as e:
 print(f"Errore: {e}")

Spiegazione : La funzione update_item_quantity verifica esplicitamente il magazzino insufficiente prima di tentare un aggiornamento. Le post-condizioni possono verificare lo stato dopo un'operazione, catturando così effetti collaterali inaspettati o fallimenti. Questa progettazione rende gli strumenti più solidi di per sé, riducendo il carico sul ragionamento di livello superiore dell'agente.

3. Riformulazione e Chiarificazione Semantica degli Input

A volte, l'input non è strettamente invalido ma ambiguo. Un agente può tentare di riformulare o chiedere chiarimenti in modo proattivo.

Esempio (Interazione concettuale con LLM) :


{
 "user_input": "Trova buoni ristoranti.",
 "agent_thought": "L'utente vuole ristoranti, ma 'buoni' è soggettivo e nessuna localizzazione è fornita. Ho bisogno di più informazioni.",
 "agent_action": {
 "type": "ask_clarification",
 "question": "Per aiutarti a trovare i migliori ristoranti, potresti dirmi che tipo di cucina ti interessa e in quale città o quartiere ti trovi?"
 }
}

Spiegazione : Invece di fallire, l'agente identifica l'ambiguità e avvia un dialogo per raccogliere il contesto necessario. Questo impedisce a uno strumento di ricerca a valle di ricevere una richiesta sottospecificata e di fallire.

Recupero Reattivo degli Errori : Strategie per Quando le Cose Vanno Storte

Nonostante le misure proattive, si verificheranno errori. Le strategie reattive si concentrano sulla rilevazione degli errori, sulla comprensione della loro causa e sull'adozione di misure correttive.

1. Classificazione degli Errori Contestuali e Meccanismi di Retry Dinamici

Non tutti gli errori sono uguali. Un errore di limite di tasso API richiede una risposta diversa rispetto a un errore di parametro invalido. Gli agenti devono classificare gli errori e applicare una logica di retry appropriata.

Esempio (Python con backoff e classificazione) :


import time
import requests
from requests.exceptions import RequestException, HTTPError

def call_external_api(url, params, max_retries=3, initial_delay=1):
 for attempt in range(max_retries):
 try:
 response = requests.get(url, params=params, timeout=5)
 response.raise_for_status() # Solleva un HTTPError per le risposte errate (4xx o 5xx)
 return response.json()
 except HTTPError as e:
 if e.response.status_code == 429: # Limite di rate
 print(f"Limite di rate raggiunto. Nuovo tentativo tra {initial_delay}s...")
 time.sleep(initial_delay)
 initial_delay *= 2 # Backoff esponenziale
 continue
 elif 400 <= e.response.status_code < 500: # Errore del cliente (ad esempio, richiesta errata)
 print(f"Errore del cliente: {e.response.status_code} - {e.response.text}. Nessun nuovo tentativo.")
 raise # Solleva immediatamente, probabilmente un'input errata
 elif 500 <= e.response.status_code < 600: # Errore del server
 print(f"Errore del server: {e.response.status_code}. Nuovo tentativo tra {initial_delay}s...")
 time.sleep(initial_delay)
 initial_delay *= 2
 continue
 except RequestException as e:
 print(f"Errore di rete o di richiesta generale: {e}. Nuovo tentativo tra {initial_delay}s...")
 time.sleep(initial_delay)
 initial_delay *= 2
 continue
 except Exception as e:
 print(f"Errore imprevisto: {e}. Nessun nuovo tentativo.")
 raise

 raise TimeoutError(f"Chiamata all'API fallita dopo {max_retries} tentativi.")

# Esempio di utilizzo (simulando un'API con limite di rate)
# class MockResponse:
# def __init__(self, status_code, text):
# self.status_code = status_code
# self.text = text
# def raise_for_status(self): 
# if 400 <= self.status_code < 600: raise HTTPError(response=self)
# def json(self): return {"data": "success"}

# # Simulare requests.get
# def mock_get(*args, **kwargs):
# if mock_get.call_count < 2:
# mock_get.call_count += 1
# return MockResponse(429, "Troppe richieste")
# return MockResponse(200, "OK")
# mock_get.call_count = 0

# requests.get = mock_get # Patch per requests.get nella dimostrazione

# try:
# result = call_external_api("http://api.example.com/data", {"query": "test"})
# print(f"Chiamata all'API riuscita: {result}")
# except Exception as e:
# print(f"Fallimento della chiamata all'API: {e}")

Spiegazione: Questa funzione categorizza gli errori HTTP (limiti di rate, errori del cliente, errori del server) e i problemi di rete. Applica un backoff esponenziale per gli errori temporanei (limiti di rate, errori del server, problemi di rete) ma solleva immediatamente per gli errori lato cliente, supponendo che l'input nella chiamata API stessa fosse errato e che un nuovo tentativo non risolverà il problema.

2. Autocorrezione tramite Re-prompting e Riflessione LLM

Quando il ragionamento interno di un agente o l'uso di uno strumento fallisce, il LLM stesso può essere utilizzato per riflettere e autocorreggersi.

Esempio (Ciclo di agente concettuale con riflessione):


def agent_step(agent_state, tools):
 try:
 # 1. Il LLM genera un piano/chiamata allo strumento
 action = llm_predict_action(agent_state.current_goal, agent_state.history)
 
 # 2. Eseguire l'azione (ad esempio, chiamare uno strumento)
 tool_output = execute_tool(action.tool_name, action.tool_args, tools)
 
 # 3. Aggiornare lo stato e continuare
 agent_state.add_to_history(action, tool_output)
 return agent_state

 except (ToolError, ReasoningError, TokenLimitExceeded) as e:
 error_message = str(e)
 print(f"L'agente ha incontrato un errore: {error_message}. Avvio della riflessione...")

 # 4. Il LLM riflette sull'errore
 reflection_prompt = f"L'agente ha tentato un'azione e ha fallito con il seguente errore: '{error_message}'. L'obiettivo attuale è '{agent_state.current_goal}'. Esaminare la storia dell'agente e l'errore. Identificare la causa principale e suggerire un nuovo piano o un'azione modificata per riprendersi. Essere specifici."
 reflection_response = llm_reflect(agent_state.history, error_message, agent_state.current_goal)

 # 5. Il LLM genera un'azione di recupero basata sulla riflessione
 recovery_action = llm_predict_action_from_reflection(reflection_response, agent_state.current_goal)
 
 # 6. Tentare il recupero
 try:
 recovered_tool_output = execute_tool(recovery_action.tool_name, recovery_action.tool_args, tools)
 agent_state.add_to_history(recovery_action, recovered_tool_output)
 print("L'agente si è ripreso con successo dall'errore.")
 return agent_state
 except Exception as recovery_e:
 print(f"L'agente ha fallito nel recupero: {recovery_e}. Escalation...")
 raise AgentFatalError(f"Fallimento dopo tentativo di recupero: {recovery_e}")

class ToolError(Exception): pass
class ReasoningError(Exception): pass
class TokenLimitExceeded(Exception): pass
class AgentFatalError(Exception): pass

def llm_predict_action(goal, history): 
 # Implementazione simulata
 if "search for" in goal and not any("location" in h for h in history): 
 raise ReasoningError("Luogo mancante per la richiesta di ricerca.")
 return type('Action', (object,), {'tool_name': 'search_tool', 'tool_args': {'query': goal}})

def execute_tool(tool_name, args, tools):
 # Implementazione simulata
 if tool_name == 'search_tool' and 'location' not in args['query']:
 raise ToolError("Lo strumento di ricerca richiede un luogo.")
 return {"result": "search_results"}

def llm_reflect(history, error_msg, goal):
 # Logica di riflessione simulata
 if "Location missing" in error_msg:
 return "Il tentativo precedente è fallito perché la richiesta di ricerca mancava di un luogo. Devo chiedere all'utente un luogo innanzitutto, o dedurlo dal contesto."
 return "Errore sconosciuto. Prova a semplificare la richiesta."

def llm_predict_action_from_reflection(reflection_response, goal):
 # Azione simulata dalla riflessione
 if "ask the user for a location" in reflection_response:
 return type('Action', (object,), {'tool_name': 'ask_user', 'tool_args': {'question': 'Quale luogo ti interessa?'}})
 return type('Action', (object,), {'tool_name': 'fallback_search', 'tool_args': {'query': goal + ' in un luogo generico'}})

# Simulare l'esecuzione dell'agente
class AgentState:
 def __init__(self, goal):
 self.current_goal = goal
 self.history = []
 def add_to_history(self, action, output):
 self.history.append({"action": action.__dict__, "output": output})

agent_tools = {}
initial_state = AgentState("cercare buoni ristoranti")

try:
 next_state = agent_step(initial_state, agent_tools)
 print("Stato dell'agente dopo il passo:", next_state.history)
except AgentFatalError as e:
 print(f"Errore fatale dell'agente: {e}")

Spiegazione: Quando si verifica un errore, l'agente non si limita a fallire. Restituisce il messaggio di errore, il suo obiettivo attuale e la sua storia di interazione a un LLM, chiedendogli di analizzare il fallimento, identificare la causa principale e proporre una strategia correttiva. Questo consente all'agente di adattare dinamicamente il suo piano.

3. Meccanismi di Backup e Degradata Elegante

Per funzionalità critiche, implementare opzioni di backup. Se uno strumento principale o una fonte di dati fallisce, l'agente dovrebbe avere un'alternativa degradata ma ancora funzionale.

  • Backup dello Strumento: Se un'API di ricerca sofisticata fallisce, tornare a una ricerca per parole chiave più semplice o a una base di conoscenza interna.
  • Backup dei Dati: Se il recupero di dati in tempo reale fallisce, utilizzare dati memorizzati nella cache o storici, informando esplicitamente l'utente sulla freschezza dei dati.
  • Backup LLM: Se un LLM potente e costoso fallisce o raggiunge limiti di rate, passare a un modello più piccolo, più veloce o ospitato localmente per compiti più semplici o per gestire errori.

Esempio (Concettuale):


{
 "agent_thought": "Tentativo di recupero del prezzo delle azioni in tempo reale per AAPL tramite 'FinancialDataAPI'.",
 "tool_call": {
 "name": "FinancialDataAPI.get_stock_price",
 "args": {"symbol": "AAPL"}
 },
 "tool_output": {
 "error": "API_UNAVAILABLE",
 "message": "Il servizio di dati finanziari esterni è attualmente non disponibile."
 },
 "agent_recovery_thought": "FinancialDataAPI ha fallito. Proverò a utilizzare dati memorizzati nella cache o uno 'HistoricalDataTool' più semplice e informerò l'utente del potenziale ritardo/anzianità.",
 "recovery_action": {
 "type": "tool_call",
 "name": "HistoricalDataTool.get_last_known_price",
 "args": {"symbol": "AAPL"}
 },
 "user_message": "Mi dispiace, il servizio di dati finanziari in tempo reale è temporaneamente non disponibile. Posso fornire l'ultimo prezzo conosciuto di un'ora fa: $X.XX. Ti andrebbe bene?"
}

Apprendimento e Miglioramento Continuo: Trasformare i Fallimenti in Forze

La gestione degli errori non dovrebbe essere un processo statico. Ogni errore è un'opportunità per l'agente e i suoi sviluppatori di apprendere e migliorare.

1. Registrazione e Osservabilità Dettagliate

Una registrazione dettagliata è la base per comprendere il comportamento dell'agente e i fallimenti. Registrare:

  • Le input dell'utente, i pensieri intermedi dell'agente, le chiamate agli strumenti e le uscite degli strumenti.
  • Tutti gli errori: tipo, messaggio, traccia dello stack e contesto pertinente (ad esempio, obiettivo attuale, stato dell'agente).
  • Tentativi di recupero: quale strategia è stata provata e quale è stato il suo risultato.

Journalizzazione Avanzata: Utilizza la registrazione strutturata (ad esempio, log JSON) per una facile analisi e parsing. Integra con piattaforme di osservabilità (ad esempio, Datadog, Splunk, dashboard personalizzati) per visualizzare le tendenze degli errori e le prestazioni dell'agente.

2. Report e Allerta Automatizzati degli Errori

Gli errori critici devono attivare avvisi per gli operatori umani. Questo consente un intervento rapido e previene periodi prolungati di malfunzionamento dell'agente.

  • Definisci soglie per i tassi di errore o per specifici tipi di errore.
  • Integra con Slack, PagerDuty, e-mail, ecc.
  • Includi abbastanza contesto negli avvisi affinché gli sviluppatori possano diagnosticare rapidamente.

3. Analisi Post-Mortem e Identificazione della Causa Radice

Controlla regolarmente i log, in particolare per errori comuni o critici. Esegui analisi post-mortem per comprendere:

  • L'errore era evitabile? Se sì, come possiamo migliorare le misure proattive?
  • Il meccanismo di recupero è stato efficace? Può essere migliorato?
  • Ci sono nuovi modelli di errore che emergono e necessitano di un trattamento specifico?

4. Affinamento e Apprendimento per Rinforzo dai Feedback Umani (RLHF)

Per gli errori legati al ragionamento del LLM o alla selezione degli strumenti:

  • Raccolta di tracce di errore: Raccogli esempi in cui il LLM ha preso una decisione errata o non è riuscito a recuperare.
  • Annotazione umana: Fai intervenire gli umani per fornire l'azione o il ragionamento corretto per questi casi falliti.
  • Affinamento fine: Utilizza questi esempi corretti per perfezionare il LLM sottostante dell'agente, insegnandogli a evitare gli errori passati e a generalizzare meglio le strategie di recupero.
  • RLHF: Incorpora i feedback umani sulla qualità dei tentativi di recupero come segnale di ricompensa per affinare ulteriormente il comportamento dell'agente.

Esempio (Punto dati concettuale RLHF):


{
 "context": [
 {"role": "user", "content": "Prenotami un volo per Londra."}, 
 {"role": "agent_thought", "content": "L'utente vuole un volo. Necessito della città di partenza e della data."}, 
 {"role": "tool_call", "content": "ask_user(question='Qual è la tua città di partenza e la tua data preferita?')"}
 ],
 "error": {
 "type": "ReasoningError",
 "message": "L'agente ha fallito nell'inferire la città di partenza dal contesto, nonostante la conversazione precedente in cui l'utente ha menzionato 'New York'."
 },
 "human_correction": {
 "action": {"type": "tool_call", "name": "FlightBookingTool.search_flights", "args": {"origin": "New York", "destination": "London", "date": ""}},
 "reasoning": "L'agente avrebbe dovuto ricordare 'New York' durante il precedente turno di conversazione. Il LLM ha bisogno di una migliore retention del contesto."
 },
 "reward_signal": -1.0, # Ricompensa negativa per la mancata utilizzazione del contesto
 "proposed_recovery": {
 "action": {"type": "tool_call", "name": "ask_user_clarification", "args": {"question": "Hai menzionato New York in precedenza. È ancora la tua città di partenza?"}}
 }
}

Conclusione: Verso agenti autonomi e resilienti

Costruire un sistema di gestione degli errori avanzato per agenti non è un compito triviale. Richiede un approccio multilivello che abbraccia la prevenzione proattiva, il recupero reattivo intelligente e un impegno per l'apprendimento continuo. Implementando una valida convalida degli input, un design degli strumenti difensivo, meccanismi di ripetizione dinamici, auto-correzione guidata da LLM e una profonda osservabilità, puoi trasformare i tuoi agenti IA da sistemi fragili in entità autonome e resilienti in grado di affrontare le complessità imprevedibili del mondo reale. L'obiettivo non è eliminare gli errori, ma consentire agli agenti di adattarsi con grazia, imparare e avere successo anche di fronte all'avversità, superando così i limiti di ciò che l'IA autonoma può realizzare.

🕒 Published:

✍️
Written by Jake Chen

AI technology writer and researcher.

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