Funkcijų įrankiai

Funkcijų įrankiai leidžia jūsų DI agentams telefoninių skambučių metu iškviesti išorines API. Naudokite juos klientų duomenims ieškoti, prieinamumui tikrinti, susitikimams rezervuoti arba bet kokiam veiksmui, kurį palaiko jūsų vidinė sistema, atlikti.

Kaip tai veikia

  1. Apibrėžiate įrankius naudodami schemą (kokius argumentus įrankis priima)
  2. Pateikiate endpoint konfigūraciją (kur ThunderPhone iškviečia jūsų API) arba jos nenurodote, kad įrankio iškvietimus gautumėte per savo organizacijos webhook
  3. Skambučio metu DI pagal pokalbį nusprendžia, kada naudoti įrankį
  4. ThunderPhone iškviečia jūsų endpoint su įrankio argumentais
  5. Jūsų API atsakymas grąžinamas DI, kad pokalbis būtų tęsiamas

Įrankio schema

Kiekvienas įrankis naudoja šią struktūrą:

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

Funkcijos apibrėžimas

LaukasTipasPrivalomasAprašymas
namestringTaipUnikalus įrankio identifikatorius
descriptionstringTaipDI nurodo, kada naudoti šį įrankį
parametersobjectTaipĮrankio argumentų JSON Schema

Endpoint konfigūracija

LaukasTipasPrivalomasAprašymas
urlstringTaipJūsų API endpoint URL
methodstringNeHTTP metodas (numatytoji reikšmė: POST)
headersobjectNeĮtraukiamos pasirinktinės antraštės

Du iškvietimo būdai

Kokią užklausą gauna jūsų serveris, priklauso nuo to, ar įrankis turi endpoint:

Įrankis su endpointĮrankis be endpoint
Kur siunčiama užklausaTiesiogiai į endpoint.urlJūsų organizacijos senasis webhook URL
TurinysTik įrankio argumentaitelephony.tool / web.tool paketas
AntraštėsJūsų endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature
Pasirašymo raktasOrganizacijos webhook paslaptisOrganizacijos webhook paslaptis

Abu būdai yra blokuojantys — DI sakinio viduryje laukia rezultato — taikant 20 s skirtąjį laiką. Užtikrinkite, kad tvarkytuvės veiktų sparčiai. Galite naudoti abu būdus: skambučio, kurio organizacija turi webhook URL, metu įrankiai su endpoint yra iškviečiami tiesiogiai, o kiti grįžta prie webhook.

Tiesioginiai galinių punktų iškvietimai

Kai AI iškviečia įrankį, turintį endpoint, ThunderPhone siunčia užklausą į jūsų URL:

Užklausos antraštės

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

Pasirinktinės antraštės iš jūsų endpoint.headers visada įtraukiamos nekeičiant, kartu su dviem ThunderPhone vardų srities antraštėmis:

Content-Type: application/json nustatoma, nebent ją pakeičia jūsų endpoint.headers — pasirinktinė Content-Type turi pirmenybę.

Užklausos turinys

Naudojant POST / PUT / PATCH, turinyje pateikiami tik įrankio argumentai (be apvalkalo), kanoniškai serializuoti (surikiuoti raktai, glausti skirtukai):

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

Naudojant GET / DELETE, argumentai siunčiami kaip užklausos parametrai, o turinys yra tuščias — tuomet parašas apskaičiuojamas pagal tuščią baitų eilutę. Žr. Webhook parašų tikrinimas.

Atsakymas

Grąžinkite JSON atsakymą su įrankio rezultatu:

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

Atsakymas suformatuojamas ir pateikiamas AI, kad jis galėtų tęsti pokalbį. Ne JSON atsakymai įvyniojami kaip {"data": "<text>"}; laiko limitų viršijimai ir ryšio klaidos pateikiami AI kaip klaidos, todėl agentas gali atsiprašyti ir tęsti, užuot sustojęs.

Siuntimas webhook režimu

Įrankiai be endpoint siunčiami į jūsų organizacijos senojo webhook URL kaip pasirašyta telephony.tool (telefono skambučiams) arba web.tool (žiniatinklio skambučiams) užklausa. Kitaip nei audito pranešimai, pristatomi į webhook galinius punktus po vykdymo, ši užklausa yra vykdymas — jūsų HTTP atsakymas yra įrankio rezultatas.

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

web.tool pateikia origin_domain vietoje from_number / to_number. Atsakykite su įrankio rezultatu JSON formatu — taikoma ta pati atsakymo sutartis kaip ir tiesioginiams galinių punktų iškvietimams. Užklausa pasirašoma naudojant organizacijos webhook slaptąjį raktą pagal neapdorotą turinį, kaip ir kiekvienas kitas webhook.


Parašo tikrinimas

Tiesioginiai įrankių iškvietimai pasirašomi taip pat kaip ir žiniatinklio kabliukai:

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

Išsamūs pavyzdžiai, įskaitant tuščio pagrindinio teksto atvejį ir pastabą dėl slaptojo rakto nebuvimo, pateikti skiltyje Tikrinkite žiniatinklio kabliukų parašus.


Pavyzdys: visas rezervavimo procesas

Toliau pateikiamas įrankių rinkinys, skirtas visai vizitų rezervavimo sistemai:

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

Geriausia praktika

Rašykite aiškius aprašus

Laukas description padeda DI suprasti, kada naudoti įrankį. Tiksliai nurodykite, ką jis daro ir kada jį tikslinga naudoti.

Tinkamai tvarkykite klaidas

Grąžinkite DI suprantamus klaidų pranešimus: {"error": "No slots available for that date"}, o ne bendrines 500 klaidas.

Atsakymai turi būti glausti

Grąžinkite tik tai, ko DI reikia pokalbiui tęsti. Didelės duomenų apkrovos lėtina atsako laiką.

Apdairiai naudokite privalomus laukus

Pažymėkite laukus kaip required tik tada, kai tai iš tiesų būtina. Prieš iškviesdamas įrankį, DI paprašys naudotojo pateikti privalomą informaciją.


Susiję