Vai al contenuto principale

Tutorial sul Function Calling di GPT-5

Guida pratica al function calling di GPT-5: dalle funzioni con JSON Schema agli strumenti free-form, vincoli Lark/CFG, preamboli e allowlist degli strumenti.
· 11 min leggi

Scopri con l'IA

Con il lancio di GPT-5, OpenAI ha ampliato il tool/function calling nell'API includendo chiamate a strumenti free-form (testo grezzo, niente JSON), vincoli grammaticali tramite Lark/CFG, allowlist degli strumenti e un migliore ragionamento sull'uso degli strumenti. Insieme, questi elementi rendono GPT-5 un modello davvero agentico; puoi collegare API, database e strumenti personalizzati alla Responses API e generare risposte fondate o persino automatizzare workflow.

In questo tutorial, tratterò function tools, strumenti personalizzati, vincoli grammaticali, allowlist degli strumenti e preamboli, con esempi di codice e spiegazioni chiare su come funzionano e quando usarli.

Function Calling in GPT-5: cosa e perché

Il Function Calling in GPT-5 ti permette di potenziare il modello con i dati e le azioni della tua applicazione. Il modello può decidere quando chiamare uno strumento, mentre tu gestisci la logica e restituisci i risultati per una risposta finale e fondata. 

Tipi di Function Calling:

  1. Function Tools (JSON Schema): offrono input e output strutturati che il modello può chiamare con precisione.
  2. Strumenti personalizzati (free-form): forniscono input e output testuali flessibili per integrazioni non strutturate.

Che cos'è il function calling?

GPT-5 supporta sia function tools strutturati (con JSON Schema) sia strumenti personalizzati che accettano payload di testo libero (come SQL, script o configurazioni) per un'integrazione fluida con runtime esterni. 

I function tools sono definiti da JSON Schema, garantendo che il modello capisca esattamente quali argomenti passare e consentendo una rigorosa validazione degli input. Puoi approfondire nel nostro tutorial su OpenAI Function Calling.

Perché usare il function calling?

  • Flessibilità: gli strumenti free-form permettono al modello di produrre il testo esatto richiesto dal tuo sistema, senza i vincoli del JSON rigido; sono ideali per codice, query o configurazioni.
  • Affidabilità e controllo: i function tools impongono argomenti strutturati, migliorando la prevedibilità e riducendo gli errori di parsing.
  • Workflow agentici: GPT-5 è progettato per un uso di strumenti e compiti di coding complessi e multi-step, rendendo il tool calling una funzionalità primaria.

Come funziona il function calling?

  1. Invia un prompt che includa tutti gli strumenti disponibili.
  2. Il modello emette una chiamata a uno strumento (con argomenti o testo free-form).
  3. La tua applicazione esegue lo strumento (esegui la funzione Python).
  4. Restituisci l'output dello strumento al modello.
  5. Il modello poi risponde o può chiamare altri strumenti.

Fonte: Function calling - OpenAI API

Quando usare Function vs. strumenti personalizzati

  • Usa i function tools quando vuoi una validazione rigorosa e argomenti tipizzati e prevedibili tramite JSON Schema.
  • Usa strumenti personalizzati (free-form) quando il tuo runtime richiede testo grezzo (come script, SQL o configurazioni) o quando hai bisogno di iterare velocemente senza schemi.

Function Tools (JSON Schema): invia dati strutturati

Usare i Function Tools con JSON Schema ti permette di ottenere risultati prevedibili e strutturati dal modello. Il modello valuta gli strumenti che hai dichiarato, decide quando usarne uno, propone argomenti validati da JSON e poi esegue la funzione corrispondente nel tuo codice. 

Dopodiché, puoi chiedere al modello di generare una risposta basandosi solo sui dati restituiti dallo strumento, assicurando che l'output sia fondato, machine-readable e facile da integrare.

Questo esempio illustra diversi concetti chiave:

  • Scoperta degli strumenti e generazione degli argomenti: il modello seleziona lo strumento appropriato e compila automaticamente i suoi argomenti tipizzati JSON.
  • Ciclo di esecuzione degli strumenti: la tua applicazione esegue gli strumenti richiesti e invia i risultati al modello.
  • Risposta finale sagomata dallo schema: la risposta finale si basa rigorosamente sugli output degli strumenti, riducendo le inesattezze e garantendo affidabilità.

