\n\n\n\n Gestione degli Errori dell'Agente: Una Guida Avanzata per Sistemi AI Affidabili - AiDebug \n

Gestione degli Errori dell’Agente: Una Guida Avanzata per Sistemi AI Affidabili

📖 7 min read1,227 wordsUpdated Apr 4, 2026

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

Man mano che gli agenti AI diventano sempre più sofisticati e autonomi, la loro capacità di navigare in ambienti complessi e reali è fondamentale. Tuttavia, il cammino verso un’operazione fluida è raramente privo di ostacoli. Gli errori – siano essi derivati da input utente ambigui, risposte inaspettate di sistemi esterni, allucinazioni del modello o difetti logici nel ragionamento dell’agente – sono una realtà ineludibile. Un agente AI davvero solido non è quello che non incontra mai un errore, ma quello che può rilevarli, diagnosticarli e recuperare con grazia, minimizzando le interruzioni 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 degli agenti resilienti. Copriremo prevenzione proattiva, recupero reattivo e apprendimento continuo, fornendoti gli strumenti per progettare agenti che non siano solo intelligenti, ma anche notevolmente solidi.

Comprendere lo Spazio degli Errori degli Agenti

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

  • Errori di Input: Prompts utente malformati, ambigui, contraddittori o fuori dal campo di applicazione.
  • Errori di Strumenti/API: Indisponibilità di servizi esterni, parametri API errati, limitazioni di frequenza, formati di dati inaspettati, fallimenti di autenticazione.
  • Errori di Ragionamento/Logica: L’agente che fraintende il suo obiettivo, allucina fatti, si blocca in cicli, non riesce a trovare uno strumento adeguato o prende decisioni errate basate sul suo stato interno.
  • Errori Contestuali: L’agente che perde il filo della cronologia della conversazione, fraintende i turni precedenti o non riesce a incorporare informazioni esterne rilevanti.
  • Errori di Risorse: Esaurimento della memoria, superamento dei limiti di token per gli LLM, o problemi di timeout.
  • Errori di Sicurezza/Allineamento: Generazione di contenuti dannosi, di parte o inappropriati; tentativi di azioni vietate.

Prevenzione Proattiva degli Errori: Costruire Resilienza sin dal Principio

Il miglior errore è quello che non si verifica mai. Le strategie proattive si concentrano sulla riduzione della probabilità di errori attraverso il design e la convalida.

1. Validazione e Sanitizzazione Solide degli Input

Prima che un agente inizi anche a elaborare, valida e sanitizza l’input utente. Non si tratta solo di prevenire attacchi di iniezione; 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 con la creazione del compito utilizzando 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 input: {'; '.join(error_details)}")
 return {"status": "error", "message": f"Input fornito non valido. Dettagli: {'; '.join(error_details)}"}

# Casi di test
process_task_creation({"title": "Breve", "due_date": "2023-13-01"})
process_task_creation({"title": "Pianifica la riunione di avvio del progetto", "description": "Bozza dell'agenda e invio degli inviti ai principali stakeholder.", "due_date": "2023-11-15", "priority": "high"})

Spiegazione: Pydantic consente di definire schemi rigorosi per l’input atteso. Se l’input grezzo non è conforme, viene sollevato un ValidationError, fornendo messaggi di errore chiari e strutturati che possono essere comunicati all’utente o utilizzati per il logging interno.

2. Design Difensivo degli Strumenti con Pre/Post-Condizioni

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

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'articolo 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 verificare lo stato dopo un'operazione, catturando effetti collaterali o fallimenti inaspettati. Questo design rende gli strumenti più solidi, riducendo il peso sul ragionamento di alto livello dell'agente.

3. Riformulazione Semantica degli Input e Richiesta di Chiarimenti

A volte, l'input non è strettamente non valido ma ambiguo. Un agente può tentare proattivamente di riformulare o chiedere chiarimenti.

Esempio (Interazione concettuale con LLM):


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

Spiegazione: Invece di fallire, l'agente identifica l'ambiguità e inizia un dialogo per raccogliere il contesto necessario. Questo previene che uno strumento di ricerca riceva una query poco specificata e fallisca.

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

