ThunderPhone 2.0 è arrivato.Parti in autonomia, da 2¢/min.Leggi l’annuncio

Function Tools

Strumenti funzione

Fornisci ai tuoi agenti AI strumenti funzione che chiamano API esterne durante la conversazione — recuperano dati dei clienti, fissano appuntamenti, aggiornano record — con parametri tipizzati.

Gli strumenti funzione consentono ai tuoi agenti vocali AI di invocare API esterne durante le chiamate telefoniche. Usali per cercare dati dei clienti, verificare la disponibilità, prenotare appuntamenti o eseguire qualsiasi azione supportata dal tuo backend.

Come funziona

  1. Definisci gli strumenti con uno schema (gli argomenti accettati dallo strumento)
  2. Fornisci una configurazione endpoint (dove ThunderPhone chiama la tua API) — oppure non specificarla per ricevere le chiamate agli strumenti sul webhook della tua organizzazione
  3. Durante una chiamata, l'AI decide quando usare uno strumento in base alla conversazione
  4. ThunderPhone chiama il tuo endpoint con gli argomenti dello strumento
  5. La risposta della tua API viene restituita all'AI per continuare la conversazione

Schema dello strumento

Ogni strumento segue questa struttura:

{
  "type": "function",
  "function": {
    "name": "search_appointments",
    "description": "Find available appointment slots for a given date",
    "parameters": {
      "type": "object",
      "properties": {
        "date": {
          "type": "string",
          "description": "Date in YYYY-MM-DD format"
        },
        "service": {
          "type": "string",
          "description": "Type of service (e.g., 'consultation', 'follow-up')"
        }
      },
      "required": ["date"]
    }
  },
  "endpoint": {
    "url": "https://api.example.com/appointments/search",
    "method": "POST",
    "headers": {
      "X-Api-Key": "your-api-key"
    }
  },
  "timeout": 120
}

Configurazione dello strumento

CampoTipoObbligatorioDescrizione
timeoutnumeroNoTempo massimo di esecuzione in secondi (predefinito: 20, massimo: 180)

Definizione della funzione

CampoTipoObbligatorioDescrizione
namestringaIdentificatore univoco dello strumento
descriptionstringaSpiega all'AI quando usare questo strumento
parametersoggettoJSON Schema per gli argomenti dello strumento

Configurazione dell'endpoint

CampoTipoObbligatorioDescrizione
urlstringaURL dell'endpoint della tua API
methodstringaNoMetodo HTTP (predefinito: POST)
headersoggettoNoIntestazioni personalizzate da includere

Due percorsi di invocazione

La richiesta ricevuta dal tuo server dipende dal fatto che lo strumento disponga di un endpoint:

Strumento con endpointStrumento senza endpoint
Destinazione della richiestaDirettamente a endpoint.urlURL webhook legacy della tua organizzazione
CorpoArgomenti dello strumento senza wrapperWrapper telephony.tool / web.tool
IntestazioniI tuoi endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature
Chiave di firmaSegreto webhook dell'organizzazioneSegreto webhook dell'organizzazione

Entrambi i percorsi sono bloccanti: l'AI attende il risultato a metà frase. Il timeout predefinito è 20 s; imposta il valore timeout di primo livello dello strumento per consentire un'esecuzione più lunga, fino al massimo della piattaforma di 180 s. Mantieni gli handler veloci. È possibile usare una combinazione: in una chiamata la cui organizzazione ha un URL webhook, gli strumenti con un endpoint vengono chiamati direttamente e gli altri usano il webhook come fallback.

Chiamate dirette agli endpoint

Quando l'IA invoca uno strumento con un endpoint, ThunderPhone invia una richiesta al tuo URL:

Intestazioni della richiesta

POST /appointments/search HTTP/1.1
Host: api.example.com
Content-Type: application/json
X-ThunderPhone-Signature: abc123...
X-ThunderPhone-Call-ID: 987654321
X-Api-Key: your-api-key

Le intestazioni personalizzate da endpoint.headers sono sempre incluse testualmente, oltre a due intestazioni nello spazio dei nomi ThunderPhone:

  • X-ThunderPhone-Signature — HMAC-SHA256 dei byte esatti del corpo della richiesta, con chiave il tuo segreto webhook dell'organizzazione
  • X-ThunderPhone-Call-ID — L'ID della chiamata corrente

Content-Type: application/json viene impostato a meno che endpoint.headers non lo sovrascriva — un Content-Type personalizzato ha la precedenza.

Corpo della richiesta

Per POST / PUT / PATCH, il corpo contiene solo gli argomenti dello strumento (senza wrapper), serializzati canonicamente (chiavi ordinate, separatori compatti):