1. Configurazione

  1. Crea un account OpenAI e genera una chiave API dalla dashboard della piattaforma. Nella dashboard, apri la sezione API, poi "View API keys" e clicca su "Create new secret key".
  2. Aggiungi un metodo di pagamento o crediti al tuo account in modo che le chiamate API possano essere eseguite; l'uso viene detratto dal tuo saldo prepagato man mano che invii richieste.
  3. Salva la tua chiave API come variabile d'ambiente denominata OPENAI_API_KEY prima di avviare la tua app. 
  4. Installa l'SDK ufficiale OpenAI per Python con pip install openai.

2. Definisci gli strumenti

Fornisci un elenco di strumenti con nome, descrizione e JSON Schema per i parametri. Lo schema aiuta il modello a fornire argomenti ben formati.

  • make_coffee si aspetta un parametro stringa: coffee_type.
  • random_coffee_fact non richiede parametri (oggetto vuoto). Queste definizioni sono passate tramite l'argomento tools nella chiamata API così il modello sa cosa è disponibile.
import os
from openai import OpenAI
import json

client = OpenAI(api_key = os.environ["OPENAI_API_KEY"])

tools = [
    {
        "type": "function",
        "name": "make_coffee",
        "description": "Gives a simple recipe for making a coffee drink.",
        "parameters": {
            "type": "object",
            "properties": {
                "coffee_type": {
                    "type": "string",
                    "description": "The coffee drink, e.g. espresso, cappuccino, latte"
                }
            },
            "required": ["coffee_type"],
        },
    },
    {
        "type": "function",
        "name": "random_coffee_fact",
        "description": "Returns a fun fact about coffee.",
        "parameters": {"type": "object","properties":{}}
    }
]

3. Implementa gli strumenti

Implementi le funzioni Python che fanno realmente il lavoro:

  • make_coffee(coffee_type) restituisce una ricetta concisa in base alla bevanda richiesta.
  • random_coffee_fact() restituisce un piccolo fact.

Entrambe restituiscono dict serializzabili in JSON, ideali per essere rimandati al modello.

def make_coffee(coffee_type):
    recipes = {
        "espresso": "Grind fine, 18g coffee → 36g espresso in ~28s.",
        "cappuccino": "Brew 1 espresso shot, steam 150ml milk, pour and top with foam.",
        "latte": "Brew 1 espresso shot, steam 250ml milk, pour for silky texture.",
    }
    return {"coffee_type": coffee_type, "recipe": recipes.get(coffee_type.lower(), "Unknown coffee type!")}

def random_coffee_fact():
    return {"fact": "Coffee is the second most traded commodity in the world, after oil."}

4. Avvia una conversazione

Avvia la conversazione con la domanda dell'utente: {"role": "user", "content": "How do I make a latte?"}

Il modello potrebbe restituire messaggi che includono un elemento function_call, ad esempio make_coffee con {"coffee_type":"latte"}. Dovresti quindi aggiungere i messaggi del modello di nuovo in input_list: input_list += response.output. Questo aiuta a mantenere lo stato della conversazione per il turno successivo.

# Track used tools
used_tools = []

input_list = [{"role": "user", "content": "How do I make a latte?"}]

response = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=input_list,
)
input_list += response.output

5. Esegui le chiamate agli strumenti e allega gli output

Dovresti iterare su response.output e identificare gli elementi in cui item.type è uguale a "function_call". Per questi elementi, effettua il parse di item.arguments e inoltra le richieste alle tue funzioni Python. 

Per la funzione make_coffee, chiamala usando make_coffee(args["coffee_type"]). Per la funzione random_coffee_fact, chiama semplicemente random_coffee_fact(). 

Dopo aver eseguito le funzioni, aggiungi un messaggio function_call_output che includa quanto segue: 

  • call_id (che collega questo output alla richiesta del modello) 
  • output (una stringa JSON del risultato della tua funzione).
for item in response.output:
    if getattr(item, "type", "") == "function_call":
        used_tools.append(item.name)
        args = json.loads(item.arguments or "{}")

        if item.name == "make_coffee":
            result = make_coffee(args["coffee_type"])
        elif item.name == "random_coffee_fact":
            result = random_coffee_fact()
        else:
            result = {"error": f"Unknown tool {item.name}"}

        input_list.append({
            "type": "function_call_output",
            "call_id": item.call_id,
            "output": json.dumps(result)
        })

6. Genera la risposta finale

