EMQX + GPT-Realtimeでリアルタイム音声エージェントを構築する
本ガイドでは、GPT-RealtimeモデルとEMQXを組み合わせて、リアルタイム音声エージェントアプリケーションを迅速に構築する方法を説明します。
一時的なAPIキーを取得する
ブラウザからネイティブWebRTCを使ってGPT-Realtimeに接続するには、まず一時的(エフェメラル)なAPIキーを取得する必要があります。このキーはOpenAIのREST APIを通じて生成できます。
export OPENAI_API_KEY="sk-xxxxxx"
curl -s -X POST https://api.openai.com/v1/realtime/client_secrets \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"session": {"type": "realtime", "model": "gpt-realtime"}}' | jq .valueリアルタイム音声チャットを実装する
以下の例は、ネイティブWebRTCを使ってGPT-Realtimeモデルに接続し、リアルタイム音声チャットを実装する方法を示しています。
// 取得したエフェメラルキーをここに設定
const EPHEMERAL_KEY = "ek_xxxxxx";
// ピアコネクションを作成
const pc = new RTCPeerConnection();
// モデルからのリモート音声を再生する設定
audioElement.current = document.createElement("audio");
audioElement.current.autoplay = true;
pc.ontrack = (e) => (audioElement.current.srcObject = e.streams[0]);
// ブラウザのマイク入力用にローカル音声トラックを追加
const ms = await navigator.mediaDevices.getUserMedia({
audio: true,
});
pc.addTrack(ms.getTracks()[0]);
// イベント送受信用のデータチャネルを設定
const dc = pc.createDataChannel("oai-events");
// セッションをSession Description Protocol(SDP)で開始
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
const sdpResponse = await fetch("https://api.openai.com/v1/realtime/calls", {
method: "POST",
body: offer.sdp,
headers: {
Authorization: `Bearer ${EPHEMERAL_KEY}`,
"Content-Type": "application/sdp",
},
});
const answer = {
type: "answer",
sdp: await sdpResponse.text(),
};
await pc.setRemoteDescription(answer);
// サーバーからのイベントを受信
dc.addEventListener("message", (e) => {
const event = JSON.parse(e.data);
console.log("Received event:", event);
});このコードはWebRTCの音声チャネルを作成するだけでなく、GPT-Realtimeモデルとのイベント送受信用にデータチャネルも作成しています。受信したすべてのイベントはコンソールにログ出力されます。テスト時に音声が聞こえないなどの問題があれば、詳細なエラー情報をコンソールで確認してください。
MCPを使ったデバイス制御
EMQXを起動し、MCPブリッジプラグインをインストールおよび設定します。
スマートライトをシミュレートするMCPサーバーを起動します。詳細な手順はEMQX MCPブリッジを使ったIoTデバイスアクセスを参照してください。
なお、EMQXはパブリックネットワーク環境にデプロイされている必要があり、MCPブリッジプラグインは有効なSSL証明書で設定されている必要があります。これにより、GPT-RealtimeはHTTPS経由でMCPサービスにアクセスできます。
フロントエンドコードをMCPツール対応に修正します。
MCPツールを有効にするため、GPT-Realtimeイベントを処理する関数
handle_event()を追加します。javascript// サーバーからのイベントを受信 dc.addEventListener("message", (e) => { const event = JSON.parse(e.data); handle_event(event); });この関数内で
session.createdイベントを処理し、セッション作成時にsession.updateイベントを送信してMCPツールを有効化します。MCPサーバーのアドレスはhttps://your-emqx-host:port/mcpに設定してください。javascriptfunction handle_event(event) { if (event.type === "session.created") { // クライアントイベントを送信 const session_update_event = { type: "session.update", session: { type: "realtime", model: "gpt-realtime", // "text"に設定することも可能 output_modalities: ["audio"], tools: [ { type: "mcp", server_label: "mqtt_mcp_bridge", server_description: "EMQX MCP over MQTT Bridge", server_url: "https://your-emqx-host:port/mcp", require_approval: "never", } ], tool_choice: "auto", // 直接セッションフィールドを設定可能。プロンプトフィールドと重複する場合はこちらが優先されます: instructions: "I have a smart light and its client ID is abc123" } }; dc.send(JSON.stringify(session_update_event)); } else if (event.type === "response.done") { console.log("Received response done:", event); } else { console.log("Received event:", event); } }
これでブラウザのフロントエンドページにアクセスし、GPT-Realtimeと音声会話を行うと、モデルがMCPツールを通じてIoTデバイスにアクセスし制御できるようになります。
TIP
GPT-RealtimeはMCPサーバーにHTTPS経由でのみアクセス可能です。以下を必ず満たしてください:
- MCPプラグインが有効な自己署名でないSSL証明書で設定されていること
- URLがIPアドレスではなくドメイン名であり、パブリックにアクセス可能であること
TIP
GPT-RealtimeはMCPサーバーへのアクセスにStreamable HTTPを必要とするため、EMQX MCPブリッジプラグインの/mcpエンドポイントを使用し、/sseエンドポイントは使用しないでください。
モデルへのメッセージ送信
前述のコードでは、システム指示であらかじめデバイスのクライアントIDをモデルに通知していました。
const session_update_event = {
type: "session.update",
session: {
...
instructions: "I have a smart light and its client ID is abc123"
}
};GPT-Realtimeは、会話中にWebRTCデータチャネルを通じてメッセージを送信し、コンテキスト情報を追加することもサポートしています。
// クライアントイベントを送信
const event = {
type: "conversation.item.create",
item: {
type: "message",
role: "user",
content: [
{
type: "input_text",
text: "I have a smart light and its client ID is abc123",
},
],
},
};
dc.send(JSON.stringify(event));