\n\n\n\n Gestione degli errori dell’agente: Una guida avanzata per sistemi di IA affidabili - AiDebug \n

Gestione degli errori dell’agente: Una guida avanzata per sistemi di IA affidabili

📖 7 min read1,237 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 – che provengano da un’input utente ambiguo, da risposte inaspettate di un sistema esterno, da allucinazioni del modello o da difetti logici nel ragionamento dell’agente – sono una realtà ineludibile. Un vero agente IA solido non è colui che non incontra mai errori, ma colui che può rilevarli, diagnosticarli e riprendersi con grazia, minimizzando così le interruzioni e massimizzando il completamento dei compiti.

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

Comprendere lo Spazio degli Errori dell’Agente

Prima di poter affrontare gli errori in modo efficace, dobbiamo classificarli. Gli errori dell’agente si dividono spesso in diverse categorie chiave:

  • Errori di Input: Input utente malformati, ambigui, contraddittori o fuori campo d’applicazione.
  • Errori di Strumenti/API: Indisponibilità del servizio esterno, parametri API errati, limitazioni di frequenza, formati di dati inattesi, fallimenti di autenticazione.
  • Errori di Ragionamento/Logica: Agente che interpreta male il suo obiettivo, allucinazioni di fatti, si ritrova bloccato in cicli, non riesce a trovare uno strumento appropriato o prende decisioni errate basate sul suo stato interno.
  • Errori Contestuali: Agente che perde il filo della cronologia della conversazione, interpreta male i turni precedenti o non riesce a integrare informazioni esterne rilevanti.
  • Errori di Risorse: Mancanza di memoria, superamento dei limiti di token per i LLM, o problemi di timeout.
  • Errori di Sicurezza/Allineamento: Generazione di contenuti dannosi, distorti 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 minimizzare la probabilità di errori mediante progettazione e validazione.

1. Validazione e Sanitizzazione Solide degli Input

Anche prima che un agente inizi a elaborare, valida e sanifica le 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 usando task_data.title, ecc.
 print(f"Compito validato 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 non valido. Dettagli: {'; '.join(error_details)}"}

# Caso di test
process_task_creation({"title": "Corto", "due_date": "2023-13-01"})
process_task_creation({"title": "Pianificare la riunione di avvio del progetto", "description": "Redigere l'ordine del giorno e invitare le parti interessate chiave.", "due_date": "2023-11-15", "priority": "high"})

Spiegazione: Pydantic consente di definire schemi rigidi per le input attese. Se l’input grezzo non corrisponde, viene sollevata una ValidationError, fornendo messaggi di errore chiari e strutturati che possono essere restituiti 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 deve essere vero dopo che lo strumento è stato 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:
 # Precondizione: l'elemento deve esistere se quantity_change è negativo
 if quantity_change < 0 and self.get_item_quantity(item_name) + quantity_change < 0:
 raise ValueError(f"Scorte insufficienti per {item_name}. Impossibile ridurre di {abs(quantity_change)}.")
 
 # Precondizione: il cambiamento di quantità deve essere diverso da zero
 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 dell'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 se le scorte sono insufficienti prima di tentare un aggiornamento. Le post-condizioni possono controllare lo stato dopo un'operazione, catturando effetti collaterali o fallimenti inaspettati. Questa progettazione rende gli strumenti più solidi in sé, riducendo così il carico sul ragionamento di livello superiore dell'agente.

3. Riformulazione e Chiarificazione Semantica degli Input

In alcuni casi, un'input non è strettamente invalida ma ambigua. Un agente può cercare di riformulare o di chiedere chiarimenti in modo proattivo.

Esempio (Interazione concettuale con un LLM) :


{
 "user_input": "Trovatemi dei buoni ristoranti.",
 "agent_thought": "L'utente vuole dei ristoranti, ma 'buoni' è soggettivo e non è fornita alcuna localizzazione. Ho bisogno di ulteriori informazioni.",
 "agent_action": {
 "type": "ask_clarification",
 "question": "Per aiutarti a trovare i migliori ristoranti, potresti dirmi che tipo di cucina preferisci e in quale città o quartiere sei interessato?"
 }
}

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 mal definita e di fallire.

Recupero Reattivo dagli 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 Riprova Dinamici