Effettui una seconda chiamata a client.responses.create, passando la input_list aggiornata che ora include gli output degli strumenti.

La variabile used_tools cattura i nomi degli strumenti comparsi nelle function call del modello. La useremo per mostrare quali strumenti sono stati utilizzati. 

final = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=input_list,
    instructions="Answer using only the tool results."
)

print("Final output:\n", final.output_text)

print("\n--- Tool Usage ---")
for t in tools:
    status = "USED ✅" if t["name"] in used_tools else "NOT USED ❌"
    print(f"{t['name']}: {status}")

Poiché l'utente ha chiesto del latte macchiato, il modello ha selezionato make_coffee con coffee_type = "latte", il tuo codice lo ha eseguito e la risposta finale è stata costruita solo da quel risultato.

Final output:
 {"coffee_type":"latte","recipe":"Brew 1 espresso shot, steam 250ml milk, pour for silky texture."}

--- Tool Usage ---
make_coffee: USED ✅
random_coffee_fact: NOT USED ❌

Strumenti personalizzati (free-form): invia testo grezzo

Usa gli strumenti personalizzati free-form di GPT-5 per permettere al modello di inviare testo grezzo direttamente al tuo strumento, come codice, SQL, comandi shell o semplice CSV, senza richiedere JSON Schema. 

Questa capacità va oltre le vecchie chiamate di funzione solo JSON, offrendoti maggiore flessibilità per l'integrazione con executor, motori di query o interpreti di linguaggi specifici di dominio (DSL). 

In modalità free-form, il modello genera una chiamata a uno strumento personalizzato con un payload di testo non strutturato che puoi instradare al tuo runtime. Poi restituisci il risultato che il modello userà per finalizzare la risposta all'utente.

Ora scriveremo il codice che consente al modello di selezionare gli strumenti appropriati e restituire testo grezzo non strutturato come output. Questo output verrà poi passato a una funzione Python per generare un risultato. In questo modo, l'utente può fornire gli ingredienti che ha e il modello genererà ricette su misura per le sue esigenze.

In questo esempio di codice, abbiamo:

  1. Inizializzato il client OpenAI e definito un singolo strumento personalizzato chiamato meal_planner che accetta input di testo grezzo.
  2. Creato la funzione Python plan_meal che prende una stringa di ingredienti separati da virgole e restituisce un'idea per un pasto.
  3. Avviato la conversazione con un messaggio dell'utente che elenca gli ingredienti disponibili.
  4. Effettuato una prima chiamata al modello istruendo GPT-5 a invocare lo strumento usando solo un elenco pulito di ingredienti, e aggiunto l'output del modello alla cronologia della conversazione.
  5. Individuato la chiamata allo strumento personalizzato emessa ed estratto la stringa di input grezza separata da virgole.
  6. Eseguito lo strumento passando l'elenco a plan_meal, quindi inviato il risultato al modello come function_call_output collegato tramite call_id.
  7. Eseguito una chiamata finale al modello per convertire l'idea grezza dello strumento in una ricetta concisa, passo dopo passo, e stampato sia i dettagli della chiamata allo strumento sia l'output finale.
from openai import OpenAI
import json
import random

client = OpenAI()

# --- Custom tool ---
tools = [
    {
        "type": "custom",
        "name": "meal_planner",
        "description": "Takes ONLY a comma-separated list of ingredients and suggests a meal idea."
    }
]

# --- Fake meal planner ---
def plan_meal(ingredients: str) -> str:
    ideas = [
        f"Stir-fry: {ingredients} with garlic & soy sauce.",
        f"One-pot rice: cook {ingredients} together in broth until fluffy.",
        f"Soup: simmer {ingredients} in stock with herbs.",
        f"Sheet-pan bake: roast {ingredients} at 200°C for ~20 min."
    ]
    return random.choice(ideas)

# --- Start conversation ---
messages = [
    {"role": "user", "content": "I only have chicken, rice, and broccoli. Any dinner ideas?"}
]

# 1) Ask model with clear instruction
resp = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=messages,
    instructions="If the user mentions ingredients, call the meal_planner tool with ONLY a comma-separated list like 'chicken, rice, broccoli'."
)

messages += resp.output

# 2) Find the tool call
tool_call = next((x for x in resp.output if getattr(x, "type", "") == "custom_tool_call"), None)
assert tool_call, "No tool call found!"

# Get clean CSV input
ingredients_csv = tool_call.input.strip()

