Алатке функција

Функцијски алати омогућавају вашим AI агентима да позивају спољне API-је током телефонских позива. Користите их за проналажење података о клијентима, проверу доступности, заказивање термина или обављање било које радње коју ваш позадински систем подржава.

Како функционише

  1. Дефинишете алате помоћу шеме (које аргументе алат прихвата)
  2. Наводите конфигурацију endpoint (где ThunderPhone позива ваш API) — или је изоставите да бисте примали позиве алата на webhook-у ваше организације
  3. Током позива, AI одлучује када ће користити алат на основу разговора
  4. ThunderPhone позива ваш крајњи приступ са аргументима алата
  5. Одговор вашег API-ја враћа се AI-ју ради наставка разговора

Шема алата

Сваки алат прати ову структуру:

{
  "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нискаДаОбјашњава AI-ју када да користи овај алат
parametersобјекатДаJSON Schema за аргументе алата

Конфигурација крајњег приступа

ПољеТипОбавезноОпис
urlнискаДаURL крајњег приступа вашег API-ја
methodнискаНеHTTP метод (подразумевано: POST)
headersобјекатНеПрилагођена заглавља која треба укључити

Два пута позивања

Захтев који ваш сервер прима зависи од тога да ли алат има endpoint:

Алат са endpointАлат без endpoint
Куда захтев идеДиректно на endpoint.urlНа застарели URL webhook-а ваше организације
ТелоСамо аргументи алатаОмотач telephony.tool / web.tool
ЗаглављаВаш endpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature
Кључ за потписивањеТајна webhook-а организацијеТајна webhook-а организације

Оба пута су блокирајућа — AI чека резултат усред реченице — са временским ограничењем од 20 s. Обрађиваче одржавајте брзим. Можете користити комбинацију: у позиву чија организација има URL webhook-а, алати са endpoint се позивају директно, а остали се враћају на webhook.

Директни позиви крајње тачке

Када 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 простору имена:

Content-Type: application/json се поставља осим ако га Ваш endpoint.headers не замени — прилагођени Content-Type има предност.

Тело захтева

За POST / PUT / PATCH, тело садржи само аргументе алатке (без омотача), канонски серијализоване (сортирани кључеви, компактни раздвајачи):

{"date":"2025-01-02","service":"consultation"}

За GET / DELETE, аргументи се шаљу као параметри упита а тело је празно — потпис се тада израчунава над празним низом бајтова. Погледајте Верификација потписа вебхука.

Одговор

Вратите JSON одговор са резултатом алатке:

{
  "available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
  "timezone": "America/Los_Angeles"
}

Одговор се форматира и прослеђује AI-ју да настави разговор. Одговори који нису JSON умотавају се као {"data": "<text>"}; истеци времена и неуспешне везе пријављују се AI-ју као грешке, тако да агент може да се извини и настави уместо да застане.

Отпремање у режиму вебхука

Алатке без 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 — исти уговор о одговору као за директне позиве крајње тачке. Захтев је потписан тајном за вебхук организације над сировим телом, као и сваки други вебхук.


Верификација потписа

Директни позиви алата потписују се на исти начин као webhook-ови:

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 });
});

Комплетни примери — укључујући случај празног тела и напомену о одсуству тајне — налазе се у одељку Верификујте webhook потписе.


Пример: Комплетан ток заказивања

Ево скупа алата за комплетан систем заказивања термина:

{
  "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 помаже AI-ју да разуме када треба да користи алат. Прецизно наведите шта алат ради и када је прикладно да се користи.

Елегантно обрађујте грешке

Вратите поруке о грешкама које AI може да разуме: {"error": "No slots available for that date"} уместо општих грешака 500.

Нека одговори буду сажети

Вратите само оно што је AI-ју потребно да настави разговор. Велики пакети података успоравају време одговора.

Промишљено користите обавезна поља

Означите поља као required само када је то заиста неопходно. AI ће затражити од корисника обавезне информације пре позивања алата.


Повезано