ThunderPhone 2.0、提供開始。セルフサービスで、1分あたり2セントから。発表内容を見る

Developer cookbook

ツール連携(API)を構築

会話の途中でエージェントからAPIを呼び出せます。データベースの検索、チケットの作成、注文の照会が可能です。

ツール統合は、エージェントが通話中に呼び出せる再利用可能な HTTP エンドポイントです。ThunderPhone にツールの JSON スキーマ記述とエンドポイント URL を渡すと、エージェントは会話に基づいて呼び出すタイミングを判断し、ThunderPhone がそのサーバーから送信 HTTP リクエストを実行して、応答をエージェントに返します。

このガイドでは、天気検索ツールをエンドツーエンドで構築します。

ツールの構成

2 つの要素があります。

  1. スキーマ — ツールの機能と受け取る引数を LLM に伝える、OpenAI 形式の関数定義 ({type: "function", function: {name, description, parameters}})です。
  2. エンドポイント — LLM がツールを使用すると判断したときに、ThunderPhone のサーバーが呼び出す URL です。リクエストは JSON POST で、本文には LLM が選択した引数が含まれます。

1. エディターを選択

ダッシュボード

接続 → APIを開き、API 接続を作成または編集して、パラメーターエディターを JSON に切り替え、 そこに形式を追加します。

統合 API

POST /v1/integrations で仕様を作成するか、 PATCH /v1/integrations/{id} で更新します。

どちらの方法でも保存済みの統合が作成されます。保存後、その統合をエージェントに関連付けます。 Agents API には書き込み可能なインライン tools フィールドはありません。このガイドでは統合 API の方法を使用します。

2. 連携を作成する

curl -X POST https://api.thunderphone.com/v1/integrations \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Weather API",
    "spec": {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Return the current weather for a zip code.",
        "parameters": {
          "type": "object",
          "properties": {
            "zip": { "type": "string", "description": "5-digit US ZIP code" }
          },
          "required": ["zip"]
        }
      }
    },
    "endpoint_url":    "https://api.example.com/weather",
    "endpoint_method": "GET",
    "headers": [
      { "key": "X-Api-Key", "value": "your-provider-key" }
    ]
  }'

返された id(UUID)を保存します。ヘッダー値は書き込み専用です。レスポンスにはヘッダー名のみが表示され、後続の PATCH では value なしでヘッダーを送信することで、保存済みの値を維持できます。

アドレスパラメータに format: "email" を宣言する

メールアドレスを受け取るパラメータでは、スキーマにそのことを明記します。

"email": { "type": "string", "format": "email", "description": "The caller's email address" }

format は単なるヒントではありません。解決済みのメールスキーマでは、エンドポイントを呼び出す前に ThunderPhone が値の前後の空白を削除し、ドメインを小文字に変換し、独立した英単語 at、dot、underscore、dash、hyphen を対応する文字に変換します。また、@、.、_、- の直前と直後の空白を削除します。文字起こしにリテラルの @ がすでに含まれているかどうかにかかわらず、これらの単語の意味は同じです。"john dot smith at gmail dot com" は john.smith@gmail.com になります。

その他の内部空白は、暗黙的に結合されるのではなく拒否されます。音声による区切り単語は英語にのみ対応しており、英語以外または認識されない空白区切りの形式は安全側に倒して失敗します。有効な国際化ドメインおよび SMTPUTF8 ローカルパートは受け入れられます。パーサーによる正規化後も、Punycode 入力は Punycode のまま、Unicode ドメイン入力は Unicode のまま維持されるため、API は発信者が指定した一般的な表現を受け取ります。最終的な値が無効な場合、ツールは呼び出されません。エージェントは invalid_email_argument を受け取り、発信者にスペルを確認してリテラルのアドレスを再送するよう指示されます。

省略された任意のメールは変更されません。プロパティが任意または nullable の場合、null、空文字列、空白のみの文字列も変更されません。同じ値でも、必須かつ非 nullable のメールでは拒否されます。

#/$defs/email や #/definitions/email などのローカルスキーマ参照に加え、anyOf、oneOf、allOf も、循環および深さの制限付きで検査されます。非ローカルまたは解決不能な $ref は既知の適用制限であり、使用可能なスキーマがツールスナップショットにない呼び出しと同様に、変更されずに渡されます。ゲートを適用する必要がある場合は、メールスキーマをローカルに保持してください。

強制されるメール形式を持たないパラメータは、モデルが生成したとおりにそのまま渡されます。