Non tutti gli errori sono uguali. Un errore di limite di frequenza API richiede una risposta diversa da un errore di parametro invalido. Gli agenti devono classificare gli errori e applicare una logica di riprova 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 HTTPError per risposte errate (4xx o 5xx)
 return response.json()
 except HTTPError as e:
 if e.response.status_code == 429: # Limite di frequenza
 print(f"Limite di frequenza raggiunto. Nuovo tentativo tra {initial_delay}s...")
 time.sleep(initial_delay)
 initial_delay *= 2 # Ritorno esponenziale
 continue
 elif 400 <= e.response.status_code < 500: # Errore del cliente (ad es., richiesta errata)
 print(f"Errore del cliente: {e.response.status_code} - {e.response.text}. Nessun nuovo tentativo.")
 raise # Rilancia immediatamente, probabilmente un input errato
 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 errore generale della richiesta: {e}. Nuovo tentativo tra {initial_delay}s...")
 time.sleep(initial_delay)
 initial_delay *= 2
 continue
 except Exception as e:
 print(f"Errore imprevista: {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 frequenza)
# 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 requests.get per la 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"Chiamata all'API fallita: {e}")

Spiegazione: Questa funzione categoriza gli errori HTTP (limiti di frequenza, errori del cliente, errori del server) e problemi di rete. Applica una strategia di ritorno esponenziale per gli errori transitori (limiti di frequenza, errori del server, problemi di rete) ma rilancia immediatamente gli errori del cliente, supponendo che l'input della chiamata all'API fosse errato e che un nuovo tentativo non risolverà il problema.

2. Autosufficienza tramite Ripetizione e Riflessione di LLM

Quando un ragionamento interno di un agente o l'utilizzo di strumenti fallisce, il LLM può essere utilizzato per riflettere e auto-corrigersi.

Esempio (Ciclo di Agente Concettuale con Riflessione):


def agent_step(agent_state, tools):
 try:
 # 1. LLM genera un piano/chiamata di strumento
 action = llm_predict_action(agent_state.current_goal, agent_state.history)
 
 # 2. Esegui l'azione (ad esempio, chiamare uno strumento)
 tool_output = execute_tool(action.tool_name, action.tool_args, tools)
 
 # 3. Aggiorna lo stato e continua
 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}. Inizio della riflessione...")

 # 4. LLM riflette sull'errore
 reflection_prompt = f"L'agente ha appena tentato un'azione ed è fallito con il seguente errore: '{error_message}'. L'obiettivo attuale è '{agent_state.current_goal}'. Rivedere la cronologia dell'agente e l'errore. Identificare la causa principale e suggerire un nuovo piano o un'azione modificata per recuperare. Sii specifico."
 reflection_response = llm_reflect(agent_state.history, error_message, agent_state.current_goal)

 # 5. 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 è riuscito a recuperare dall'errore.")
 return agent_state
 except Exception as recovery_e:
 print(f"L'agente non è riuscito a recuperare: {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 "cercare" in goal and not any("posizione" in h for h in history): 
 raise ReasoningError("Posizione 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 'posizione' not in args['query']:
 raise ToolError("Lo strumento di ricerca richiede una posizione.")
 return {"result": "search_results"}

def llm_reflect(history, error_msg, goal):
 # Logica di riflessione simulata
 if "Posizione mancante" in error_msg:
 return "Il tentativo precedente è fallito perché la richiesta di ricerca mancava di una posizione. Devo prima chiedere all'utente una posizione, o dedurla dal contesto."
 return "Errore sconosciuto. Prova a semplificare la richiesta."

def llm_predict_action_from_reflection(reflection_response, goal):
 # Azione simulata a partire dalla riflessione
 if "chiedere all'utente una posizione" in reflection_response:
 return type('Action', (object,), {'tool_name': 'ask_user', 'tool_args': {'question': 'Quale posizione ti interessa?'}})
 return type('Action', (object,), {'tool_name': 'fallback_search', 'tool_args': {'query': goal + ' in una posizione generica'}})

# 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 passaggio:", 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. Rimanda il messaggio di errore, il suo obiettivo attuale e la sua cronologia di interazione a un LLM, spingendolo ad analizzare il fallimento, identificare la causa principale e proporre una strategia correttiva. Questo permette all'agente di adattare dinamicamente il suo piano.

3. Meccanismi di Recupero e Degrado Grazioso

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

  • Recupero di Strumento: Se un'API di ricerca sofisticata fallisce, tornare a una ricerca per parole chiave più semplice o a una base di conoscenze interna.
  • Recupero di 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.
  • Recupero LLM: Se un LLM potente e costoso fallisce o raggiunge limiti di frequenza, passare a un modello più piccolo, più veloce o ospitato localmente per compiti più semplici o gestione degli errori.

Esempio (Concettuale):


{
 "agent_thought": "Tentativo di recupero del prezzo delle azioni in tempo reale per AAPL utilizzando 'FinancialDataAPI'.",
 "tool_call": {
 "name": "FinancialDataAPI.get_stock_price",
 "args": {"symbol": "AAPL"}
 },
 "tool_output": {
 "error": "API_UNAVAILABLE",
 "message": "Il servizio di dati finanziari esterni è attualmente offline."
 },
 "agent_recovery_thought": "FinancialDataAPI ha fallito. Proverò a utilizzare dati memorizzati nella cache o un 'HistoricalDataTool' più semplice e informerò l'utente del potenziale ritardo/datamento obsoleto.",
 "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. Va bene per te?"
}

Apprendimento Continuo e Miglioramento: 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 Approfondita e Osservabilità

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

  • Le voci degli utenti, i pensieri intermedi dell'agente, le chiamate agli strumenti e le uscite degli strumenti.
  • Tutti gli errori: tipo, messaggio, traccia della pila e contesto rilevante (ad esempio, obiettivo attuale, stato dell'agente).
  • Tentativi di recupero: quale strategia è stata tentata e il suo risultato.

Registrazione Avanzata: Utilizza una registrazione strutturata (ad esempio, registri JSON) per una migliore analisi e parsing facilitato. Integra piattaforme di osservabilità (ad esempio, Datadog, Splunk, dashboard personalizzati) per visualizzare le tendenze degli errori e le prestazioni dell'agente.

2. Segnalazione e Allerta Automatica degli Errori

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

  • Definire soglie per i tassi di errore o per tipi di errore specifici.
  • Integrare con Slack, PagerDuty, email, ecc.
  • Includere un contesto sufficiente nelle allerte affinché gli sviluppatori possano diagnosticare rapidamente.

3. Analisi Post-Mortem e Identificazione della Causa Radice

Rivedere regolarmente i registri, specialmente per i fallimenti comuni o critici. Condurre analisi post-mortem per comprendere:

  • L'errore era evitabile? Se sì, come possiamo migliorare le misure proattive?
  • Il meccanismo di recupero è stato efficace? Potrebbe essere migliorato?
  • Appaiono nuovi modelli di errore che richiedono un trattamento specifico?

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

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

  • Raccolta delle tracce di errore: Raccogliere esempi in cui il LLM ha preso una decisione errata o non è riuscito a recuperarsi.
  • Annotazione umana: Far sì che gli esseri umani forniscano l'azione o il ragionamento corretto per questi casi di fallimento.
  • Adattamento fine: Utilizzare questi esempi corretti per adattare il LLM sottostante dell'agente, insegnandogli a evitare errori passati e a generalizzare meglio le strategie di recupero.
  • RLHF: Integrare il feedback umano sulla qualità dei tentativi di recupero come segnale di ricompensa per affinare ulteriormente il comportamento dell'agente.

Esempio (punto di dato concettuale RLHF):


{
 "context": [
 {"role": "user", "content": "Prenotami un volo per Londra."}, 
 {"role": "agent_thought", "content": "L'utente vuole un volo. Need departure city and date."}, 
 {"role": "tool_call", "content": "ask_user(question='Qual è la tua città di partenza e quale data preferisci?')"}
 ],
 "error": {
 "type": "ReasoningError",
 "message": "L'agente non è riuscito a 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' dello scambio precedente nella conversazione. Il LLM ha bisogno di una migliore retention di contesto."
 },
 "reward_signal": -1.0, # Ricompensa negativa per il fallimento nell'utilizzare il 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 avanzato di gestione degli errori dell’agente non è un compito semplice. Richiede un approccio multilivello che include prevenzione proattiva, recupero reattivo intelligente e un impegno nell'apprendimento continuo. Implementando una validazione d'ingresso solida, un design di strumenti difensivi, meccanismi di ripetizione dinamici, auto-correzione guidata dal LLM e un'osservabilità approfondita, puoi trasformare i tuoi agenti IA da sistemi fragili in entità autonome e altamente resilienti in grado di affrontare le complessità imprevedibili del mondo reale. L'obiettivo non è eliminare gli errori, ma permettere agli agenti di adattarsi con facilità, apprendere e, infine, avere successo anche di fronte alle avversità, spingendo così i limiti di ciò che l'IA autonoma può raggiungere.

🕒 Published:

✍️
Written by Jake Chen

AI technology writer and researcher.

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