ThunderPhone 2.0 è arrivato.Attivazione self-service, da 2¢/min.Leggi l’annuncio

Function Tools

Strumenti di funzione

Fornisci ai tuoi agenti AI strumenti di 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 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 (quali argomenti accetta lo strumento)
  2. Fornisci una configurazione endpoint (dove ThunderPhone chiama la tua API) oppure omettila per ricevere le chiamate degli 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
CapacitàDove viene eseguitoConfigurazione
Strumenti integratiThunderPhoneIstruzioni nel prompt; alcuni strumenti richiedono anche un'impostazione dell'agente
Connessioni appThunderPhone e il provider connessoConnetti l'account e associa le azioni approvate
Connessioni API e strumenti funzioneLa tua API HTTPDefinisci l'endpoint e lo schema, oppure ricevi le chiamate funzione tramite webhook
Server MCPUn server MCP remotoAggiungi il server, rileva i suoi strumenti e associalo all'agente

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
namestringaSìIdentificatore univoco dello strumento
descriptionstringaSìSpiega all'AI quando usare questo strumento
parametersoggettoSìSchema JSON per gli argomenti dello strumento

Configurazione dell'endpoint

CampoTipoObbligatorioDescrizione
urlstringaSìURL 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 abbia un endpoint:

Strumento con endpointStrumento senza endpoint
Destinazione della richiestaDirettamente a endpoint.urlURL webhook legacy della tua organizzazione
CorpoFormato della richiesta selezionato (argomenti senza wrapper per impostazione predefinita)Involucro telephony.tool / web.tool
Intestazioniendpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature-V2 + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature-V2 + 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 timeout di primo livello dello strumento per consentire un'esecuzione più lunga, fino al massimo della piattaforma di 180 s. Mantieni gli handler rapidi. Puoi combinarli: in una chiamata la cui organizzazione dispone di un URL webhook, gli strumenti con un endpoint vengono chiamati direttamente e gli altri usano il webhook.

Chiamate dirette agli endpoint

Quando l'AI richiama 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-V2: t=1767225600,v1=5f2c...
X-ThunderPhone-Signature: abc123...
X-ThunderPhone-Call-ID: 987654321
X-Api-Key: your-api-key

Le intestazioni personalizzate di endpoint.headers vengono sempre incluse alla lettera, insieme a queste intestazioni con namespace ThunderPhone:

  • X-ThunderPhone-Signature-V2 — HMAC-SHA256 con timestamp su metodo, percorso, query e corpo, con chiave nel tuo segreto webhook dell'organizzazione. Verifica questa firma e rifiuta le richieste più vecchie di 5 minuti; consulta Verificare le firme webhook
  • X-ThunderPhone-Signature — HMAC-SHA256 legacy dei byte esatti del corpo della richiesta, stessa chiave, ancora inviata per i verificatori esistenti
  • X-ThunderPhone-Call-ID — L'ID della chiamata corrente

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

Formato della richiesta

Ogni connessione API ha un'impostazione request_format. Scegli vapi o retell quando uno strumento importato deve continuare a chiamare un endpoint creato per quella piattaforma. Le connessioni nuove ed esistenti usano standard per impostazione predefinita.

Per POST / PUT / PATCH, ThunderPhone serializza in modo canonico l'involucro selezionato (chiavi ordinate, separatori compatti). Entrambe le firme coprono questi byte esatti.

Standard

standard invia solo gli argomenti dello strumento, senza wrapper. Questo è il comportamento predefinito e preserva il corpo della richiesta originale di ThunderPhone byte per byte:

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

Vapi

vapi invia il messaggio server tool-calls di Vapi nella forma documentata da Vapi. Il call.id è l'ID della chiamata ThunderPhone. L'id della chiamata allo strumento è univoco per l'invocazione e viene riutilizzato per associare il risultato dell'endpoint. La chiamata appare sia in toolCallList sia in toolWithToolCallList, con function.arguments come oggetto JSON:

{
  "message": {
    "type": "tool-calls",
    "timestamp": 1725000000123,
    "call": { "id": "987654321" },
    "toolCallList": [
      {
        "id": "call_00112233445566778899aabbccddeeff",
        "type": "function",
        "function": {
          "name": "search_appointments",
          "arguments": { "date": "2025-01-02", "service": "consultation" }
        }
      }
    ],
    "toolWithToolCallList": [
      {
        "type": "function",
        "function": { "name": "search_appointments" },
        "toolCall": {
          "id": "call_00112233445566778899aabbccddeeff",
          "type": "function",
          "function": {
            "name": "search_appointments",
            "arguments": { "date": "2025-01-02", "service": "consultation" }
          }
        }
      }
    ]
  }
}

