CodeGym /Corsi /ChatGPT Apps /Come GPT decide di invocare uno strumento: il modello di ...

Come GPT decide di invocare uno strumento: il modello di tool-call e il ruolo delle descrizioni

ChatGPT Apps
Livello 4 , Lezione 0
Disponibile

1. Perché capire il tool-call

Semplificando, una normale app web funziona così: «l’utente preme un pulsante — noi chiamiamo una funzione». Nel mondo delle ChatGPT Apps lo scenario è diverso: l’utente dice qualcosa, il modello ragiona e, se lo ritiene opportuno, forma un’invocazione strutturata di uno strumento (tool-call).

Cioè voi non scrivete:

onClick={() => callSuggestGiftsApi(formData)}

ma invece:

  1. Descrivete lo strumento suggest_gifts (nome, descrizione, schema degli argomenti).
  2. Spiegate al modello nel system-prompt perché quello strumento è utile.
  3. Delegate il controllo al modello: decide lui quando e come invocarlo.

Da qui è importante capire molto presto due cose:

  1. GPT non vede il vostro codice backend. Vede solo l’«header» dello strumento: nome, descrizione e schema dei parametri.
  2. Quanto «intelligentemente» il modello userà la vostra App dipende quasi direttamente da come avete scritto quelle descrizioni. Buone descrizioni sono il vostro «prompt dello strumento».

La lezione di oggi riguarda proprio questo «cervello» tra l’utente e il vostro server.

2. Modello mentale del tool-call: cosa succede in pratica

Partiamo dalla visione d’insieme. Scenario tipico per GiftGenius:

  1. Utente: «Scegli un regalo per un amico di 30 anni, budget 100 dollari, gli piacciono i videogiochi».
  2. GPT legge il messaggio e guarda quali strumenti sono disponibili. Nella nostra App, per esempio, c’è suggest_gifts.
  3. GPT decide: «Per rispondere bene, devo invocare questo strumento».
  4. Invece di una normale risposta testuale genera una struttura: nome dello strumento + argomenti JSON.
  5. Il client di ChatGPT vede: «Ok, è un tool-call», e lo invia al vostro MCP/server.
  6. Il vostro server esegue la business logic e restituisce un output strutturato.
  7. GPT riceve il risultato, lo legge e, sulla base della risposta dello strumento, formula una risposta comprensibile all’utente e/o aggiorna il widget.

Dal punto di vista delle OpenAI API, è lo stesso meccanismo di LLM-function-calling: nella risposta del modello, al posto del testo, appare un oggetto con il name dello strumento e gli arguments, e finish_reason è marcato come tool_calls. Il modello non esegue il codice da solo — propone solo quale strumento chiamare, e la chiamata reale la fa il client (ChatGPT/Apps SDK).

Ecco come appare (sequenza semplificata):

