ThunderPhone 2.0 jest już dostępny.Uruchom samodzielnie, od 2¢/min.Przeczytaj komunikat

Function Tools

Narzędzia funkcji

Wyposaż swoich agentów AI w narzędzia funkcji, które wywołują zewnętrzne interfejsy API w trakcie rozmowy — pobierają dane klientów, umawiają wizyty, aktualizują rekordy — z parametrami o określonych typach.

Narzędzia funkcji umożliwiają agentom AI wywoływanie zewnętrznych interfejsów API podczas rozmów telefonicznych. Używaj ich do wyszukiwania danych klientów, sprawdzania dostępności, umawiania wizyt lub wykonywania dowolnych działań obsługiwanych przez Twój backend.

Jak to działa

  1. Zdefiniuj narzędzia za pomocą schematu (jakie argumenty akceptuje narzędzie)
  2. Podaj konfigurację endpoint (gdzie ThunderPhone wywołuje Twój interfejs API) — lub ją pomiń, aby otrzymywać wywołania narzędzi na webhooku organizacji
  3. Podczas rozmowy AI decyduje, kiedy użyć narzędzia, na podstawie konwersacji
  4. ThunderPhone wywołuje Twój endpoint z argumentami narzędzia
  5. Odpowiedź Twojego API jest przekazywana z powrotem do AI, aby kontynuować konwersację
MożliwośćGdzie działaKonfiguracja
Wbudowane narzędziaThunderPhoneInstrukcje w prompcie; niektóre narzędzia wymagają także ustawienia agenta
Połączenia z aplikacjamiThunderPhone i połączony dostawcaPołącz konto i przypisz zatwierdzone działania
Połączenia API i narzędzia funkcjiTwój interfejs HTTP APIZdefiniuj endpoint i schemat lub odbieraj wywołania funkcji przez webhook
Serwery MCPZdalny serwer MCPDodaj serwer, wykryj jego narzędzia i przypisz go do agenta

Schemat narzędzia

Każde narzędzie ma następującą strukturę:

{
  "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
}

Konfiguracja narzędzia

PoleTypWymaganeOpis
timeoutliczbaNieMaksymalny czas wykonania w sekundach (domyślnie: 20, maksymalnie: 180)

Definicja funkcji

PoleTypWymaganeOpis
nameciąg znakówTakUnikalny identyfikator narzędzia
descriptionciąg znakówTakWyjaśnia AI, kiedy użyć tego narzędzia
parametersobiektTakSchemat JSON argumentów narzędzia

Konfiguracja endpointu

PoleTypWymaganeOpis
urlciąg znakówTakAdres URL endpointu Twojego API
methodciąg znakówNieMetoda HTTP (domyślnie: POST)
headersobiektNieNiestandardowe nagłówki do uwzględnienia

Dwie ścieżki wywołania

To, jakie żądanie otrzyma Twój serwer, zależy od tego, czy narzędzie ma endpoint:

Narzędzie z endpointNarzędzie bez endpoint
Dokąd trafia żądanieBezpośrednio do endpoint.urlStarszy adres URL webhooka organizacji
TreśćWybrany format żądania (domyślnie same argumenty)Otoczka telephony.tool / web.tool
Nagłówkiendpoint.headers + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature-V2 + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature
Klucz podpisywaniaSekret webhooka organizacjiSekret webhooka organizacji

Obie ścieżki są blokujące — AI czeka w środku zdania na wynik. Domyślny limit czasu wynosi 20 s; ustaw timeout na najwyższym poziomie narzędzia, aby zezwolić na dłuższe wykonanie, do maksymalnej wartości platformy wynoszącej 180 s. Utrzymuj szybką obsługę handlerów. Możesz używać obu ścieżek: w rozmowie, której organizacja ma adres URL webhooka, narzędzia z endpoint są wywoływane bezpośrednio, a pozostałe korzystają z webhooka.

Bezpośrednie wywołania punktów końcowych

Gdy AI wywołuje narzędzie z endpoint, ThunderPhone wysyła żądanie na Twój adres URL:

Nagłówki żądania

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

Niestandardowe nagłówki z endpoint.headers są zawsze dołączane dosłownie, wraz z tymi nagłówkami z przestrzeni nazw ThunderPhone:

  • X-ThunderPhone-Signature-V2 — HMAC-SHA256 ze znacznikiem czasu obejmujący metodę, ścieżkę, parametry zapytania i treść, z kluczem w postaci sekretu webhooka organizacji. Zweryfikuj ten nagłówek i odrzucaj żądania starsze niż 5 minut; zobacz Weryfikowanie podpisów webhooków
  • X-ThunderPhone-Signature — starszy HMAC-SHA256 dokładnych bajtów treści żądania, z tym samym kluczem, nadal wysyłany dla istniejących weryfikatorów
  • X-ThunderPhone-Call-ID — identyfikator bieżącego połączenia

Content-Type: application/json jest ustawiany, chyba że endpoint.headers go zastępuje — niestandardowy Content-Type ma pierwszeństwo.

Format żądania

Każde połączenie API ma ustawienie request_format. Wybierz vapi lub retell, gdy zaimportowane narzędzie powinno nadal wywoływać punkt końcowy utworzony dla tej platformy. Nowe i istniejące połączenia domyślnie używają standard.

Dla POST / PUT / PATCH ThunderPhone serializuje wybraną kopertę kanonicznie (posortowane klucze, zwarte separatory). Oba podpisy obejmują dokładnie te bajty.

Standard

standard wysyła tylko argumenty narzędzia, bez opakowania. To ustawienie domyślne zachowuje oryginalne ciało żądania ThunderPhone bajt po bajcie:

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

