Open in
Инструменты функций
Предоставьте своим ИИ-агентам инструменты функций, которые вызывают внешние API во время разговора — получают данные клиентов, записывают на приём, обновляют записи — с типизированными параметрами.
Инструменты-функции позволяют вашим ИИ-агентам вызывать внешние API во время телефонных разговоров. Используйте их для поиска данных клиентов, проверки доступности, записи на приём или выполнения любых действий, поддерживаемых вашим бэкендом.
Как это работает
- Определите инструменты с помощью схемы (какие аргументы принимает инструмент)
- Укажите конфигурацию
endpoint(куда ThunderPhone вызывает ваш API) или не указывайте её, чтобы получать вызовы инструментов на вебхук вашей организации - Во время звонка ИИ решает, когда использовать инструмент, на основе разговора
- ThunderPhone вызывает ваш эндпоинт с аргументами инструмента
- Ответ вашего API передаётся обратно ИИ для продолжения разговора
| Возможность | Где выполняется | Настройка |
|---|---|---|
| Встроенные инструменты | ThunderPhone | Инструкции в промпте; для некоторых инструментов также требуется настройка агента |
| Подключения приложений | ThunderPhone и подключённый провайдер | Подключите аккаунт и добавьте одобренные действия |
| Подключения API и инструменты-функции | Ваш HTTP API | Определите эндпоинт и схему или получайте вызовы функций через вебхук |
| Серверы MCP | Удалённый сервер MCP | Добавьте сервер, найдите его инструменты и подключите его к агенту |
Схема инструмента
Каждый инструмент имеет следующую структуру:
{
"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 для аргументов инструмента |
Конфигурация эндпоинта
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
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-V2 + X-ThunderPhone-Signature | Content-Type + X-ThunderPhone-Signature-V2 + 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-V2: t=1767225600,v1=5f2c...
X-ThunderPhone-Signature: abc123...
X-ThunderPhone-Call-ID: 987654321
X-Api-Key: your-api-keyПользовательские заголовки из вашего endpoint.headers всегда включаются
дословно, а также добавляются следующие заголовки в пространстве имён ThunderPhone:
X-ThunderPhone-Signature-V2— HMAC-SHA256 с меткой времени для метода, пути, запроса и тела, подписанный вашим секретом вебхука организации. Проверяйте эту подпись и отклоняйте запросы старше 5 минут; см. Проверка подписей вебхуковX-ThunderPhone-Signature— устаревший HMAC-SHA256 точных байтов тела запроса с тем же ключом, который по-прежнему отправляется для существующих средств проверкиX-ThunderPhone-Call-ID— идентификатор текущего звонка
Content-Type: application/json устанавливается, если только ваш endpoint.headers
не переопределяет его — пользовательский Content-Type имеет приоритет.
Формат запроса
У каждого API-подключения есть настройка request_format. Выберите vapi или
retell, если импортированный инструмент должен продолжать вызывать конечную точку, созданную для
этой платформы. Для новых и существующих подключений по умолчанию используется standard.
Для POST / PUT / PATCH ThunderPhone канонически сериализует выбранную
оболочку (отсортированные ключи, компактные разделители). Обе подписи
охватывают эти точные байты.
Standard
standard отправляет только аргументы инструмента, без обёртки. Это значение
по умолчанию, которое сохраняет исходное тело запроса ThunderPhone байт в байт:
{"date":"2025-01-02","service":"consultation"}Vapi
vapi отправляет серверное сообщение tool-calls Vapi
в формате, описанном в документации Vapi. call.id — это идентификатор звонка ThunderPhone. Идентификатор
вызова инструмента id уникален для вызова и повторно используется для сопоставления
результата конечной точки. Вызов отображается и в toolCallList, и в
toolWithToolCallList, а function.arguments представлен в виде JSON-объекта:
{
"message": {
"type": "tool-calls",
"timestamp": 1725000000123,
"call": { "id": "987654321" },
"toolCallList": [
{
"id": "call_00112233445566778899aabbccddeeff",
"type": "function",
"function": {
"name": "search_appointments",
"arguments": { "date": "2025-01-02", "service": "consultation" }
}
}
],
"toolWithToolCallList": [
{
"type": "function",
"function": { "name": "search_appointments" },
"toolCall": {
"id": "call_00112233445566778899aabbccddeeff",
"type": "function",
"function": {
"name": "search_appointments",
"arguments": { "date": "2025-01-02", "service": "consultation" }
}
}
}
]
}
}Поля, специфичные для Vapi, такие как assistant, artifact и полный объект call,
не отправляются.
Верните оболочку ответа Vapi с соответствующим идентификатором вызова инструмента:
{
"results": [
{
"toolCallId": "call_00112233445566778899aabbccddeeff",
"result": "{\"available_slots\":[\"9:00 AM\",\"2:00 PM\"]}"
}
]
}ThunderPhone передаёт ИИ строку result без оболочки. Для совместимости с конечными
точками, которые уже возвращают JSON-значение, также допускается JSON-значение. Значение
error вместо result возвращается ИИ как ошибка инструмента. Если конечная точка возвращает
некорректную или несоответствующую оболочку Vapi, ThunderPhone записывает
предупреждение в журнал и использует стандартный разбор ответа вместо сбоя
звонка.
Retell
retell отправляет запрос, описанный для пользовательской
функции Retell:
{
"name": "search_appointments",
"call": { "call_id": "987654321" },
"args": { "date": "2025-01-02", "service": "consultation" }
}Объект call содержит только call_id. ThunderPhone не включает
специфичные для Retell поля, которые невозможно корректно предоставить при отправке, например
agent_id, transcript, transcript_object, metadata или динамические
переменные. Retell принимает тело ответа любого типа; ThunderPhone передаёт его
с использованием стандартной обработки ответа.
Для GET / DELETE аргументы отправляются как параметры запроса,
а тело остаётся пустым независимо от request_format. Подпись V2
охватывает строку запроса в отправленном виде; устаревшая подпись вычисляется по
пустой строке байтов. См.
Проверка подписей вебхуков.
Ответ
Верните JSON-ответ с результатом инструмента:
{
"available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
"timezone": "America/Los_Angeles"
}Ответ форматируется и передаётся ИИ для продолжения разговора. Ответы не в формате JSON
оборачиваются в {"data": "<text>"}; тайм-ауты и сбои подключения сообщаются ИИ как ошибки,
чтобы агент мог извиниться и продолжить работу, а не зависнуть.
Диспетчеризация в режиме вебхука
Инструменты без endpoint направляются на URL устаревшего вебхука вашей организации
как подписанный запрос telephony.tool (телефонные звонки) или web.tool
(веб-звонки). В отличие от уведомлений аудита,
доставляемых на конечные точки вебхуков после выполнения, этот запрос является
выполнением — ваш 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 — используется тот же контракт ответа, что и при прямых вызовах конечных точек. Запрос подписывается секретом вебхука организации и содержит оба заголовка подписи, как и любой другой вебхук.
Проверка подписи
Прямые вызовы инструментов подписываются так же, как вебхуки, с использованием секрета вебхуков вашей организации:
X-ThunderPhone-Signature-V2подписывает временную метку, метод, путь и параметры запроса в отправленном виде, а также точные байты тела. Отклоняйте запросы, если их временная метка отличается от времени на ваших часах более чем на 5 минут.X-ThunderPhone-Signature(устаревший) подписывает только байты тела: дляGET/DELETE— пустую строку байтов. Он по-прежнему отправляется без изменений.
Вспомогательные функции SDK проверяют заголовок V2, включая 5-минутное окно:
import json
from fastapi import FastAPI, HTTPException, Request
from thunderphone import verify_signature
app = FastAPI()
@app.post("/appointments/search")
async def search_appointments(request: Request):
body = await request.body()
query = request.scope.get("query_string", b"").decode("latin-1")
path = request.url.path + (f"?{query}" if query else "")
if not verify_signature(
request.headers.get("X-ThunderPhone-Signature-V2"),
secret=WEBHOOK_SECRET,
method=request.method,
path=path,
body=body,
):
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}import express from "express";
import { verifySignature } from "@thunderphone/sdk";
app.post('/appointments/search', express.raw({type: 'application/json'}), (req, res) => {
const ok = verifySignature({
header: req.header('X-ThunderPhone-Signature-V2'),
secret: WEBHOOK_SECRET,
method: req.method,
path: req.originalUrl,
body: req.body,
});
if (!ok) 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 });
});Полные рецепты — подписываемая строка, рабочий пример, код без
SDK, случай GET / DELETE и оговорка об отсутствии секрета — см. в разделе Проверка подписей вебхуков.
Пример: полный процесс записи
Вот набор инструментов для полноценной системы записи на приём:
{
"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, которые можно подключать к агентам.
Один вспомогательный инструмент проверки для вебхуков и вызовов инструментов.