ThunderPhone 2.0 уже доступен.Самостоятельное подключение — от 2 центов/мин.Читать анонс

Function Tools

Инструменты функций

Предоставьте своим ИИ-агентам инструменты функций, которые вызывают внешние API прямо во время разговора: получают данные клиентов, записывают на приём, обновляют записи — с типизированными параметрами.

Инструменты функций позволяют вашим ИИ-агентам вызывать внешние API во время телефонных звонков. Используйте их для поиска данных клиентов, проверки доступности, бронирования встреч или выполнения любых действий, которые поддерживает ваш бэкенд.

Как это работает

  1. Определите инструменты с помощью схемы (какие аргументы принимает инструмент)
  2. Укажите конфигурацию endpoint (куда ThunderPhone вызывает ваш API) — или не указывайте её, чтобы получать вызовы инструментов на вебхук вашей организации
  3. Во время звонка ИИ решает, когда использовать инструмент, на основе разговора
  4. ThunderPhone вызывает ваш endpoint с аргументами инструмента
  5. Ответ вашего 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-SignatureContent-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 подписывают пустую строку байтов
Python
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}
Node.js
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 только при реальной необходимости. Перед вызовом инструмента ИИ запросит у пользователя обязательную информацию.


Связанные материалы