{"date":"2025-01-02","service":"consultation"}

Per GET / DELETE, gli argomenti vengono inviati come parametri di query e il corpo è vuoto — la firma viene quindi calcolata sulla stringa di byte vuota. Consulta Verifica le firme webhook.

Risposta

Restituisci una risposta JSON con il risultato dello strumento:

{
  "available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
  "timezone": "America/Los_Angeles"
}

La risposta viene formattata e fornita all'IA per continuare la conversazione. Le risposte non JSON vengono racchiuse in {"data": "<text>"}; i timeout e gli errori di connessione vengono segnalati all'IA come errori, così l'agente può scusarsi e proseguire anziché bloccarsi.

Instradamento in modalità webhook

Gli strumenti senza un endpoint vengono instradati all'URL webhook legacy della tua organizzazione come richiesta firmata telephony.tool (chiamate telefoniche) o web.tool (chiamate web). A differenza delle notifiche di audit consegnate agli endpoint webhook dopo l'esecuzione, questa richiesta è l'esecuzione — la tua risposta HTTP è il risultato dello strumento.

{
  "type": "telephony.tool",
  "data": {
    "call_id": 987654321,
    "tool_name": "search_appointments",
    "arguments": { "date": "2026-04-21" },
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  }
}

web.tool include origin_domain invece di from_number / to_number. Rispondi con il risultato dello strumento in JSON — lo stesso contratto di risposta delle chiamate dirette agli endpoint. La richiesta è firmata con il segreto webhook dell'organizzazione sul corpo non elaborato, come ogni altro webhook.


Verifica della firma

Le chiamate dirette agli strumenti vengono firmate allo stesso modo dei webhook:

  • HMAC-SHA256 sugli esatti byte del corpo della richiesta (il JSON canonico — chiavi ordinate, nessuno spazio aggiuntivo)
  • Con la tua chiave segreta webhook dell'organizzazione
  • Gli strumenti GET / DELETE firmano la stringa di byte vuota
Python
import hmac
import hashlib
 
def verify_tool_call(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)
 
@app.post("/appointments/search")
async def search_appointments(request: Request):
    body = await request.body()
    signature = request.headers.get("X-ThunderPhone-Signature", "")
 
    if not verify_tool_call(body, signature, WEBHOOK_SECRET):
        raise HTTPException(status_code=401)
 
    data = json.loads(body)
    date = data["date"]
 
    # Look up availability
    slots = await get_available_slots(date)
 
    return {"available_slots": slots}
Node.js
app.post('/appointments/search', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-thunderphone-signature'] || '';
  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');
 
  if (!signature ||
      signature.length !== expected.length ||
      !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
    return res.status(401).send('Invalid signature');
  }
 
  const { date, service } = JSON.parse(req.body);
 
  // Look up availability
  const slots = getAvailableSlots(date, service);
 
  res.json({ available_slots: slots });
});

Le procedure complete — incluso il caso del corpo vuoto e l'avvertenza sull'assenza di una chiave segreta — sono disponibili in Verifica delle firme dei webhook.


Esempio: flusso di prenotazione completo

Ecco un insieme di strumenti per un sistema completo di prenotazione appuntamenti:

{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "search_appointments",
        "description": "Find available appointment slots",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "service": { "type": "string" }
          },
          "required": ["date"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/search",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "book_appointment",
        "description": "Book an appointment at a specific time",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "time": { "type": "string", "description": "HH:MM format" },
            "customer_name": { "type": "string" },
            "customer_phone": { "type": "string" }
          },
          "required": ["date", "time", "customer_name"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/book",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "cancel_appointment",
        "description": "Cancel an existing appointment",
        "parameters": {
          "type": "object",
          "properties": {
            "confirmation_number": { "type": "string" }
          },
          "required": ["confirmation_number"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/cancel",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    }
  ]
}

Best practice

Scrivi descrizioni chiare

Il campo description aiuta l'IA a capire quando usare lo strumento. Specifica chiaramente cosa fa e quando è appropriato usarlo.

Gestisci gli errori in modo efficace

Restituisci messaggi di errore comprensibili per l'IA: {"error": "No slots available for that date"} invece di errori 500 generici.

Mantieni concise le risposte

Restituisci solo ciò di cui l'IA ha bisogno per continuare la conversazione. I payload di grandi dimensioni rallentano i tempi di risposta.

Usa con criterio i campi obbligatori

Contrassegna i campi come required solo quando è davvero necessario. L'IA chiederà all'utente le informazioni obbligatorie prima di chiamare lo strumento.


Correlati