# 3) Run the tool
meal_result = plan_meal(ingredients_csv)

# 4) Send tool output back
messages.append({
    "type": "function_call_output",
    "call_id": tool_call.call_id,
    "output": meal_result
})

# 5) Final model response
final = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=messages,
    instructions="Turn the meal idea into a short recipe with 3-4 steps."
)

print("\n--- Tool Call ---")
print("Name:", tool_call.name)
print("Input:", ingredients_csv)
print("Output:", meal_result)


print("\n--- Final Output ---\n", final.output_text)

Di conseguenza, abbiamo ricevuto statistiche sugli strumenti utilizzati, inclusi installazione, input e output. Inoltre, l'output finale fornisce una ricetta completa su come preparare il pasto.

--- Tool Call ---
Name: meal_planner
Input: chicken, rice, broccoli
Output: One-pot rice: cook chicken, rice, broccoli together in broth until fluffy.

--- Final Output ---
 One-Pot Chicken, Rice & Broccoli

- Season bite-size chicken pieces with salt and pepper; sear in a little oil in a pot until lightly browned.
- Add 1 cup rinsed rice and 2 cups broth (or water + salt). Bring to a boil, then cover and simmer on low for 12 minutes.
- Scatter 2 cups small broccoli florets on top, cover, and cook 5-7 more minutes until rice is fluffy and broccoli is tender.
- Rest 5 minutes off heat, fluff, and adjust seasoning. Optional: stir in a knob of butter or a splash of soy.

Vincoli grammaticali (Lark): limita gli output degli strumenti

GPT-5 ora supporta grammatiche context-free (CFG) per garantire che i formati di output siano rigidamente controllati. 

Applicando una grammatica, come SQL o un linguaggio specifico di dominio (DSL), alle sue risposte, GPT-5 garantisce che gli output seguano costantemente la struttura richiesta. La CFG è particolarmente importante per processi automatizzati e workflow critici. 

GPT-5 può usare grammatiche definite in formati come Lark per limitare gli output degli strumenti durante la generazione. Questo approccio al decoding vincolato migliora l'affidabilità prevenendo derive di formato e assicurando coerenza strutturale.

Ora scriveremo il codice in cui il modello seleziona uno strumento e produce SOLO un'espressione aritmetica valida secondo la grammatica; questa espressione viene poi valutata in Python, il risultato viene restituito alla conversazione e la risposta finale è presentata chiaramente in linguaggio naturale.

In questo esempio di codice, abbiamo:

  1. Inizializzato il client OpenAI e definito uno strumento personalizzato math_solver che deve produrre SOLO un'espressione aritmetica valida secondo la grammatica (Lark CFG).
  2. Avviato la conversazione con una domanda di matematica in linguaggio naturale dall'utente.
  3. Effettuato una prima chiamata al modello istruendo GPT-5 a chiamare math_solver con un'espressione conforme alla grammatica.
  4. Individuato la chiamata allo strumento emessa ed estratto la stringa dell'espressione grezza dal payload dello strumento.
  5. Valutato l'espressione localmente in un ambiente ristretto per produrre un risultato concreto.
  6. Restituito il risultato dell'esecuzione dello strumento alla conversazione, collegato tramite l'call_id originale.
  7. Eseguito una chiamata finale al modello per presentare chiaramente il risultato valutato in linguaggio naturale.
from openai import OpenAI
import json

client = OpenAI()

# --- Local fake evaluator ---
def eval_expression(expr: str) -> str:
    try:
        # Evaluate safely using Python's eval on restricted globals
        result = eval(expr, {"__builtins__": {}}, {})
        return f"{expr} = {result}"
    except Exception as e:
        return f"Error evaluating expression: {e}"

# --- Custom tool with grammar constraint ---
tools = [
    {
        "type": "custom",
        "name": "math_solver",
        "description": "Solve a math problem by outputting ONLY a valid arithmetic expression.",
        "format": {
            "type": "grammar",
            "syntax": "lark",
            "definition": r"""
start: expr

?expr: term
     | expr "+" term   -> add
     | expr "-" term   -> sub

?term: factor
     | term "*" factor -> mul
     | term "/" factor -> div

?factor: NUMBER
       | "(" expr ")"

%import common.NUMBER
%ignore " "
"""
        },
    }
]

# --- User asks ---
msgs = [
    {"role": "user", "content": "What is (12 + 8) * 3 minus 5 divided by 5?"}
]

