Skip to content

EMQX + GPT-Realtimeでリアルタイム音声エージェントを構築する

本ガイドでは、GPT-RealtimeモデルとEMQXを組み合わせて、リアルタイム音声エージェントアプリケーションを迅速に構築する方法を説明します。

一時的なAPIキーを取得する

ブラウザからネイティブWebRTCを使ってGPT-Realtimeに接続するには、まず一時的(エフェメラル)なAPIキーを取得する必要があります。このキーはOpenAIのREST APIを通じて生成できます。

bash
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モデルに接続し、リアルタイム音声チャットを実装する方法を示しています。

javascript
// 取得したエフェメラルキーをここに設定
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を使ったデバイス制御

  1. EMQXを起動し、MCPブリッジプラグインをインストールおよび設定します。

  2. スマートライトをシミュレートするMCPサーバーを起動します。詳細な手順はEMQX MCPブリッジを使ったIoTデバイスアクセスを参照してください。

    なお、EMQXはパブリックネットワーク環境にデプロイされている必要があり、MCPブリッジプラグインは有効なSSL証明書で設定されている必要があります。これにより、GPT-RealtimeはHTTPS経由でMCPサービスにアクセスできます。

  3. フロントエンドコードを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に設定してください。

    javascript
    function 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をモデルに通知していました。

javascript
const session_update_event = {
    type: "session.update",
    session: {
        ...
        instructions: "I have a smart light and its client ID is abc123"
    }
};

GPT-Realtimeは、会話中にWebRTCデータチャネルを通じてメッセージを送信し、コンテキスト情報を追加することもサポートしています。

javascript
// クライアントイベントを送信
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));