ThunderPhone 2.0 este acum disponibil.Self-service, de la 2 ¢/min.Citiți anunțul

Function Tools

Instrumente de funcții

Oferiți agenților dumneavoastră AI instrumente de funcții care apelează API-uri externe în timpul conversației — preiau date despre clienți, programează întâlniri, actualizează înregistrări — cu parametri tipizați.

Instrumentele de funcție le permit agenților dumneavoastră vocali cu AI să invoce API-uri externe în timpul apelurilor telefonice. Folosiți-le pentru a căuta date despre clienți, a verifica disponibilitatea, a programa întâlniri sau a efectua orice acțiune acceptată de backendul dumneavoastră.

Cum funcționează

  1. Definiți instrumente cu o schemă (ce argumente acceptă instrumentul)
  2. Furnizați o configurație endpoint (unde ThunderPhone apelează API-ul dumneavoastră) — sau omiteți-o pentru a primi apelurile instrumentelor pe webhookul organizației dumneavoastră
  3. În timpul unui apel, AI-ul decide când să utilizeze un instrument pe baza conversației
  4. ThunderPhone apelează endpointul dumneavoastră cu argumentele instrumentului
  5. Răspunsul API-ului dumneavoastră este transmis înapoi AI-ului pentru a continua conversația
CapacitateUnde ruleazăConfigurare
Instrumente integrateThunderPhoneInstrucțiuni în prompt; unele instrumente necesită și o setare a agentului
Conexiuni de aplicațiiThunderPhone și furnizorul conectatConectați contul și atașați acțiunile aprobate
Conexiuni API și instrumente de funcțieAPI-ul dumneavoastră HTTPDefiniți endpointul și schema sau primiți apeluri de funcție printr-un webhook
Servere MCPUn server MCP la distanțăAdăugați serverul, descoperiți instrumentele acestuia și atașați-l la agent

Schema instrumentului

Fiecare instrument respectă această structură:

{
  "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
}

Configurarea instrumentului

CâmpTipObligatoriuDescriere
timeoutnumberNuDurata maximă de execuție în secunde (implicit: 20, maxim: 180)

Definiția funcției

CâmpTipObligatoriuDescriere
namestringDaIdentificator unic pentru instrument
descriptionstringDaExplică AI-ului când să utilizeze acest instrument
parametersobjectDaSchemă JSON pentru argumentele instrumentului

Configurarea endpointului

CâmpTipObligatoriuDescriere
urlstringDaURL-ul endpointului API-ului dumneavoastră
methodstringNuMetodă HTTP (implicit: POST)
headersobjectNuAnteturi personalizate de inclus

Două căi de invocare

Solicitarea primită de serverul dumneavoastră depinde de existența unui endpoint pentru instrument:

Instrument cu endpointInstrument fără endpoint
Unde ajunge solicitareaDirect la endpoint.urlLa adresa URL webhook moștenită a organizației dumneavoastră
CorpFormatul de solicitare selectat (argumente simple în mod implicit)Anvelopa telephony.tool / web.tool
Anteteendpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature-V2 + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature
Cheie de semnareSecretul webhook al organizațieiSecretul webhook al organizației

Ambele căi sunt blocante — AI-ul așteaptă rezultatul în mijlocul propoziției. Timpul de expirare implicit este de 20 s; setați timeout la nivelul superior al instrumentului pentru a permite o execuție mai lungă, până la maximul platformei de 180 s. Mențineți handlerele rapide. Puteți utiliza o combinație: într-un apel al cărui organizație are un URL de webhook, instrumentele cu un endpoint sunt apelate direct, iar celelalte revin la webhook.

Apeluri directe către endpoint

Când AI-ul invocă un instrument care are un endpoint, ThunderPhone trimite o solicitare către URL-ul dumneavoastră:

Antete de solicitare

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

Antetele personalizate din endpoint.headers sunt incluse întotdeauna literal, împreună cu aceste antete din spațiul de nume ThunderPhone:

  • X-ThunderPhone-Signature-V2 — HMAC-SHA256 cu marcaj temporal pentru metodă, cale, interogare și corp, utilizând secretul webhook al organizației. Verificați-o și respingeți cererile mai vechi de 5 minute; consultați Verificarea semnăturilor webhook
  • X-ThunderPhone-Signature — HMAC-SHA256 moștenit al octeților exacți ai corpului cererii, cu aceeași cheie, încă trimis pentru validatoarele existente
  • X-ThunderPhone-Call-ID — ID-ul apelului curent

Content-Type: application/json este setat, cu excepția cazului în care endpoint.headers îl suprascrie — un Content-Type personalizat are prioritate.

Formatul solicitării

Fiecare conexiune API are o setare request_format. Alegeți vapi sau retell atunci când un instrument importat trebuie să continue să apeleze un endpoint creat pentru acea platformă. Conexiunile noi și cele existente folosesc implicit standard.

Pentru POST / PUT / PATCH, ThunderPhone serializează canonic anvelopa selectată (chei sortate, separatori compacți). Ambele semnături acoperă exact acești octeți.

