ThunderPhone 2.0 er lanceret.Selvbetjening fra 2 cent pr. minut.Læs mere om lanceringen

Function Tools

Funktionsværktøjer

Giv dine AI-agenter funktionsværktøjer, der kalder eksterne API

Funktionsværktøjer giver dine AI-agenter mulighed for at kalde eksterne API'er under opkald. Brug dem til at slå kundedata op, kontrollere tilgængelighed, booke aftaler eller udføre enhver handling, som din backend understøtter.

Sådan fungerer det

  1. Du definerer værktøjer med et skema (hvilke argumenter værktøjet accepterer)
  2. Du angiver en endpoint-konfiguration (hvor ThunderPhone kalder dit API) — eller udelader den for at modtage værktøjskald på din organisations webhook
  3. Under et opkald beslutter AI'en, hvornår et værktøj skal bruges, baseret på samtalen
  4. ThunderPhone kalder dit endpoint med værktøjsargumenterne
  5. Dit API-svar sendes tilbage til AI'en for at fortsætte samtalen
FunktionalitetHvor den kørerOpsætning
Indbyggede værktøjerThunderPhonePromptinstruktioner; nogle værktøjer kræver også en agentindstilling
AppforbindelserThunderPhone og den tilsluttede udbyderForbind kontoen, og tilknyt godkendte handlinger
API-forbindelser og funktionsværktøjerDit HTTP APIDefiner endpointet og skemaet, eller modtag funktionskald via en webhook
MCP-servereEn fjern MCP-serverTilføj serveren, find dens værktøjer, og tilknyt den til agenten

Værktøjsskema

Hvert værktøj følger denne struktur:

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

Værktøjskonfiguration

FeltTypePåkrævetBeskrivelse
timeouttalNejMaksimal udførelsestid i sekunder (standard: 20, maksimum: 180)

Funktionsdefinition

FeltTypePåkrævetBeskrivelse
namestrengJaUnik identifikator for værktøjet
descriptionstrengJaForklarer AI'en, hvornår dette værktøj skal bruges
parametersobjektJaJSON-skema for værktøjsargumenter

Endpointkonfiguration

FeltTypePåkrævetBeskrivelse
urlstrengJaURL'en til dit API-endpoint
methodstrengNejHTTP-metode (standard: POST)
headersobjektNejTilpassede headere, der skal inkluderes

To kaldestier

Hvilken anmodning din server modtager, afhænger af, om værktøjet har et endpoint:

Værktøj med endpointVærktøj uden endpoint
Hvor anmodningen sendes henDirekte til endpoint.urlDin organisations ældre webhook-URL
BrødtekstValgt anmodningsformat (rene argumenter som standard)telephony.tool / web.tool-indpakning
HeadereDine endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature-V2 + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature
SigneringsnøgleOrganisationens webhook-hemmelighedOrganisationens webhook-hemmelighed

Begge stier er blokerende — AI'en venter midt i en sætning på resultatet. Standardtimeouttet er 20 s; angiv værktøjets timeout på øverste niveau for at tillade en længere udførelse, op til platformens maksimum på 180 s. Hold handlere hurtige. En blanding er helt fint: Ved et opkald, hvis organisation har en webhook-URL, kaldes værktøjer med et endpoint direkte, og resten falder tilbage til webhooken.

Direkte endpointkald

Når AI'en kalder et værktøj, der har et endpoint, sender ThunderPhone en anmodning til din URL:

Anmodningsheadere

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

Brugerdefinerede headere fra dine endpoint.headers medtages altid uændret sammen med disse ThunderPhone-navnerumsheadere:

  • X-ThunderPhone-Signature-V2 — tidsstemplet HMAC-SHA256 over metoden, stien, forespørgslen og brødteksten, med din organisations-webhookhemmelighed som nøgle. Verificer denne, og afvis forespørgsler, der er mere end 5 minutter gamle; se Verificer webhooksignaturer
  • X-ThunderPhone-Signature — ældre HMAC-SHA256 af de nøjagtige byte i forespørgselsbrødteksten, med samme nøgle, sendes stadig til eksisterende verifikatorer
  • X-ThunderPhone-Call-ID — Det aktuelle opkalds-ID

Content-Type: application/json angives, medmindre dit endpoint.headers overskriver det — en brugerdefineret Content-Type har forrang.

Anmodningsformat

Hver API-forbindelse har en indstilling for request_format. Vælg vapi eller retell, når et importeret værktøj fortsat skal kalde et endpoint, der er bygget til den pågældende platform. Nye og eksisterende forbindelser bruger som standard standard.

For POST / PUT / PATCH serialiserer ThunderPhone den valgte indpakning kanonisk (sorterede nøgler, kompakte skilletegn). Begge signaturer dækker disse nøjagtige bytes.