Nonostante le misure proattive, si verificheranno errori. Le strategie reattive si concentrano sulla rilevazione degli errori, comprensione delle loro cause e assunzione di azioni correttive.

1. Classificazione Contestuale degli Errori 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 dovrebbero 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 velocità
 print(f"Limite di velocità raggiunto. Ritento tra {initial_delay}s...")
 time.sleep(initial_delay)
 initial_delay *= 2 # Riposo esponenziale
 continue
 elif 400 <= e.response.status_code < 500: # Errore del client (ad es., richiesta errata)
 print(f"Errore del client: {e.response.status_code} - {e.response.text}. Non riprovo.")
 raise # Solleva immediatamente, probabile input errato
 elif 500 <= e.response.status_code < 600: # Errore del server
 print(f"Errore del server: {e.response.status_code}. Ritento 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}. Ritento tra {initial_delay}s...")
 time.sleep(initial_delay)
 initial_delay *= 2
 continue
 except Exception as e:
 print(f"Errore imprevisto: {e}. Non riprovo.")
 raise

 raise TimeoutError(f"Impossibile chiamare l'API dopo {max_retries} tentativi.")

# Esempio di utilizzo (simulando un'API limitata)
# 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"}

# # Simula requests.get
# def mock_get(*args, **kwargs):
# if mock_get.call_count < 2:
# mock_get.call_count += 1
# return MockResponse(429, "Richieste Troppo Frequenti")
# 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 API riuscita: {result}")
# except Exception as e:
# print(f"Chiamata API fallita: {e}")

Spiegazione: Questa funzione classifica gli errori HTTP (limiti di velocità, errori del client, errori del server) e problemi di rete. Applica un riposo esponenziale per errori transitori (limiti di velocità, errori del server, problemi di rete) ma solleva immediatamente per errori lato client, assumendo che l'input alla chiamata API fosse errato e un nuovo tentativo non lo risolverebbe.

2. Autocorrezione tramite Riapparizioni e Riflessione LLM

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

Esempio (Ciclo dell'Agente Concettuale con Riflessione):


def agent_step(agent_state, tools):
 try:
 # 1. LLM genera un piano/chiamata allo strumento
 action = llm_predict_action(agent_state.current_goal, agent_state.history)
 
 # 2. Esegui l'azione (ad es., chiama 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 riflessione...")

 # 4. LLM riflette sull'errore
 reflection_prompt = f"L'agente ha appena tentato un'azione e non è riuscito con il seguente errore: '{error_message}'. L'obiettivo attuale è '{agent_state.current_goal}'. Esamina la storia dell'agente e l'errore. Identifica la causa principale e suggerisci 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. Tenta 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}. Innalzando...")
 raise AgentFatalError(f"Fallito 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 fittizia
 if "search for" in goal and not any("location" in h for h in history): 
 raise ReasoningError("Manca la posizione per la query di ricerca.")
 return type('Action', (object,), {'tool_name': 'search_tool', 'tool_args': {'query': goal}})

def execute_tool(tool_name, args, tools):
 # Implementazione fittizia
 if tool_name == 'search_tool' and 'location' 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 fittizia
 if "Manca la posizione" in error_msg:
 return "Il tentativo precedente è fallito perché la query di ricerca mancava di una posizione. Devo chiedere all'utente per una posizione prima, o dedurla dal contesto."
 return "Errore sconosciuto. Prova a semplificare la richiesta."

def llm_predict_action_from_reflection(reflection_response, goal):
 # Azione fittizia dalla riflessione
 if "ask the user for a location" 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 a generic location'}})

# Simula 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("search for good restaurants")

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 fallisce semplicemente. Alimenta indietro il messaggio di errore, il suo obiettivo attuale e la sua storia di interazione a un LLM, sollecitandolo ad analizzare il fallimento, identificare la causa principale e proporre una strategia correttiva. Questo consente all'agente di adattare dinamicamente il proprio piano.

3. Meccanismi di Fallback e Degradazione Graduale

Per funzionalità critiche, implementa opzioni di fallback. Se un strumento primario o una fonte di dati fallisce, l'agente dovrebbe avere un'alternativa degradata ma ancora funzionale.

  • Fallback dello Strumento: Se un'API di ricerca sofisticata fallisce, torna a una ricerca per parole chiave più semplice o a una base di conoscenza interna.
  • Fallback dei Dati: Se il recupero dei dati in tempo reale fallisce, utilizza dati memorizzati in cache o storici, informando esplicitamente l'utente sulla freschezza dei dati.
  • Fallback LLM: Se un LLM potente e costoso fallisce o raggiunge limiti di velocità, passa a un modello più piccolo, veloce o ospitato localmente per compiti più semplici o gestione degli errori.

Esempio (Concettuale):


{
 "agent_thought": "Tentativo di recuperare il prezzo delle azioni in tempo reale di 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 non disponibile."
 },
 "agent_recovery_thought": "FinancialDataAPI non ha funzionato. Proverò ad utilizzare dati memorizzati in cache o uno 'HistoricalDataTool' più semplice e informerò l'utente del potenziale ritardo/del ritardo.",
 "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. Sarebbe accettabile?"
}