I campi esclusivi di Vapi come assistant, artifact e l'oggetto call completo non vengono inviati.

Restituisci l'involucro di risposta di Vapi con l'ID della chiamata allo strumento corrispondente:

{
  "results": [
    {
      "toolCallId": "call_00112233445566778899aabbccddeeff",
      "result": "{\"available_slots\":[\"9:00 AM\",\"2:00 PM\"]}"
    }
  ]
}

ThunderPhone fornisce all'IA la stringa result senza involucro. Accetta anche un valore JSON per la compatibilità con endpoint che ne restituiscono già uno. Un error al posto di result viene restituito all'IA come errore dello strumento. Se l' endpoint restituisce un involucro Vapi non valido o non corrispondente, ThunderPhone registra un avviso e passa all'analisi standard della risposta invece di interrompere la chiamata.

Retell

retell invia la richiesta documentata per una funzione personalizzata Retell:

{
  "name": "search_appointments",
  "call": { "call_id": "987654321" },
  "args": { "date": "2025-01-02", "service": "consultation" }
}

L'oggetto call include solo call_id. ThunderPhone non include campi specifici di Retell che non può fornire correttamente al momento dell'invio, come agent_id, transcript, transcript_object, metadata o variabili dinamiche. Retell accetta qualsiasi corpo della risposta; ThunderPhone lo inoltra utilizzando la gestione standard della risposta.

Per GET / DELETE, gli argomenti vengono inviati come parametri di query e il corpo è vuoto indipendentemente da request_format. La firma V2 copre la stringa di query così come inviata; la firma legacy viene calcolata sulla stringa di byte vuota. Consulta Verifica le firme dei 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'AI per continuare la conversazione. Le risposte non JSON vengono racchiuse in {"data": "<text>"}; i timeout e gli errori di connessione vengono segnalati all'AI 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 recapitate 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 formato JSON — lo stesso contratto di risposta delle chiamate dirette agli endpoint. La richiesta è firmata con il segreto webhook dell'organizzazione, con entrambe le intestazioni della firma, come qualsiasi altro webhook.


Verifica della firma

Le chiamate dirette agli strumenti sono firmate come i webhook, usando il segreto webhook della tua organizzazione:

  • X-ThunderPhone-Signature-V2 firma il timestamp, il metodo, il percorso e la query così come inviati, nonché i byte esatti del corpo. Rifiuta le richieste il cui timestamp differisce di più di 5 minuti dal tuo orologio.
  • X-ThunderPhone-Signature (legacy) firma solo i byte del corpo, la stringa di byte vuota per GET / DELETE. Viene ancora inviata, invariata.

Gli helper dell'SDK verificano l'header V2, inclusa la finestra di 5 minuti:

Python
import json
 
from fastapi import FastAPI, HTTPException, Request
from thunderphone import verify_signature
 
app = FastAPI()
 
@app.post("/appointments/search")
async def search_appointments(request: Request):
    body = await request.body()
    query = request.scope.get("query_string", b"").decode("latin-1")
    path = request.url.path + (f"?{query}" if query else "")
 
    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=WEBHOOK_SECRET,
        method=request.method,
        path=path,
        body=body,
    ):
        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
import express from "express";
import { verifySignature } from "@thunderphone/sdk";
 
app.post('/appointments/search', express.raw({type: 'application/json'}), (req, res) => {
  const ok = verifySignature({
    header: req.header('X-ThunderPhone-Signature-V2'),
    secret: WEBHOOK_SECRET,
    method: req.method,
    path: req.originalUrl,
    body: req.body,
  });
  if (!ok) 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 — la stringa firmata, un esempio pratico, il codice senza SDK, il caso GET / DELETE e l'avvertenza in assenza di segreto — sono disponibili in Verificare le 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 con precisione cosa fa e quando è appropriato utilizzarlo.

Gestisci gli errori in modo efficace

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

Mantieni concise le risposte

Restituisci solo ciò di cui l'IA ha bisogno per continuare la conversazione. 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