Dinaminė konfigūracija kiekvienam skambučiui
Pagal numatytuosius nustatymus kiekvienam telefono numeriui ir publikuojamam raktui priskiriamas statinis agentas. Kai reikia tinkinti pagal skambintoją arba pagal lankytoją — VIP nukreipimui, prisijungusio naudotojo kontekstui, A/B raginimų testams — perjunkite į webhook režimą ir leiskite serveriui nuspręsti.
Kaip tai veikia
- Užsiprenumeruokite
telephony.incoming(telefonui) arbaweb.incoming(valdikliui) įvykį. Abu yra blokuojantys webhook pranešimai: ThunderPhone laukia iki 10 sekundžių jūsų atsakymo prieš tęsdamas skambutį. - ThunderPhone siunčia jums
{call_id, from_number, to_number}(valdiklio sesijose vietoj numerių pateikiami valdikliui skirti laukai — žr. užklausos schemą). - Jūsų serveris atsako agento konfigūracija (raginimu, balsu, produktu, įrankiais). ThunderPhone šią konfigūraciją naudoja skambučiui.
- Jei grąžinate
{}, baigiasi laukimo laikas arba įvyksta klaida, kaip atsarginis variantas naudojamas statiškai priskirtas agentas. Saugus numatytasis nustatymas.
1. Sukonfigūruokite webhook paskirties vietą
Telefono skambučiai
Telefono numeriams užsiprenumeruokite savo galinį tašką įvykiui telephony.incoming:
curl -X POST https://api.thunderphone.com/v1/developer/webhook-endpoints \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"label": "Prod call-incoming",
"url": "https://example.com/thunderphone/incoming",
"events": ["telephony.incoming"]
}'
Atsakyme pateikiama vienkartinė secret reikšmė — išsaugokite ją; naudosite ją
parašo patikrinimui.
Žiniatinklio valdiklis
Valdiklio sesijoms sukurkite publikuojamą raktą su mode="webhook",
į kurį įtrauktas jūsų galinio taško URL:
curl -X POST https://api.thunderphone.com/v1/publishable-key \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Dynamic widget",
"mode": "webhook",
"webhook_url": "https://example.com/thunderphone/widget-incoming",
"allowed_domains": ["example.com"]
}'
Valdiklis kiekvienos sesijos pradžioje siųs POST užklausą į šį URL.
2. Įgyvendinkite tvarkyklę
Trys pagrindinės taisyklės:
- Patikrinkite parašą kiekvienoje užklausoje (žr. Webhook parašų tikrinimas). Nepraleiskite to kūrimo aplinkoje — vieną kartą tinkamai įgyvendinkite ir pakartotinai naudokite.
- Atsakykite greitai. Dešimt sekundžių yra griežta riba, o kiekviena sekundė skambintojui yra tyla. Jei reikia, atlikite duomenų bazės paieškas, bet sinchroniškai nekvieskite tolesnių LLM — jei norite dinaminio raginimo generavimo, iš anksto apskaičiuokite ir išsaugokite talpykloje.
- Sklandžiai grįžkite prie numatytojo sprendimo. Bet kokia nenumatyta būsena turi grąžinti
{}, kad statiškai priskirtas agentas galėtų tvarkyti skambutį.
import hashlib
import hmac
import json
import os
from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
SECRET = os.environ["THUNDERPHONE_WEBHOOK_SECRET"]
def verify(body: bytes, sig: str) -> bool:
expected = hmac.new(SECRET.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig or "")
@app.post("/thunderphone/incoming")
async def incoming(request: Request):
body = await request.body()
if not verify(body, request.headers.get("X-ThunderPhone-Signature", "")):
raise HTTPException(401)
event = json.loads(body)
if event["type"] not in ("telephony.incoming", "web.incoming"):
return {} # fall back to default
caller = event["data"]["from_number"]
# Cheap DB lookup: is this a known VIP?
customer = lookup_customer(caller)
if customer and customer.tier == "vip":
return {
"prompt": f"You are a VIP concierge for {customer.name}. Be proactive…",
"voice": "john",
"product": "storm-base",
}
return {} # default agent handles non-VIPs
def lookup_customer(phone: str):
# ... your CRM integration ...
pass
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.THUNDERPHONE_WEBHOOK_SECRET;
function verify(body, sig) {
const expected = crypto.createHmac("sha256", SECRET).update(body).digest("hex");
return sig &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}
app.post(
"/thunderphone/incoming",
express.raw({ type: "application/json" }),
async (req, res) => {
if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString("utf8"));
const IMPORTANT_TYPES = new Set([
"telephony.incoming",
"web.incoming",
]);
if (!IMPORTANT_TYPES.has(event.type)) return res.json({});
const customer = await lookupCustomer(event.data.from_number);
if (customer?.tier === "vip") {
return res.json({
prompt: `You are a VIP concierge for ${customer.name}. Be proactive…`,
voice: "john",
product: "storm-base",
});
}
res.json({}); // fall back to default agent
},
);
3. Atsakymo schema
Atsakymo turinys tiksliai atitinka gaunamo skambučio atsakymo schemą. Dažniausiai naudojami laukai:
| Laukas | Tipas | Aprašymas |
|---|---|---|
prompt | eilutė (būtina) | Sistemos raginimas agentui |
voice | eilutė (būtina) | Balso ID iš GET /v1/voices |
product | eilutė | Numatytoji reikšmė yra spark |
background_track | eilutė | null | Aplinkos garso ID |
acknowledgement_prompt_mode | eilutė | auto arba manual (tik Storm su patvirtinimu) |
acknowledgement_prompt | eilutė | Būtina, kai režimas yra manual |
tools | masyvas | Įterptos funkcijų įrankių schemos — žr. Funkcijų įrankiai |
Modeliai
Prisijungusio naudotojo kontekstas
Valdikliuose, veikiančiuose webhook režimu, lankytojo puslapis jau žino, kas jis
yra. Iškvieskite savo webhook su užklausos eilutės parametru, kurį persiunčia valdiklio SDK
(?customer_id=123), ir raskite klientą serverio pusėje.
A/B raginimų diegimas
Prieš kurdami tai patys, atkreipkite dėmesį, kad ThunderPhone turi integruotą
eksperimentų funkciją
(/dashboard/experiments ir agentų kūrimo priemonės skirtuką A/B), kuri
apibrėžia variantus, paskirsto srautą ir lygina rezultatus pagal variantą –
webhook nereikalingas.
Jei vis tiek reikia valdymo webhook pusėje: maišykite call_id → segmentas;
pateikite A raginimą reikšmėms 0..49 ir B raginimą reikšmėms 50..99. Įrašykite,
kurį segmentą pasirinkote, savo DB ir vėliau susiekite jį su užbaigto
skambučio įvertinimu.
Nukreipimas pagal laiką
Darbo valandomis → balso agentas „tiesioginė pagalba“; ne darbo valandomis → balso agentas „palikti žinutę“.
Tvarkyklėje pakanka paprasto perjungimo pagal new Date().getUTCHours().
Tolesni veiksmai
Tikslios užklausų ir atsakymų schemos, įskaitant kiekvieną konfigūracijos raktą.
Teisingai nustatykite HMAC vieną kartą; naudokite visur.
Derinkite dinaminį nukreipimą su konkretaus agento įrankiais.
Pakartotiniai bandymai, eiliškumas, skirtojo laiko pabaiga.