Standard

standard trimite doar argumentele instrumentului, fără un înveliș. Aceasta este opțiunea implicită și păstrează corpul original al solicitării ThunderPhone octet cu octet:

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

Vapi

vapi trimite mesajul de server tool-calls al Vapi în forma documentată de Vapi. call.id este ID-ul apelului ThunderPhone. ID-ul id al apelului instrumentului este unic pentru invocare și este reutilizat pentru a corela rezultatul endpointului. Apelul apare atât în toolCallList, cât și în toolWithToolCallList, cu function.arguments ca obiect 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" }
          }
        }
      }
    ]
  }
}

Câmpurile exclusiv Vapi, precum assistant, artifact și obiectul complet call, nu sunt trimise.

Returnați anvelopa de răspuns Vapi cu ID-ul corespunzător al apelului instrumentului:

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

ThunderPhone oferă AI-ului șirul result neîmpachetat. Acceptă și o valoare JSON pentru compatibilitate cu endpointurile care returnează deja una. Un error în loc de result este transmis AI-ului ca eroare a instrumentului. Dacă endpointul returnează o anvelopă Vapi incorectă sau necorespunzătoare, ThunderPhone înregistrează un avertisment și revine la analiza standard a răspunsului, în loc să eșueze apelul.

Retell

retell trimite solicitarea documentată pentru o funcție personalizată Retell:

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

Obiectul call include doar call_id. ThunderPhone nu include câmpuri specifice Retell pe care nu le poate furniza corect la expediere, precum agent_id, transcript, transcript_object, metadata sau variabile dinamice. Retell acceptă orice corp de răspuns; ThunderPhone îl transmite folosind gestionarea standard a răspunsurilor.

Pentru GET / DELETE, argumentele sunt trimise ca parametri de interogare iar corpul este gol indiferent de request_format. Semnătura V2 acoperă șirul de interogare așa cum este trimis; semnătura veche este calculată pe șirul gol de octeți. Consultați Verificarea semnăturilor webhook.

Răspuns

Returnați un răspuns JSON cu rezultatul instrumentului:

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

Răspunsul este formatat și furnizat AI-ului pentru a continua conversația. Răspunsurile non-JSON sunt împachetate ca {"data": "<text>"}; expirările și erorile de conexiune sunt raportate AI-ului ca erori, astfel încât agentul își poate cere scuze și poate continua, în loc să rămână blocat.

Distribuire în modul webhook

Instrumentele fără un endpoint sunt distribuite către URL-ul webhook legacy al organizației dumneavoastră ca o solicitare semnată telephony.tool (apeluri telefonice) sau web.tool (apeluri web). Spre deosebire de notificările de audit livrate către endpointurile webhook după execuție, această solicitare este execuția — răspunsul HTTP reprezintă rezultatul instrumentului.

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

web.tool conține origin_domain în loc de from_number / to_number. Răspundeți cu rezultatul instrumentului ca JSON — același contract de răspuns ca pentru apelurile directe către endpoint. Cererea este semnată cu secretul webhook al organizației, cu ambele antete de semnătură, la fel ca orice alt webhook.


Verificarea semnăturii

Apelurile directe de instrumente sunt semnate la fel ca webhookurile, folosind secretul webhook al organizației dumneavoastră:

  • X-ThunderPhone-Signature-V2 semnează marcajul temporal, metoda, calea și interogarea așa cum sunt trimise, precum și octeții exacți ai corpului. Respingeți solicitările al căror marcaj temporal diferă cu mai mult de 5 minute de ceasul dumneavoastră.
  • X-ThunderPhone-Signature (moștenit) semnează numai octeții corpului, șirul gol de octeți pentru GET / DELETE. Este trimis în continuare, nemodificat.

Funcțiile auxiliare SDK verifică antetul V2, inclusiv fereastra de 5 minute:

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 });
});

Rețetele complete — șirul semnat, un exemplu detaliat, cod fără SDK, cazul GET / DELETE și avertismentul privind absența unui secret — sunt în Verificați semnăturile webhookurilor.


Exemplu: flux complet de programare

Iată un set de instrumente pentru un sistem complet de programări:

{
  "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" }
      }
    }
  ]
}

Practici recomandate

Scrieți descrieri clare

Câmpul description ajută AI-ul să înțeleagă când să utilizeze instrumentul. Specificați clar ce face și când este potrivit să fie utilizat.

Gestionați erorile cu eleganță

Returnați mesaje de eroare pe care AI-ul le poate înțelege: {"error": "No slots available for that date"} în locul erorilor 500 generice.

Mențineți răspunsurile concise

Returnați doar ceea ce AI-ul are nevoie pentru a continua conversația. Încărcăturile utile mari încetinesc timpii de răspuns.

Utilizați cu discernământ câmpurile obligatorii

Marcați câmpurile drept required numai atunci când este cu adevărat necesar. AI-ul va solicita utilizatorului informațiile obligatorii înainte de a apela instrumentul.


Resurse asociate