Open in
Công cụ hàm
Trang bị cho tác nhân AI của bạn các công cụ hàm để gọi API bên ngoài trong cuộc trò chuyện — truy xuất dữ liệu khách hàng, đặt lịch hẹn, cập nhật hồ sơ — với tham số có kiểu.
Công cụ hàm cho phép tác nhân AI của bạn gọi API bên ngoài trong cuộc gọi điện thoại. Dùng chúng để tra cứu dữ liệu khách hàng, kiểm tra lịch trống, đặt lịch hẹn hoặc thực hiện bất kỳ hành động nào mà backend của bạn hỗ trợ.
Cách hoạt động
- Bạn định nghĩa công cụ bằng schema (các đối số mà công cụ chấp nhận)
- Bạn cung cấp cấu hình
endpoint(nơi ThunderPhone gọi API của bạn) — hoặc bỏ qua để nhận lệnh gọi công cụ trên webhook của tổ chức - Trong cuộc gọi, AI quyết định thời điểm dùng công cụ dựa trên cuộc trò chuyện
- ThunderPhone gọi endpoint của bạn với các đối số của công cụ
- Phản hồi API của bạn được gửi lại cho AI để tiếp tục cuộc trò chuyện
| Khả năng | Nơi chạy | Thiết lập |
|---|---|---|
| Công cụ tích hợp sẵn | ThunderPhone | Hướng dẫn trong prompt; một số công cụ cũng cần cài đặt tác nhân |
| Kết nối ứng dụng | ThunderPhone và nhà cung cấp đã kết nối | Kết nối tài khoản và gắn hành động đã phê duyệt |
| Kết nối API và công cụ hàm | API HTTP của bạn | Định nghĩa endpoint và schema, hoặc nhận lệnh gọi hàm qua webhook |
| Máy chủ MCP | Máy chủ MCP từ xa | Thêm máy chủ, khám phá công cụ của máy chủ và gắn vào tác nhân |
Schema công cụ
Mỗi công cụ tuân theo cấu trúc sau:
{
"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
}Cấu hình công cụ
| Trường | Loại | Bắt buộc | Mô tả |
|---|---|---|---|
timeout | number | Không | Thời gian thực thi tối đa tính bằng giây (mặc định: 20, tối đa: 180) |
Định nghĩa hàm
| Trường | Loại | Bắt buộc | Mô tả |
|---|---|---|---|
name | string | Có | Mã định danh duy nhất của công cụ |
description | string | Có | Giải thích cho AI khi nào nên dùng công cụ này |
parameters | object | Có | JSON Schema cho các đối số của công cụ |
Cấu hình endpoint
| Trường | Loại | Bắt buộc | Mô tả |
|---|---|---|---|
url | string | Có | URL endpoint API của bạn |
method | string | Không | Phương thức HTTP (mặc định: POST) |
headers | object | Không | Header tùy chỉnh cần bao gồm |
Hai đường dẫn gọi
Yêu cầu mà máy chủ của bạn nhận được phụ thuộc vào việc công cụ có
endpoint hay không:
Tool có endpoint | Tool không có endpoint | |
|---|---|---|
| Nơi gửi yêu cầu | Trực tiếp đến endpoint.url | URL webhook cũ của tổ chức bạn |
| Nội dung | Định dạng yêu cầu đã chọn (mặc định là đối số không bọc) | Envelope telephony.tool / web.tool |
| Header | endpoint.headers của bạn + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature | Content-Type + X-ThunderPhone-Signature-V2 + X-ThunderPhone-Signature |
| Khóa ký | Bí mật webhook của tổ chức | Bí mật webhook của tổ chức |
Cả hai đường dẫn đều chặn — AI đang chờ kết quả giữa câu nói.
Thời gian chờ mặc định là 20 giây; đặt timeout cấp cao nhất của công cụ
để cho phép thời gian thực thi dài hơn, tối đa 180 giây theo giới hạn
nền tảng. Giữ handler xử lý nhanh. Có thể kết hợp:
trong một cuộc gọi mà tổ chức có URL webhook, công cụ có endpoint sẽ được
gọi trực tiếp và các công cụ còn lại sẽ dùng webhook.
Lệnh gọi endpoint trực tiếp
Khi tác nhân AI gọi một công cụ có endpoint, ThunderPhone sẽ gửi
một yêu cầu đến URL của bạn:
Header yêu cầu
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-keyCác header tùy chỉnh từ endpoint.headers của bạn luôn được gửi nguyên văn,
cùng với các header có không gian tên ThunderPhone sau:
X-ThunderPhone-Signature-V2— HMAC-SHA256 có dấu thời gian trên phương thức, đường dẫn, truy vấn và phần thân, được tạo khóa bằng bí mật webhook của tổ chức. Xác minh header này và từ chối các yêu cầu cũ hơn 5 phút; xem Xác minh chữ ký webhookX-ThunderPhone-Signature— HMAC-SHA256 cũ của các byte chính xác trong phần thân yêu cầu, dùng cùng khóa, vẫn được gửi cho các trình xác minh hiện cóX-ThunderPhone-Call-ID— ID cuộc gọi hiện tại
Content-Type: application/json được thiết lập trừ khi endpoint.headers
của bạn ghi đè — Content-Type tùy chỉnh được ưu tiên.
Định dạng yêu cầu
Mỗi kết nối API có một cài đặt request_format. Chọn vapi hoặc
retell khi một công cụ được nhập cần tiếp tục gọi một endpoint được xây dựng cho
nền tảng đó. Kết nối mới và hiện có mặc định dùng standard.
Với POST / PUT / PATCH, ThunderPhone tuần tự hóa khung dữ liệu đã chọn
theo quy tắc chuẩn (khóa được sắp xếp, dấu phân tách gọn). Cả hai chữ ký
đều bao phủ chính xác các byte này.
Tiêu chuẩn
standard chỉ gửi các đối số của công cụ, không có trình bao bọc. Đây là
mặc định và giữ nguyên phần thân yêu cầu gốc của ThunderPhone theo từng byte:
{"date":"2025-01-02","service":"consultation"}Vapi
vapi gửi thông báo máy chủ tool-calls
của Vapi theo định dạng mà Vapi tài liệu hóa. call.id là ID cuộc gọi ThunderPhone.
id của lệnh gọi công cụ là duy nhất cho lần thực thi và được dùng lại để đối chiếu
kết quả của endpoint. Lệnh gọi xuất hiện trong cả toolCallList và
toolWithToolCallList, với function.arguments là một đối tượng 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" }
}
}
}
]
}
}Các trường chỉ dành cho Vapi như assistant, artifact và toàn bộ đối tượng call
không được gửi.
Trả về khung phản hồi của Vapi với ID lệnh gọi công cụ tương ứng:
{
"results": [
{
"toolCallId": "call_00112233445566778899aabbccddeeff",
"result": "{\"available_slots\":[\"9:00 AM\",\"2:00 PM\"]}"
}
]
}ThunderPhone cung cấp chuỗi result đã được bỏ khung cho tác nhân AI. Hệ thống cũng chấp nhận một
giá trị JSON để tương thích với các endpoint đã trả về giá trị đó. Một
error thay cho result được trả về tác nhân AI dưới dạng lỗi công cụ. Nếu
endpoint trả về một khung Vapi không hợp lệ hoặc không khớp, ThunderPhone ghi
cảnh báo và chuyển sang phân tích phản hồi tiêu chuẩn thay vì làm thất bại
cuộc gọi.
Retell
retell gửi yêu cầu được tài liệu hóa cho một hàm tùy chỉnh
Retell:
{
"name": "search_appointments",
"call": { "call_id": "987654321" },
"args": { "date": "2025-01-02", "service": "consultation" }
}Đối tượng call chỉ bao gồm call_id. ThunderPhone không bao gồm các
trường dành riêng cho Retell mà hệ thống không thể cung cấp chính xác khi gửi đi, như
agent_id, transcript, transcript_object, metadata hoặc các biến
động. Retell chấp nhận mọi nội dung phản hồi; ThunderPhone chuyển tiếp nội dung đó
bằng cơ chế xử lý phản hồi tiêu chuẩn.
Với GET / DELETE, các đối số được gửi dưới dạng tham số truy vấn
và phần thân trống, bất kể request_format. Chữ ký V2
bao phủ chuỗi truy vấn khi được gửi; chữ ký cũ được tính trên
chuỗi byte rỗng. Xem
Xác minh chữ ký webhook.
Phản hồi
Trả về phản hồi JSON với kết quả công cụ:
{
"available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
"timezone": "America/Los_Angeles"
}Phản hồi được định dạng và cung cấp cho tác nhân AI để tiếp tục
cuộc trò chuyện. Phản hồi không phải JSON được bọc thành {"data": "<text>"};
lỗi hết thời gian chờ và kết nối được báo cho tác nhân AI dưới dạng lỗi, để
tác nhân có thể xin lỗi và tiếp tục thay vì bị treo.
Điều phối chế độ webhook
Các công cụ không có endpoint được điều phối đến URL webhook cũ
của tổ chức bạn dưới dạng yêu cầu telephony.tool (cuộc gọi điện thoại) hoặc web.tool
(cuộc gọi web) có chữ ký. Không giống thông báo kiểm toán
được gửi đến endpoint webhook sau khi thực thi, yêu cầu này chính là
việc thực thi — phản hồi HTTP của bạn là kết quả công cụ.
{
"type": "telephony.tool",
"data": {
"call_id": 987654321,
"tool_name": "search_appointments",
"arguments": { "date": "2026-04-21" },
"from_number": "+14155550199",
"to_number": "+15551234567"
}
}web.tool mang origin_domain thay cho from_number /
to_number. Phản hồi bằng kết quả công cụ dưới dạng JSON — cùng hợp đồng phản hồi
như các lệnh gọi endpoint trực tiếp. Yêu cầu được ký bằng secret webhook của tổ chức,
với cả hai header chữ ký, như mọi webhook khác.
Xác thực chữ ký
Các lệnh gọi công cụ trực tiếp được ký giống như webhook, bằng bí mật webhook của tổ chức bạn:
X-ThunderPhone-Signature-V2ký dấu thời gian, phương thức, đường dẫn và truy vấn như đã gửi, cùng chính xác các byte của nội dung. Từ chối yêu cầu có dấu thời gian lệch quá 5 phút so với đồng hồ của bạn.X-ThunderPhone-Signature(phiên bản cũ) chỉ ký các byte của nội dung, hoặc chuỗi byte rỗng choGET/DELETE. Header này vẫn được gửi, không thay đổi.
Các trình trợ giúp SDK kiểm tra header V2, bao gồm cả khoảng thời gian 5 phút:
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}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 });
});Các hướng dẫn đầy đủ — chuỗi được ký, ví dụ minh họa, mã không dùng
SDK, trường hợp GET / DELETE và lưu ý khi không có bí mật — có tại Xác minh chữ ký webhook.
Ví dụ: Quy trình đặt lịch hoàn chỉnh
Dưới đây là một bộ công cụ cho hệ thống đặt lịch hẹn hoàn chỉnh:
{
"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" }
}
}
]
}Thực hành tốt nhất
Viết mô tả rõ ràng
Trường description giúp tác nhân AI hiểu khi nào nên sử dụng công cụ. Hãy nêu cụ thể công cụ làm gì và khi nào phù hợp để sử dụng.
Xử lý lỗi hợp lý
Trả về thông báo lỗi mà tác nhân AI có thể hiểu: {"error": "No slots available for that date"} thay vì lỗi 500 chung chung.
Giữ phản hồi ngắn gọn
Chỉ trả về những gì tác nhân AI cần để tiếp tục cuộc trò chuyện. Payload lớn làm chậm thời gian phản hồi.
Sử dụng trường bắt buộc hợp lý
Chỉ đánh dấu trường là required khi thực sự cần thiết. Tác nhân AI sẽ hỏi người dùng thông tin bắt buộc trước khi gọi công cụ.
Liên quan
Prompt các thao tác cuộc gọi do nền tảng quản lý mà không cần xác định endpoint.
Công cụ do nền tảng quản lý cho HubSpot, Salesforce, Slack, Google Calendar, Google Sheets và Cal.com — không cần endpoint.
Gắn máy chủ MCP và cho phép tác nhân AI gọi các công cụ của máy chủ đó.
Tích hợp REST có thể tái sử dụng mà bạn có thể gắn vào tác nhân AI.
Một trình hỗ trợ xác minh cho webhook và lệnh gọi công cụ.