Torna al blog

API e SDKGuida

Function calling in Python con un'API compatibile con OpenAI

Invia i tool, leggi tool_calls e restituisci il risultato con role tool: un ciclo di function calling in due passaggi in Python con MiniMax-M2.7.

Di CheapTokens TeamPubblicato il Aggiornato il 6 min di lettura

Diagramma di un ciclo di tool call in due passaggi: invia i tool, leggi tool_calls, restituisci il risultato come messaggio tool
In questa pagina
  1. Come funziona il function calling
  2. Configura il client
  3. Dichiara una funzione e invia la prima richiesta
  4. Cosa dovresti vedere
  5. Più chiamate e cicli più lunghi
  6. Perché MiniMax-M2.7 è adatto ai cicli di tool
  7. Se non funziona
  8. Prossimi passi

Il function calling (tool calling) consente a un modello di chiedere al tuo codice di eseguire una funzione. Invii una lista tools con la richiesta, il modello risponde con tool_calls invece che con del testo, esegui tu stesso la funzione e rimandi il suo risultato in un messaggio con role: tool. Il modello scrive poi la risposta finale. Questa guida mostra l'intero ciclo in Python verso l'endpoint compatibile con OpenAI https://api.cheaptokens.dev/v1/chat/completions con MiniMax-M2.7 (id MiniMaxAI/MiniMax-M2.7, contesto 180K, fino a 8.192 token di output per risposta).

Il tool calling è indicato come supportato per ogni modello del catalogo (CheapTokens docs, verificato il 22 ottobre 2026). Ti serve una chiave API; se non hai ancora fatto una richiesta, inizia con la tua prima richiesta API.

Come funziona il function calling#

Il modello non esegue mai nulla. Produce solo una richiesta strutturata: "chiama get_weather con {\"city\": \"Paris\"}". Il tuo programma fa il lavoro vero e riporta il risultato. Questo crea un ciclo di due richieste:

  1. Richiesta 1. Invii il messaggio dell'utente più la lista tools. Il modello risponde in testo oppure restituisce una o più tool_calls.
  2. Il tuo codice. Analizzi ogni chiamata, esegui la funzione corrispondente e raccogli il risultato.
  3. Richiesta 2. Invii di nuovo l'intera conversazione: i messaggi originali, il messaggio dell'assistente con tool_calls e un messaggio role: tool per ogni chiamata. Il modello usa i risultati per scrivere la risposta finale.

Ogni richiesta reinvia tutta la cronologia, quindi i cicli di tool consumano più token di input rispetto a una singola domanda. Se l'argomento è nuovo per te, leggi cos'è un token.

Configura il client#

Installa l'SDK OpenAI (pip install openai) e metti la chiave in una variabile d'ambiente invece che nel codice sorgente:

bash
export CHEAPTOKENS_API_KEY="your-key-here"

La base URL è https://api.cheaptokens.dev/v1. Prendi l'id del modello dalla documentazione CheapTokens: provider diversi chiamano lo stesso modello in modi diversi. Questa guida usa MiniMaxAI/MiniMax-M2.7.

Dichiara una funzione e invia la prima richiesta#

Una funzione è descritta nell'array tools. Ogni voce ha type: function e un oggetto function con name, description e parameters scritti come JSON Schema. La descrizione è ciò che il modello legge per decidere quando chiamare la funzione, quindi scrivila come una frase breve e concreta.

Qui sotto c'è uno script completo. La funzione meteo è uno stub con dati fittizi, così l'esempio non richiede servizi aggiuntivi:

python
import json
import os

from openai import OpenAI

client = OpenAI(
    base_url='https://api.cheaptokens.dev/v1',
    api_key=os.environ['CHEAPTOKENS_API_KEY'],
)
MODEL = 'MiniMaxAI/MiniMax-M2.7'


def get_weather(city: str) -> dict:
    # Stub with fake data: replace with a real lookup
    fake = {'Paris': 14, 'Tokyo': 19}
    return {'city': city, 'temp_c': fake.get(city, 10)}


FUNCTIONS = {'get_weather': get_weather}

tools = [
    {
        'type': 'function',
        'function': {
            'name': 'get_weather',
            'description': 'Get the current temperature in Celsius for a city.',
            'parameters': {
                'type': 'object',
                'properties': {
                    'city': {'type': 'string', 'description': 'City name, for example Paris'},
                },
                'required': ['city'],
            },
        },
    }
]

messages = [{'role': 'user', 'content': 'What is the weather in Paris right now?'}]

# Step 1: the model decides whether to call a function
first = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
message = first.choices[0].message

if not message.tool_calls:
    print(message.content)
    raise SystemExit

