---
title: "関数ツール"
description: "型付きパラメータを使用して、会話中に外部APIを呼び出す関数ツールをAIエージェントに提供します。顧客データの取得、予約の登録、レコードの更新が可能です。"
---

関数ツールにより、AIエージェントは通話中に外部APIを呼び出せます。顧客データの検索、空き状況の確認、予約の登録、バックエンドでサポートされる任意のアクションの実行に使用できます。

## 仕組み

1. スキーマ（ツールが受け付ける引数）を使用してツールを定義します
2. `endpoint` 設定（ThunderPhone がAPIを呼び出す場所）を指定します。指定しない場合は、組織のWebhookでツール呼び出しを受信します
3. 通話中、AIは会話に基づいてツールを使用するタイミングを判断します
4. ThunderPhone がツール引数とともにエンドポイントを呼び出します
5. APIレスポンスがAIに返され、会話を継続します

| 機能 | 実行場所 | 設定 |
| --- | --- | --- |
| [組み込みツール](/ja/guides/built-in-tools) | ThunderPhone | プロンプト指示。一部のツールにはエージェント設定も必要です |
| [アプリ接続](/ja/guides/connect-apps) | ThunderPhone と接続済みプロバイダー | アカウントを接続し、承認済みアクションを追加します |
| [API接続](/ja/guides/api-connections) と関数ツール | 独自のHTTP API | エンドポイントとスキーマを定義するか、Webhook経由で関数呼び出しを受信します |
| [MCPサーバー](/ja/guides/mcp-servers) | リモートMCPサーバー | サーバーを追加し、そのツールを検出してエージェントに追加します |

---

## ツールスキーマ

各ツールは次の構造に従います。

```json
{
  "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` | 文字列 | はい | このツールを使用するタイミングをAIに説明します |
| `parameters` | オブジェクト | はい | ツール引数のJSONスキーマ |

### エンドポイント設定

| フィールド | 型 | 必須 | 説明 |
|-------|------|----------|-------------|
| `url` | 文字列 | はい | APIエンドポイントURL |
| `method` | 文字列 | いいえ | HTTPメソッド（デフォルト: `POST`） |
| `headers` | オブジェクト | いいえ | 含めるカスタムヘッダー |

<Note>
  `endpoint` 設定はAIモデルに送信されません。ThunderPhone がツール呼び出しを実行するためにのみ使用します。
</Note>

---

## 2つの呼び出し経路

サーバーが受信するリクエストは、ツールに
`endpoint` があるかどうかによって異なります。

