ThunderPhone 2.0 уже доступний.Підключайте самостійно — від 2 ¢/хв.Прочитати анонс

Function Tools

Інструменти функцій

Надайте своїм ШІ-агентам інструменти функцій, які викликають зовнішні API під час розмови — отримують дані клієнтів, бронюють зустрічі, оновлюють записи — із типізованими параметрами.

Інструменти функцій дають вашим AI-агентам змогу викликати зовнішні API під час телефонних дзвінків. Використовуйте їх для пошуку даних клієнтів, перевірки доступності, бронювання зустрічей або виконання будь-яких дій, які підтримує ваш бекенд.

Як це працює

  1. Визначте інструменти за допомогою схеми (які аргументи приймає інструмент)
  2. Надайте конфігурацію endpoint (куди ThunderPhone викликає ваш API) — або не вказуйте її, щоб отримувати виклики інструментів на вебхуку вашої організації
  3. Під час дзвінка AI вирішує, коли використовувати інструмент, на основі розмови
  4. ThunderPhone викликає вашу кінцеву точку з аргументами інструменту
  5. Відповідь вашого API передається AI для продовження розмови
МожливістьДе виконуєтьсяНалаштування
Вбудовані інструменти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рядокТакПояснює AI, коли використовувати цей інструмент
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-SignatureContent-Type + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature
Ключ підписуСекрет вебхука організаціїСекрет вебхука організації

Обидва шляхи є блокувальними — AI очікує результат посеред речення. Стандартний тайм-аут становить 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 — це ID виклику ThunderPhone. ID виклику інструмента 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 з відповідним ID виклику інструмента:

{
  "results": [
    {
      "toolCallId": "call_00112233445566778899aabbccddeeff",
      "result": "{\"available_slots\":[\"9:00 AM\",\"2:00 PM\"]}"
    }
  ]
}

ThunderPhone передає ШІ розгорнутий рядок result. Також підтримується значення 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 (вебвиклики). На відміну від сповіщень аудиту, що доставляються до endpoint вебхуків після виконання, цей запит є виконанням — ваша 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-хвилинне вікно:

Python
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}
Node.js
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 лише за справжньої потреби. Перед викликом інструмента ШІ запитає в користувача обов’язкову інформацію.


Пов’язані матеріали