Open in
Usanidi unaobadilika kwa kila simu
Chagua ejenti anayejibu — au andika upya prompt na mipangilio yake — kivyake kwa kila simu inayoingia, kwa kuongozwa na mantiki maalum katika webhook unayoidhibiti.
Kwa chaguomsingi, kila nambari ya simu na ufunguo unaoweza kuchapishwa una ejenti tuli iliyowekwa. Unapohitaji ubinafsishaji wa kila mpigaji au kila mgeni — uelekezaji wa VIP, muktadha wa mtumiaji aliyeingia, majaribio ya prompt ya A/B — badilisha hadi hali ya webhook na uruhusu seva yako iamue.
Jinsi inavyofanya kazi
- Jiandikishe kwa tukio la
telephony.incoming(simu) auweb.incoming(wijeti). Zote ni webhook zinazosubiri: ThunderPhone husubiri hadi sekunde 10 kwa jibu lako kabla ya kuendelea na simu. - ThunderPhone hukutumia
{call_id, from_number, to_number}(vipindi vya wijeti hubeba sehemu mahususi za wijeti badala ya nambari — tazama schema ya ombi). - Seva yako hujibu kwa usanidi wa ejenti (prompt, sauti, bidhaa, zana). ThunderPhone hutumia usanidi huo kwa simu.
- Ukirejesha
{}, ukichelewa kujibu, au hitilafu ikitokea, ejenti iliyowekwa kwa njia tuli hutumika kama mbadala. Chaguo-msingi salama.
1. Sanidi mahali webhook itakapotumwa
Kwa nambari za simu, jiandikishe endpoint yako kwa 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"]
}'Jibu linajumuisha secret ya matumizi ya mara moja — ihifadhi; utaitumia
kwa uthibitishaji wa sahihi.
Kwa vipindi vya wijeti, unda ufunguo unaoweza kuchapishwa katika mode="webhook"
ukiwa na URL ya endpoint yako tayari imejumuishwa:
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"]
}'Wijeti itatuma POST kwenye URL hii kila kipindi kinapoanza.
2. Tekeleza kishughulikiaji
Kanuni tatu za kuzingatia:
- Thibitisha sahihi kwenye kila ombi (tazama Thibitisha sahihi za webhook). Usiruke hatua hii katika dev — ifanye kwa usahihi mara moja na uitumie tena.
- Jibu haraka. Sekunde kumi ndiyo kikomo cha juu, na kila sekunde ni ukimya kwa mpigaji simu. Fanya utafutaji wa hifadhidata ikihitajika, lakini usipigie LLM za baadaye kwa usawazishaji — ukitaka utengenezaji wa prompt unaobadilika, hesabu mapema na uhifadhi kwenye cache.
- Rudi kwenye chaguo la msingi kwa usafi. Hali yoyote isiyotarajiwa inapaswa kurejesha
{}ili ejenti iliyowekwa kwa uthabiti ishughulikie simu.
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. Schema ya jibu
Mwili wa jibu unalingana na schema ya jibu la simu inayoingia haswa. Sehemu zinazotumiwa mara nyingi:
| Sehemu | Aina | Maelezo |
|---|---|---|
prompt | mfuatano wa maandishi (inahitajika) | Prompt ya mfumo kwa ejenti |
voice | mfuatano wa maandishi (inahitajika) | Kitambulisho cha sauti kutoka GET /v1/voices |
product | mfuatano wa maandishi | Chaguo-msingi ni spark |
background_track | mfuatano wa maandishi | null | Kitambulisho cha sauti ya mazingira |
acknowledgement_prompt_mode | mfuatano wa maandishi | auto au manual (Storm yenye uthibitisho pekee) |
acknowledgement_prompt | mfuatano wa maandishi | Inahitajika wakati hali ni manual |
tools | orodha | Schema za zana za vitendaji za ndani — tazama Zana za Vitendaji |
Hifadhi ejenti iliyohifadhiwa na utoe vigeu
Rudisha {"agent_id": 12, "variables": {"name": "Ada"}} ili kutumia ejenti
iliyohifadhiwa ya shirika hilo pamoja na data ya kila simu. Prompt yake inaweza kuwa na {{name}} au
{{name|Friend}}. Vigeu vya webhook huunganishwa juu ya vigeu vya kiwango cha ombi; null
hutumia chaguo-msingi la kishikilia nafasi, au maandishi matupu ikiwa hakuna lililotolewa. Thamani
za mwisho na majina ambayo hayajatatuliwa huonekana katika maelezo ya simu na webhook za ukamilishaji.
Majibu ya ejenti iliyohifadhiwa hukubali agent_id na variables pekee. Ikiwa prompt
ipo, jibu hutumia usanidi wa ndani na hupuuza agent_id (ikiwemo metadata ya
null au isiyo namba kamili); prompt ya ndani lazima bado iwe halali. Majibu ya usanidi wa ndani
yanaweza pia kujumuisha variables. Majibu ya ejenti iliyohifadhiwa hutumia mgawanyo wa A/B
uliotumwa wa ejenti kwenye simu za simu na wijeti, kisha hutoa vigeu. Tazama vigeu vya simu
kwa vikwazo na usaidizi wa API ya kipindi. Usanidi wa kuzuia hutoka kwenye URL ya zamani ya
namba ya simu/shirika au ufunguo wa wijeti wa hali ya webhook; matukio ya endpoint-system
yanayoingia ni arifa pekee.
Mifumo
Muktadha wa mtumiaji aliyeingia
Katika wijeti za hali ya webhook, ukurasa wa mgeni tayari unajua yeye
ni nani. Piga webhook yako kwa kigezo cha mfuatano wa hoja ambacho SDK
ya wijeti hupitisha (?customer_id=123) na umtafute mteja upande wa seva.
Utoaji wa prompt wa A/B
Kabla ya kutekeleza hili mwenyewe, fahamu kwamba ThunderPhone ina kipengele cha ndani cha
Majaribio
(/dashboard/experiments na kichupo cha A/B cha kiunda ejenti) kinachofafanua
vibadala, kugawanya trafiki, na kulinganisha matokeo kwa kila kibadala —
hakuna webhook inayohitajika.
Ikiwa bado unahitaji udhibiti upande wa webhook: fanya hesabu ya mseto wa call_id → kikundi;
toa prompt A kwa 0..49 na prompt B kwa 50..99. Rekodi kikundi
ulichochagua katika DB yako mwenyewe na baadaye ukihusianishe na alama ya simu
iliyokamilika.
Uelekezaji kulingana na muda
Saa za kazi → ejenti wa "usaidizi wa moja kwa moja"; baada ya saa za kazi → ejenti wa "chukua ujumbe".
Badilisha moja kwa moja kwa kutumia new Date().getUTCHours() katika kishughulikiaji chako.
Hatua zinazofuata
Schema sahihi za ombi + jibu, zikiwemo kila ufunguo wa usanidi.
Sanidi HMAC kwa usahihi mara moja; itumie tena kila mahali.
Unganisha uelekezaji unaobadilika na zana za kila ejenti.
Majaribio tena, mpangilio, muda wa kuisha.