HN 日本語サマリー

← 一覧へ戻る
AI・機械学習

アウトバウンドSIP通話がすべての組織で利用可能になりました

Outbound SIP calling is now available to all organizations (developers.openai.com)

44 pointsby arbayi32 コメント

要約

OpenAIは、GPT-Liveを通じて電話通信とSIP(セッション開始プロトコル)の統合を発表しました。これにより、SIPトランクを介してGPT-Liveと電話の着信・発信通話を接続できるようになります。プロバイダーが通話オーディオをOpenAIと交換し、バックエンドがセッション設定やビジネスロジックを処理します。この記事では、着信通話の処理方法と、アウトバウンド通話の発信方法について詳しく説明しています。

全文翻訳

テレフォニーとSIP

SIPトランクを介して、着信および発信電話をGPT-Liveに接続します。プロバイダーはOpenAIと通話オーディオを交換し、バックエンドはセッション構成、通話制御、およびビジネスロジックを処理します。SIPシグナリングはTLSを使用し、GPT-Liveは通話オーディオにSRTPを必要とします。

  • 着信通話を処理する: incoming-call webhookを受信し、通話を受け入れるか拒否します。
  • 発信通話をかける: プロバイダー経由で電話番号を発信するセッションを作成します。着信通話 webhook や受け入れリクエストは不要です。

バックエンドがセッションイベントを受信したりコマンドを送信したりする必要がある場合は、サイドバンド接続を使用します。SIPがオーディオを伝送する間、既存の会話にアタッチされます。重複する webhook配信や複数の接続で観測されたイベントがツールを二重に実行しないように、各アクションに1つのハンドラーを割り当ててください。

着信通話を処理する

SIPプロバイダーからの着信電話をOpenAIプロジェクトにルーティングします。バックエンドはwebhookを受信し、各通話を受け入れるかどうかを決定します。

電話番号とトランクの構成

TwilioなどのSIPトランキングプロバイダーを使用して、電話番号をGPT-Liveに接続します。プロバイダーから番号を購入した後:

  1. OpenAI設定でプロジェクトを開きます。「Webhooks」の下で、live.transport.incoming を購読するwebhookエンドポイントを作成します。
  2. 「General」の下でプロジェクトIDを見つけます。proj_で始まります。webhookを所有するのと同じプロジェクトを使用します。
  3. プロバイダーのSIPトランクを sip:$PROJECT_ID@sip.api.openai.com;transport=tls にポイントします。欧州のデータレジデンシーの場合は、代わりに sip:$PROJECT_ID@sip-eu.api.openai.com;transport=tls を使用します。

このフローを使用する前に、プロジェクトでGPT-Live SIPサポートが有効になっており、プロバイダーのSIPトランクがそのプロジェクトにルーティングされていることを確認してください。

着信通話の受信

OpenAIがプロジェクトの通話を受信すると、live.transport.incoming イベントをwebhookエンドポイントに送信します。webhook署名を検証し、配信を重複排除してから、通話を受け入れるか拒否します。

webhookは data.type: "sip" でSIP通話を識別し、data.session_id を提供します。このセッションIDは、すべてのLive通話アクションで変更せずに使用します。data.sip_headers は信頼できない発信者メタデータとして扱い、認証としては使用しません。

例として、webhook本文は次のようになります。

{
  "object": "event",
  "id": "evt_abc123",
  "type": "live.transport.incoming",
  "created_at": 1750287018,
  "data": {
    "type": "sip",
    "session_id": "live_123",
    "sip_headers": [
      { "name": "From", "value": "sip:+14155550123@sip.example.com" },
      { "name": "To", "value": "sip:+18005550100@sip.example.com" },
      { "name": "Call-ID", "value": "call-123@example.com" }
    ]
  }
}

created_at フィールドは秒単位のUnixタイムスタンプです。配信ヘッダーには、重複排除のための webhook-id、配信試行のための webhook-timestamp、および署名検証のための webhook-signature が含まれます。サーバーサイドハンドラーについては、webhook検証例を参照してください。

既存の統合では、data.type がない非推奨の live.call.incoming イベントを引き続き受信する場合があります。移行中は、両方の名前を処理し、レガシー配信と再試行が完了するまで古いサブスクリプションを保持してください。同じ保留中の通話は、Realtime webhookを発生させることもあります。両方のAPIで受け入れるのではなく、受け入れ/拒否の決定に1つのハンドラーを割り当ててください。

