Инструменты функций
Предоставьте своим ИИ-агентам инструменты функций, которые вызывают внешние API прямо во время разговора: получают данные клиентов, записывают на приём, обновляют записи — с типизированными параметрами.
Инструменты функций позволяют вашим ИИ-агентам вызывать внешние API во время телефонных звонков. Используйте их для поиска данных клиентов, проверки доступности, бронирования встреч или выполнения любых действий, которые поддерживает ваш бэкенд.
Как это работает
- Определите инструменты с помощью схемы (какие аргументы принимает инструмент)
- Укажите конфигурацию
endpoint(куда ThunderPhone вызывает ваш API) — или не указывайте её, чтобы получать вызовы инструментов на вебхук вашей организации - Во время звонка ИИ решает, когда использовать инструмент, на основе разговора
- ThunderPhone вызывает ваш endpoint с аргументами инструмента
- Ответ вашего 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"
}
},
"timeout": 120
}Конфигурация инструмента
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
timeout | число | Нет | Максимальное время выполнения в секундах (по умолчанию: 20, максимум: 180) |
Определение функции
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
name | строка | Да | Уникальный идентификатор инструмента |
description | строка | Да | Объясняет ИИ, когда использовать этот инструмент |
parameters | объект | Да | JSON Schema для аргументов инструмента |
Конфигурация endpoint
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
url | строка | Да | URL endpoint вашего 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 с; задайте timeout
верхнего уровня инструмента, чтобы разрешить более длительное выполнение — до максимума
платформы в 180 с. Обработчики должны работать быстро. Допускается смешанный вариант:
при звонке, организация которого имеет URL вебхука, инструменты с endpoint
вызываются напрямую, а остальные используют вебхук.
Прямые вызовы endpoint
Когда ИИ вызывает инструмент с 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"
}Ответ форматируется и передаётся ИИ для продолжения
разговора. Ответы не в формате JSON оборачиваются как {"data": "<text>"};
тайм-ауты и ошибки подключения сообщаются ИИ как ошибки, чтобы
агент мог извиниться и продолжить работу, а не зависнуть.
Маршрутизация в режиме webhook
Инструменты без endpoint направляются на устаревший URL webhook
вашей организации как подписанный запрос telephony.tool (телефонные звонки)
или web.tool (веб-звонки). В отличие от уведомлений аудита,
доставляемых на endpoint 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 — применяется тот же
контракт ответа, что и для прямых вызовов endpoint. Запрос подписывается
секретом 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 и вызовов инструментов.