ThunderPhone 2.0 er lanceret.Selvbetjening fra 2 cent/min.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 telefonopkald. Brug dem til at slå kundedata op, tjekke 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 din 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

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 Schema for værktøjsargumenter

Endpoint-konfiguration

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

To kaldstier

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ødtekstKun værktøjsargumentertelephony.tool / web.tool-indpakning
HeadersDine endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature
SigneringsnøgleOrganisationens webhook-hemmelighedOrganisationens webhook-hemmelighed

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

Direkte endepunktskald

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

Anmodningsheaders

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

Brugerdefinerede headers fra dit endpoint.headers inkluderes altid ordret samt to ThunderPhone-navnerumsheaders:

  • X-ThunderPhone-Signature — HMAC-SHA256 af de nøjagtige bytes i anmodningens brødtekst, med din organisations webhook-hemmelighed som nøgle
  • X-ThunderPhone-Call-ID — ID'et for det aktuelle opkald

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

Anmodningstekst

For POST / PUT / PATCH indeholder brødteksten kun værktøjs- argumenterne (ingen indpakning), serialiseret kanonisk (sorterede nøgler, kompakte separatorer):

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

For GET / DELETE sendes argumenterne som forespørgselsparametre, og brødteksten er tom — signaturen beregnes derefter 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 webhook-endepunkter efter udførelse, er denne anmodning selve 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 — den samme svarskontrakt som ved direkte endepunktskald. Anmodningen signeres med organisationens webhook-hemmelighed over den rå brødtekst, ligesom alle andre webhooks.


Signaturverifikation

Direkte værktøjskald signeres på samme måde som webhooks:

  • HMAC-SHA256 over de nøjagtige bytes i request-bodyen (den kanoniske JSON — sorterede nøgler, intet ekstra mellemrum)
  • Med din organisations webhook-hemmelighed som nøgle
  • GET- / DELETE-værktøjer signerer den tomme bytestreng
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 });
});

Fuldstændige opskrifter — herunder tilfældet med tom body og forbeholdet om manglende 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

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

Hold svar korte

Returner kun det, AI'en har brug for for at fortsætte samtalen. Store payloads sænker svartiderne.

Brug obligatoriske felter med omtanke

Markér kun felter som required, når det er helt nødvendigt. AI'en beder brugeren om obligatoriske oplysninger, før den kalder værktøjet.


Relateret