# Step 2: run every requested call and send the results back
messages.append(message)
for call in message.tool_calls:
    args = json.loads(call.function.arguments)
    result = FUNCTIONS[call.function.name](**args)
    messages.append({
        'role': 'tool',
        'tool_call_id': call.id,
        'content': json.dumps(result),
    })

final = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
print(final.choices[0].message.content)

Tre dettagli decidono se il ciclo funziona:

  • call.function.arguments è una stringa JSON, non un dizionario. Analizzala con json.loads.
  • Ogni messaggio role: tool deve portare il tool_call_id della chiamata a cui risponde, cioè call.id.
  • Il content di un messaggio tool è una stringa. Usa json.dumps per risultati strutturati.

Cosa dovresti vedere#

Se stampi i campi dopo la prima richiesta, appaiono così (l'id della chiamata cambia a ogni esecuzione):

text
finish_reason: tool_calls
call.id: call_abc123
call.function.name: get_weather
call.function.arguments: {"city": "Paris"}

Lo script stampa poi la risposta finale, una frase semplice del tipo "Al momento ci sono 14 °C a Parigi." La formulazione cambia da un'esecuzione all'altra; il numero proviene dalla tua funzione.

Più chiamate e cicli più lunghi#

Un modello può restituire più di una chiamata in una singola risposta, per esempio quando chiedi il meteo in due città. Per questo l'esempio itera su message.tool_calls e risponde a ogni chiamata con il suo id. La guida al function calling di OpenAI descrive lo stesso comportamento: con il valore predefinito tool_choice di auto il modello può chiamare zero, una o più funzioni (OpenAI docs, verificato il 22 ottobre 2026; la pagina non ha una data di ultimo aggiornamento e descrive la forma più recente della Responses API, mentre questa guida usa la forma Chat Completions dell'endpoint /v1/chat/completions).

Gli agenti ripetono i due passaggi finché il modello smette di chiedere tool. Avvolgi le richieste in un ciclo e imposta sempre un limite di passaggi:

python
for _ in range(5):  # safety limit against endless loops
    response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
    message = response.choices[0].message
    if not message.tool_calls:
        print(message.content)
        break
    messages.append(message)
    for call in message.tool_calls:
        args = json.loads(call.function.arguments)
        result = FUNCTIONS[call.function.name](**args)
        messages.append({'role': 'tool', 'tool_call_id': call.id, 'content': json.dumps(result)})

Se vuoi che la risposta appaia token per token, combinalo con lo streaming in Python.

Perché MiniMax-M2.7 è adatto ai cicli di tool#

Un ciclo di tool invia molte richieste con una cronologia che cresce. Per questo pattern CheapTokens offre MiniMax-M2.7: contesto 180K, fino a 8.192 token di output per risposta, un unico prezzo a qualsiasi ora del giorno senza fasce di punta e un unico saldo per tutti i modelli. Contando insieme input e output, è più economico del prezzo di riferimento di OpenRouter (confronto prezzi CheapTokens, verificato il 31 ottobre 2026).

Prezzi attuali per 1M di token: input $0.05, output $0.15. Come carico di esempio (non una misurazione), il round trip a due richieste qui sopra con circa 2.700 token di input e 350 di output in totale costa $0.00019. L'elenco completo dei prezzi è nella pagina dei prezzi.

Per provare un altro modello, cambia solo l'id; l'elenco degli id è nella pagina dei modelli.

Se non funziona#

  • message.tool_calls è None. Il modello ha risposto in testo. Rendi la funzione description più specifica e assicurati che la domanda richieda davvero la funzione.
  • json.JSONDecodeError su arguments. Il modello ha prodotto JSON non valido. Avvolgi json.loads in try/except e rimanda il testo dell'errore come risultato del tool, così il modello può riprovare con argomenti corretti.
  • La seconda richiesta fallisce o il modello ignora il risultato. Verifica che ogni chiamata abbia esattamente un messaggio role: tool con un tool_call_id corrispondente e che il messaggio dell'assistente con tool_calls sia nella cronologia prima di questi.
  • KeyError in FUNCTIONS[...]. Il modello ha indicato una funzione che non hai registrato. Restituisci una breve stringa di errore come risultato del tool invece di andare in crash.
  • La risposta è troncata. Controlla finish_reason: il valore length significa che è stato raggiunto il limite di token di output. Vedi perché una risposta AI si interrompe a metà frase.
  • Errori 401 o di autenticazione. Verifica che la chiave sia in CHEAPTOKENS_API_KEY e che la base URL termini con /v1.

Prossimi passi#

Sostituisci lo stub con una funzione reale: una query al database, una chiamata di ricerca o una calcolatrice. Aggiungi altre voci a tools e a FUNCTIONS e mantieni breve ogni risultato delle funzioni, perché torna nella cronologia a ogni richiesta successiva. Per ottenere una chiave e fare la prima chiamata, apri la documentazione CheapTokens.