Standard

standard sender kun værktøjsargumenterne uden wrapper. Dette er standarden og bevarer ThunderPhone's oprindelige anmodningsbrødtekst byte for byte:

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

Vapi

vapi sender Vapis tool-calls-servermeddelelse i det format, Vapi dokumenterer. call.id er ThunderPhone-opkalds-ID'et. Værktøjskaldets id er unikt for påkaldelsen og genbruges til at matche endpointets resultat. Opkaldet vises både i toolCallList og toolWithToolCallList, med function.arguments som et JSON-objekt:

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

Vapi-specifikke felter som assistant, artifact og det fulde call-objekt sendes ikke.

Returner Vapis svarindpakning med det matchende værktøjskalds-ID:

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

ThunderPhone giver AI'en den udpakkede result-streng. Den accepterer også en JSON-værdi for kompatibilitet med endpoints, der allerede returnerer en. En error i stedet for result returneres til AI'en som en værktøjsfejl. Hvis endpointet returnerer en forkert formateret eller ikke-matchende Vapi-indpakning, logger ThunderPhone en advarsel og falder tilbage til sin standardfortolkning af svar i stedet for at afbryde opkaldet.

Retell

retell sender anmodningen, der er dokumenteret for en Retell-tilpasset funktion:

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

call-objektet indeholder kun call_id. ThunderPhone inkluderer ikke Retell-specifikke felter, som den ikke reelt kan levere ved afsendelse, såsom agent_id, transcript, transcript_object, metadata eller dynamiske variabler. Retell accepterer enhver svartekst; ThunderPhone videresender den ved hjælp af standardhåndteringen af svar.

For GET / DELETE sendes argumenterne som queryparametre og brødteksten er tom uanset request_format. V2-signaturen dækker querystrengen, som den sendes; den ældre signatur beregnes over den tomme bytestreng. Se Verificer webhook-signaturer.

Svar

Returner et JSON-svar med værktøjsresultatet:

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

Svaret formateres og gives til AI'en, så den kan fortsætte samtalen. Ikke-JSON-svar indpakkes som {"data": "<text>"}; timeouts og forbindelsesfejl rapporteres til AI'en som fejl, så agenten kan undskylde og fortsætte i stedet for at gå i stå.

Afsendelse i webhooktilstand

Værktøjer uden et endpoint sendes til din organisations ældre webhook-URL som en signeret telephony.tool-anmodning (telefonopkald) eller web.tool-anmodning (webopkald). I modsætning til revisionsnotifikationerne, der leveres til webhookendpoints efter udførelse, er denne anmodning udførelsen — dit HTTP-svar er værktøjsresultatet.

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

web.tool indeholder origin_domain i stedet for from_number / to_number. Svar med værktøjsresultatet som JSON — samme svarformat som ved direkte endpointkald. Anmodningen er signeret med organisationens webhook-hemmelighed og begge signaturheadere, ligesom alle andre webhooks.


Signaturverificering

Direkte værktøjskald signeres på samme måde som webhooks med din organisations webhook-hemmelighed:

  • X-ThunderPhone-Signature-V2 signerer tidsstemplet, metoden, stien og forespørgselsstrengen som sendt samt de nøjagtige bytes i brødteksten. Afvis anmodninger, hvis deres tidsstempel afviger mere end 5 minutter fra dit ur.
  • X-ThunderPhone-Signature (forældet) signerer kun bytes i brødteksten og den tomme bytestreng for GET / DELETE. Den sendes stadig uændret.

SDK-hjælpefunktionerne kontrollerer V2-headeren, herunder 5-minuttersvinduet:

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

Fuldstændige opskrifter — den signerede streng, et gennemarbejdet eksempel, kode uden SDK'et, tilfældet med GET / DELETE og forbeholdet om ingen hemmelighed — findes i Bekræft webhook-signaturer.


Eksempel: Komplet bookingflow

Her er et sæt værktøjer til et komplet system til tidsbestilling:

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

Bedste praksis

Skriv klare beskrivelser

Feltet description hjælper AI'en med at forstå, hvornår værktøjet skal bruges. Vær specifik om, hvad det gør, og hvornår det er relevant.

Håndter fejl elegant

Returnér fejlmeddelelser, som AI'en kan forstå: {"error": "No slots available for that date"} frem for generiske 500-fejl.

Hold svar korte

Returnér kun det, AI'en har brug for, for at fortsætte samtalen. Store payloads gør svartiderne langsommere.

Brug obligatoriske felter med omtanke

Markér kun felter som required, når det er absolut nødvendigt. AI'en vil bede brugeren om obligatoriske oplysninger, før værktøjet kaldes.


Relateret