# 1) First model call: GPT-5 must emit a grammar-valid expression
resp = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=msgs,
    instructions="Always call the math_solver tool with a grammar-valid arithmetic expression like '(12 + 8) * 3 - 5 / 5'."
)

msgs += resp.output

tool_call = next(
    x for x in resp.output
    if getattr(x, "type", "") in ("custom_tool_call", "tool_call", "function_call")
)

expr = getattr(tool_call, "input", "") or getattr(tool_call, "arguments", "")
print("\n=== Grammar-Constrained Expression ===")
print(expr)

# 2) Run the local evaluator
tool_result = eval_expression(expr)
print("\n=== Tool Execution Result ===")
print(tool_result)

# 3) Return tool output
msgs.append({
    "type": "function_call_output",
    "call_id": tool_call.call_id,
    "output": tool_result
})

# 4) Final pass: GPT-5 presents answer nicely
final = client.responses.create(
    model="gpt-5",
    input=msgs,
    tools=tools,
    instructions="Present the evaluated result clearly in natural language."
)

print("\n=== Final Output ===")
print(final.output_text)

Come puoi vedere, il modello ha selezionato lo strumento e poi ha generato un'espressione vincolata dalla grammatica. Successivamente, viene fatta passare attraverso la funzione Python per generare la risposta. Alla fine, otteniamo una risposta corretta in linguaggio naturale.

=== Grammar-Constrained Expression ===
(12 + 8) * 3 - 5 / 5

=== Tool Execution Result ===
(12 + 8) * 3 - 5 / 5 = 59.0

=== Final Output ===
59

Allowlist degli strumenti: limita in sicurezza ciò che il modello può usare

L'impostazione allowed_tools ti consente di limitare il modello a una selezione sicura di strumenti dal tuo toolkit completo. Questo migliora la prevedibilità e previene chiamate indesiderate, pur fornendo al modello flessibilità all'interno del set consentito. 

Puoi lasciare che il modello scelga tra gli strumenti consentiti o richiedere che ne usi uno, rispecchiando i comportamenti di "force tool" visti in altri stack e allineandoti alle best practice generali per l'orchestrazione delle chiamate agli strumenti.

Ora scriveremo il codice in cui consentiamo un singolo strumento, facciamo in modo che il modello chiami solo quello strumento, lo eseguiamo, rimandiamo i risultati indietro e poi produciamo un riepilogo conciso per l'utente.

Nota: crea un account Firecrawl gratuito, genera una chiave API, salvala come variabile d'ambiente FIRECRAWL_API_KEY e installa l'SDK Python di Firecrawl usando pip install firecrawl-py.

In questo esempio di codice, abbiamo:

  1. Definito un set completo di strumenti con dummy_web_search e firecrawl_search.
  2. Limitato il modello a firecrawl_search soltanto tramite tool_choice = { type: "allowed_tools", mode: "required", tools: [...] }.
  3. Effettuato la prima chiamata al modello, catturato il nome della chiamata allo strumento e gli argomenti, e stampati per visibilità.
  4. Quando viene invocato firecrawl_search, deserializzato gli argomenti, eseguito Firecrawl, convertita la sua risposta in un dict serializzabile in JSON ed estratto il payload dati sicuro.
  5. Rimandato i risultati dello strumento nella conversazione usando function_call_output collegato da call_id, poi effettuata una chiamata finale al modello per riassumere in 3–4 punti elenco con link Markdown.
  6. Incluso un ramo di fallback che eseguirebbe lo strumento fittizio (anche se l'allowlist lo impedisce in questa esecuzione).
  7. Stampata la risposta finale in linguaggio naturale.
from openai import OpenAI
from firecrawl import Firecrawl
import json
import os

client = OpenAI()
firecrawl = Firecrawl(api_key=os.environ["FIRECRAWL_API_KEY"])

# --- Full toolset ---
tools = [
    {
        "type": "function",
        "name": "dummy_web_search",
        "description": "A fake web search that always returns static dummy results.",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {"type": "string"},
                "limit": {"type": "integer"}
            },
            "required": ["query"]
        },
    },
    {
        "type": "function",
        "name": "firecrawl_search",
        "description": "Perform a real web search using Firecrawl.",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {"type": "string"},
                "limit": {"type": "integer"}
            },
            "required": ["query"]
        },
    },
]