通話の受け入れまたは拒否

アプリケーションの承認およびルーティングルールを適用します。通話を受け入れるには、トップレベルの session オブジェクトを含む認証済みの POST /v1/live/sessions/{session_id}/accept リクエストを送信します。

{
  "session": {
    "type": "live",
    "model": "gpt-live-1",
    "instructions": "You are answering an inbound support call.",
    "audio": { "output": { "voice": "marin" } },
    "delegation": { "type": "client" }
  }
}

通話制御リクエストには、信頼できるバックエンドから Authorization: Bearer $OPENAI_API_KEY を使用します。受け入れ時に音声と委任モードを選択します。SIPはオーディオフォーマットをネゴシエートするため、audio.format は省略します。例ではクライアント委任を選択しています。バックエンドは委任された作業を処理する必要があります。委任とツールを参照してください。

受け入れが成功すると、セッション初期化後に空のボディで 200 OK が返されます。通話が受け入れられたと見なす前に、HTTPエラーを処理してください。

通話を拒否するには、{ "status_code": 486 }(ビジーの場合)のようなSIPステータスを持つ POST /v1/live/sessions/{session_id}/reject を送信します。ステータスは300から699の間の整数である必要があります。最初に行われた受け入れまたは拒否の決定が優先されます。後続の競合する決定は decision_already_made を返します。

バックエンドの接続

受け入れ後、wss://api.openai.com/v1/live/sessions/{session_id}/attach でサイドバンドWebSocketを接続します。受け入れられたセッションIDと、同じプロジェクト認証および接続ヘッダーを使用します。session.start を再度送信しないでください。

SIPは通話オーディオを伝送します。サイドバンドは、トランスクリプト、委任、ツール、コマンド、および反射オーディオに使用します。複数の接続がイベントを観測した場合でも、各サイドエフェクトに1つの所有者を選択してください。

接続後、アクティブな通話制御を使用して、キーパッドトーンの送信、通話の転送、または切断を行います。

発信通話をかける

セッションの作成を介して、SIPプロバイダー経由で電話番号に電話をかけます。プロバイダーが電話ネットワーク接続を処理し、GPT-Liveが会話を伝送します。

アウトバウンドSIP通話は、Realtime APIの通話作成エンドポイントではなく、Live APIを通じてすべての組織で利用可能です。

トランクの構成

TLSシグナリングとSDES-SRTPメディアをサポートするトランクを使用します。電話をかける前に、プロバイダーの設定でSRTPを有効にしてください。

各リクエストでトランク構成を指定します。

フィールド値
transport.destinationE.164形式の電話番号(例: +14155550123)。SIP URI宛先はサポートされていません。
transport.trunk.provider_urlプロバイダーのエンドポイント(例: sips:sip.example.com:5061)。デフォルトポートは 5061 です。;transport=tcp はオプションです。
transport.trunk.auth.typeSIP Digest認証の場合は digest。
transport.trunk.auth.usernameプロバイダーのSIPユーザー名。
transport.trunk.auth.passwordプロバイダーのSIPパスワード。
transport.trunk.caller_numberプロバイダーに送信する発信者電話番号(E.164形式)。

プロバイダーのエンドポイントはTLSシグナリングに sips: を使用する必要があります。URLに認証情報、パス、URIヘッダー、その他のURIパラメータを含めないでください。ローカルホスト名およびリテラルのプライベートまたはローカルIPアドレスは拒否されます。OpenAI APIキーとSIP認証情報はサーバーに保管してください。

セッションの作成

セッション構成と transport.type: "sip" を指定して POST /v1/live/sessions を送信します。セッション作成時に音声と委任モードを選択します。SIPはオーディオフォーマットをネゴシエートするため、audio.format は省略します。

この例では curl と jq を使用しています。サーバー環境で OPENAI_API_KEY、SIP_USERNAME、SIP_PASSWORD を設定し、例のプロバイダーエンドポイントと電話番号を自分の値に置き換えてください。例ではクライアント委任を選択しています。バックエンドは委任された作業を処理する必要があります。

