เครื่องมือฟังก์ชัน
เครื่องมือฟังก์ชันช่วยให้ AI เอเจนต์ของคุณเรียกใช้ API ภายนอกระหว่างการโทรได้ ใช้เพื่อค้นหาข้อมูลลูกค้า ตรวจสอบเวลาว่าง จองนัดหมาย หรือดำเนินการใดๆ ที่แบ็กเอนด์ของคุณรองรับ
วิธีการทำงาน
- กำหนดเครื่องมือด้วยสคีมา (อาร์กิวเมนต์ที่เครื่องมือรับได้)
- ระบุการกำหนดค่า
endpoint(ตำแหน่งที่ ThunderPhone เรียก API ของคุณ) หรือไม่ต้องระบุเพื่อรับการเรียกใช้เครื่องมือที่เว็บฮุกขององค์กร - ระหว่างการโทร AI จะตัดสินใจว่าจะใช้เครื่องมือเมื่อใดตามบทสนทนา
- ThunderPhone เรียก endpoint ของคุณพร้อมอาร์กิวเมนต์ของเครื่องมือ
- ระบบจะส่งการตอบกลับจาก 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 | string | ใช่ | ตัวระบุที่ไม่ซ้ำกันสำหรับเครื่องมือ |
description | string | ใช่ | อธิบายให้ AI ทราบว่าควรใช้เครื่องมือนี้เมื่อใด |
parameters | object | ใช่ | JSON Schema สำหรับอาร์กิวเมนต์ของเครื่องมือ |
การกำหนดค่า Endpoint
| ฟิลด์ | ประเภท | ต้องระบุ | คำอธิบาย |
|---|---|---|---|
url | string | ใช่ | URL endpoint ของ API คุณ |
method | string | ไม่ | เมธอด HTTP (ค่าเริ่มต้น: POST) |
headers | object | ไม่ | เฮดเดอร์แบบกำหนดเองที่จะรวมไว้ |
เส้นทางการเรียกใช้สองแบบ
คำขอที่เซิร์ฟเวอร์ของคุณได้รับขึ้นอยู่กับว่าเครื่องมือมี
endpoint หรือไม่:
เครื่องมือที่มี endpoint | เครื่องมือที่ไม่มี endpoint | |
|---|---|---|
| ปลายทางของคำขอ | ส่งไปยัง endpoint.url โดยตรง | URL เว็บฮุกเดิม ขององค์กรคุณ |
| เนื้อหาคำขอ | อาร์กิวเมนต์ของเครื่องมือโดยตรง | ซองข้อมูล telephony.tool / web.tool |
| เฮดเดอร์ | endpoint.headers ของคุณ + X-ThunderPhone-Call-ID + X-ThunderPhone-Signature | Content-Type + X-ThunderPhone-Signature |
| คีย์สำหรับการลงนาม | ความลับเว็บฮุกขององค์กร | ความลับเว็บฮุกขององค์กร |
ทั้งสองเส้นทางเป็นแบบ บล็อกการทำงาน — AI กำลังรอผลลัพธ์อยู่ระหว่างประโยค —
โดยมีระยะหมดเวลา 20 วินาที ให้ตัวจัดการทำงานอย่างรวดเร็ว สามารถใช้ร่วมกันได้:
ในการโทรที่องค์กรมี URL เว็บฮุก เครื่องมือที่มี endpoint จะถูก
เรียกใช้โดยตรง และเครื่องมือที่เหลือจะกลับไปใช้เว็บฮุก
การเรียกใช้ endpoint โดยตรง
เมื่อ 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 สองรายการ:
X-ThunderPhone-Signature— HMAC-SHA256 ของไบต์เนื้อหาคำขอ แบบตรงตัว โดยใช้ ข้อมูลลับ webhook ขององค์กร เป็นคีย์X-ThunderPhone-Call-ID— ID การโทรปัจจุบัน
ระบบจะตั้งค่า Content-Type: application/json เว้นแต่ endpoint.headers
ของคุณจะเขียนทับ — Content-Type แบบกำหนดเองจะมีผลเหนือกว่า
เนื้อหาคำขอ
สำหรับ POST / PUT / PATCH เนื้อหาจะมี เฉพาะ
อาร์กิวเมนต์ของเครื่องมือเท่านั้น (ไม่มีตัวห่อ) ซึ่งจัดลำดับแบบมาตรฐาน (เรียงคีย์ ตัวคั่นแบบกระชับ):
{"date":"2025-01-02","service":"consultation"}
สำหรับ GET / DELETE อาร์กิวเมนต์จะถูกส่งเป็น พารามิเตอร์ query
และเนื้อหาจะว่างเปล่า — จากนั้นลายเซ็นจะคำนวณจาก
สตริงไบต์ว่าง ดู
ยืนยันลายเซ็น webhook
การตอบกลับ
ส่งคืนการตอบกลับ JSON พร้อมผลลัพธ์ของเครื่องมือ:
{
"available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
"timezone": "America/Los_Angeles"
}
ระบบจะจัดรูปแบบการตอบกลับและส่งให้ AI เพื่อดำเนิน
การสนทนาต่อ การตอบกลับที่ไม่ใช่ JSON จะถูกห่อเป็น {"data": "<text>"}
การหมดเวลาและการเชื่อมต่อล้มเหลวจะถูกรายงานให้ AI เป็นข้อผิดพลาด เพื่อให้
เอเจนต์ขออภัยและดำเนินการต่อได้แทนที่จะหยุดค้าง
การส่งต่อในโหมด webhook
เครื่องมือที่ ไม่มี endpoint จะถูกส่งต่อไปยัง URL webhook แบบเดิม
ขององค์กรคุณในรูปแบบคำขอ telephony.tool (การโทรศัพท์) หรือ web.tool
(การเรียกผ่านเว็บ) ที่ลงลายเซ็นแล้ว ซึ่งต่างจาก การแจ้งเตือนสำหรับการตรวจสอบ
ที่ส่งไปยัง endpoint ของ 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 — เป็นสัญญาการตอบกลับเดียวกัน
กับการเรียกใช้ endpoint โดยตรง คำขอจะลงลายเซ็นด้วยข้อมูลลับ webhook ขององค์กร
บนเนื้อหาแบบดิบ เช่นเดียวกับ webhook อื่นทั้งหมด
การตรวจสอบลายเซ็น
การเรียกใช้เครื่องมือโดยตรงจะมีการลงลายเซ็นด้วยวิธีเดียวกับ webhook:
- HMAC-SHA256 บนไบต์ของเนื้อหาคำขอที่ตรงกันทุกประการ (JSON แบบมาตรฐาน — เรียงลำดับคีย์ ไม่มีช่องว่างเพิ่มเติม)
- ใช้ webhook secret ขององค์กรคุณเป็นคีย์
- เครื่องมือ
GET/DELETEลงลายเซ็นบนสตริงไบต์ว่าง
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 });
});
ตัวอย่างฉบับสมบูรณ์ — รวมถึงกรณีเนื้อหาว่างและข้อควรระวังเมื่อไม่มี secret — อยู่ใน ตรวจสอบลายเซ็น 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 จะขอข้อมูลที่จำเป็นจากผู้ใช้ก่อนเรียกใช้เครื่องมือ
ที่เกี่ยวข้อง
เครื่องมือที่แพลตฟอร์มจัดการให้สำหรับ HubSpot, Salesforce, Slack, Google Calendar, Google Sheets และ Cal.com โดยไม่ต้องมีปลายทาง
เชื่อมต่อเซิร์ฟเวอร์ MCP และให้เอเจนต์เรียกใช้เครื่องมือของเซิร์ฟเวอร์
การผสานรวม REST ที่ใช้ซ้ำได้ซึ่งคุณสามารถเชื่อมต่อกับเอเจนต์
ตัวช่วยตรวจสอบเดียวสำหรับ webhook และการเรียกใช้เครื่องมือ