ThunderPhone 2.0 er lansert.Kom i gang selv, fra 2 ¢/min.Les mer om lanseringen

Function Tools

Funksjonsverktøy

Gi AI-agentene dine funksjonsverktøy som kaller eksterne API-er midt i samtalen — henter kundedata, bestiller avtaler, oppdaterer poster — med typede parametere.

Funksjonsverktøy lar AI-stemmeagentene dine kalle eksterne API-er under telefonsamtaler. Bruk dem til å slå opp kundedata, sjekke tilgjengelighet, bestille avtaler eller utføre handlinger som backend-systemet ditt støtter.

Slik fungerer det

  1. Du definerer verktøy med et skjema (hvilke argumenter verktøyet godtar)
  2. Du oppgir en endpoint-konfigurasjon (der ThunderPhone kaller API-et ditt) — eller lar den stå tom for å motta verktøykall på organisasjonens webhook
  3. Under en samtale avgjør AI-en når den skal bruke et verktøy basert på samtalen
  4. ThunderPhone kaller endepunktet ditt med verktøyargumentene
  5. API-svaret ditt sendes tilbake til AI-en for å fortsette samtalen

Verktøyskjema

Hvert verktøy følger denne strukturen:

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

Verktøykonfigurasjon

FeltTypePåkrevdBeskrivelse
timeouttallNeiMaksimal kjøretid i sekunder (standard: 20, maksimum: 180)

Funksjonsdefinisjon

FeltTypePåkrevdBeskrivelse
namestrengJaUnik identifikator for verktøyet
descriptionstrengJaForklarer AI-en når dette verktøyet skal brukes
parametersobjektJaJSON-skjema for verktøyargumenter

Endepunktkonfigurasjon

FeltTypePåkrevdBeskrivelse
urlstrengJaURL-en til API-endepunktet ditt
methodstrengNeiHTTP-metode (standard: POST)
headersobjektNeiEgendefinerte headere som skal inkluderes

To kallingsveier

Hvilken forespørsel serveren din mottar, avhenger av om verktøyet har et endpoint:

Verktøy med endpointVerktøy uten endpoint
Hvor forespørselen sendesDirekte til endpoint.urlOrganisasjonens eldre webhook-URL
BrødtekstKun verktøyargumentertelephony.tool / web.tool-innpakning
HeadereDine endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature
SigneringsnøkkelOrganisasjonens webhook-hemmelighetOrganisasjonens webhook-hemmelighet

Begge veiene er blokkerende — AI-en venter midt i en setning på resultatet. Standard tidsavbrudd er 20 s; angi verktøyets øverste timeout for å tillate lengre kjøretid, opptil plattformens maksimum på 180 s. Hold handlere raske. En kombinasjon fungerer fint: I en samtale der organisasjonen har en webhook-URL, blir verktøy med et endpoint kalt direkte, mens resten faller tilbake til webhooken.

Direkte endepunktkall

Når KI-en påkaller et verktøy som har et endpoint, sender ThunderPhone en forespørsel til URL-en din:

Forespørselsoverskrifter

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

Egendefinerte overskrifter fra endpoint.headers inkluderes alltid ordrett, i tillegg til to ThunderPhone-navngitte overskrifter:

  • X-ThunderPhone-Signature — HMAC-SHA256 av de nøyaktige byteverdiene i forespørselsbrødteksten, med organisasjonens webhook-hemmelighet som nøkkel
  • X-ThunderPhone-Call-ID — ID-en til den gjeldende samtalen

Content-Type: application/json angis med mindre endpoint.headers overstyrer den — en egendefinert Content-Type har forrang.

Forespørselsbrødtekst

For POST / PUT / PATCH inneholder brødteksten bare verktøyets argumenter (uten innpakning), serialisert kanonisk (sorterte nøkler, kompakte skilletegn):

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

For GET / DELETE sendes argumentene som spørringsparametere, og brødteksten er tom — signaturen beregnes da over den tomme bytestrengen. Se Verifiser webhook-signaturer.

Svar

Returner et JSON-svar med verktøyresultatet:

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

Svaret formateres og gis til KI-en slik at samtalen kan fortsette. Ikke-JSON-svar pakkes inn som {"data": "<text>"}; tidsavbrudd og tilkoblingsfeil rapporteres til KI-en som feil, slik at stemmeagenten kan beklage og gå videre i stedet for å stoppe opp.

Utsending i webhook-modus

Verktøy uten et endpoint sendes til organisasjonens eldre webhook-URL som en signert telephony.tool-forespørsel (telefonsamtaler) eller web.tool-forespørsel (websamtaler). I motsetning til revisjonsvarsler som leveres til webhook-endepunkter etter utførelse, er denne forespørselen selve utførelsen — HTTP-svaret ditt er verktøyresultatet.

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

web.tool inneholder origin_domain i stedet for from_number / to_number. Svar med verktøyresultatet som JSON — samme svaravtale som for direkte endepunktkall. Forespørselen signeres med organisasjonens webhook-hemmelighet over råbrødteksten, som alle andre webhooks.


Signaturverifisering

Direkte verktøykall signeres på samme måte som webhooks:

  • HMAC-SHA256 over de nøyaktige byteverdiene i forespørselsbrødteksten (den kanoniske JSON-en — sorterte nøkler, uten ekstra mellomrom)
  • Med organisasjonens webhook-hemmelighet som nøkkel
  • GET- og DELETE-verktøy signerer den tomme bytestrengen
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 });
});

Fullstendige oppskrifter — inkludert tilfellet med tom brødtekst og forbeholdet om manglende hemmelighet — finner du i Verifiser webhook-signaturer.


Eksempel: Komplett bestillingsflyt

Her er et sett med verktøy for et komplett system for timebestilling:

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

Beste praksis

Skriv tydelige beskrivelser

Feltet description hjelper AI-en med å forstå når verktøyet skal brukes. Vær spesifikk om hva det gjør, og når det er passende å bruke det.

Håndter feil på en god måte

Returner feilmeldinger AI-en kan forstå: {"error": "No slots available for that date"} i stedet for generiske 500-feil.

Hold svarene konsise

Returner bare det AI-en trenger for å fortsette samtalen. Store nyttelaster forsinker responstiden.

Bruk obligatoriske felt med omhu

Merk felt som required bare når det er helt nødvendig. AI-en vil be brukeren om obligatorisk informasjon før den kaller verktøyet.


Relatert