Realtime Agent を Twilio に接続
Twilio には Media Streams API があり、電話通話の生の音声を WebSocket サーバーへ送信します。このセットアップを使用して、あなたの 音声エージェントの概要 を Twilio に接続できます。デフォルトの Realtime Session トランスポートを websocket
モードで使用し、Twilio から届くイベントを Realtime Session に接続することも可能です。ただし、適切なオーディオ形式を設定し、電話通話は Web ベースの会話よりも遅延が大きいため、割り込みタイミングを調整する必要があります。
セットアップ体験を向上させるために、割り込み処理や音声転送を含め、Twilio との接続を代行する専用のトランスポートレイヤーを用意しました。
セットアップ
Section titled “セットアップ”-
Twilio アカウントと Twilio 電話番号を所有していることを確認します。
-
Twilio からのイベントを受信できる WebSocket サーバーをセットアップします。
ローカルで開発している場合は、
ngrok
や Cloudflare Tunnel などのローカルトンネルを設定し、ローカルサーバーを Twilio からアクセス可能にする必要があります。TwilioRealtimeTransportLayer
を使用して Twilio に接続できます。 -
拡張パッケージをインストールして Twilio アダプターを導入します:
Terminal window npm install @openai/agents-extensions -
RealtimeSession
に接続するためにアダプターとモデルをインポートします:import { TwilioRealtimeTransportLayer } from '@openai/agents-extensions';import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';const agent = new RealtimeAgent({name: 'My Agent',});// Create a new transport mechanism that will bridge the connection between Twilio and// the OpenAI Realtime API.const twilioTransport = new TwilioRealtimeTransportLayer({twilioWebSocket: websocketConnection,});const session = new RealtimeSession(agent, {// set your own transporttransport: twilioTransport,}); -
RealtimeSession
を Twilio に接続します:session.connect({ apiKey: 'your-openai-api-key' });
RealtimeSession
から期待されるすべてのイベントや動作(ツール呼び出し、ガードレールなど)がそのまま利用できます。RealtimeSession
を音声エージェントで使う方法については 音声エージェントの概要 を参照してください。
ヒントと考慮事項
Section titled “ヒントと考慮事項”-
スピードが鍵です。
Twilio から必要なすべてのイベントと音声を受け取るには、WebSocket 接続への参照を取得したらすぐに
TwilioRealtimeTransportLayer
インスタンスを作成し、直ちにsession.connect()
を呼び出してください。 -
Twilio の元イベントにアクセスします。
Twilio から送信される元イベントにアクセスしたい場合は、
RealtimeSession
インスタンスのtransport_event
をリッスンできます。Twilio からの各イベントはtwilio_message
という type と、生のイベントデータが入ったmessage
プロパティを持ちます。 -
デバッグログを確認します。
詳細を確認したい場合は、
DEBUG=openai-agents*
環境変数を使用すると Agents SDK のすべてのデバッグログを表示できます。あるいはDEBUG=openai-agents:extensions:twilio*
を使って Twilio アダプターのログのみに絞ることも可能です。
フルサーバー例
Section titled “フルサーバー例”以下は、Twilio からのリクエストを受け取り RealtimeSession
に転送する WebSocket サーバーのエンドツーエンド例です。
import Fastify from 'fastify';import dotenv from 'dotenv';import fastifyFormBody from '@fastify/formbody';import fastifyWs from '@fastify/websocket';import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';import { TwilioRealtimeTransportLayer } from '@openai/agents-extensions';
// Load environment variables from .env filedotenv.config();
// Retrieve the OpenAI API key from environment variables. You must have OpenAI Realtime API access.const { OPENAI_API_KEY } = process.env;if (!OPENAI_API_KEY) { console.error('Missing OpenAI API key. Please set it in the .env file.'); process.exit(1);}const PORT = +(process.env.PORT || 5050);
// Initialize Fastifyconst fastify = Fastify();fastify.register(fastifyFormBody);fastify.register(fastifyWs);
const agent = new RealtimeAgent({ name: 'Triage Agent', instructions: 'You are a helpful assistant that starts every conversation with a creative greeting.',});
// Root Routefastify.get('/', async (request, reply) => { reply.send({ message: 'Twilio Media Stream Server is running!' });});
// Route for Twilio to handle incoming and outgoing calls// <Say> punctuation to improve text-to-speech translationfastify.all('/incoming-call', async (request, reply) => { const twimlResponse = `<?xml version="1.0" encoding="UTF-8"?><Response> <Say>O.K. you can start talking!</Say> <Connect> <Stream url="wss://${request.headers.host}/media-stream" /> </Connect></Response>`.trim(); reply.type('text/xml').send(twimlResponse);});
// WebSocket route for media-streamfastify.register(async (fastify) => { fastify.get('/media-stream', { websocket: true }, async (connection) => { const twilioTransportLayer = new TwilioRealtimeTransportLayer({ twilioWebSocket: connection, });
const session = new RealtimeSession(agent, { transport: twilioTransportLayer, });
await session.connect({ apiKey: OPENAI_API_KEY, }); console.log('Connected to the OpenAI Realtime API'); });});
fastify.listen({ port: PORT }, (err) => { if (err) { console.error(err); process.exit(1); } console.log(`Server is listening on port ${PORT}`);});
process.on('SIGINT', () => { fastify.close(); process.exit(0);});