jq -n \
  --arg username "$SIP_USERNAME" \
  --arg password "$SIP_PASSWORD" \
  '{ 
    "session": {
      "model": "gpt-live-1",
      "instructions": "Help the user schedule an appointment.",
      "audio": { "output": { "voice": "marin" } },
      "delegation": { "type": "client" }
    },
    "transport": {
      "type": "sip",
      "destination": "+14155550123",
      "trunk": {
        "provider_url": "sips:sip.example.com:5061",
        "auth": {
          "type": "digest",
          "username": $username,
          "password": $password
        },
        "caller_number": "+14155550100"
      }
    }
  }' | curl https://api.openai.com/v1/live/sessions \
    -H "Authorization: Bearer $OPENAI_API_KEY" \
    -H "Content-Type: application/json" \
    --data-binary @-

リクエストは、セッション初期化後に 200 OK を返します。

{
  "session": { "id": "live_123" },
  "transport": { "type": "sip" }
}

この応答は、通話が応答されたことを意味するものではありません。SDPやトランク認証情報は含まれていません。サイドバンド接続と通話制御のために session.id を変更せずに保持してください。アウトバウンド通話には、着信通話 webhook や受け入れリクエストは必要ありません。

通話の監視と終了

wss://api.openai.com/v1/live/sessions/{session_id}/attach にOpenAI APIキーを使用してバックエンドを接続します。session.start を再度送信しないでください。サイドバンド接続は、SIPがオーディオを伝送する間、会話イベント、委任された作業、および通話の進行状況を伝送します。

イベント意味
transport.ringingプロバイダーが呼び出し中または早期メディアを報告します。
transport.answered通話に応答され、メディアが確立されました。
transport.failedセッション初期化後に通話設定が失敗しました。error.code と error.message を確認してください。

各通話進行イベントには event_id と session_id が含まれます。作成後すぐに接続してください。サイドバンドは直前の3秒間のイベントのみをリプレイするため、後で接続すると以前の通話進行を見逃す可能性があります。リプレイされたイベントは元のイベントIDを保持します。イベントは event_id で重複排除してください。

接続後、アクティブな通話制御を使用して、キーパッドトーンの送信、通話の転送、または切断を行います。

制限とエラーの処理

呼び出し中は3分、接続された通話は2時間に制限されます。これらの制限は作成リクエストで構成できません。

無効なセッション構成は、作成リクエストで返されます。トランスポート設定の失敗は 502 を返し、初期化タイムアウトは 504 を返します。作成が成功した後、非同期設定の失敗については transport.failed を監視してください。

アクティブな通話の管理

これらの制御は、着信および発信SIP通話の両方に適用されます。

キーパッドイベントの送受信

発信者がキーを押すと、サイドバンドは transport.dtmf.received を受信します。DTMFトーンをSIPトランクに送信するには、サイドバンドWebSocket経由で transport.dtmf.send クライアントイベントを送信します。トーンが正常に送信された後、サイドバンドは transport.dtmf.sent を受信します。

event_id を指定すると、通知には client_event_id として含まれます。失敗した場合は、error.client_event_id を持つ error イベントが返されます。

通話の転送または終了

通話を転送するには、宛先として { "target_uri": "sip:agent@example.com" } を指定して POST /v1/live/sessions/{session_id}/refer を送信します。切断するには、リクエストボディなしで POST /v1/live/sessions/{session_id}/hangup を送信します。どちらも成功すると、空のボディで 200 OK を返します。

転送先はSIPの Refer-To ヘッダーとなり、tel:+14155550123 のような電話URIや sip:agent@example.com のようなSIP URIになります。プロバイダーが残りの転送フローを処理します。

使用状況と正常なクローズで使用状況のレポートとクローズ理由を確認するために、session.closed が最終的な使用状況を提供するまでサイドバンドを開いたままにしてください。

SIPネットワーク要件

GPT-Live SIP通話は、シグナリングとメディアに別々のネットワークパスを使用します。両方を許可するようにネットワークを構成してください。

  • SIPシグナリング: sip.api.openai.com および sip-eu.api.openai.com はGeoIPルーティングされたエンドポイントです。DNSによって返されるアドレスに対して、ポート 5061 へのアウトバウンドTCP/TLSトラフィックを許可してください。
  • SRTPメディア: APIは、ネゴシエートされたSDPで別個のメディアIPアドレスとUDPポートを指定します。以下のCIDRとの間で双方向SRTPトラフィックをUDPで許可してください。
    • 13.79.45.80/28
    • 23.98.140.64/28
    • 40.67.149.176/28
    • 40.83.204.240/28

GPT-Liveの次のステップ