Torna al Blog
AI

n8n e AI Agent: come automatizzare senza perdere il controllo

Guida pratica per usare gli AI Agent in n8n con guardrail, human-in-the-loop, validazione output, monitoring dei costi e fallback chain per mantenere il controllo.

Team n8n.it- Specialisti in Automazione30 marzo 202611 min read
n8n e AI Agent: come automatizzare senza perdere il controllo

Il problema: AI potente ma imprevedibile

Gli AI Agent sono la funzionalità più potente di n8n. Puoi creare assistenti che leggono email, rispondono ai clienti, analizzano documenti, prendono decisioni e agiscono autonomamente. Ma questa potenza porta un rischio reale: un agent senza controlli può inviare email sbagliate, modificare dati critici o accumulare costi imprevisti.

Questa guida ti mostra come sfruttare gli AI Agent di n8n mantenendo il pieno controllo su cosa fanno, quanto costano e quando devono fermarsi.

Come funzionano gli AI Agent in n8n

n8n integra nativamente gli AI Agent con un'architettura a nodi:

Trigger (webhook, schedule, email)
  -> AI Agent node
     -> LLM (GPT-4, Claude, Llama)
     -> Tools (funzioni che l'agent può chiamare)
     -> Memory (contesto delle conversazioni)
  -> Output (azione, risposta, decisione)

L'agent riceve un input, ragiona usando il modello LLM, decide quali tool usare, esegue azioni e produce un output. Il problema è che ogni passo di questa catena può produrre risultati inattesi.

Principio 1: guardrail sull'input

Il problema

Se l'agent riceve input inaspettati (prompt injection, dati malformati, richieste fuori contesto), può comportarsi in modi imprevedibili.

La soluzione: validazione prima dell'agent

// Nodo Code: prima dell'AI Agent
const input = $json;

// 1. Sanitizza l'input
let cleanInput = input.message
  .replace(/<[^>]*>/g, '')  // Rimuovi HTML
  .replace(/\{[^}]*\}/g, '')  // Rimuovi template injection
  .trim();

// 2. Verifica lunghezza
if (cleanInput.length > 2000) {
  cleanInput = cleanInput.substring(0, 2000) + '... [troncato]';
}

if (cleanInput.length < 3) {
  return [{ json: { skip: true, reason: 'Input troppo corto' } }];
}

// 3. Rileva prompt injection base
const injectionPatterns = [
  /ignora le istruzioni/i,
  /ignore previous/i,
  /system prompt/i,
  /forget your instructions/i,
  /sei ora un/i,
  /you are now/i,
  /act as/i,
  /new role/i
];

const isInjection = injectionPatterns.some(p => p.test(cleanInput));

if (isInjection) {
  return [{
    json: {
      skip: true,
      reason: 'Possibile prompt injection rilevata',
      alert: true
    }
  }];
}

// 4. Classifica il tipo di richiesta
const categories = {
  support: /ordine|spedizione|reso|rimborso|problema|aiuto/i,
  sales: /prezzo|costo|preventivo|sconto|offerta|acquist/i,
  info: /come|cosa|quando|dove|perch/i,
  other: /.*/
};

let category = 'other';
for (const [cat, pattern] of Object.entries(categories)) {
  if (pattern.test(cleanInput)) {
    category = cat;
    break;
  }
}

return [{
  json: {
    original_input: input.message,
    clean_input: cleanInput,
    category,
    skip: false
  }
}];

Principio 2: system prompt robusto

Il problema

Un system prompt generico come "Sei un assistente utile" dà troppa libertà all'agent. Senza confini chiari, l'agent può inventare informazioni, promettere sconti non autorizzati o rispondere a domande fuori ambito.

Il template system prompt

Sei l'assistente AI di {{ nomeAzienda }}.

RUOLO: rispondi alle domande dei clienti sul supporto e-commerce.

CONFINI RIGIDI - NON FARE MAI:
- Non inventare informazioni su prodotti, prezzi o disponibilità
- Non promettere sconti, rimborsi o azioni che richiedono approvazione umana
- Non rispondere a domande su argomenti non correlati al business
- Non eseguire azioni irreversibili senza conferma
- Non condividere informazioni interne dell'azienda
- Non fornire consigli legali, medici o finanziari

QUANDO NON SAI:
- Rispondi: "Non ho questa informazione. Ti metto in contatto
  con un operatore che potrà aiutarti."
- Non inventare mai una risposta

TONO: professionale ma cordiale. Usa il "tu".
Rispondi sempre in italiano.

DATI DISPONIBILI:
- Catalogo prodotti: usa il tool "search_products"
- Stato ordini: usa il tool "check_order"
- FAQ: usa il tool "search_faq"