# --- Dummy implementation ---
def dummy_web_search(query, limit=3):
    return {
        "query": query,
        "results": [f"[Dummy] Result {i+1} for '{query}'" for i in range(limit)]
    }

# --- User asks ---
messages = [
    {"role": "user", "content": "Find me the latest info about the stock market."}
]

# --- Restrict to Firecrawl only ---
resp = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=messages,
    instructions="Only the firecrawl_search tool is allowed. Do not use dummy_web_search.",
    tool_choice={
        "type": "allowed_tools",
        "mode": "required",  # force Firecrawl
        "tools": [{"type": "function", "name": "firecrawl_search"}],
    },
)

for item in resp.output:
    if getattr(item, "type", "") in ("function_call", "tool_call"):
        print("\n--- Tool Call ---")
        print("Tool name:", item.name)
        print("Arguments:", item.arguments)

        if item.name == "firecrawl_search":
            args = json.loads(item.arguments)

            # Firecrawl returns a SearchData object → convert it
            results_obj = firecrawl.search(
                query=args["query"],
                limit=args.get("limit", 3),
            )

            # 🔥 Convert to JSON-serializable dict
            if hasattr(results_obj, "to_dict"):
                results = results_obj.to_dict()
            elif hasattr(results_obj, "dict"):
                results = results_obj.dict()
            else:
                results = json.loads(results_obj.json()) if hasattr(results_obj, "json") else results_obj

            print("\n--- Firecrawl Raw Results ---")
            print(json.dumps(results, indent=2)[:500])

            # ✅ Extract only the data portion for summarization
            safe_results = results.get("data", results)

            # Feed back to GPT
            messages += resp.output
            messages.append({
                "type": "function_call_output",
                "call_id": item.call_id,
                "output": json.dumps(safe_results)  # now safe
            })

            # Final pass
            final = client.responses.create(
                model="gpt-5",
                tools=tools,
                input=messages,
                instructions="Summarize the Firecrawl search results into 3-4 bullet points with clickable [Title](URL) links."
            )

            print("\n--- Final Natural Language Answer ---")
            print(final.output_text)

        elif item.name == "dummy_web_search":
            args = json.loads(item.arguments)
            results = dummy_web_search(args["query"], args.get("limit", 3))
            print("\n--- Dummy Web Search Results ---")
            print(json.dumps(results, indent=2))

Ha selezionato la ricerca Firecrawl e, utilizzandola, ha generato la risposta finale in formato markdown.

Preamboli: spiega al modello come chiamare gli strumenti

I preamboli sono brevi spiegazioni che il modello genera prima di invocare uno strumento, chiarendo il motivo della chiamata. Migliorano la trasparenza, costruiscono fiducia e rendono più chiaro il debugging in workflow complessi. Puoi abilitarli usando un'istruzione semplice come "spiega prima di chiamare uno strumento", in genere senza aggiungere latenza percepibile. 

Ora scriveremo il codice in cui il modello genera prima una riga "Preamble:" che spiega perché sta chiamando uno strumento, poi chiama lo strumento, tu lo esegui localmente, restituisci il risultato e il modello produce una risposta finale chiara.

In questo esempio di codice, abbiamo:

  1. Inizializzato il client OpenAI e definito un function tool strettamente tipizzato (medical_advice) con JSON Schema.
  2. Aggiunto un'istruzione di sistema che dice al modello di produrre una riga "Preamble:" prima di qualsiasi chiamata a uno strumento.
  3. Iniziato la conversazione con una domanda sui sintomi da parte dell'utente.
  4. Effettuata la prima chiamata al modello per ottenere sia il preambolo sia la chiamata allo strumento con i suoi argomenti.
  5. Estratto ed eseguito lo strumento localmente per generare il consiglio.
  6. Restituito l'output dello strumento al modello, collegato tramite call_id.
  7. Eseguita una chiamata finale al modello così GPT‑5 presenta il risultato come risposta concisa e facile da capire.
from openai import OpenAI
import json
import random

client = OpenAI()

# --- Fake tool implementation ---
def medical_advice(symptom: str):
    remedies = {
        "headache": "You can take acetaminophen (paracetamol) or ibuprofen, rest in a quiet room, and stay hydrated.",
        "cough": "Drink warm fluids, use honey in tea, and consider over-the-counter cough syrup.",
        "fever": "Use acetaminophen to reduce fever, stay hydrated, and rest. See a doctor if >39°C.",
    }
    return {"advice": remedies.get(symptom.lower(), "Please consult a healthcare provider for guidance.")}

