Alejandro Rioja.
AI Agents

Perché il tuo Agente IA Continua a Fallire in Produzione (E Come Risolverlo)

Alejandro Rioja
Alejandro Rioja
6 min di lettura
TL;DR

La maggior parte dei guasti degli agenti in produzione deriva da cinque cause: prompt fragili che non gestiscono i casi limite, logica di retry mancante per errori API transitori, nessuna osservabilità per vedere cosa si rompe, loop incontrollabili senza condizione di uscita e definizioni di strumenti abbastanza ambigue da far scegliere al modello quella sbagliata. Tutti e cinque sono risolvibili senza cambiare modelli o framework.

Newsletter gratuita

Ogni mercoledì. 28.400+ operatori. Zero riempitivo.

Indice

Aggiornato giugno 2026.

TL;DR: La maggior parte dei guasti degli agenti in produzione deriva da cinque cause: prompt fragili che non gestiscono i casi limite, logica di retry mancante per errori API transitori, nessuna osservabilità per vedere cosa si rompe, loop incontrollabili senza condizione di uscita e definizioni di strumenti abbastanza ambigue da far scegliere al modello quella sbagliata. Tutti e cinque sono risolvibili senza cambiare modelli o framework.

[Lettura dell’operatore] Gestisco più di 30 agenti in produzione. Ho avuto tutti questi guasti. Quelli che hanno bruciato più tempo non erano quelli esotici — erano i noiosissimi guasti infrastrutturali che pensavo di aver gestito.

Guasto 1: Prompt fragili che si rompono su input di casi limite

Un prompt che funziona sui tuoi casi di test fallirà su input che non hai anticipato. Non è una limitazione del modello — è un problema di scrittura delle istruzioni.

Sintomi: L’agente produce output insensato, chiama lo strumento sbagliato o restituisce JSON malformato quando l’input è leggermente diverso da ciò che hai testato.

Causa principale: Il tuo system prompt descrive solo il percorso felice. Non dice al modello cosa fare quando i dati mancano, sono malformati o ambigui.

Correzione: Aggiungi gestione esplicita dei casi limite al tuo system prompt:

code
If the input data is missing a required field, return:
{ "status": "error", "reason": "missing_field", "field": "<fieldname>" }
Do NOT attempt to infer or hallucinate missing values.

If you are uncertain which tool to call, call no tool and return:
{ "status": "clarification_needed", "question": "..." }

Il modello segue le istruzioni esplicite per i casi limite in modo affidabile. L’errore è presumere che generalizzerà le istruzioni del percorso felice per gestire i casi disordinati.

Guasto 2: Nessuna logica di retry per errori API transitori

Ogni API esterna che il tuo agente chiama fallirà ad un certo punto. L’API di Claude, la Meta Graph API, il tuo database — tutte restituiscono errori 5xx, timeout o rate limit. Se il tuo agente non ha logica di retry, un errore transitorio uccide l’intera esecuzione.

Sintomi: Le esecuzioni degli agenti falliscono casualmente a diversi passaggi. I log mostrano un 503 o 429 senza tentativo di follow-up.

Correzione: Avvolgi ogni chiamata esterna in un retry con backoff esponenziale:

typescript
async function withRetry<T>(fn: () => Promise<T>, retries = 3, baseDelayMs = 500): Promise<T> {
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      return await fn();
    } catch (err: any) {
      const isTransient = err.status === 429 || err.status >= 500 || err.code === "ECONNRESET";
      if (!isTransient || attempt === retries) throw err;
      const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * 100;
      await new Promise((r) => setTimeout(r, delay));
    }
  }
  throw new Error("unreachable");
}

// Usage
const result = await withRetry(() => client.messages.create({ ... }));

Tre retry con backoff esponenziale gestisce ~99% dei guasti transitori. Aggiungi questo ad ogni chiamata esterna e metà dei tuoi guasti casuali scomparirà.

Guasto 3: Nessuna osservabilità — non riesci a vedere cosa si rompe

Questa è la modalità di guasto più comune in produzione e quella che costa più tempo nel debug: l’agente fallisce silenziosamente o produce output errato, e non hai idea di dove nella catena sia andato storto.

Sintomi: Sai che qualcosa non va ma non riesci a identificare il passaggio. Aggiungi istruzioni console.log e riesegui manualmente cercando di riprodurre.

Correzione: Logging strutturato ad ogni passaggio, con un ID di esecuzione che traccia l’intera esecuzione:

typescript
function createLogger(runId: string, agentName: string) {
  return {
    step: (step: string, data: object) =>
      console.log(JSON.stringify({ runId, agent: agentName, step, ts: new Date().toISOString(), ...data })),
    error: (step: string, err: unknown) =>
      console.error(JSON.stringify({ runId, agent: agentName, step, error: String(err), ts: new Date().toISOString() })),
  };
}