AZIONI PERMESSE:
- Cercare informazioni (prodotti, ordini, FAQ)
- Creare ticket di supporto
- Aggiornare informazioni di contatto del cliente

AZIONI CHE RICHIEDONO APPROVAZIONE UMANA:
- Rimborsi superiori a 50 euro
- Modifiche a ordini già spediti
- Sconti personalizzati
- Cancellazione account

Perché funziona

I modelli linguistici seguono meglio le istruzioni quando:

  • I confini sono espliciti e specifici (non vaghi)
  • Le azioni proibite sono elencate chiaramente
  • C'è un fallback definito per i casi incerti
  • Il tono e la lingua sono specificati

Principio 3: human-in-the-loop

Il concetto

Non tutto deve essere completamente automatico. Per le azioni ad alto rischio, inserisci un punto di controllo umano.

Implementazione in n8n

AI Agent produce una risposta/azione
  -> Nodo Code: valuta il rischio
  -> IF rischio basso (informazioni, FAQ):
     -> Esegui automaticamente
  -> IF rischio medio (creazione ticket, aggiornamento dati):
     -> Esegui ma notifica il team
  -> IF rischio alto (rimborso, cancellazione, promessa al cliente):
     -> Metti in pausa
     -> Invia richiesta approvazione (Slack/email)
     -> Attendi approvazione umana (webhook callback)
     -> IF approvato: esegui
     -> IF rifiutato: rispondi al cliente con alternativa

Valutazione rischio automatica

// Nodo Code: classifica il rischio dell'azione proposta dall'agent
const agentOutput = $json;

let riskLevel = 'low';
const reasons = [];

// Azioni finanziarie
if (agentOutput.action === 'refund' || agentOutput.action === 'discount') {
  riskLevel = 'high';
  reasons.push('Azione finanziaria');
}

// Importo elevato
if (agentOutput.amount && agentOutput.amount > 50) {
  riskLevel = 'high';
  reasons.push(`Importo: ${agentOutput.amount} EUR`);
}

// Modifiche irreversibili
if (['delete', 'cancel', 'close'].includes(agentOutput.action)) {
  riskLevel = 'high';
  reasons.push('Azione irreversibile');
}

// Comunicazione esterna
if (agentOutput.sends_email || agentOutput.sends_sms) {
  if (riskLevel !== 'high') riskLevel = 'medium';
  reasons.push('Comunicazione esterna al cliente');
}

// Incertezza dell'agent
if (agentOutput.confidence && agentOutput.confidence < 0.7) {
  if (riskLevel === 'low') riskLevel = 'medium';
  reasons.push(`Confidenza bassa: ${agentOutput.confidence}`);
}

return [{
  json: {
    ...agentOutput,
    risk_level: riskLevel,
    risk_reasons: reasons,
    requires_approval: riskLevel === 'high'
  }
}];

Flusso di approvazione via Slack

-> Invia messaggio Slack con bottoni:

L'AI Agent richiede approvazione:

Azione: {{ action }}
Cliente: {{ customerName }}
Importo: {{ amount }} EUR
Motivo: {{ reasoning }}
Rischio: {{ riskLevel }} ({{ riskReasons }})

[Approva] [Rifiuta] [Modifica e approva]

-> Webhook attende callback dal bottone Slack
-> Processa in base alla risposta

Principio 4: validazione dell'output

Il problema

L'AI può generare output plausibile ma sbagliato: prezzi inventati, date impossibili, email con tono inappropriato, risposte in lingua sbagliata.

Controlli automatici sull'output

// Nodo Code: valida output dell'agent prima dell'invio
const output = $json;
const errors = [];
const warnings = [];

// 1. Lingua
const italianWords = output.response.match(
  /\b(il|lo|la|di|che|è|per|un|una|con|sono|non|come|più|questo|questa)\b/g
);
if (!italianWords || italianWords.length < 3) {
  errors.push('La risposta potrebbe non essere in italiano');
}

// 2. Lunghezza ragionevole
if (output.response.length < 20) {
  warnings.push('Risposta molto corta');
}
if (output.response.length > 2000) {
  warnings.push('Risposta molto lunga, potrebbe confondere il cliente');
}

// 3. Informazioni sensibili
const sensitivePatterns = [
  /password/i,
  /chiave api/i,
  /api key/i,
  /credenzial/i,
  /\b\d{16}\b/,  // Numeri carta di credito
  /\b[A-Z]{2}\d{2}[A-Z0-9]{11,30}\b/  // IBAN
];

