ThunderPhone 2.0 đã chính thức ra mắt.Tự thiết lập, từ 2 xu/phút.Xem thông báo ra mắt

Function Tools

Công cụ hàm

Cung cấp 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 khi trò chuyện — truy xuất dữ liệu khách hàng, đặt lịch hẹn, cập nhật bản ghi — 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 các 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 tình trạng còn chỗ, đặ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

  1. Bạn định nghĩa công cụ bằng một lược đồ (các đối số mà công cụ chấp nhận)
  2. 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 các lệnh gọi công cụ trên webhook của tổ chức
  3. Trong cuộc gọi, tác nhân AI quyết định thời điểm sử dụng công cụ dựa trên cuộc hội thoại
  4. ThunderPhone gọi endpoint của bạn với các đối số của công cụ
  5. Phản hồi API của bạn được gửi lại cho tác nhân AI để tiếp tục cuộc hội thoại

Lược đồ công cụ

Mỗi công cụ tuân theo cấu trúc này:

{
  "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ườngLoạiBắt buộcMô tả
timeoutsốKhôngThờ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ườngLoạiBắt buộcMô tả
namechuỗiMã định danh duy nhất cho công cụ
descriptionchuỗiCho tác nhân AI biết khi nào cần sử dụng công cụ này
parametersđối tượngJSON Schema cho các đối số của công cụ

Cấu hình endpoint

TrườngLoạiBắt buộcMô tả
urlchuỗiURL endpoint API của bạn
methodchuỗiKhôngPhương thức HTTP (mặc định: POST)
headersđối tượngKhôngHeader tùy chỉnh cần bao gồm

Hai cách 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:

Công cụ endpointCông cụ không có endpoint
Nơi gửi yêu cầuTrực tiếp đến endpoint.urlURL webhook cũ của tổ chức bạn
Nội dungChỉ các đối số của công cụEnvelope telephony.tool / web.tool
Headerendpoint.headers của bạn + X-ThunderPhone-Call-ID + X-ThunderPhone-SignatureContent-Type + X-ThunderPhone-Signature
Khóa kýSecret webhook của tổ chứcSecret webhook của tổ chức

Cả hai cách đều chặn — tác nhân AI đang chờ kết quả ngay 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ữ trình xử lý nhanh. Bạn có thể kết hợp: trong cuộc gọi mà tổ chức có URL webhook, các công cụ có endpoint đượ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: abc123...
X-ThunderPhone-Call-ID: 987654321
X-Api-Key: your-api-key

Các header tùy chỉnh từ endpoint.headers của bạn luôn được đưa vào nguyên văn, cùng với hai header có không gian tên ThunderPhone:

  • X-ThunderPhone-Signature — HMAC-SHA256 của chính xác các byte trong phần thân yêu cầu, sử dụng secret webhook của tổ chức làm khóa
  • X-ThunderPhone-Call-ID — ID cuộc gọi hiện tại

Content-Type: application/json được đặt trừ khi endpoint.headers của bạn ghi đè nó — Content-Type tùy chỉnh được ưu tiên.

Phần thân yêu cầu

Với POST / PUT / PATCH, phần thân chỉ chứa các đối số của công cụ (không có lớp bao), được tuần tự hóa theo chuẩn (khóa được sắp xếp, dấu phân cách gọn):

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

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 — khi đó chữ ký đượ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 chứa 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 AI để tiếp tục cuộc trò chuyện. Phản hồi không phải JSON được bao trong {"data": "<text>"}; lỗi hết thời gian chờ và lỗi kết nối được báo cho AI dưới dạng lỗi, để tác nhân AI có thể xin lỗi và tiếp tục thay vì bị đình trệ.

Đ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à lần 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 trên phần thân thô, giống mọi webhook khác.


Xác minh chữ ký

Các lệnh gọi công cụ trực tiếp được ký giống như webhook:

  • HMAC-SHA256 trên chính xác byte của phần thân yêu cầu (JSON chuẩn hóa — khóa được sắp xếp, không có khoảng trắng thừa)
  • Dùng secret webhook của tổ chức bạn làm khóa
  • Công cụ GET / DELETE ký chuỗi byte rỗng
Python
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}
Node.js
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 });
});

Các hướng dẫn đầy đủ — bao gồm trường hợp phần thân rỗng và lưu ý khi không có secret — có trong Xác minh chữ ký webhook.


Ví dụ: Quy trình đặt lịch hoàn chỉnh

Dưới đây là 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 cần sử dụng công cụ. Hãy nêu rõ công cụ làm gì và khi nào nên 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ề thông tin tác nhân AI cần để tiếp tục cuộc hội thoại. Payload lớn làm chậm thời gian phản hồi.

Sử dụng trường bắt buộc một cách 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