sequenceDiagram
    participant U as Utente
    participant G as GPT (modello)
    participant C as Client ChatGPT
    participant S as MCP/Backend

    U->>G: "Scegli un regalo per un amico..."
    G->>C: tool-call: { name: "suggest_gifts", args: {...} }
    C->>S: HTTP /mcp tools/call (suggest_gifts, args)
    S-->>C: Risultato (JSON con l'elenco dei regali)
    C-->>G: tool result
    G-->>U: Risposta + widget aggiornato

Conclusione principale: non scrivete if (userAskedAboutGifts) callSuggestGifts(). Create lo strumento e la sua descrizione, e la decisione la prende il modello.

3. Cosa vede il modello: System Prompt + elenco degli strumenti

Per capire come GPT decide cosa fare, occorre chiarire quale insieme di informazioni ha al momento della scelta.

Semplificando, il modello vede:

  • il system prompt della vostra App (lo analizzeremo nel modulo 5);
  • la cronologia del dialogo: i messaggi dell’utente, le proprie risposte, i risultati dei precedenti tool-call;
  • l’elenco degli strumenti disponibili (tools) con nomi, descrizioni e schemi dei parametri;
  • annotazioni aggiuntive degli strumenti (readOnly/destructive, ecc.).

Non vede:

  • l’implementazione delle funzioni;
  • le query SQL;
  • la struttura delle vostre tabelle;
  • il contenuto del repository privato con il servizio.

Parleremo più avanti in dettaglio di MCP. Per ora basta sapere che a livello MCP gli strumenti sono dichiarati come descrittori: ciascuno ha name, description e inputSchema (JSON Schema). Durante l’handshake ChatGPT richiede al server MCP l’elenco degli strumenti e inizia a considerarli come «azioni» disponibili.

Esempio di descrittore per GiftGenius (JSON semplificato):

{
  "name": "suggest_gifts",
  "description": "Seleziona idee regalo in base all'età, agli interessi e al budget",
  "inputSchema": {
    "type": "object",
    "properties": {
      "age": { "type": "integer" },
      "budget": { "type": "number" }
    },
    "required": ["age", "budget"]
  }
}

Il modello qui «legge» solo testo e struttura: cosa sia age, cosa sia budget, che cosa faccia lo strumento in generale. La prossima lezione tratterà proprio di come descrivere al meglio inputSchema. Ora — di come da questa descrizione nasce la decisione «ok, invoco suggest_gifts».

4. Come appare il tool-call dal punto di vista dell’API

ChatGPT invoca gli strumenti (tools) del vostro server MCP più o meno come un OpenAI Agent invoca funzioni nel vostro backend. Nell’Apps SDK di ChatGPT è tutto un po’ più incapsulato, ma la meccanica di base è la stessa.

Immaginiamo di fare nel nostro backend una normale richiesta alle OpenAI API, passando lo strumento suggest_gifts che il modello può invocare nella sua risposta:

const response = await openai.responses.create({
  model: 'gpt-5-mini',
  messages: [
    {
      role: 'user',
      content: 'Mi serve un regalo per un amico di 30 anni, budget 100 dollari'
    }
  ],
  tools: [ // qui passiamo l'elenco delle funzioni che la LLM può "invocare"
    {
      name: 'suggest_gifts',
      description: 'Seleziona regali in base all\'età, al budget e agli interessi',
      parameters: {
        type: 'object',
        properties: {
          age: { type: 'integer' },
          budget: { type: 'number' }
        },
        required: ['age', 'budget']
      }
    }
  ]
});

Se il modello decide di invocare lo strumento, riceverete in risposta non del testo, ma un messaggio dell’assistente con qualcosa del genere:

{
  "role": "assistant",
  "tool_calls": [
    {
      "id": "call_1",
      "name": "suggest_gifts",
      "arguments": "{\"age\":30,\"budget\":100}"
    }
  ],
  "content": []
}

In questo modo la LLM comunica al vostro backend che deve chiamare la funzione suggest_gifts(30,100).

Qui contano tre cose:

  1. Il nome dello strumento (name) — il modello inserisce davvero la stringa che avete indicato nella descrizione dei tools quando avete inviato la prima richiesta.
  2. Gli argomenti (arguments) — una stringa JSON costruita sulla base di parameters/inputSchema.
  3. L’assenza di una normale risposta testuale (per il momento) — al suo posto ottenete una struttura per l’invocazione dello strumento.

Nelle applicazioni ChatGPT è lo stesso: il modello restituisce «voglio invocare suggest_gifts con questi parametri», e il client (ChatGPT) effettua una richiesta HTTP al vostro MCP/server: tools/call con il nome dello strumento e gli argomenti.

5. Come decide il modello: tool o testo

Ora la parte più interessante: quando GPT prende in considerazione i vostri strumenti?

La meccanica, semplificando, è questa:

  1. Il modello vede il nuovo messaggio dell’utente e il contesto corrente.
  2. Internamente ha un «livello» che genera il messaggio successivo dell’assistente, ma invece di fornire sempre testo normale, può scegliere uno tra vari tipi di completamento:
    • normale risposta testuale (finish_reason: "stop");
    • uno o più tool-call (finish_reason: "tool_calls");
    • a volte altri casi (ad esempio, «serve un altro messaggio dell’utente»).
  3. Questa scelta è influenzata da:
    • quanto la richiesta dell’utente somiglia ai compiti descritti nei vostri strumenti;
    • quanto la descrizione dello strumento dica esplicitamente «usami in questo caso»;
    • i dati del system prompt dell’app, impostati nella configurazione dell’Apps SDK.

Detto in modo semplice, il modello «prova» a calare il vostro strumento sulla richiesta corrente. Se la descrizione è: «Seleziona regali per età e interessi», e l’utente chiede «analisi del bilancio dello Stato», il modello non proverà nemmeno a invocarlo. Se la descrizione è troppo vaga — «fa cose fantastiche» — il modello non capirà per quali richieste usarlo.

Un dettaglio interessante: il modello non è obbligato a invocare uno strumento, anche se lo avete descritto. GPT può decidere: «Qui è tutto chiaro, rispondo da solo, senza tool-call». Per questo più avanti nel corso ci alleneremo attivamente a scrivere descrizioni di strumenti che rendano per il modello l’uso dello strumento il più ovvio e conveniente possibile.

6. Nome dello strumento: perché tool1 è una cattiva idea

Il nome dello strumento è di fatto l’identificatore che il modello userà nelle sue invocazioni. Potrebbe sembrare un campo puramente tecnico, ma in pratica il nome influenza molto il comportamento del modello.

Se chiamate lo strumento tool1, il modello non capisce nulla. Per lui è solo una sequenza di caratteri. Se invece lo chiamate suggest_gifts, search_products o fetch_user_orders, già il nome fornisce un forte segnale su cosa faccia lo strumento.

Pensate a come leggete voi codice sconosciuto. Vedendo una funzione calculateCartTotal, avete un’idea di cosa aspettarvi. Al modello serve lo stesso «ancoraggio semantico».

Per GiftGenius, nomi sensati degli strumenti possono essere:

suggest_gifts
search_products
get_product_details
create_order

È positivo se il nome è:

  • corto ma significativo;
  • coerente nello stile (snake_case, lettere latine, verbo_sostantivo);
  • rappresenta un’azione specifica.

Cattiva idea — mescolare azioni differenti in un unico strumento, tipo do_all_gift_stuff. Per il modello è più difficile capire quando usarlo e, come vedremo nelle prossime lezioni, questo rompe lo schema degli argomenti e complica il debug.

7. Descrizione dello strumento: il vostro prompt per il modello

Se il nome è il titolo, la description è una mini‑documentazione, ma non per lo sviluppatore umano: per GPT. Lo sviluppatore leggerà il codice; il modello — no. Si basa sul testo della descrizione per scegliere quando invocare lo strumento e quali argomenti passarvi.

È importante scrivere la descrizione come «istruzioni per l’uso»:

  • quando usare lo strumento;
  • quali limiti abbia;
  • che cosa non deve fare.

Prendiamo il nostro suggest_gifts. Ecco tre versioni di descrizione.

Troppo generica:

"Seleziona regali."

Il modello non capisce per quale scopo, per chi, con quali parametri. Questo strumento può «competere» nella testa del modello con le sue conoscenze generali sui regali, e spesso deciderà di rispondere con testo.

Troppo restrittiva:

"Seleziona regali solo per fratelli minori per il compleanno."

Qui di fatto abbiamo vietato di usare lo strumento quasi sempre. Qualsiasi altro scenario — mamma, collega, anniversario — «non rientra», e il modello eviterà la chiamata.

Ottimale:

"Usa questo strumento quando devi selezionare regali per una persona in base all'età, al tipo di relazione (amico, partner, collega, ecc.), al budget e agli interessi.
Non chiamarlo per domande non legate ai regali (per esempio, politica o meteo)."

Qui è descritto chiaramente cosa fa lo strumento, quali parametri ha e quando invocarlo, con una condizione negativa — per quali richieste è meglio non usarlo.

Il modello «apprezza» confini chiari. Più chiaramente indicate in quali formulazioni dell’utente (intent) lo strumento è appropriato, più prevedibile sarà il comportamento dell’App.

Mini‑esercizio

Potete subito, senza alzarvi dalla scrivania, prendere la vostra futura App (magari non sui regali) e pensare per uno dei suoi strumenti tre descrizioni: molto ampia, molto stretta e bilanciata. Poi testate come GPT si comporta con le diverse versioni.

8. Schema degli argomenti: come aiuta la decisione

Parleremo in dettaglio di JSON Schema nella prossima lezione, ma per capire i tool-call è utile una percezione di alto livello.

Quando il modello decide di invocare uno strumento, deve:

  1. Capire quali argomenti lo strumento si aspetta.
  2. Estrarre questi valori dal testo dell’utente (o dal contesto).
  3. Formare il JSON con questi argomenti.

Per questo nella descrizione dello strumento c’è lo schema dei parametri (inputSchema), che indica al modello:

  • quali campi esistono (age, budget, relationship_type, interests ecc.);
  • quali campi sono obbligatori (required);
  • quali tipi hanno (integer, number, string, array, ecc.);
  • a volte — quali valori sono ammessi (enum) e le spiegazioni dei campi (description).

Un’interfaccia TypeScript molto semplice per i parametri di suggest_gifts può essere:

interface SuggestGiftsParams {
  age: number;
  relationship_type: 'friend' | 'partner' | 'colleague';
  budget: number;
  interests?: string[];
}

A livello di modello questo diventa un JSON Schema, e il modello dal nome e dalla descrizione di ciascun campo deduce che:

  • age va preso da frasi come «30 anni», «per un adolescente», ecc.;
  • budget da «budget 100 dollari», «fino a 50 euro»;
  • relationship_type da «amico», «collega»;
  • interests — da «gli piacciono i videogiochi».

Se fornite uno schema senza descrizioni e con nomi di campo astratti (a, b, c), il modello sbaglierà molto più spesso nel riempire gli argomenti. Torneremo su questo nel modulo su localizzazione e suggerimenti UX. L’idea chiave qui è semplice: lo schema non è solo una validazione lato backend, è prima di tutto un suggerimento al modello su cosa mettere dove.

Abbiamo parlato di come lo schema aiuti il modello a comporre correttamente gli argomenti. Ma oltre a «cosa e come invocare» è importante anche «si può invocarlo adesso ed è sicuro?». Qui entrano in gioco permessi e metainformazioni sugli strumenti.

9. Permessi e contesto: non ogni strumento è sempre disponibile

Oltre a nome, descrizione e schema degli argomenti, gli strumenti hanno un’altra dimensione importante — sicurezza e accesso. Gli strumenti in una vera App possono differire molto per livello di «pericolosità». Una cosa è cercare regali in un catalogo pubblico, un’altra è addebitare denaro sulla carta dell’utente.

L’Apps SDK e MCP permettono di riflettere questo nelle descrizioni e nelle annotazioni degli strumenti — per esempio contrassegnandoli come read-only o destructive.

Il concetto è questo:

  • Gli strumenti che leggono solo dati pubblici (search_products, get_weather) possono essere invocati senza conferme extra.
  • Gli strumenti che modificano qualcosa (create_order, cancel_order, charge_user) sono contrassegnati come «distruttivi». La UI di ChatGPT può chiedere una conferma aggiuntiva all’utente («Sei sicuro di voler effettuare l’ordine?»), e il modello stesso li proporrà più raramente senza una richiesta esplicita.

Nei moduli futuri, quando configureremo MCP, vedrete come queste annotazioni (_meta, destructiveHint, readOnlyHint) appaiono in veri descrittori JSON, come influenzano la UX e come ChatGPT costruisce i dialoghi ««Are you sure?»» prima dell’invocazione. Per ora basta capire:

  • GPT considera non solo il testo della descrizione, ma anche la metainformazione sulla sicurezza.
  • Uno strumento che richiede autenticazione non verrà usato finché l’utente non ha effettuato l’accesso (o finché l’App non ha ottenuto il token necessario).

È un altro fattore che influenza la decisione «invocare il tool o no»: anche se per senso lo strumento è adatto, può non essere disponibile per i permessi, e il modello sceglierà un’altra via.

10. Da dove arrivano gli strumenti in ChatGPT

Dal punto di vista architetturale, uno strumento può arrivare al modello in due modi principali.

Primo, dalla configurazione della vostra ChatGPT App. Quando registrate l’App, indicate quali server MCP (e i loro strumenti) sono collegati, o quali tools integrati sono nell’applicazione stessa. All’avvio della sessione ChatGPT riceve questa configurazione e capisce quali strumenti sono disponibili.

Secondo, direttamente da MCP. MCP (Model Context Protocol) definisce il modo standard con cui un client (nel nostro caso ChatGPT/Apps SDK) scopre cosa sa fare il vostro server: fa una richiesta tools/list, riceve un JSON con le descrizioni degli strumenti e li conserva come capabilities. Analizzeremo la meccanica in un modulo dedicato a MCP; ora è importante capire l’idea generale.

Schematicamente:

flowchart LR
  A[ChatGPT Client] -->|handshake| B[MCP Server]
  B -->|tools/list| A
  A -->|passa l'elenco| G[GPT Model]

Dopo questo, l’elenco degli strumenti diventa parte del contesto per il modello. Se cambiate lo schema o la descrizione dello strumento sul server e riavviate l’App, il nuovo descrittore arriverà a ChatGPT al successivo handshake, e il modello inizierà a prendere decisioni di invocazione in modo diverso.

E un punto pratico importante: quando modificate solo il backend (l’implementazione dello strumento), il modello non lo sa. Ma quando cambiate name/description/schema, state davvero cambiando il «cervello» dell’App. A volte è più utile correggere una riga nella description che scrivere 200 righe di codice con euristiche.

11. Applichiamo a GiftGenius: creiamo uno strumento che il modello vorrà invocare

Colleghiamo ora tutto alla nostra app didattica GiftGenius. Supponiamo di avere già un server MCP o uno strato backend in cui registriamo gli strumenti. Registriamo lo strumento suggest_gifts con server.registerTool(...).

Abbozzo minimale in TypeScript (per ora senza logica reale):

// pseudo-mcp-server/tools/suggestGifts.ts
server.registerTool(
  'suggest_gifts', // nome dello strumento
  {
    title: 'Selezione regali',
    description:
      'Usa questo strumento per selezionare idee regalo in base all\'età, ' +
      'al tipo di relazione e al budget. Non chiamarlo per domande non legate ai regali.',
    inputSchema: { // descrizione dei parametri dello strumento
      type: 'object',
      properties: {
        age: { type: 'integer', description: 'Età del destinatario in anni' },
        relationship_type: {
          type: 'string',
          description: 'Tipo di relazione: friend, partner, colleague'
        },
        budget: {
          type: 'number',
          description: 'Budget massimo per il regalo nella valuta dell\'utente'
        }
      },
      required: ['age', 'budget']
    }
  },
  async ({ age, relationship_type, budget }) => { // codice della funzione/strumento
    // La logica reale arriverà dopo
    return { suggestions: [] };
  }
);

Notate i dettagli che abbiamo curato già in questa fase, anche se la logica è ancora uno «stub»:

  • Nome: suggest_gifts, non tool1.
  • Descrizione: spiega esplicitamente quando invocare lo strumento e quando non farlo.
  • Descrizioni dei campi: aiutano il modello a mappare correttamente il testo dell’utente sugli argomenti.

Di conseguenza, quando l’utente scrive «Scegli un regalo per un collega da 50 dollari», il modello vede che:

  • esiste uno strumento chiamato suggest_gifts con descrizione sulla selezione dei regali;
  • ha i campi age, relationship_type, budget;
  • budget — è il «budget massimo per il regalo», relationship_type — «tipo di relazione: friend, partner, colleague».

Anche se gli utenti si esprimono in modo impreciso («fino a cinquanta», «per il compagno di progetto»), il modello avrà abbastanza contesto per provare a comporre correttamente il JSON degli argomenti.

Quando il nostro strumento funzionerà davvero (nel modulo su backend e MCP), sarete già ben orientati: GPT lo invocherà in modo prevedibile, semplicemente perché abbiamo progettato bene l’interfaccia e la descrizione.

12. Un po’ di pratica per voi

Per evitare che l’argomento resti solo teorico, vi consiglio un piccolo esperimento subito dopo la lezione.

Per prima cosa, prendete uno degli scenari di GiftGenius o ideate una nuova App. Annotate su carta o in un editor una funzione che volete esplicitamente dare al modello — qualcosa come search_products, find_hotels, calculate_shipping.

Poi ideate per lo stesso strumento tre coppie «nome + descrizione»:

  1. Nome e descrizione molto astratti.
  2. Troppo specifici (quasi un caso speciale).
  3. Nome + descrizione ben bilanciati, dove è chiaramente indicato quando invocare lo strumento e cosa non deve fare.

Quindi — facoltativo — usando un normale OpenAI SDK, potete fare una semplice richiesta con queste varianti e osservare come cambia il comportamento del modello: se lo strumento viene invocato, come compila gli argomenti. Nella ricerca sull’argomento viene proposto proprio un esercizio di questo tipo per suggest_gifts.

13. Errori tipici nella progettazione di tool-call e descrizioni

Errore n. 1: chiamare gli strumenti tool1, handler, doStuff.
Un tale naming è completamente inutile per il modello. GPT non indovina «le intenzioni dello sviluppatore» dal nome del file; ha bisogno di un nome semanticamente chiaro. Se fornite un set di tool1, tool2, tool3 senza descrizioni, lo strumento praticamente non verrà invocato: il modello semplicemente non capirà cosa faccia ciascuno e o li ignorerà o ne sceglierà uno a caso.

Errore n. 2: trattare la description come commenti per umani.
Molti scrivono nella descrizione qualcosa di formale come «Funzione per la selezione dei regali», pensando che i dettagli si sappiano dal codice. Ma il codice il modello non lo vede: vede solo il testo della descrizione e lo schema degli argomenti. Una descrizione poco chiara diventa fonte di allucinazioni: GPT proverà a rispondere da sola quando invece serviva invocare lo strumento, o invocherà lo strumento in situazioni strane.

Errore n. 3: rendere la descrizione troppo ampia o troppo stretta.
Se scrivete «Fa cose fantastiche», il modello non capisce i confini d’uso. Se scrivete «Seleziona un regalo solo per il fratello minore per il diciottesimo compleanno», di fatto vietate di usare lo strumento quasi sempre. Una descrizione ottimale definisce un’area di compiti chiara (selezione dei regali in base a vari parametri), l’elenco dei parametri chiave (età, relazione, budget, interessi) e specifica per quali classi di domande lo strumento non va usato.

Errore n. 4: ignorare lo schema degli argomenti come parte del «prompt».
Alcuni sviluppatori vedono il JSON Schema solo come mezzo di validazione sul server. In realtà il modello analizza attivamente i nomi dei campi, i loro tipi e le descrizioni per capire quali dati estrarre dal testo dell’utente. Se chiamate un campo x senza descrizione e lo rendete opzionale, GPT inizierà a riempirlo in modo caotico o a non riempirlo affatto. Uno schema corretto con nomi chiari e brevi descrizioni riduce fortemente il numero di tool-call non validi.

Errore n. 5: aspettarsi che il modello «debba» invocare lo strumento.
Gli sviluppatori a volte si stupiscono: «Perché GPT non ha invocato il mio strumento, se esiste?». La risposta quasi sempre è: dalla descrizione o dal system prompt non risulta che lo strumento serva proprio per quella domanda, oppure il testo della richiesta rientra in una zona in cui il modello ritiene più semplice rispondere da solo.

Errore n. 6: mescolare più azioni eterogenee in un unico strumento.
A volte si vuole fare un manage_orders universale, che cerchi ordini, ne crei di nuovi e annulli quelli vecchi. Per un umano si può ancora spiegare; per il modello risulta uno strumento nebuloso senza confini chiari. GPT capisce peggio quando invocarlo e compila gli argomenti con più difficoltà — perché all’interno diventano tanti campi opzionali. Meglio separare queste azioni in più strumenti ristretti (get_order, create_order, cancel_order) con descrizioni e schemi chiari.

Errore n. 7: non tenere conto dei permessi e della sicurezza nel design dei tool.
Se descrivete uno strumento che può fare azioni distruttive (addebiti, eliminazione dati) ma non lo contrassegnate come destructive e non ne limitate l’uso nella descrizione, create un rischio. La UI di ChatGPT non chiederà conferme aggiuntive, e il modello potrebbe decidere di invocarlo anche in scenari «di confine». Annotazioni corrette e una descrizione attenta («usalo solo dopo il consenso esplicito dell’utente») aiutano a ridurre questi rischi già a livello di tool-call.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION