\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,236 wordsUpdated Apr 4, 2026

Introduzione : La Realtà Inevitabile degli Errori nell’IA Agentica

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, risposte inaspettate da un sistema esterno, allucinazioni del modello o difetti logici nel ragionamento dell’agente – sono una realtà inevitabile. Un vero agente IA solido non è quello che non incontra mai errori, ma quello che può rilevarli, diagnosticarli e riprendersi con grazia, minimizzando così le interruzioni e massimizzando il raggiungimento degli obiettivi.

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

Comprendere lo Spazio degli Errori dell’Agente

Prima di poter gestire gli errori in modo efficace, dobbiamo categorizarli. Gli errori degli agenti si dividono spesso in diverse categorie chiave :

  • Errori di Input : Input utente malformati, ambigui, contraddittori o fuori dal campo di applicazione.
  • Errori di Strumenti/API : Indisponibilità del servizio esterno, parametri API errati, limitazioni di frequenza, formati di dati inaspettati, fallimenti di autenticazione.
  • Errori di Ragionamento/Logica : Agente che interpreta male il suo obiettivo, allucinazioni di fatti, bloccato in loop, incapace di trovare uno strumento appropriato o che 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, 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 mediante progettazione e convalida.

1. Validazione e Sanitizzazione Solide degli Input

Anche prima che un agente inizi a elaborare, valida e sanitizza gli input utente. Non si tratta solo di prevenire attacchi di injection; si tratta di garantire che l’input sia in un formato utilizzabile e all’interno dei 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)}"}

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

Spiegazione : Pydantic consente di definire schemi rigorosi per gli input attesi. 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. Questo include la definizione di precondizioni chiare (ciò che deve essere vero prima che lo strumento venga chiamato) e post-condizioni (ciò che deve essere vero dopo che lo strumento è stato eseguito con successo).

Esempio (Python con controlli espliciti) :

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'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)}.")
 
 # Pre-condizione : 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"Impossibile aggiornare la 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

A volte, un input non è strettamente invalido ma ambiguo. Un agente può provare a riformulare o chiedere chiarimenti in modo proattivo.

Esempio (Interazione concettuale con un LLM) :


{
 "user_input": "Trovami dei buoni ristoranti.",
 "agent_thought": "L'utente vuole ristoranti, ma 'buoni' è soggettivo e non è fornita alcuna localizzazione. Ho bisogno di più 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 query male definita e di fallire.

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

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 rispetto a un errore di parametro non valido. 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 # Riduzione 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 # Ri-sollevare 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 inaspettata: {e}. Nessun nuovo tentativo.")
 raise

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

# Esempio d'uso (simulando un'API limitata dalla 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 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 i problemi di rete. Applica una strategia di riduzione esponenziale per gli errori temporanei (limiti di frequenza, errori del server, problemi di rete), ma restituisce immediatamente gli errori del cliente, assumendo che l'input della chiamata all'API fosse errato e che un nuovo tentativo non risolverà il problema.

2. Auto-Correzione tramite Ripetizione e Riflessione del LLM

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

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. 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}. Inizio della riflessione...")

 # 4. Il LLM riflette sull'errore
 reflection_prompt = f"L'agente ha appena tentato un'azione e ha fallito con il seguente errore: '{error_message}'. L'obiettivo attuale è '{agent_state.current_goal}'. Esaminare la cronologia dell'agente e l'errore. Identificare la causa profonda e suggerire un nuovo piano o un'azione modificata per recuperare. 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 è riuscito a recuperare dall'errore.")
 return agent_state
 except Exception as recovery_e:
 print(f"L'agente non è riuscito a recuperare: {recovery_e}. Escalazione...")
 raise AgentFatalError(f"Fallimento dopo il 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. Cerca di semplificare la richiesta."

def llm_predict_action_from_reflection(reflection_response, goal):
 # Azione simulata 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 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. Riporta il messaggio di errore, il suo obiettivo attuale e la sua cronologia di interazione in un LLM, incitandolo ad analizzare il fallimento, identificare la causa profonda e proporre una strategia correttiva. Questo permette all'agente di adattare dinamicamente il suo piano.

3. Meccanismi di Recupero e Degradazione Gracevole

Per funzionalità critiche, implementare opzioni di recupero. Se uno strumento o una sorgente di dati principale fallisce, l'agente dovrebbe avere un'alternativa degradante ma ancora funzionante.

  • 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 della 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 per la gestione degli errori.

Esempio (Concettuale):


{
 "agent_thought": "Tentativo di recuperare il 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 possibile ritardo/data vecchia.",
 "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 noto di 1 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 imparare e migliorare.

1. Registrazione e Osservabilità Approfondite

Una registrazione dettagliata è la pietra miliare della comprensione del comportamento e dei fallimenti dell'agente. Registrare:

  • Le entrate degli utenti, le riflessioni intermedie dell'agente, le chiamate degli 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 il suo risultato.

Journalizzazione Avanzata: Utilizza una registrazione strutturata (ad esempio, log JSON) per un miglior parsing e un'analisi facilitata. 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 avvisi agli operatori umani. Questo consente un intervento rapido e previene periodi prolungati di malfunzionamento dell'agente.

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

3. Analisi Post-Mortem e Identificazione della Causa Radice

Esaminare regolarmente i log, specialmente per i fallimenti comuni o critici. Effettuare analisi post-mortem per comprendere:

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

4. Regolazione 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 riprendersi.
  • Annotazione umana: Assicurarsi che degli umani forniscano l'azione o il ragionamento corretto per questi casi di fallimento.
  • Regolazione fine: Utilizzare questi esempi corretti per affinare 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 dati 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' dallo scambio precedente nella conversazione. Il LLM ha bisogno di una migliore retenzione del contesto."
 },
 "reward_signal": -1.0, # Ricompensa negativa per 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 banale. Richiede un approccio multilivello che abbraccia la prevenzione proattiva, il recupero reattivo intelligente e un impegno per l'apprendimento continuo. Implementando una validazione dell'input solida, un design di strumenti difensivi, meccanismi di retry dinamici, autocorrezione guidata da LLM, e una profonda osservabilità, puoi trasformare i tuoi agenti IA da sistemi fragili a entità autonome e altamente resilienti capaci di navigare nelle complessità imprevedibili del mondo reale. L'obiettivo non è eliminare gli errori, ma consentire agli agenti di adattarsi con facilità, apprendere e, infine, avere successo anche di fronte all'avversità, espandendo così i confini 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