const log = createLogger(crypto.randomUUID(), "newsletter-agent");
log.step("fetch_topic", { topicId: topic.id, topic: topic.name });
// ... do work ...
log.step("draft_complete", { subject: draft.subject, wordCount: draft.body.split(" ").length });

Se sei su Cloudflare Workers, questi log vanno a Logpush o Workers Tail. Se stai girando localmente o su un VPS, invia a un aggregatore di log. Il JSON strutturato significa che puoi filtrare per runId per vedere esattamente cosa è successo in una singola esecuzione.

Guasto 4: Loop incontrollabili senza condizione di uscita

I loop agentici — dove il modello chiama strumenti e itera fino a quando una condizione non è soddisfatta — possono andare avanti all’infinito se quella condizione non viene mai soddisfatta o il modello la identifica erroneamente.

Sintomi: L’agente spende centinaia di dollari in costi API prima del timeout. O esegue la stessa chiamata allo strumento ancora e ancora senza fare progressi.

Correzione: Avere sempre un limite di iterazione rigido e un controllo del progresso:

typescript
const MAX_ITERATIONS = 10;
let iterations = 0;
let lastToolCallName = "";
let sameToolCallCount = 0;

while (true) {
  iterations++;
  if (iterations > MAX_ITERATIONS) {
    log.error("loop", { reason: "exceeded_max_iterations" });
    break;
  }

  const response = await client.messages.create({ ... });

  // Detect stuck loops: same tool called 3x in a row
  const toolCall = response.content.find(b => b.type === "tool_use");
  if (toolCall?.name === lastToolCallName) {
    sameToolCallCount++;
    if (sameToolCallCount >= 3) {
      log.error("loop", { reason: "stuck_loop", tool: toolCall.name });
      break;
    }
  } else {
    sameToolCallCount = 0;
    lastToolCallName = toolCall?.name ?? "";
  }

  if (response.stop_reason === "end_turn") break;
}

Questo cattura sia le modalità di guasto “è andato avanti troppo a lungo” che “ha girato sul posto”. Il limite dovrebbe essere abbastanza generoso per il percorso felice ma abbastanza stretto da limitare il raggio di esplosione.

Guasto 5: Definizioni di strumenti ambigue che il modello risolve in modo errato

Se dai al modello due strumenti con descrizioni sovrapposte, a volte chiamerà quello sbagliato. Questo è particolarmente comune con strumenti come search_database vs get_record o send_email vs create_draft.

Sintomi: Il modello chiama la categoria giusta di strumento ma sceglie quello specifico sbagliato. O chiama uno strumento nel contesto sbagliato (usando uno strumento di scrittura quando solo la lettura era appropriata).

Correzione: Rendi le descrizioni degli strumenti mutuamente esclusive e aggiungi esplicitamente “quando NON usare questo”:

typescript
const tools = [
  {
    name: "get_subscriber",
    description: "Fetch a single subscriber record by email. Use ONLY when you have a specific email address. Do NOT use for searching or listing subscribers.",
    input_schema: { ... }
  },
  {
    name: "search_subscribers",
    description: "Search subscribers by tag, segment, or status. Use when you need to find subscribers matching a criteria — NOT when you have a specific email address.",
    input_schema: { ... }
  }
];

La clausola “NON usare quando X” è la parte che la maggior parte delle persone salta. È la parte più importante. I modelli sono più bravi a seguire vincoli negativi espliciti che a inferirli da descrizioni positive.

Ancora una cosa: testa i tuoi agenti su input negativi

La maggior parte degli agenti viene testata solo su input puliti del percorso felice. La produzione ha input sporchi: stringhe vuote, campi null, casi limite Unicode, risposte API che restituiscono 200 ma con uno schema inaspettato.

Aggiungi una suite di test che eserciti esplicitamente:

Se il tuo agente si rompe su uno di questi, correggilo prima che vada in produzione. L’ambiente di produzione troverà ogni ipotesi che hai fatto.

La conclusione dell’operatore

La maggior parte dei guasti degli agenti in produzione sono problemi infrastrutturali che si mascherano da problemi del modello. Prima di cambiare modelli, aggiungi retry, logging strutturato, limiti di loop e gestione esplicita dei casi limite ai tuoi prompt. Correggi le definizioni di strumenti ambigue. Poi testa su input negativi. Fai tutto questo prima di incolpare il modello — nella mia esperienza, il modello di solito è l’ultima cosa che deve cambiare.

Continua a leggere

Ricevi il manuale dell'IA nella tua casella di posta

Ogni mercoledì. 28.400+ operatori. Zero riempitivo.

↵ per tutti i risultati esc esc per chiudere