ページの一覧

ドキュメント

通話中の道具

通話の途中で AI に自社の API を呼ばせる方法。道具の定義、署名付きの呼び出し、確認してから使う道具、時間切れ、二重実行の防止。

Markdown で見る

道具(tools)を会話の設定に書くと、AI は通話の途中で自社の API を呼び、その結果を使って答えます。予約の空きを調べる、電話番号で顧客を探す、予約を入れる、といった使い方ができます。連携先を問わない汎用の形で、サービスの側は 1 本の URL で受けて、道具の名前で振り分けても構いません。

Google カレンダーや kintone などとつなぐだけなら、道具を自作せずに連携を使えます。

道具の定義#

json
"tools": [
  {
    "name": "find_free_slots",
    "description": "予約の空きを調べる。相手が希望の日を言ったときに使う",
    "url": "https://saas.example.com/voicast/tools",
    "parameters": {
      "properties": {
        "date": { "type": "string", "description": "希望の日(YYYY-MM-DD)" },
        "menu": { "type": "string", "enum": ["検診", "治療", "クリーニング"] }
      },
      "required": ["date"]
    }
  },
  {
    "name": "create_booking",
    "description": "予約を入れる。空きを伝えて、相手が日時を選んだら使う",
    "url": "https://saas.example.com/voicast/tools",
    "confirm": true,
    "parameters": {
      "properties": {
        "start": { "type": "string", "description": "開始の日時(ISO 8601)" },
        "name": { "type": "string" },
        "phone": { "type": "string" }
      },
      "required": ["start", "name"]
    }
  }
]
項目 必須 内容
name ○ 英小文字で始まり、英小文字・数字・_ の 40 文字まで。finish は使えません。連携の道具の名前と重ならないこと
description ○ いつ使うか(500 文字まで)。AI はこれを読んで使うかを決めます
url ○ 呼ぶ先。https で、インターネットから引ける名前だけ(IP アドレスの直書き・localhost・社内向けの名前は不可)。500 文字まで
parameters 引数。properties は 20 個まで、型は string・number・integer・boolean、enum は 50 個まで
confirm true なら、AI は内容(日時・名前など)を読み上げ、相手が同意してから使います。予約・取り消しなど書き換える道具に付けます

道具は 1 つの会話の設定に 10 個までです。AI は道具を使う前に「お調べしますので、少々お待ちください」と一言伝えます。

届く要求#

AI が道具を使うと決めると、voicast が url に POST します。

POST https://saas.example.com/voicast/tools
content-type: application/json
user-agent: voicast-tools/1
webhook-id: msg_6f1c…
webhook-timestamp: 1790000042
webhook-signature: v1,Rk9x…
idempotency-key: toolu_01AbC…

{
  "type": "tool.call",
  "tool": "find_free_slots",
  "tool_call_id": "toolu_01AbC…",
  "arguments": { "date": "2026-10-07" },
  "call": {
    "id": "call_…",
    "channel": "phone",
    "provider_call_id": "CA…",
    "from": "+819012345678",
    "tenant": { "id": "ten_…", "external_id": "clinic-123" }
  }
}
項目 内容
tool 道具の名前
tool_call_id 呼び出しごとに 1 つの ID。見出しの idempotency-key も同じ
arguments AI が決めた引数
call.tenant.external_id 自社の顧客の ID。どの会社の予約表を見るかを決めるのに使います
call.from かけてきた番号(電話で、TwiML の from を渡したとき)

返すもの#

2xx と JSON を返します。JSON はそのまま AI に渡ります。

json
{ "slots": ["2026-10-07T10:00:00+09:00", "2026-10-07T14:30:00+09:00"] }
  • 待つのは 8 秒までです。超えると失敗として扱います。
  • 2xx 以外の応答・JSON でない応答・時間切れ・接続の失敗は、AI に失敗として伝わり、AI はおわびして担当者からの折り返しにします。
  • 応答の JSON が長すぎると、4,000 文字で切って渡します。AI に要る項目だけを返します。
  • 別の URL へ移す応答(3xx)には付いて行きません。
  • 「空きが無い」「見つからない」など、想定した結果は 2xx で { "slots": [] } や { "found": false, "message": "その番号の方は見つかりませんでした" } のように返すと、AI が自然に伝えられます。

署名の検証#

要求には Standard Webhooks の署名が付いています。確かめ方とコード(Node.js・Python・PHP・Go)は 結果の受け取り と同じで、鍵だけが違います。

署名の鍵(whsec_…)は環境ごとに 1 つです。管理画面の環境の設定(管理者以上)、API の GET /v1/tools/secret、MCP の get_tools_secret で確かめ、「作り直す」・POST /v1/tools/secret/rotate・rotate_tools_secret で作り直せます。作り直してから 24 時間は、古い鍵の署名も並べて送るので、そのあいだに自社の側の鍵を差し替えます(ライブラリには新旧の両方の鍵を渡せます)。

js
import express from 'express';
import { Webhook } from 'standardwebhooks';

const wh = new Webhook(process.env.VOICAST_TOOLS_SECRET); // whsec_…
const app = express();

app.post('/voicast/tools', express.raw({ type: 'application/json' }), async (req, res) => {
  let body;
  try {
    body = wh.verify(req.body, req.headers);
  } catch {
    return res.sendStatus(401);
  }
  const clinic = body.call.tenant.external_id;
  switch (body.tool) {
    case 'find_free_slots':
      return res.json({ slots: await findSlots(clinic, body.arguments.date) });
    case 'create_booking':
      // 同じ tool_call_id は 1 回だけ実行する
      return res.json(await createBookingOnce(body.tool_call_id, clinic, body.arguments));
    default:
      return res.status(404).json({ error: 'unknown tool' });
  }
});

二重実行の防止#

予約を入れる道具などは、同じ要求が 2 度届いても 1 回だけ実行します。tool_call_id(見出しの idempotency-key)を保存し、同じ ID には前の結果を返します。

js
async function createBookingOnce(toolCallId, clinic, args) {
  const done = await db.toolRuns.get(toolCallId);
  if (done) return done.result;
  const result = await createBooking(clinic, args);
  await db.toolRuns.put(toolCallId, { result });
  return result;
}

確認してから使う道具#

confirm: true の道具は、AI が内容を読み上げ、相手が「はい」と答えてから使います。予約・変更・取り消し・登録など、相手の情報を書き換える道具には必ず付けます。読み取るだけの道具(空きを調べる・顧客を探す)には要りません。

気をつけること#

  • 道具の結果に無いことを AI は答えません。答えてほしい情報(料金・場所など)は応答に入れるか、会話の設定の faq に書きます。
  • 個人情報は、AI に要る分だけを返します(生年月日などをそのまま返さない)。
  • 呼ばれる URL は、署名を確かめるまで何もしないようにします。署名の無い要求・古い webhook-timestamp の要求は断ります。