for (const pattern of sensitivePatterns) {
  if (pattern.test(output.response)) {
    errors.push('Possibile informazione sensibile nella risposta');
    break;
  }
}

// 4. Promesse non autorizzate
const promisePatterns = [
  /ti garantiamo/i,
  /rimborso totale/i,
  /sconto del \d+/i,
  /gratis/i,
  /omaggio/i
];

for (const pattern of promisePatterns) {
  if (pattern.test(output.response)) {
    warnings.push(`Possibile promessa non autorizzata: "${output.response.match(pattern)[0]}"`);
  }
}

// 5. Allucinazioni su prodotti/prezzi
if (output.mentioned_products) {
  // Verifica che i prodotti esistano nel catalogo
  for (const product of output.mentioned_products) {
    const exists = await checkProductExists(product);
    if (!exists) {
      errors.push(`Prodotto non trovato nel catalogo: "${product}"`);
    }
  }
}

const isValid = errors.length === 0;

return [{
  json: {
    ...output,
    validation: {
      is_valid: isValid,
      errors,
      warnings,
      checked_at: new Date().toISOString()
    }
  }
}];

Cosa fare quando la validazione fallisce

IF errori di validazione:
  -> Non inviare la risposta al cliente
  -> Genera risposta di fallback:
     "Grazie per la tua domanda. Ti metto in contatto
      con un operatore che potrà aiutarti al meglio."
  -> Crea ticket per il team con:
     - Domanda originale del cliente
     - Risposta generata (per review)
     - Errori di validazione
  -> Log per miglioramento futuro

Principio 5: monitoring dei costi

Il problema

Un agent mal configurato può generare centinaia di chiamate API al minuto. Con GPT-4 a circa 10 euro per milione di token in input, un loop incontrollato può costare centinaia di euro in poche ore.

Budget cap nel workflow

// Nodo Code: prima di ogni chiamata LLM
const dailyBudget = 10; // euro
const monthlyBudget = 200; // euro

// Leggi contatore dal Google Sheet o database
const todayCost = $('Get Daily Cost').first().json.total || 0;
const monthCost = $('Get Monthly Cost').first().json.total || 0;

if (todayCost >= dailyBudget) {
  return [{
    json: {
      blocked: true,
      reason: `Budget giornaliero raggiunto: ${todayCost.toFixed(2)} EUR`,
      fallback: 'Usa risposta template'
    }
  }];
}

if (monthCost >= monthlyBudget) {
  return [{
    json: {
      blocked: true,
      reason: `Budget mensile raggiunto: ${monthCost.toFixed(2)} EUR`,
      fallback: 'Disabilita agent, usa solo risposte manuali'
    }
  }];
}

// Alert al 80% del budget
if (todayCost >= dailyBudget * 0.8) {
  // Invia alert ma continua
  return [{
    json: {
      blocked: false,
      warning: `Budget giornaliero all'${((todayCost/dailyBudget)*100).toFixed(0)}%`
    }
  }];
}

return [{ json: { blocked: false } }];

Tracking costi per chiamata

// Dopo ogni chiamata LLM: registra il costo
const usage = $json.usage;

const costs = {
  model: 'gpt-4o',
  input_tokens: usage.prompt_tokens,
  output_tokens: usage.completion_tokens,
  // Prezzi GPT-4o (aggiorna periodicamente)
  input_cost: (usage.prompt_tokens / 1000000) * 2.50,
  output_cost: (usage.completion_tokens / 1000000) * 10.00,
  total_cost: 0,
  timestamp: new Date().toISOString()
};

costs.total_cost = costs.input_cost + costs.output_cost;

return [{ json: costs }];

Report costi settimanale

Schedule Trigger (lunedi alle 9:00)
  -> Somma costi della settimana per:
     - Modello LLM
     - Workflow
     - Tipo di azione
  -> Genera report:

Report costi AI - Settimana {{ weekNumber }}

Costo totale: {{ totalCost }} EUR
Chiamate totali: {{ totalCalls }}
Costo medio per chiamata: {{ avgCost }} EUR

Top 5 workflow per costo:
1. {{ workflow1 }}: {{ cost1 }} EUR ({{ calls1 }} chiamate)
2. ...

Budget rimanente: {{ remaining }} EUR ({{ percentUsed }}% usato)

Principio 6: fallback chain

Il concetto

Mai dipendere da un singolo modello o provider. Se OpenAI è giù, il tuo servizio clienti AI non deve fermarsi.

Implementazione