| | `endpoint` **あり**のツール | `endpoint` **なし**のツール |
|---|---|---|
| リクエストの送信先 | `endpoint.url` に直接送信 | 組織の[レガシー Webhook URL](/api-reference/organizations#legacy-single-url-webhook) |
| 本文 | 選択したリクエスト形式（デフォルトでは引数のみ） | `telephony.tool` / `web.tool` エンベロープ |
| ヘッダー | `endpoint.headers` + `X-ThunderPhone-Call-ID` + `X-ThunderPhone-Signature-V2` + `X-ThunderPhone-Signature` | `Content-Type` + `X-ThunderPhone-Signature-V2` + `X-ThunderPhone-Signature` |
| 署名キー | 組織の Webhook シークレット | 組織の Webhook シークレット |

どちらの経路も**ブロッキング**です。AIは文の途中で結果を
待機します。デフォルトのタイムアウトは**20 秒**です。より長い実行を許可するには、ツールのトップレベルの
`timeout` を設定します。上限はプラットフォームの**180 秒**です。
ハンドラーは高速に保ってください。混在させることも可能です。
組織にWebhook URLがある通話では、`endpoint` を持つツールは
直接呼び出され、その他はWebhookにフォールバックします。

## 直接エンドポイント呼び出し

AI が `endpoint` を持つツールを呼び出すと、ThunderPhone は
設定した URL にリクエストを送信します。

### リクエストヘッダー

```http
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署名を検証する](/ja/guides/verify-webhook-signatures)を参照してください。
- `X-ThunderPhone-Signature` — リクエスト本文の正確なバイト列に対する従来のHMAC-SHA256。同じキーを使用し、既存の検証ツール向けに引き続き送信されます。
- `X-ThunderPhone-Call-ID` — 現在の通話ID

`Content-Type: application/json` は、`endpoint.headers` で
上書きされない限り設定されます。カスタム `Content-Type` が優先されます。

<Warning>
  署名には
  [`GET /v1/webhook`](/api-reference/organizations#legacy-single-url-webhook)
  の組織レベルの Webhook シークレットが使用されます。
  組織でレガシー Webhook を一度も設定していない場合、シークレットは存在せず、
  ツール呼び出しには `X-ThunderPhone-Call-ID` **のみが**含まれます。署名がない場合に
  即座に失敗するハンドラーでは、これらの呼び出しが拒否されます。
  シークレットを取得するにはレガシー Webhook を設定するか、
  独自の共有シークレットを `endpoint.headers` に指定してください。
</Warning>

### リクエスト形式

各 API 接続には `request_format` 設定があります。インポートしたツールで、そのプラットフォーム向けに構築されたエンドポイントを引き続き呼び出す場合は、`vapi` または
`retell` を選択します。新規および既存の接続のデフォルトは `standard` です。

`POST` / `PUT` / `PATCH` では、ThunderPhone は選択された
エンベロープを正規形式でシリアライズします（キーをソートし、区切り文字を圧縮）。両方の署名は、この完全に一致するバイト列を対象とします。

#### Standard

`standard` はラッパーなしで、ツール引数のみを送信します。これはデフォルトであり、ThunderPhone の元のリクエスト本文をバイト単位でそのまま保持します。

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

#### Vapi

`vapi` は、Vapi のドキュメントに記載された形式で Vapi の [`tool-calls` サーバーメッセージ](https://docs.vapi.ai/tools/custom-tools)
を送信します。`call.id` は ThunderPhone の通話 ID です。ツール呼び出しの `id` は呼び出しごとに一意であり、エンドポイントの結果との照合に再利用されます。通話は `toolCallList` と
`toolWithToolCallList` の両方に表示され、`function.arguments` は JSON オブジェクトです。

```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" }
          }
        }
      }
    ]
  }
}
```

`assistant`、`artifact`、完全な `call` オブジェクトなどの Vapi 専用フィールドは送信されません。

対応するツール呼び出し ID を含む Vapi のレスポンスエンベロープを返します。

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

ThunderPhone は、ラップ解除された `result` 文字列を AI に渡します。すでに JSON 値を返すエンドポイントとの互換性のため、JSON 値も受け入れます。`result` の代わりに `error` がある場合は、ツールエラーとして AI に返されます。エンドポイントが不正な形式または不一致の Vapi エンベロープを返した場合、ThunderPhone は警告をログに記録し、通話を失敗させるのではなく標準のレスポンス解析にフォールバックします。

#### Retell

`retell` は、[Retell カスタム関数](https://docs.retellai.com/build/single-multi-prompt/custom-function)向けにドキュメント化されたリクエストを送信します。

```json
{
  "name": "search_appointments",
  "call": { "call_id": "987654321" },
  "args": { "date": "2025-01-02", "service": "consultation" }
}
```

`call` オブジェクトに含まれるのは `call_id` のみです。ThunderPhone は、ディスパッチ時に正確に提供できない `agent_id`、`transcript`、`transcript_object`、`metadata`、動的変数などの Retell 固有フィールドを含めません。Retell は任意のレスポンス本文を受け入れます。ThunderPhone は標準のレスポンス処理でそのまま渡します。

`GET` / `DELETE` では、引数は**クエリパラメータ**として送信され、`request_format` にかかわらず本文は空です。V2 署名は送信されたクエリ文字列を対象とし、レガシー署名は空のバイト文字列に対して計算されます。
[Webhook 署名を検証する](/ja/guides/verify-webhook-signatures)を参照してください。

### レスポンス

ツール結果を含む JSON レスポンスを返します。

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

レスポンスは整形され、会話を継続するために AI に提供されます。
JSON 以外のレスポンスは `{"data": "<text>"}` としてラップされます。
タイムアウトと接続障害はエラーとして AI に報告されるため、
エージェントは停止するのではなく、謝罪して次に進むことができます。

## Webhook モードのディスパッチ

`endpoint` を**持たない**ツールは、組織のレガシー Webhook URL に、
署名付きの `telephony.tool`（電話通話）または `web.tool`
（Web 通話）リクエストとしてディスパッチされます。実行後に Webhook エンドポイントへ配信される
[監査通知](/ja/webhooks/events)とは異なり、このリクエスト**自体が**
実行です。HTTP レスポンスがツール結果となります。

```json
{
  "type": "telephony.tool",
  "data": {
    "call_id": 987654321,
    "tool_name": "search_appointments",
    "arguments": { "date": "2026-04-21" },
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  }
}
```

`web.tool` には `from_number` / `to_number` の代わりに `origin_domain` が含まれます。ツールの結果は JSON で返します。レスポンスの契約は直接エンドポイント呼び出しと同じです。リクエストには、他のすべての webhook と同様に、両方の署名ヘッダーを使用して組織の webhook シークレットで署名されます。

<Note>
  登録済みの [Webhook エンドポイント](/ja/webhooks/endpoints)も、各ツールの実行後に
  ブロックしない `telephony.tool` / `web.tool` **通知を追加で**
  受信します。どのパスで実行されたかにかかわらず、ツールのレスポンスも含まれます。
  監査証跡に役立ちます。[イベントカタログ](/ja/webhooks/events)を参照してください。
</Note>

---

## 署名の検証

直接ツール呼び出しは、組織のWebhookシークレットをキーとして、Webhookと同じ方法で署名されます。

- `X-ThunderPhone-Signature-V2` は、送信されたタイムスタンプ、メソッド、パス、クエリ、および正確な本文バイト列に署名します。タイムスタンプが現在時刻から5分を超えているリクエストは拒否してください。
- `X-ThunderPhone-Signature`（レガシー）は本文バイト列のみ、`GET` / `DELETE` では空のバイト文字列に署名します。現在も変更なく送信されます。

SDKヘルパーは、5分の有効期間を含めてV2ヘッダーを検証します。

<CodeGroup>
```python 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}
```

```javascript 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 });
});
```
</CodeGroup>

署名対象文字列、実例、SDKを使わないコード、`GET` / `DELETE` のケース、シークレットがない場合の注意事項を含む完全な手順は、[Webhook署名を検証する](/ja/guides/verify-webhook-signatures)を参照してください。

---

## 例: 完全な予約フロー

完全な予約システム用のツールセットは次のとおりです。

```json
{
  "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" }
      }
    }
  ]
}
```

---

## ベストプラクティス

<AccordionGroup>
  <Accordion title="明確な説明を記述する">
    `description` フィールドは、AI がツールを使用する**タイミング**を理解するのに役立ちます。ツールの機能と適切な使用タイミングを具体的に記述してください。
  </Accordion>

  <Accordion title="エラーを適切に処理する">
    汎用的な 500 エラーではなく、AI が理解できるエラーメッセージを返してください: `{"error": "No slots available for that date"}`。
  </Accordion>

  <Accordion title="レスポンスを簡潔に保つ">
    AI が会話を続けるために必要な情報だけを返してください。大きなペイロードは応答時間を遅くします。
  </Accordion>

  <Accordion title="必須フィールドを適切に使用する">
    本当に必要な場合にのみフィールドを `required` として指定してください。AI はツールを呼び出す前に、発信者に必須情報を尋ねます。
  </Accordion>
</AccordionGroup>

---

## 関連項目

<CardGroup cols={2}>
  <Card title="組み込みツール" icon="wrench" href="/ja/guides/built-in-tools">
    エンドポイントを定義せずに、プラットフォーム管理の通話アクションをプロンプトで指定します。
  </Card>
  <Card title="アプリ接続" icon="plug" href="/ja/guides/connect-apps">
    HubSpot、Salesforce、Slack、Google
    Calendar、Google Sheets、Cal.com 向けのプラットフォーム管理ツール。エンドポイントは不要です。
  </Card>
  <Card title="MCP サーバー" icon="server" href="/ja/guides/mcp-servers">
    MCP サーバーを接続し、エージェントがそのツールを呼び出せるようにします。
  </Card>
  <Card title="API 接続" icon="code" href="/ja/guides/api-connections">
    エージェントに接続できる再利用可能な REST 統合です。
  </Card>
  <Card title="Webhook 署名を検証する" icon="shield-check" href="/ja/guides/verify-webhook-signatures">
    Webhook とツール呼び出しに対応する単一の検証ヘルパーです。
  </Card>
</CardGroup>
