Инструменти за функции
Функционалните инструменти позволяват на вашите ИИ агенти да извикват външни API интерфейси по време на телефонни разговори. Използвайте ги, за да намирате данни за клиенти, да проверявате наличност, да резервирате часове или да извършвате всяко действие, което вашият бекенд поддържа.
Как работи
- Дефинирате инструменти със схема (какви аргументи приема инструментът)
- Предоставяте конфигурация на
endpoint(къде ThunderPhone извиква вашия API интерфейс) — или я пропускате, за да получавате извикванията на инструменти чрез уебхука на организацията си - По време на разговор ИИ решава кога да използва инструмент въз основа на разговора
- ThunderPhone извиква вашата крайна точка с аргументите на инструмента
- Отговорът на вашия API интерфейс се подава обратно към ИИ, за да продължи разговорът
Схема на инструмента
Всеки инструмент следва тази структура:
{
"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"
}
}
}
Дефиниция на функция
| Поле | Тип | Задължително | Описание |
|---|---|---|---|
name | низ | Да | Уникален идентификатор на инструмента |
description | низ | Да | Обяснява на ИИ кога да използва този инструмент |
parameters | обект | Да | JSON схема за аргументите на инструмента |
Конфигурация на крайна точка
| Поле | Тип | Задължително | Описание |
|---|---|---|---|
url | низ | Да | URL адрес на вашата API крайна точка |
method | низ | Не | HTTP метод (по подразбиране: POST) |
headers | обект | Не | Персонализирани заглавки за включване |
Два начина за извикване
Заявката, която вашият сървър получава, зависи от това дали инструментът има
endpoint:
Инструмент с endpoint | Инструмент без endpoint | |
|---|---|---|
| Къде се изпраща заявката | Директно към endpoint.url | URL адреса на наследения уебхук на организацията ви |
| Тяло | Само аргументите на инструмента | Обвивка telephony.tool / web.tool |
| Заглавки | Вашите endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature | Content-Type + X-ThunderPhone-Signature |
| Ключ за подписване | Тайната на уебхука на организацията | Тайната на уебхука на организацията |
И двата начина са блокиращи — ИИ изчаква резултата по средата на изречението —
с изчакване от 20 сек. Поддържайте обработващите функции бързи. Може да ги комбинирате:
при разговор, чиято организация има URL адрес за уебхук, инструментите с endpoint се
извикват директно, а останалите се връщат към уебхука.
Директни извиквания на крайни точки
Когато AI извика инструмент, който има endpoint, ThunderPhone изпраща
заявка към вашия URL адрес:
Заглавки на заявката
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
Персонализираните заглавки от вашия endpoint.headers винаги се включват
дословно, както и две заглавки в пространството от имена на ThunderPhone:
X-ThunderPhone-Signature— HMAC-SHA256 на точните байтове на тялото на заявката, с ключ вашата тайна за webhook на организациятаX-ThunderPhone-Call-ID— Идентификаторът на текущото обаждане
Content-Type: application/json се задава, освен ако вашият endpoint.headers
не го замени — персонализиран Content-Type има предимство.
Тяло на заявката
За POST / PUT / PATCH тялото съдържа само аргументите на инструмента
(без обвивка), сериализирани канонично (сортирани ключове, компактни
разделители):
{"date":"2025-01-02","service":"consultation"}
За GET / DELETE аргументите се изпращат като параметри на заявката
и тялото е празно — тогава подписът се изчислява върху празния
низ от байтове. Вижте
Проверка на подписи на webhook.
Отговор
Върнете JSON отговор с резултата от инструмента:
{
"available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
"timezone": "America/Los_Angeles"
}
Отговорът се форматира и предоставя на AI, за да продължи
разговора. Отговорите, които не са JSON, се обвиват като {"data": "<text>"};
превишаването на времето за изчакване и неуспешните връзки се докладват на AI като грешки, така че
агентът да може да се извини и да продължи, вместо да блокира.
Диспечиране в режим webhook
Инструментите без endpoint се диспечират към наследения URL адрес за
webhook на вашата организация като подписана заявка telephony.tool (телефонни обаждания) или web.tool
(уеб обаждания). За разлика от известията за одит,
доставяни до крайни точки за webhook след изпълнение, тази заявка е
самото изпълнение — вашият HTTP отговор е резултатът от инструмента.
{
"type": "telephony.tool",
"data": {
"call_id": 987654321,
"tool_name": "search_appointments",
"arguments": { "date": "2026-04-21" },
"from_number": "+14155550199",
"to_number": "+15551234567"
}
}
web.tool съдържа origin_domain вместо from_number /
to_number. Отговорете с резултата от инструмента като JSON — същия договор за отговор
като при директните извиквания на крайни точки. Заявката се подписва с тайната за
webhook на организацията върху необработеното тяло, както всеки друг webhook.
Проверка на подписа
Директните извиквания на инструменти се подписват по същия начин като уебкуките:
- HMAC-SHA256 върху точните байтове на тялото на заявката (каноничния JSON — сортирани ключове, без допълнителни интервали)
- С ключ — вашата тайна за уебкуки на организацията
- Инструментите
GET/DELETEподписват празния байтов низ
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 });
});
Пълните рецепти — включително случая с празно тяло и уточнението при липса на тайна — са в Проверка на подписите на уебкуки.
Пример: Пълен поток за резервиране
Ето набор от инструменти за цялостна система за резервиране на часове:
{
"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" }
}
}
]
}
Най-добри практики
Пишете ясни описания
Полето description помага на ИИ да разбере кога да използва инструмента. Посочете конкретно какво прави и кога е подходящо да се използва.
Обработвайте грешките коректно
Връщайте съобщения за грешки, които ИИ може да разбере: {"error": "No slots available for that date"}, вместо общи грешки 500.
Поддържайте отговорите кратки
Връщайте само това, от което ИИ се нуждае, за да продължи разговора. Големите полезни товари забавят времето за отговор.
Използвайте задължителните полета разумно
Отбелязвайте полетата като required само когато това е наистина необходимо. ИИ ще поиска от потребителя задължителната информация, преди да извика инструмента.
Свързано
Управлявани от платформата инструменти за HubSpot, Salesforce, Slack, Google Calendar, Google Sheets и Cal.com — не е необходима крайна точка.
Свържете MCP сървър и позволете на агента да извиква неговите инструменти.
Повторно използваеми REST интеграции, които можете да свържете с агенти.
Един помощен инструмент за проверка за webhook-и и извиквания на инструменти.