ThunderPhone 2.0 正式上線。自助開通,每分鐘 2¢ 起。查看公告

Function Tools

函式工具

為你的 AI 智慧體提供函式工具,可在對話進行中呼叫外部 API——擷取客戶資料、預約、更新紀錄——並使用具型別的參數。

函式工具可讓你的語音智慧體在通話期間呼叫外部 API。使用它們查詢客戶資料、確認可用時段、預約,或執行你的後端支援的任何動作。

運作方式

  1. 使用結構描述定義工具(工具接受哪些參數)
  2. 提供 endpoint 設定(ThunderPhone 在何處呼叫你的 API)——或省略它,以便在你的組織 webhook 上接收工具呼叫
  3. 通話期間,人工智慧會根據對話決定何時使用工具
  4. ThunderPhone 使用工具參數呼叫你的端點
  5. 你的 API 回應會回傳給人工智慧,以繼續對話
功能執行位置設定方式
內建工具ThunderPhone提示指令;部分工具也需要設定智慧體
應用程式連線ThunderPhone 與已連線的服務供應商連接帳號並附加已核准的動作
API 連線與函式工具你的 HTTP API定義端點與結構描述,或透過 webhook 接收函式呼叫
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字串是你的 API 端點 URL
method字串否HTTP 方法(預設值:POST)
headers物件否要包含的自訂標頭

兩種呼叫路徑

你的伺服器收到哪種請求,取決於工具是否具有 endpoint:

含有 endpoint 的工具不含 endpoint 的工具
請求傳送位置直接傳送至 endpoint.url你組織的舊版 Webhook 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
簽署金鑰組織 Webhook 密鑰組織 Webhook 密鑰

兩種路徑都是阻塞式——人工智慧會在句子中途等待 結果。預設逾時時間為20 秒;請設定工具頂層的 timeout 以允許更長的執行時間,最長可達平台的180 秒 上限。請讓處理常式保持快速。混用沒有問題: 在組織具有 webhook URL 的通話中,具有 endpoint 的工具會 直接呼叫,其餘工具則會改由 webhook 處理。

直接端點呼叫

當 AI 呼叫具有 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,並以你的組織 webhook 密鑰作為金鑰。 請驗證此簽章並拒絕超過 5 分鐘的請求;請參閱 驗證 webhook 簽章
  • X-ThunderPhone-Signature ——針對完整請求內文位元組的舊版 HMAC-SHA256, 使用相同金鑰,仍會傳送給既有的驗證工具
  • X-ThunderPhone-Call-ID ——目前的通話 ID

除非你的 endpoint.headers 覆寫它,否則會設定 Content-Type: application/json —— 自訂 Content-Type 優先。

請求格式

每個 API 連線都有 request_format 設定。當匯入的工具應持續呼叫為該平台建立的端點時,請選擇 vapi 或 retell。新的和既有的連線預設為 standard。

對於 POST / PUT / PATCH,ThunderPhone 會以標準形式序列化所選的 封裝格式(排序鍵值、精簡分隔符)。兩種簽章都涵蓋這些完全相同的位元組。

Standard

standard 僅傳送工具引數,不含任何包裝。這是預設行為,並會逐位元組保留 ThunderPhone 原始的請求主體:

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

Vapi

vapi 會以 Vapi 文件記載的格式傳送 Vapi 的 tool-calls 伺服器訊息。 call.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 物件。

請傳回包含相符工具呼叫 ID 的 Vapi 回應封裝:

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

ThunderPhone 會將未封裝的 result 字串提供給 AI。為了相容於已經傳回 JSON 值的端點,也接受 JSON 值。以 error 取代 result 時,會以工具錯誤的形式傳回給 AI。如果端點傳回格式錯誤或不相符的 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 簽章涵蓋傳送的查詢字串;舊版簽章則根據空位元組字串計算。請參閱 驗證 webhook 簽章。

回應

傳回包含工具結果的 JSON 回應:

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

系統會格式化回應並提供給 AI,以繼續進行 對話。非 JSON 回應會包裝為 {"data": "<text>"}; 逾時與連線失敗會以錯誤形式回報給 AI,讓 智慧體可以致歉並繼續處理,而不會停滯。

Webhook 模式分派

沒有 endpoint 的工具會作為已簽名的 telephony.tool(電話通話)或 web.tool (網頁通話)請求,分派至你的組織舊版 Webhook URL。不同於在執行後 傳送至 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 回傳工具結果——其回應契約與直接呼叫端點時相同。此請求會使用組織的 webhook 密鑰簽署,並包含兩個簽章標頭,如同其他所有 webhook。


簽章驗證

直接工具呼叫的簽署方式與 Webhook 相同,使用你組織的 Webhook 密鑰:

  • 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 情境,以及未設定密鑰時的注意事項——請參閱驗證 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 會在呼叫工具前向使用者詢問必填資訊。


相關內容