Richiesta al primo LLM (GPT-4o)
  -> IF risposta ok: usa questa
  -> IF errore/timeout:
     -> Secondo LLM (Claude 3.5 Sonnet)
     -> IF risposta ok: usa questa
     -> IF errore/timeout:
        -> Terzo LLM (Llama locale via Ollama)
        -> IF risposta ok: usa questa
        -> IF errore:
           -> Risposta template dal database FAQ
           -> Crea ticket manuale

Nodo Code: fallback con timeout

// Wrapper per chiamate LLM con fallback
const models = [
  {
    name: 'gpt-4o',
    provider: 'openai',
    timeout: 15000,
    maxTokens: 500
  },
  {
    name: 'claude-3-5-sonnet',
    provider: 'anthropic',
    timeout: 15000,
    maxTokens: 500
  },
  {
    name: 'llama3',
    provider: 'ollama',
    timeout: 30000,
    maxTokens: 500
  }
];

let lastError = null;

for (const model of models) {
  try {
    // Tenta la chiamata (gestita dai nodi n8n successivi)
    return [{
      json: {
        selected_model: model.name,
        provider: model.provider,
        timeout: model.timeout,
        attempt: models.indexOf(model) + 1
      }
    }];
  } catch (error) {
    lastError = error;
    // Log dell'errore e prova il successivo
    continue;
  }
}

// Tutti i modelli hanno fallito
return [{
  json: {
    fallback: true,
    use_template: true,
    error: lastError?.message
  }
}];

Principio 7: logging e audit trail

Cosa registrare per ogni interazione

// Log completo per audit
const auditLog = {
  // Identificazione
  interaction_id: crypto.randomUUID(),
  timestamp: new Date().toISOString(),
  workflow_id: $workflow.id,
  execution_id: $execution.id,

  // Input
  user_input: $json.original_input,
  sanitized_input: $json.clean_input,
  input_category: $json.category,

  // Agent
  model_used: $json.selected_model,
  system_prompt_version: 'v2.3',
  tools_called: $json.tools_used || [],
  tokens_used: $json.usage?.total_tokens || 0,
  cost: $json.cost || 0,

  // Output
  agent_response: $json.response,
  action_taken: $json.action || 'none',
  risk_level: $json.risk_level,

  // Validazione
  validation_passed: $json.validation?.is_valid,
  validation_errors: $json.validation?.errors || [],

  // Risultato
  sent_to_customer: $json.sent || false,
  required_human: $json.requires_approval || false,
  human_approved: $json.approved || null,

  // Feedback (compilato dopo)
  customer_satisfied: null,
  feedback: null
};

return [{ json: auditLog }];

Dashboard di performance

Metriche da monitorare mensilmente:

MetricaTargetCalcolo
Tasso di risposta autonoma> 70%Risposte senza intervento umano / totale
Tasso di escalation< 20%Richieste passate a umano / totale
Accuratezza> 90%Risposte corrette / risposte verificate
Tempo medio di risposta< 10 secondiMedia dei tempi di risposta
Costo per interazione< 0,05 euroCosto LLM totale / interazioni
Soddisfazione cliente> 4/5Feedback post-interazione

Esempio completo: assistente e-commerce

Mettiamo insieme tutti i principi in un workflow reale:

1. Cliente invia messaggio (webhook/chat)
2. Validazione input (sanitizzazione, anti-injection)
3. Budget check (costo giornaliero sotto soglia?)
4. AI Agent con system prompt robusto:
   - Tool: search_products (catalogo)
   - Tool: check_order (stato ordini)
   - Tool: search_faq (knowledge base)
   - Tool: create_ticket (supporto)
5. Validazione output (lingua, contenuto, promesse)
6. Risk assessment (basso/medio/alto)
7. IF rischio basso: invia risposta al cliente
   IF rischio medio: invia + notifica team
   IF rischio alto: pausa + richiesta approvazione
8. Log audit completo
9. Tracking costi
10. IF errore in qualsiasi punto: fallback chain

Questo workflow gestisce il 70-80% delle richieste in autonomia, escala il 15-20% con notifica e blocca il 5% per approvazione umana. Il risultato è un servizio clienti che risponde in secondi, 24 ore su 24, senza perdere il controllo.

La regola fondamentale

Automatizzare con l'AI non significa "lasciare fare all'AI". Significa progettare un sistema dove l'AI fa il lavoro pesante ma le decisioni critiche restano sotto il tuo controllo. I guardrail non rallentano l'automazione: la rendono affidabile.


Vuoi implementare AI Agent con guardrail nel tuo n8n? Contattaci per una consulenza specializzata.

T

Team n8n.it

Specialisti in Automazione

Siamo un team di esperti n8n focalizzati sull'automazione dei processi aziendali e la sicurezza delle implementazioni self-hosted.

Articoli correlati