# --- Tool definition ---
tools = [{
    "type": "function",
    "name": "medical_advice",
    "description": "Provide safe, general over-the-counter advice for common symptoms.",
    "parameters": {
        "type": "object",
        "properties": {"symptom": {"type": "string"}},
        "required": ["symptom"],
        "additionalProperties": False
    },
    "strict": True,
}]

# --- Messages (system preamble instruction) ---
messages = [
    {"role": "system", "content": "Before you call a tool, explain why you are calling it in ONE short sentence prefixed with 'Preamble:'."},
    {"role": "user", "content": "What should I take for a headache?"}
]

# 1) First call: expect preamble + tool call
resp = client.responses.create(model="gpt-5", input=messages, tools=tools)
print("=== First Response ===")
for item in resp.output:
    t = getattr(item, "type", None)
    if t == "message":
        # Extract just the model's text
        content = getattr(item, "content", None)
        text = None
        if isinstance(content, list):
            text = "".join([c.text for c in content if hasattr(c, "text")])
        elif content:
            text = str(content)
        if text:
            # ✅ Only print once
            print(text)
    if t in ("function_call", "tool_call", "custom_tool_call"):
        print("Tool:", getattr(item, "name", None))
        print("Args:", getattr(item, "arguments", None))

# Extract tool call
tool_call = next(x for x in resp.output if getattr(x, "type", None) in ("function_call","tool_call","custom_tool_call"))
messages += resp.output

# 2) Execute tool locally
args = json.loads(getattr(tool_call, "arguments", "{}"))
symptom = args.get("symptom", "")
tool_result = medical_advice(symptom)

# 3) Return tool result
messages.append({
    "type": "function_call_output",
    "call_id": tool_call.call_id,
    "output": json.dumps(tool_result)
})

# 4) Final model call → natural answer
final = client.responses.create(model="gpt-5", input=messages, tools=tools)
print("\n=== Final Answer ===")
print(final.output_text)

Puoi vedere che, prima di chiamare lo strumento, fornisce una spiegazione del motivo della chiamata. Questo è utile per osservabilità e debugging.

Riepilogo

GPT-5 è il modello più avanzato di OpenAI fino ad oggi, in particolare nei workflow di coding e basati su agenti. Le sue ultime funzionalità API consentono lo sviluppo semplice di sistemi di livello produttivo dall'inizio alla fine.

In questo tutorial, hai imparato a:

  1. Restituire output strutturati usando Function Tools e JSON Schema per un'automazione a valle affidabile.
  2. Abilitare l'esecuzione free-form con strumenti personalizzati (testo grezzo), consentendo al modello di generare codice, SQL, script shell e file CSV oltre alle sole chiamate JSON.
  3. Vincolare i formati usando vincoli grammaticali Lark quando la precisione è cruciale, come in matematica, SQL o linguaggi specifici di dominio (DSL).
  4. Limitare l'accesso agli strumenti con allowlist (o imponendo un singolo strumento) per migliorare sicurezza, controllo e determinismo.
  5. Migliorare la trasparenza con brevi preamboli, così il modello spiega perché sta chiamando uno strumento specifico, aiutando osservabilità e debugging.

Per saperne di più su cosa puoi fare con i vari strumenti di OpenAI, ti consiglio di seguire il nostro percorso di competenze OpenAI Fundamentals e il corso Working with the OpenAI API.

Abid Ali Awan

Autore

Abid Ali Awan

In quanto data scientist certificato, sono appassionato di sfruttare tecnologie all’avanguardia per creare applicazioni di machine learning innovative. Con una solida esperienza in riconoscimento vocale, analisi e reportistica dei dati, MLOps, AI conversazionale e NLP, ho affinato le mie competenze nello sviluppo di sistemi intelligenti in grado di avere un impatto concreto. Oltre alla mia expertise tecnica, sono anche un comunicatore efficace, con il talento di rendere chiari e sintetici concetti complessi. Di conseguenza, sono diventato un blogger molto seguito in ambito data science, condividendo idee ed esperienze con una community in crescita di professionisti dei dati. Attualmente mi concentro sulla creazione e sull’editing di contenuti, lavorando con large language model per sviluppare contenuti potenti e coinvolgenti che possano aiutare aziende e singoli a valorizzare al meglio i propri dati.

Argomenti

Correlato