Salta al contenuto
22/30Capitolo 22 di 30

Cos’è un AI agent: cinque tipi classici, due definizioni rivali

Il mondo dell’aspirapolvere rotto quattro volte, fino ai cinque tipi classici di agent. Poi un tool trasforma 39 token in 420.

In questa pagina

Ecco la stessa domanda, posta due volte allo stesso modello, con gli stessi pesi e greedy decoding. L’unica differenza è che la seconda volta c’era un tool nel catalogo.

TEXT
no tools in the catalogue
  turn 1  prompt=  39  out=  8  finish=stop        TEXT "The capital of France is Paris."
  => model calls=1  prompt tokens=39  output=8  wall=974 ms

one tool in the catalogue: get_temperature(city)
  turn 1  prompt= 185  out= 20  finish=tool_calls  CALL get_temperature({"city": "Paris"})
          tool  get_temperature -> {"city":"Paris","celsius":11}
  turn 2  prompt= 235  out= 18  finish=stop        TEXT "The capital of France is Paris. It is
                                                        currently at 11 degrees Celsius."
  => model calls=2  prompt tokens=420  output=38  wall=6,685 ms

Una chiamata è diventata due. Trentanove input token sono diventati 420, un fattore 10,8. Meno di un secondo è diventato quasi sette. E la risposta ha acquisito un fatto che nessuno aveva chiesto, da un tool che il modello ha scelto di chiamare per una domanda che non menzionava mai il meteo.

Il secondo sistema è ciò che la maggior parte dell’industria nel 2026 chiama agent. Oppure non lo è, a seconda di quale delle due definizioni più lette apri — e quelle due non dicono la stessa cosa. Una non è nemmeno d’accordo con sé stessa.

Questo disaccordo è il capitolo. Non è una lite di vocabolario: le due definizioni tracciano il confine su assi diversi, e l’asse che scegli decide cosa costruisci e cosa ti viene fatturato. Entrambe poggiano su una tassonomia più vecchia, e il modo più economico per conquistarla è costruire il peggior agent del mondo.

Mostra dettagli

Cosa serve a questo capitolo dai precedenti.

  • Capitolo 13 ha misurato quanto costa una singola chiamata in tempo; questo capitolo lo moltiplica per il numero di turni.
  • Capitolo 15: il prompt è lo stato completo del modello, perché nulla sopravvive alla chiamata.
  • Capitolo 16: gli input token crescono con il quadrato della conversazione.
  • Capitolo 18: il catalogo dei tool, e il viaggio di andata e ritorno in cui il modello chiede e il tuo codice esegue.

Niente tensori qui. Il capitolo è in TypeScript, dove lo mette la regola linguistica del Capitolo 14, e il suo ciclo è l’antenato diretto di quello del Capitolo 23.

L’esempio più antico del campo è un aspirapolvere in un mondo di due caselle, A e B, ciascuna pulita o sporca.1 Sopravvive in ogni manuale perché è il mondo più piccolo in cui un agent può avere ragione o torto.

Il percetto è una coppia — dove mi trovo, e se qui è sporco — e le azioni sono SUCK, LEFT e RIGHT. L’intero programma è una riga.

reflex.tsTS
type Percept = { dirty: boolean; where?: "A" | "B" };
type Action = "SUCK" | "LEFT" | "RIGHT";

const textbook = (p: Percept): Action =>
  p.dirty ? "SUCK" : p.where === "A" ? "RIGHT" : "LEFT";   

Eseguilo contro ogni configurazione iniziale del mondo a due caselle:

TEXT
A dirty, B dirty, start A    -> steps=3 clean=true
A clean, B dirty, start A    -> steps=2 clean=true
A dirty, B clean, start B    -> steps=2 clean=true

Questo è un simple reflex agent: agisce solo sul percetto corrente, senza memoria di ciò che è venuto prima. Non è una categoria giocattolo — un termostato lo è, e lo è anche una singola chiamata a un modello linguistico senza conversazione allegata.

Ora rompilo come fa la realtà. Un vero robot aspirapolvere ha un sensore di sporco e un paraurti, non una casella etichettata A sotto il tappeto. Togli la posizione dal percetto e non cambiare nient’altro:

reflex.tsTS
const dirtOnly = (p: Percept): Action => (p.dirty ? "SUCK" : "RIGHT");
TEXT
A dirty, B dirty, start A    -> steps=3   clean=true   still dirty=0
      t=0 at=A percept={dirty:true}  -> SUCK
      t=1 at=A percept={dirty:false} -> RIGHT
      t=2 at=B percept={dirty:true}  -> SUCK

A dirty, B clean, start B    -> steps=500 clean=false  still dirty=1
      t=0 at=B percept={dirty:false} -> RIGHT
      t=1 at=B percept={dirty:false} -> RIGHT
      t=2 at=B percept={dirty:false} -> RIGHT
      t=3 at=B percept={dirty:false} -> RIGHT

Lo stesso programma, due caselle. Da uno stato iniziale finisce in tre passi; da un altro sbatte contro il muro di destra cinquecento volte e continuerebbe finché la batteria non muore. Non può percepire la differenza tra le due situazioni, quindi non può agire in modo diverso. Russell e Norvig enunciano il risultato generale in una riga: i loop infiniti sono spesso inevitabili per i simple reflex agent in ambienti parzialmente osservabili.1

C’è una correzione che costa una riga e nessuna memoria, e vale la pena misurarla prima di tirare fuori qualcosa di più intelligente.

reflex.tsTS
let seed = 12345;
const rnd = () => ((seed = (seed * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff);

const coin = (p: Percept): Action => (p.dirty ? "SUCK" : rnd() < 0.5 ? "LEFT" : "RIGHT");  

Duemila esecuzioni di un corridoio tutto sporco a tre dimensioni, con lo stesso generatore seeded per tutto:

stanzepassi medimedianapeggiore su 2.000mai completato
24,04130
416,614810
868,7523060

La randomizzazione elimina del tutto il loop. Ma costa: otto stanze richiedono quindici mosse se sai cosa stai facendo, e questo agent ne fa in media 68,7 e una volta ne ha impiegate 306. È tutto il capitolo in miniatura. Ogni capacità che aggiungiamo compra correttezza in un caso che l’agent precedente non poteva gestire, e la fa pagare in una valuta che prima devi nominare.

Dare un nome alle parti, ora che servono

Link alla sezione: Dare un nome alle parti, ora che servono

Un agent percepisce il proprio ambiente attraverso sensori e agisce attraverso attuatori. L’agent program è la funzione dai percetti alle azioni — ogni listato sopra ne è uno. La sequenza di percetti è tutto ciò che è stato percepito finora, e un simple reflex agent ignora tutto tranne l’ultimo elemento.

Razionalità è la parola che la maggior parte degli articoli sbaglia, e metterla a fuoco rende utilizzabile il resto del capitolo. Un agent non è razionale o irrazionale in sé. Russell e Norvig definiscono un agent razionale come uno che, per ogni possibile sequenza di percetti, seleziona l’azione che ci si aspetta massimizzi la sua misura di performance, date le evidenze di quella sequenza e qualunque conoscenza incorporata abbia.1 La misura di performance non è dentro l’agent: appartiene al progettista, e la razionalità è definita solo rispetto a essa.

La specifica si scrive convenzionalmente come quattro cose, PEAS: performance measure, environment, actuators, sensors.

il robot aspirapolvereun agent di supporto in produzione
Performance measurecaselle pulite, per unità di batteriaticket risolti, per dollaro, senza escalation
Environmentil pavimento, lo sporco, i mobili, il tappetola coda dei ticket, il tuo database, il cliente
Actuatorsruote, aspirazionechiamate ai tool
Sensorssensore di sporco, paraurtiil messaggio dell’utente, i risultati dei tool

Nota quale riga stona. Quasi ogni team che costruisce agent nel 2026 scrive E, A e S — gli schemi dei tool, le integrazioni, il formato dei messaggi — perché senza quelli il codice non gira. Quasi nessuno scrive P. Senza P, «il nostro agent sta andando bene» non ha un significato che qualcuno possa verificare, e «razionale» non può essere applicato al sistema, solo a una demo. Il Capitolo 29 parla di trasformare P in un numero, ed è per questo che esiste.

TEXT
    ┌───────────────────────── the environment ─────────────────────────┐
    │                                                                   │
    │   ┌──────────────────────── the agent ─────────────────────┐      │
    │   │                                                        │      │
 ───┼──►│  sensors  ──►  the agent program  ──►  actuators  ─────┼──────┼──►
percept │                                                        │    action
    │   └────────────────────────────────────────────────────────┘      │
    └───────────────────────────────────────────────────────────────────┘

              the performance measure lives out here, in the head of
              whoever built the thing, and the agent cannot change it

Gli ambienti di task vengono ulteriormente classificati lungo sette assi, cinque dei quali decidono gran parte della difficoltà qui: completamente o parzialmente osservabile, deterministico o no, episodico o sequenziale, statico o dinamico, noto o ignoto.1 Un agent che parla con tool reali su una rete reale si trova nell’angolo difficile di tutti e cinque — non deterministico anche a temperatura zero (Capitolo 17) e, il punto sottovalutato, ignoto, perché non hai un modello affidabile di ciò che i tuoi stessi tool fanno al mondo. Ecco perché il loop del Capitolo 23 ha bisogno di gestione degli errori più che di pianificazione.

Aggiungere memoria, e trovare il muro successivo

Link alla sezione: Aggiungere memoria, e trovare il muro successivo

I pavimenti reali non sono monodimensionali, quindi promuoviamo il mondo a una pianta. I cancelletti sono muri, gli asterischi sono sporco, e il robot parte nella camera centrale:

TEXT
        col  0 1 2 3 4 5 6
      row 0  * . . # . . *
      row 1  . # . # . # .
      row 2  . # . S . # .        S = the robot starts here
      row 3  . # . # . # .
      row 4  * . . # . . *

L’upgrade ovvio è la memoria. L’agent mantiene una mappa: ogni casella su cui è stato e ogni casella in cui il paraurti ha scattato. La sua regola è entrare in una casella adiacente che non ha visitato — destra, poi giù, poi sinistra, poi su — e arretrare quando tutto intorno è noto. Questo è un model-based reflex agent: mantiene uno stato interno dalla storia dei percetti, così può agire su ciò che al momento non vede.

È un miglioramento reale, e ancora non basta:

TEXT
5,000 steps allowed -> steps=5,000  distinct squares visited=13/25  still dirty=2/4

Cinquemila mosse, metà pavimento mai vista. La mappa è corretta e le regole sono corrette. Ciò che l’agent non sa fare è usare la mappa per andare da qualche parte: le sue regole rispondono sempre e solo a «in quale dei miei quattro vicini dovrei entrare», quindi quando finisce le caselle non visitate accanto a sé non ha modo di esprimere il pensiero c’è una casella non visitata a otto mosse di distanza e vorrei trovarmi lì. Sa dov’è. Non sa dove vuole essere.

Un obiettivo, e poi un motivo per preferire una rotta a un’altra

Link alla sezione: Un obiettivo, e poi un motivo per preferire una rotta a un’altra

Un goal-based agent conserva, oltre al proprio modello del mondo, una descrizione della situazione che vuole produrre, e sceglie le azioni cercando tra sequenze di azioni finché non ne trova una che termina lì. Gli obiettivi trasformano la selezione dell’azione da lookup a ricerca.

L’obiettivo è «non resta nessuna casella sporca». La ricerca è una visita in ampiezza fino alla casella sporca più vicina, e il percorso che restituisce è il piano.

TEXT
goal-based (fewest moves)      -> moves=27  battery=52  still dirty=0
      from 2,3 -> 4,6 via 5 moves:  2,3 2,4 3,4 4,4 4,5 4,6
      from 4,6 -> 0,6 via 4 moves:  4,6 3,6 2,6 1,6 0,6
      from 0,6 -> 4,0 via 10 moves: 0,6 0,5 0,4 1,4 2,4 2,3 2,2 3,2 4,2 4,1 4,0
      from 4,0 -> 0,0 via 4 moves:  4,0 3,0 2,0 1,0 0,0

Ventisette mosse, pavimento pulito. Ma guarda la colonna della batteria e l’ultimo tratto del piano. La colonna 0 è moquette: attraversare una casella con moquette costa sei unità di batteria, una casella piastrellata costa una. L’agent è tornato a casa salendo per la colonna 0 perché sono quattro mosse invece di otto, e quelle quattro mosse sulla moquette sono costate 24 dove la deviazione da otto mosse sarebbe costata 13.

Non può fare altrimenti. Un obiettivo è un test binario: il pavimento è pulito oppure no. Ogni piano che termina con il pavimento pulito lo soddisfa allo stesso modo, quindi quando più piani riescono l’agent non ha nulla con cui scegliere tra loro. Preferire un successo a un altro richiede un numero sugli esiti, e quel numero è una funzione di utilità. Un agent che la massimizza è un utility-based agent.

La modifica al codice è un termine dentro la ricerca. La ricerca in ampiezza conta le mosse; falla contare il costo e hai l’algoritmo di Dijkstra e un agent diverso:

search.tsTS
const nd = dist.get(k)! + (byCost ? cell.cost : 1);   // <- the entire difference
TEXT
goal-based    (fewest moves)   -> moves=27  battery=52  still dirty=0
utility-based (cheapest route) -> moves=31  battery=41  still dirty=0
      from 4,0 -> 0,0 via 8 moves: 4,0 4,1 4,2 3,2 2,2 1,2 0,2 0,1 0,0

Quattro mosse in più, undici unità di batteria in meno: ventuno per cento più economico. Stesso obiettivo, stessa mappa, stesso codice tranne un termine. I due agent differiscono solo per ciò in cui cercano di essere bravi, e prendono strade diverse per tornare a casa.

Questo è anche il primo punto in cui l’agent ha bisogno di qualcosa che non può produrre. Qualcuno deve decidere quanto vale un’unità di batteria rispetto a una mossa. L’utilità è la misura di performance scritta in una forma con cui l’agent può calcolare, e scriverla è compito del progettista. Quando le persone dicono che un agent «ha ottimizzato la cosa sbagliata» quasi mai intendono un bug. Intendono che questa riga è stata scritta con poca cura.

Ora lascia che lo sporco ritorni. Quattro stanze tornano sporche a quattro ritmi diversi, e l’agent non li conosce mai. Visita una stanza per tick e vede solo quella stanza. La misura di performance è il numero di room-tick trascorsi sporchi su 4.000 tick — più basso è meglio.

Un learning agent, nella scomposizione del manuale, è uno qualunque dei precedenti più tre parti: un learning element che modifica l’agent, un critic che gli dice come sta andando rispetto a uno standard di performance fisso, e un problem generator che propone azioni che vale la pena provare per ciò che insegnerebbero.1 Tre policy nello stesso ambiente. La prima non impara; la seconda e la terza imparano la stessa cosa e la usano in modo diverso.

policyroom-tick sporchi su 4.000rispetto alla ronda
ronda fixed round-robin, nessun learning2.290
learner A: stima il tasso di sporco di ogni stanza, poi va dove lo sporco è più probabile11.8205,2× peggio
learner B: stesse stime, pesate per quanto tempo è passato dall’ultima visita1.57631% meglio

I tassi nascosti erano 0,35 per la cucina, 0,05 per l’ingresso, 0,02 per lo studio e 0,01 per la soffitta — e il learner A li ha trovati. Ha identificato correttamente la cucina come la stanza più sporca della casa, poi è andato in cucina a ogni tick per il resto della simulazione mentre le altre tre restavano sporche per sempre. È cinque volte peggio che non imparare affatto, e non è rotto.

La lezione è quella della sezione sull’utilità. Il learner A ha massimizzato «probabilità che la stanza che sto per visitare sia sporca». La misura di performance era «room-tick trascorsi sporchi». Numeri diversi; il secondo è ciò che il critic stava valutando, e nessuno l’ha detto all’agent. Il learner B moltiplica lo stesso tasso appreso per il tempo dall’ultima visita — lo sporco che si aspetta di trovare invece della possibilità di trovarne — e batte la ronda da cui era partito.

Un dettaglio implementativo ha deciso il risultato. Nella prima versione del learner B, una stanza in cui non era comparso sporco in tre visite riceveva un tasso esattamente pari a zero — e zero per qualunque cosa fa zero, quindi non veniva più visitata e la stima non poteva mai essere corretta. Smussare la frazione, successi più uno su tentativi più due, ha trasformato 11.895 in 1.576. «Non ancora osservato» e «misurato e risultato zero» sono affermazioni diverse, e un sistema che le memorizza nello stesso campo prende decisioni che non può annullare.

TEXT
  1  simple reflex    percept ────────────────────────────────► rules ────► action
  2  model-based      percept ──► [state] ──────────────────► rules ────► action
  3  goal-based       percept ──► [state] ──► [goal] ──────► search ───► action
  4  utility-based    percept ──► [state] ──► [goal] ──► [U] ──► argmax ► action
  5  learning         all of the above, plus [critic] ──► changes the parts above

Ognuno dei cinque è oggi in produzione con un altro nome.

tipo classicocosa porta tra i percettila sua forma nel 2026cosa non può fare
simple reflexnullauna chiamata al modello senza cronologia: un classificatore, un endpoint di estrazione, una completion single-turnqualunque cosa dipenda dal turno precedente
model-based reflexstato interno costruito dalla storia dei percettiuna chat: il transcript, reinviato intero a ogni chiamatascegliere dove dovrebbe finire la conversazione
goal-basedstato più una descrizione della situazione desiderataun loop reason-and-act con una condizione di arresto2preferire un piano riuscito a un altro
utility-basedstato, obiettivo e un numero sugli esitiloop evaluator–optimiser, e ranking delle risposte candidate in base a un criterio scritto (Capitolo 25)inventare il criterio
learningtutto questo, più un critic e un problem generatorReflexion, che scrive le proprie lezioni in un buffer episodico invece di aggiornare i pesi;3 memoria utente persistente (Capitolo 24)scegliere lo standard rispetto a cui il critic assegna il punteggio

Due righe sono più vicine di un’analogia, in un modo che costa denaro.

La chat è un model-based reflex agent il cui modello non è interno. Nel manuale lo stato è una variabile dentro l’agent program. In una chat è il transcript: vive dalla tua parte, viene reinviato per intero a ogni chiamata, e viene ricostruito da zero dentro il modello ogni volta. Questo è il conto quadratico del Capitolo 16, ed è lo stesso oggetto che il manuale disegnava come una scatola etichettata «state». Ecco la differenza, misurata su una domanda di follow-up con e senza i due messaggi precedenti:

TEXT
with the transcript      prompt=67  "The current temperature in Lisbon, Portugal is 15°C."
without the transcript   prompt=29  "Lisbon is the capital of Portugal, not a city in Portugal."

Lo stesso modello, le stesse tre parole di input utente, e il secondo è il robot nel corridoio che sbatte contro il muro. Non c’erano tool in quella esecuzione, quindi il 15 è inventato — ma è lo stato a dare un senso al follow-up. Lo ricostruisci ogni volta e paghi 2,3× gli input token per farlo in una conversazione di due turni. Il Capitolo 16 ha misurato dove arriva quel moltiplicatore al turno quaranta.

Reflexion è un learning agent che cambia il proprio input invece del proprio programma. Nella scomposizione del manuale il learning element modifica il performance element. Reflexion lascia stare i pesi e scrive testo riflessivo in un buffer episodico che il tentativo successivo legge.3 Il learning element è un prompt, la memoria una riga di database, il performance element un modello congelato — e il diagramma è quello del manuale, invariato.

Ed ecco il limite onesto della mappatura. I cinque tipi classificano l’agent program. Nel 2026 quel programma è diviso a metà: una parte è il tuo codice, una parte è dentro pesi che non hai addestrato. Quando un modello decide da solo di chiamare un tool, il test dell’obiettivo è nel tuo programma o nel modello? La tassonomia non ha risposta, perché quando fu scritta non c’era nessun altro posto in cui potesse essere — e quella domanda è esattamente il punto in cui le due definizioni moderne si separano.

Rispondere, chiamare e fermarsi, in una traccia

Link alla sezione: Rispondere, chiamare e fermarsi, in una traccia

Le definizioni sono argomenti sul comportamento, e sono molto più facili da giudicare con una traccia davanti.

Il loop qui sotto invia la conversazione a un modello; se la risposta contiene una chiamata a un tool, esegue il tool, aggiunge il risultato e invia di nuovo l’intero contenuto. Gira contro un Qwen2.5-0.5B-Instruct locale dietro un endpoint in stile OpenAI su questa macchina — la giuntura del Capitolo 14, quindi al loop non sa né importa cosa ci sia dietro la porta.

loop.tsTS
const BASE = process.env.LLM_BASE_URL ?? "http://127.0.0.1:8799/v1";

async function loop(question: string, maxTurns = 6) {
  const messages: Msg[] = [
    { role: "system", content: SYSTEM },
    { role: "user", content: question },
  ];

  for (let turn = 1; turn <= maxTurns; turn++) {
    const reply = await call(messages, TOOLS);
    const calls = reply.choices[0].message.tool_calls ?? [];
    messages.push(reply.choices[0].message);

    if (!calls.length) return messages;                      

    for (const c of calls) {
      const out = runTool(c.function.name, JSON.parse(c.function.arguments));
      messages.push({ role: "tool", name: c.function.name, content: out });
    }
  }
  throw new Error("turn cap reached");                       
}

Due righe portano tutta l’idea, ed entrambe sono marcate; il resto è contabilità. Tutti e tre i comportamenti sono visibili in una sola esecuzione. Se gli chiedi qualcosa che può fare da solo, il modello risponde. Se gli chiedi qualcosa che non può fare, chiama:

TEXT
=== a question the model cannot answer, one tool available
  turn 1  prompt= 187  out= 21  finish=tool_calls  CALL get_temperature({"city": "Oslo"})
          tool  get_temperature -> {"city":"Oslo","celsius":4}
  turn 2  prompt= 238  out= 12  finish=stop        TEXT "The current temperature in Oslo is 4
                                                        degrees Celsius."
  => model calls=2  prompt tokens=425  output=33  wall=6,257 ms
  => stopped by: the model produced text instead of a call

E si ferma — il terzo comportamento, e il più facile da perdere, perché sembra che non stia succedendo nulla. Il loop termina perché il turno 2 è tornato senza una chiamata a un tool. Nessuno l’ha deciso; l’ha deciso il modello, emettendo prosa. La condizione di terminazione di questo programma è il segno di un’assenza.

Vale la pena dedicare spazio ad altre due esecuzioni. Invitato a confrontare due città, il modello emette entrambe le chiamate ai tool in un solo turno, riceve indietro entrambe le letture, e sbaglia il confronto:

TEXT
  turn 1  prompt= 188  out= 43  finish=tool_calls  CALL get_temperature({"city": "Oslo"}),
                                                        get_temperature({"city": "Lisbon"})
          tool  get_temperature -> {"city":"Oslo","celsius":4}
          tool  get_temperature -> {"city":"Lisbon","celsius":19}
  turn 2  prompt= 284  out= 13  finish=stop        TEXT "Oslo is currently warmer than Lisbon
                                                        at 4°C."

I tool hanno funzionato. La chiamata parallela ha funzionato. Il loop ha funzionato. La risposta è falsa, con entrambi i numeri corretti lì nel transcript. Avvolgere un modello in un loop non lo fa ragionare; dà a un modello che sbaglia la capacità di agire in base al proprio errore — che è il Capitolo 30 in anticipo, e metà del Capitolo 29.

Ora elimina il return marcato e lascia che il loop arrivi invece al suo limite. Stessa domanda, stesso modello:

TEXT
  turn 1  prompt= 187  out= 21  CALL get_temperature({"city": "Oslo"})
  turn 2  prompt= 238  out= 12  TEXT "The current temperature in Oslo is 4 degrees Celsius."
  turn 3  prompt= 261  out= 30  TEXT "Could you please specify the exact location you're..."
  turn 4  prompt= 302  out= 14  TEXT "Sure! Could you tell me which city you're interested in?"
  turn 5  prompt= 327  out= 35  TEXT "I'm sorry, but I need more details to provide an..."
  turn 6  prompt= 373  out= 12  TEXT "Which city would you like to know the temperature for?"
  => model calls=6  prompt tokens=1,688  output=124  wall=25,261 ms  stopped by: turn cap

Quattro volte gli input token, quattro volte il tempo di wall clock, e un finale in cui l’agent ha dimenticato cosa gli era stato chiesto e sta interrogando l’utente su una domanda a cui l’utente ha risposto al turno uno. La risposta corretta era sullo schermo al turno 2, e ogni turno successivo ha peggiorato il transcript.

Quindi un agent non è un loop. È un loop più una regola per uscirne, e questo ne ha esattamente una. Il Capitolo 23 ne trova cinque, e mostra cosa si rompe quando ne manca una.

Entrambe citate anziché parafrasate, perché le parafrasi sono il punto in cui si fabbrica la confusione.

La prima definizione mette il confine su chi controlla il flusso. Building effective agents di Anthropic nomina l’ambiguità e decide così:

"At Anthropic, we categorize all these variations as agentic systems, but draw an important architectural distinction between workflows and agents: Workflows are systems where LLMs and tools are orchestrated through predefined code paths. Agents, on the other hand, are systems where LLMs dynamically direct their own processes and tool usage, maintaining control over how they accomplish tasks."4

Il test è una domanda sul tuo codice sorgente: chi ha scelto il prossimo passo? Un switch nel tuo programma: workflow. Il modello: agent. Lo stesso documento dice che gli agent «are typically just LLMs using tools based on environmental feedback in a loop» — che è esattamente il listato sopra.

La seconda definizione mette il confine sull’indipendenza dall’utente. A practical guide to building agents di OpenAI apre la pagina definitoria così:

"While conventional software enables users to streamline and automate workflows, agents are able to perform the same workflows on the users' behalf with a high degree of independence. Agents are systems that independently accomplish tasks on your behalf."5

Due frasi dopo, nella stessa pagina, esclude:

"Applications that integrate LLMs but don't use them to control workflow execution—think simple chatbots, single-turn LLMs, or sentiment classifiers—are not agents."5

Leggi quelle citazioni in ordine. Le frasi iniziali tracciano la linea sull’indipendenza: questa cosa se ne va e finisce il lavoro senza di me? La quarta la traccia sul controllo dell’esecuzione, che è esattamente la linea di Anthropic. Test diversi, stessa pagina, e ci sono sistemi reali su cui non concordano.

Sotto c’è una collisione di vocabolario, e provoca discussioni in riunioni vere. Nel primo documento un workflow è un’architettura, ed è la cosa che non è un agent. Nel secondo un workflow è «a sequence of steps that must be executed to meet the user's goal» — il lavoro stesso, che ogni agent possiede. «Abbiamo sostituito il workflow con un agent» è coerente sotto la prima definizione e quasi privo di senso sotto la seconda.

Tre sistemi che esistono nel 2026, sotto entrambe le definizioni.

Descrivi un task; legge file, esegue la suite di test, modifica, li esegue di nuovo, e si ferma quando passano o quando rinuncia. Nulla nel tuo codice decide che il prossimo passo è «esegui i test» — lo fa il modello, da ciò che l’ultimo tool ha restituito.

Definizione uno: agent, perché il modello dirige il proprio processo. Definizione due: agent, perché svolge il task in modo indipendente, riconosce il completamento e restituisce il controllo. Entrambi i documenti citano questa forma come esempio centrale.

Una pipeline notturna di triage dei ticket

Link alla sezione: Una pipeline notturna di triage dei ticket

Per ogni nuovo ticket di supporto, tre chiamate al modello in ordine fisso — classifica, estrai i campi, scrivi la bozza di risposta — e poi invia. Nessun modello sceglie mai cosa succede dopo; lo fa un loop for. Gira alle 03:00 e nessuno la guarda.

Definizione uno: non è un agent. È prompt chaining, elencato per nome come workflow. Definizione due: entrambe le risposte. Secondo le frasi iniziali svolge task in modo indipendente per tuo conto; secondo la quarta non usa il modello per controllare l’esecuzione del workflow, ed è esclusa. Questo sistema è il motivo per cui leggi l’intera pagina invece della citazione estratta.

Un chat assistant con un tool di ricerca

Link alla sezione: Un chat assistant con un tool di ricerca

Un turno utente. Il modello decide da sé se cercare prima di rispondere, poi risponde e ti aspetta.

Definizione uno: agent, perché il modello dirige dinamicamente il proprio uso dei tool sui risultati dell’ambiente, che è il test dichiarato. Definizione due: non è un agent, perché non c’è indipendenza — un turno, poi restituisce il controllo — e i «simple chatbots» sono nella lista delle esclusioni per nome.

Due dei tre cambiano lato. Non è un fallimento di nessuno dei due documenti. È un avvertimento su un tipo di riunione in cui due persone che concordano completamente su cosa fa un sistema passano un’ora a non concordare su come chiamarlo.

La via d’uscita sono due assi, non uno

Link alla sezione: La via d’uscita sono due assi, non uno

Le definizioni collidono perché ciascuna comprime due domande indipendenti in una parola. Separale e il disaccordo diventa una tabella, che è più utile di un verdetto.

il tuo codice sceglie il prossimo passoil modello sceglie il prossimo passo
una persona guarda ogni turnoun form con un modello dentro: classificatori, estrazione, completion single-turnuna chat con tool — la definizione uno dice agent, la definizione due dice no
nessuno guarda finché non è finitouna pipeline — l’apertura della definizione due dice agent, la sua quarta frase dice notutti concordano: un agent

Ogni definizione contesta una cella diversa, e le altre due non sono affatto in discussione. Quindi quando l’etichetta conta — in un contratto, una revisione del rischio, un postmortem — le due frasi che vale la pena scrivere non sono «è un agent» ma chi ha scelto il prossimo passo e chi stava guardando. A entrambe si può rispondere leggendo il codice, nessuna richiede la definizione di qualcuno, e insieme portano tutte le conseguenze che l’etichetta stava sostituendo.

Nulla di tutto questo è nuovo. Wooldridge e Jennings passarono in rassegna i sensi concorrenti di «agent» nel 1995;6 Franklin e Graesser fecero la domanda di questo capitolo nel 1996, raccolsero le definizioni in circolazione e scoprirono che non concordavano.7 Una survey del 2023 definisce ancora gli agent dai principi primi — «artificial entities that sense their environment, make decisions, and take actions»8 — perché non esisteva nulla di stabilito da citare, e CoALA descrive parti invece di tracciare un confine.9 Trent’anni di rifiuto di mettersi d’accordo dicono che la parola sta facendo più di un lavoro.

Ora la conseguenza che arriva prima della filosofia, cioè il conto.

Ogni misurazione qui ha la stessa forma. La singola chiamata è costata 39 input token; la stessa domanda con un tool è costata 420 su due chiamate; il loop senza la regola di arresto è costato 1.688 su sei. La crescita è peggiore che lineare, perché il turno n porta con sé ogni turno precedente: la colonna prompt di quell’esecuzione a sei turni legge 187, 238, 261, 302, 327, 373. Il Capitolo 16 ha derivato che il totale è Θ(n2)\Theta(n^2) e ha adattato la curva su una conversazione reale. Un agent trasforma ogni task in quella conversazione, che un umano la veda o no.

Se quei conteggi di token misurati fossero andati a un endpoint commerciale alle tariffe che il Capitolo 16 ha letto il 6 settembre 2026 — $2,00 per milione di input token e $12,00 per milione di output — le quattro esecuzioni costerebbero così:

esecuzionechiamate al modelloinput tokenoutput tokencosto
la domanda, senza tool1398$0.000174
la stessa domanda, un tool nel catalogo242038$0.001296
una domanda che richiede il tool242533$0.001246
la stessa, con la regola di arresto rimossa61.688124$0.004864

La riga due contro la riga uno è il numero da tenere. Sette volte e mezzo il costo, per una risposta peggiore a una domanda che il modello già conosceva. Nulla era configurato male: esisteva un tool, quindi il modello l’ha usato — e la scoperta del Capitolo 18, che a pesare è il prezzo del catalogo e non la sua accuratezza, ha qui la dimostrazione più economica con un catalogo di uno.

Ed è per questo che la metà utile di entrambi i documenti è la metà su come non costruire questo. Anthropic è diretto: trova la soluzione più semplice possibile e aggiungi complessità solo quando serve, il che «might mean not building agentic systems at all», perché gli agentic systems «trade latency and cost for better task performance» e «for many applications, optimizing single LLM calls with retrieval and in-context examples is usually enough».4 Il caso a favore di un agent è ristretto: problemi aperti in cui non puoi prevedere il numero di passi e non puoi hardcodare un percorso, in un ambiente di cui ti fidi, accettando «higher costs, and the potential for compounding errors».4 La schermata di OpenAI è l’immagine speculare — giudizio complesso, insiemi di regole non manutenibili, dati non strutturati — e finisce allo stesso modo: «otherwise, a deterministic solution may suffice».5

Quindi, nella tassonomia di questo capitolo: un numero fisso di passi in un ordine fisso è una pipeline, e chiamarla agent non la renderà più veloce. Se il numero di passi dipende da ciò che trovi lungo la strada, vuoi un loop — e compri quella flessibilità con N chiamate, un transcript quadratico, e un sistema che può sbagliare N volte invece di una.

Ora hai la tassonomia, entrambe le definizioni moderne, i due assi che le rendono compatibili, e un breve loop che risponde, chiama e si ferma.

Quel loop ha un solo modo per finire: il modello smette di chiedere tool. Il Capitolo 23 lo rompe apposta, sette volte, e ogni rottura aggiunge un pezzo. Un task impossibile, e non finisce mai — un limite di turni. Una notte di esecuzione, e arriva il conto — un budget in dollari. Un tool che fallisce — un errore su cui il modello può agire. La stessa chiamata due volte — una chiave di idempotenza. Un file che non avrebbe dovuto toccare — un’approvazione umana. Un riavvio a metà — persistenza della sessione. Un tool che impiega tre minuti in silenzio — progresso e cancellazione. Ciò che ne esce è un harness, il file su cui gira il resto di questo corso.

Resta la domanda di cui parlava davvero la diagonale contestata di questo capitolo. Un loop che decide il proprio prossimo passo deve decidere quando fermarsi, e abbiamo appena visto cosa succede quando non ci riesce: sei turni, quattro volte il conto, e un agent che interroga l’utente su una domanda a cui aveva già risposto. Fermarsi non è una sola condizione. Quante ce ne sono, e quale scatta per prima?


LLM Powered Autonomous Agents di Lilian Weng (2023) è la scomposizione più nota di un language agent in pianificazione, memoria e uso dei tool, ed è la lettura successiva giusta accanto ai due documenti dei vendor; i suoi tre componenti sono i Capitoli 23, 24 e 18 di questo corso, in quest’ordine.

Ogni numero in questo capitolo è stato prodotto su questa macchina e nulla è stato stimato. Il corridoio, la pianta, i quattro agent che la percorrono e le tre policy di ronda sono il TypeScript sopra, eseguito su Node 22; le cifre dell’agent randomizzato sono medie su 2.000 esecuzioni seeded ciascuna e le cifre della ronda sono singole esecuzioni seeded da 4.000 tick. Le tracce del modello provengono da Qwen2.5-0.5B-Instruct in float32 su CPU con greedy decoding, servito su loopback da un piccolo endpoint Python locale che carica i pesi e parla nel formato OpenAI chat-completions — di nuovo la giuntura, con i tensori sul lato Python e il loop sul lato TypeScript — quindi i conteggi dei token sono quelli del tokenizer di quel modello e le latenze sono quelle di quella macchina. Le uniche cifre prese da altrove sono i due prezzi nella tabella dei costi, che sono le tariffe lette dal Capitolo 16 dalla pagina prezzi di OpenAI il 6 settembre 2026, applicate qui ai conteggi di token misurati localmente come illustrazione e non come fattura osservata.

  1. Russell, S. e Norvig, P. Artificial Intelligence: A Modern Approach, 4ª edizione, capitolo 2, Intelligent Agents. Fonte del mondo dell’aspirapolvere, della specifica PEAS, della definizione di razionalità relativa a una misura di performance, delle sette proprietà degli ambienti di task, dei cinque tipi di agent usati qui, e dell’osservazione secondo cui i loop infiniti sono spesso inevitabili per i simple reflex agent in ambienti parzialmente osservabili. Il codice di accompagnamento del libro è aimacode/aima-python su GitHub (8.806 star, ultimo push 30 giugno 2026, letto 7 settembre 2026) — vale la pena nominarlo con precisione per ciò che è. È il repository di accompagnamento di un libro, non un’implementazione di riferimento su cui altri progetti costruiscono come lo sono karpathy/micrograd (17.412) e karpathy/nanoGPT (62.852). Ecco perché questo capitolo lo cita e lo linka invece di tradurlo, e perché l’argomento dell’ecosistema che ha mantenuto il Capitolo 5 in Python non si applica qui: nulla in questo capitolo tocca un tensore, e il loop scritto sopra è l’antenato diretto di quello del Capitolo 23. 2 3 4 5

  2. Yao, S., Zhao, J., Yu, D., Du, N., Shafran, I., Narasimhan, K. e Cao, Y. ReAct: Synergizing Reasoning and Acting in Language Models. arXiv:2210.03629 (2022). L’interleaving di tracce di reasoning e azioni a cui si riferisce la riga goal-based della tabella di mappatura.

  3. Shinn, N., Cassano, F., Berman, E., Gopinath, A., Narasimhan, K. e Yao, S. Reflexion: Language Agents with Verbal Reinforcement Learning. arXiv:2303.11366 (2023). Il riassunto del meccanismo fornito dal paper è il motivo per cui si mappa sul learning agent: rinforza gli agent «not by updating weights, but instead through linguistic feedback», con agent che «verbally reflect on task feedback signals, then maintain their own reflective text in an episodic memory buffer to induce better decision-making in subsequent trials». 2

  4. Anthropic, Building effective agents, 19 dicembre 2024, anthropic.com/engineering/building-effective-agents, letto 7 settembre 2026. Fonte della distinzione workflow/agent citata sopra, del termine ombrello «agentic systems», della descrizione degli agent come «typically just LLMs using tools based on environmental feedback in a loop», dell’indicazione di trovare la soluzione più semplice possibile e che questo «might mean not building agentic systems at all», e del caso a favore e contro gli agent, inclusi «higher costs, and the potential for compounding errors» e la raccomandazione di condizioni di arresto «such as a maximum number of iterations» per mantenere il controllo. 2 3

  5. OpenAI, A practical guide to building agents, pagine 4–7, letto 7 settembre 2026. Fonte di «Agents are systems that independently accomplish tasks on your behalf», dell’esclusione di «simple chatbots, single-turn LLMs, or sentiment classifiers», della definizione di workflow come «a sequence of steps that must be executed to meet the user's goal», delle due caratteristiche centrali di un agent, dei tre componenti — modello, tool, istruzioni — e dei criteri di screening per decidere quando costruirne uno, con chiusura in «otherwise, a deterministic solution may suffice». 2 3

  6. Wooldridge, M. e Jennings, N. R. Intelligent Agents: Theory and Practice. The Knowledge Engineering Review, volume 10, numero 2 (1995). La survey che divise l’uso del campo in una nozione debole di agency — autonomia, abilità sociale, reattività, proattività — e nozioni più forti che prendono in prestito il vocabolario mentale. Letta oggi, è una registrazione della stessa discussione che i due documenti di questo capitolo stanno ancora avendo.

  7. Franklin, S. e Graesser, A. Is It an Agent, or Just a Program? A Taxonomy for Autonomous Agents. Proceedings of the Third International Workshop on Agent Theories, Architectures, and Languages, Springer (1996). Citato qui per ciò che è anziché per una citazione: una survey che raccolse le definizioni di «agent» allora in circolazione, trovò che non concordavano, e propose una tassonomia per sostituire la discussione. Trent’anni dopo la discussione è in una documentazione progettata meglio ed è per il resto invariata.

  8. Xi, Z. et al. The Rise and Potential of Large Language Model Based Agents: A Survey. arXiv:2309.07864 (2023). Citato sopra per la sua definizione iniziale, «AI agents are artificial entities that sense their environment, make decisions, and take actions», che è la definizione da manuale riformulata nel 2023 perché non c’era una definizione moderna condivisa da citare.

  9. Sumers, T. R., Yao, S., Narasimhan, K. e Griffiths, T. L. Cognitive Architectures for Language Agents. arXiv:2309.02427 (2023). Organizza i language agent come «modular memory components, a structured action space to interact with internal memory and external environments, and a generalized decision-making process to choose actions», e li colloca esplicitamente nella storia dell’AI simbolica e delle scienze cognitive. La tassonomia della memoria ritorna nel Capitolo 24, dove la tabella a tre store ne è l’ombra pratica.

Pronto a lasciare scegliere LIA?

Crea con ogni modello AI in un unico posto — inizia gratis oggi.