Dinamikus hívásonkénti konfiguráció
Válassza ki a válaszoló AI-ügynököt — vagy írja át annak promptját és beállításait — minden egyes bejövő híváshoz külön, az Ön által vezérelt webhookban megvalósított egyéni logika alapján.
Alapértelmezés szerint minden telefonszámhoz és publikálható kulcshoz statikusan van hozzárendelve egy ügynök. Ha hívónkénti vagy látogatónkénti testreszabásra van szüksége — VIP-irányításra, bejelentkezett felhasználói kontextusra, A/B prompttesztekre — váltson webhook módra, és hagyja, hogy a szervere döntsön.
Működése
- Iratkozzon fel a
telephony.incoming(telefon) vagy aweb.incoming(widget) eseményre. Mindkettő blokkoló webhook: a ThunderPhone akár 10 másodpercig vár a válaszára, mielőtt folytatná a hívást. - A ThunderPhone elküldi Önnek a következőt:
{call_id, from_number, to_number}(a widgetes munkamenetek a számok helyett widgetspecifikus mezőket tartalmaznak — lásd a kérés sémáját). - A szervere egy ügynökkonfigurációval válaszol (prompt, hang, termék, eszközök). A ThunderPhone ezt a konfigurációt használja a híváshoz.
- Ha
{}értéket ad vissza, időtúllépés történik vagy hiba lép fel, a statikusan hozzárendelt ügynök lesz a tartalék. Biztonságos alapértelmezés.
1. A webhook célhelyének konfigurálása
Telefonszámok esetén iratkoztassa fel a végpontját a telephony.incoming eseményre:
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"]
}'A válasz egy egyszer használatos secret értéket tartalmaz — mentse el; ezt fogja használni
az aláírás ellenőrzéséhez.
Widgetes munkamenetekhez hozzon létre egy mode="webhook" beállítású publikálható kulcsot,
amelybe be van építve a végpont URL-je:
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"]
}'A widget minden munkamenet indításakor POST kérést küld erre az URL-re.
2. A kezelő implementálása
Három gyakorlati szabály:
- Ellenőrizze az aláírást minden kérésnél (lásd: Webhook-aláírások ellenőrzése). Ezt fejlesztés során se hagyja ki — végezze el egyszer helyesen, majd használja újra.
- Válaszoljon gyorsan. A tíz másodperc a szigorú felső határ, és minden másodperc néma várakozás a hívó számára. Végezzen adatbázis-lekérdezéseket, ha szükséges, de ne hívjon szinkron módon downstream LLM-eket — dinamikus promptgeneráláshoz számítsa elő és gyorsítótárazza az adatokat.
- Biztosítson tiszta tartalék megoldást. Minden váratlan állapotnak
{}értéket kell visszaadnia, hogy a statikusan hozzárendelt ügynök kezelje a hívást.
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 ...
passimport 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. Válaszséma
A válasz törzse pontosan megfelel a bejövő hívás válaszsémájának. A gyakran használt mezők:
| Mező | Típus | Leírás |
|---|---|---|
prompt | string (kötelező) | Az ügynök rendszerpromptja |
voice | string (kötelező) | Hangazonosító a GET /v1/voices végpontból |
product | string | Alapértelmezett értéke: spark |
background_track | string | null | Háttérhang-azonosító |
acknowledgement_prompt_mode | string | auto vagy manual (csak visszaigazolással rendelkező Storm esetén) |
acknowledgement_prompt | string | Kötelező, ha a mód manual |
tools | array | Beágyazott függvényeszköz-sémák — lásd: Függvényeszközök |
Minták
Bejelentkezett felhasználói környezet
Webhook módú widgetekben a látogató oldala már tudja, ki ő.
Hívja meg a webhookot egy lekérdezési karakterlánc-paraméterrel, amelyet a widget SDK
továbbít (?customer_id=123), majd keresse meg az ügyfelet szerveroldalon.
A/B prompt bevezetése
Mielőtt ezt saját maga valósítaná meg, vegye figyelembe, hogy a ThunderPhone natív
Kísérletek funkcióval rendelkezik
(a /dashboard/experiments és az ügynökkészítő A/B lapja), amely
változatokat határoz meg, megosztja a forgalmat, és változatonként hasonlítja össze az eredményeket —
nincs szükség webhookra.
Ha mégis webhookoldali vezérlésre van szüksége: hashelje a call_id értékét → kosár;
a 0..49 értékekhez az A promptot, az 50..99 értékekhez a B promptot szolgálja ki. Rögzítse,
melyik kosarat választotta a saját adatbázisában, majd később korrelálja azt a
befejezett hívás értékelésével.
Időalapú útválasztás
Üzleti időben → „élő támogatási” ügynök; munkaidőn kívül → „üzenetrögzítő”
ügynök. Egyszerű váltás a kezelőben a new Date().getUTCHours() alapján.
Következő lépések
Pontos kérési és válaszsémák, minden konfigurációs kulccsal együtt.
Állítsa be egyszer helyesen a HMAC-et; használja újra mindenhol.
Kombinálja a dinamikus útválasztást ügynökspecifikus eszközökkel.
Újrapróbálkozások, sorrendiség, időtúllépések.