Apprendimento Continuo e Miglioramento: Trasformare i Fallimenti in Punti di Forza

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. Logging Dettagliato e Osservabilità

Un logging dettagliato è la base per comprendere il comportamento e i fallimenti dell'agente. Registra:

  • Input dell'utente, pensieri intermedi dell'agente, chiamate agli strumenti e output degli strumenti.
  • Tutti gli errori: tipo, messaggio, traccia dello stack e contesto rilevante (ad es., obiettivo attuale, stato dell'agente).
  • Attività di recupero: quale strategia è stata tentata e qual è stato il suo esito.

Logging Avanzato: Utilizza il logging strutturato (ad es., log JSON) per una più facile analisi e parsing. Integra con piattaforme di osservabilità (ad es., Datadog, Splunk, dashboard personalizzate) per visualizzare le tendenze degli errori e le prestazioni dell'agente.

2. Segnalazione Automatica degli Errori e Allerta

Errori critici dovrebbero attivare allerta agli operatori umani. Questo consente un intervento tempestivo e previene periodi prolungati di malfunzionamento dell'agente.

  • Imposta soglie per tassi di errore o tipi di errore specifici.
  • Integra con Slack, PagerDuty, email, ecc.
  • Includi abbastanza contesto negli avvisi per consentire agli sviluppatori di diagnosticare rapidamente.

3. Analisi Post-Mortem e Identificazione della Causa Principale

Esamina regolarmente i log, specialmente per fallimenti comuni o critici. Conduci analisi post-mortem per capire:

  • L'errore era evitabile? Se sì, come possiamo migliorare le misure proattive?
  • Il meccanismo di recupero è stato efficace? Potrebbe essere migliorato?
  • Ci sono nuovi modelli di errori emergenti che richiedono una gestione specifica?

4. Messa a punto e Apprendimento per Rinforzo dal Feedback Umano (RLHF)

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

  • Raccogliere Tracce di Errore: Raccogli esempi in cui il LLM ha preso una decisione errata o non è riuscito a riprendersi.
  • Annotazione Umana: Far sì che esseri umani forniscano l'azione corretta o il ragionamento per questi casi falliti.
  • Fine-tuning: Utilizzare questi esempi corretti per fare il fine-tuning del LLM sottostante dell'agente, insegnandogli ad evitare errori passati e a generalizzare migliori 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 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 data preferita?')"}
 ],
 "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' dal turno precedente nella conversazione. Il LLM ha bisogno di una migliore ritenzione del 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 per agenti non è un compito banale. Richiede un approccio multilivello che comprende prevenzione proattiva, recupero reattivo intelligente e un impegno per l'apprendimento continuo. Implementando una solida validazione degli input, un design difensivo degli strumenti, meccanismi di riprova dinamici, autocorrezione guidata da LLM e una completa osservabilità, puoi trasformare i tuoi agenti AI da sistemi fragili a entità altamente resilienti e autonome, capaci di affrontare le complesse e imprevedibili sfide del mondo reale. L'obiettivo non è eliminare gli errori, ma consentire agli agenti di adattarsi con grazia, apprendere e, in ultima analisi, avere successo anche in situazioni avverse, spingendo i confini 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