対応する形式は date-time、time、date、duration、email、hostname、ipv4、ipv6、uuid です。現時点で正規化および強制されるのは email のみです。

3. エンドポイントをサンドボックスでテストする

連携をエージェントにリンクする前に、ThunderPhone のサーバーから署名付きリクエストを送信して接続性を確認します。

curl -X POST https://api.thunderphone.com/v1/integrations/test-request \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url":    "https://api.example.com/weather?zip=94110",
    "method": "GET",
    "headers": { "X-Api-Key": "your-provider-key" }
  }'
Response
{
  "ok": true,
  "status": 200,
  "elapsed_ms": 187,
  "response_headers": { "content-type": "application/json" },
  "response_preview": "{\"temperature_f\": 64, ...}"
}

このテストは ThunderPhone の SSRF 保護も強化します。localhost またはプライベート IP 範囲へのリクエストは 400 code=url_not_allowed を返します。

4. エージェントに連携をリンクする

エージェントを作成または更新するときに、integration_ids を使用して紐付けます。

curl -X PATCH https://api.thunderphone.com/v1/agents/12 \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "integration_ids": ["f9b5a1a4-..."]
  }'

1つのエージェントに複数の連携をリンクできます。エージェントのプロンプトから名前で参照できます。たとえば、「発信者が天候について尋ねたときは get_weather を使用する」のように指定します。スキーマの説明から暗黙的に検出させることもできます。

5. エンドポイントを実装する

エージェントがツールを呼び出すと、ThunderPhone は設定した endpoint_url に署名付き POST リクエストを送信します。

POST /weather HTTP/1.1
Host: api.example.com
X-Api-Key: your-provider-key
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<HMAC-SHA256 hex>
X-ThunderPhone-Signature: <HMAC-SHA256 hex>
X-ThunderPhone-Call-ID: 987654321
Content-Type: application/json

{"zip": "94110"}

サーバーは、LLM に返される JSON を応答として返します。

{"temperature_f": 64, "condition": "Partly cloudy", "wind_mph": 8}

LLM はその応答を取り込み、発信者に自然な要約を読み上げます。

6. ループをテストする

エージェントに対してマイクセッションを実行し、ツールが処理する質問(「94110の天気は?」)をします。通話の文字起こしには、往復のすべての処理が表示されます。

{
  "call_id": 987654321,
  "transcripts": [
    { "role": "user",
      "content": "What's the weather in 94110?" },
    { "role": "tool_call",
      "content": "{\"tool_call\": \"get_weather\", \"arguments\": {\"zip\": \"94110\"}}" },
    { "role": "tool_response",
      "content": "{\"tool_name\": \"get_weather\", \"response\": {\"temperature_f\": 64, \"condition\": \"Partly cloudy\"}}" },
    { "role": "agent",
      "content": "It's 64 degrees and partly cloudy." }
  ]
}

これはGET /v1/calls/{call_id}/transcriptから取得できます。エントリごとのタイミングと音声オフセットを含む生イベントストリームは、GET /v1/calls/{call_id}/historyで確認できます。

通話一覧および分析画面では、ThunderPhoneはテスト通話を実際の通話トラフィックと分けて報告します。Browser Talkのテストとシミュレーションでは実際の電話番号を再利用でき、着信のように見える場合がありますが、本番通話数、評価、または問題指標には含まれません。

よくある注意点

エージェントがツールを呼び出さない

LLM はツールの説明に基づいて判断します。発信者の質問が説明と一致しない場合、モデルはツールを呼び出しません。説明を具体化して一般的な同義語や表現を追加するか、エージェントのプロンプトで明示してください(「発信者が天気について尋ねたら、get_weather を使用する。」)。

ツールが返すデータ量が多すぎる

6 kB を超える応答は、文字起こしプレビューで切り詰められます。レコード全体ではなく、LLM が必要とするフィールドだけを返してください。

タイムアウト

ツールエンドポイントのデフォルトのタイムアウトは10秒です。より長い時間が必要な場合は、非同期で処理してください。{"status": "pending", "request_id": "..."} を返し、別のツール呼び出しで結果を取得します。

バージョニング

連携に対するすべての PATCH は新しいリビジョンを作成します。GET /v1/integrations/{id}/versionsを確認すると、誰が何を変更したかを確認できます。ツールのスキーマを破損した場合は、古いスナップショットを手動で PATCH し直してロールバックできます。


次のステップ