Vapi

vapi wysyła komunikat serwera tool-calls Vapi w formacie udokumentowanym przez Vapi. call.id to identyfikator połączenia ThunderPhone. Identyfikator wywołania narzędzia id jest unikalny dla wywołania i jest ponownie używany do dopasowania wyniku punktu końcowego. Połączenie pojawia się zarówno w toolCallList, jak i toolWithToolCallList, z function.arguments jako obiektem 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" }
          }
        }
      }
    ]
  }
}

Pola dostępne wyłącznie w Vapi, takie jak assistant, artifact i pełny obiekt call, nie są wysyłane.

Zwróć kopertę odpowiedzi Vapi z pasującym identyfikatorem wywołania narzędzia:

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

ThunderPhone przekazuje AI rozpakowany ciąg result. Akceptuje również wartość JSON dla zgodności z punktami końcowymi, które już ją zwracają. error zamiast result jest zwracany do AI jako błąd narzędzia. Jeśli punkt końcowy zwróci nieprawidłową lub niedopasowaną kopertę Vapi, ThunderPhone zapisuje ostrzeżenie i przechodzi do standardowego przetwarzania odpowiedzi zamiast przerywać połączenie.

Retell

retell wysyła żądanie udokumentowane dla funkcji niestandardowej Retell:

{
  "name": "search_appointments",
  "call": { "call_id": "987654321" },
  "args": { "date": "2025-01-02", "service": "consultation" }
}

Obiekt call zawiera tylko call_id. ThunderPhone nie uwzględnia pól specyficznych dla Retell, których nie może rzetelnie dostarczyć podczas wysyłki, takich jak agent_id, transcript, transcript_object, metadata ani zmienne dynamiczne. Retell akceptuje dowolną treść odpowiedzi; ThunderPhone przekazuje ją dalej, stosując standardowe przetwarzanie odpowiedzi.

Dla GET / DELETE argumenty są wysyłane jako parametry zapytania a treść jest pusta niezależnie od request_format. Podpis V2 obejmuje ciąg zapytania w wysłanej postaci; starszy podpis jest obliczany dla pustego ciągu bajtów. Zobacz Weryfikowanie podpisów webhooków.

Odpowiedź

Zwróć odpowiedź JSON z wynikiem narzędzia:

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

Odpowiedź jest formatowana i przekazywana AI, aby mogła kontynuować rozmowę. Odpowiedzi inne niż JSON są opakowywane jako {"data": "<text>"}; przekroczenia limitu czasu i błędy połączenia są zgłaszane AI jako błędy, dzięki czemu agent może przeprosić i kontynuować zamiast się zatrzymać.

Przekazywanie w trybie webhooka

Narzędzia bez endpoint są przekazywane na starszy adres URL webhooka Twojej organizacji jako podpisane żądanie telephony.tool (połączenia telefoniczne) lub web.tool (połączenia internetowe). W przeciwieństwie do powiadomień audytowych dostarczanych do punktów końcowych webhooków po wykonaniu, to żądanie jest wykonaniem — Twoja odpowiedź HTTP jest wynikiem narzędzia.

{
  "type": "telephony.tool",
  "data": {
    "call_id": 987654321,
    "tool_name": "search_appointments",
    "arguments": { "date": "2026-04-21" },
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  }
}

web.tool zawiera origin_domain zamiast from_number / to_number. Odpowiedz wynikiem narzędzia w formacie JSON — obowiązuje ten sam kontrakt odpowiedzi co w przypadku bezpośrednich wywołań punktów końcowych. Żądanie jest podpisane sekretem webhooka organizacji, z użyciem obu nagłówków podpisu, tak jak każdy inny webhook.


Weryfikacja podpisu

Bezpośrednie wywołania narzędzi są podpisywane tak samo jak webhooki, przy użyciu sekretu webhooka organizacji:

  • X-ThunderPhone-Signature-V2 podpisuje znacznik czasu, metodę, ścieżkę i parametry zapytania w przesłanej postaci oraz dokładne bajty treści. Odrzucaj żądania, których znacznik czasu różni się od czasu systemowego o więcej niż 5 minut.
  • X-ThunderPhone-Signature (starsza wersja) podpisuje tylko bajty treści, pusty ciąg bajtów dla GET / DELETE. Nadal jest wysyłany bez zmian.

Pomocnicze funkcje SDK sprawdzają nagłówek V2, w tym 5-minutowe okno:

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

Pełne instrukcje — podpisywany ciąg, szczegółowy przykład, kod bez SDK, przypadek GET / DELETE oraz zastrzeżenie dotyczące braku sekretu — znajdziesz w sekcji Weryfikowanie podpisów webhooków.


Przykład: kompletny proces rezerwacji

Oto zestaw narzędzi dla kompletnego systemu Umawiania wizyt:

{
  "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" }
      }
    }
  ]
}

Najlepsze praktyki

Pisz jasne opisy

Pole description pomaga AI zrozumieć, kiedy użyć narzędzia. Precyzyjnie opisz, co robi i kiedy należy go użyć.

Prawidłowo obsługuj błędy

Zwracaj komunikaty o błędach zrozumiałe dla AI: {"error": "No slots available for that date"} zamiast ogólnych błędów 500.

Zachowaj zwięzłość odpowiedzi

Zwracaj tylko to, czego AI potrzebuje, aby kontynuować rozmowę. Duże ładunki danych spowalniają czas odpowiedzi.

Rozważnie używaj pól wymaganych

Oznaczaj pola jako required tylko wtedy, gdy jest to naprawdę konieczne. AI poprosi użytkownika o wymagane informacje przed wywołaniem narzędzia.


Powiązane