OpenAIのRealtime APIとは?使い方や料金!音声チャットと文字起こしをしてみた

OpenAIのRealtime APIとは、自然な会話を実現するために設計されたAPIです。

これまでは音声を処理する際は一度テキストに変換していましたが、Realtime APIでは直接処理できるようになり、遅延を軽減させることに成功しました。

この記事ではRealtime APIの使い方や文字起こしのやり方について解説します。

また、使用する場合のAPI料金も紹介します。

目次

OpenAIのRealtime APIとは?

OpenAIのRealtime APIは、人間同士の会話のように自然で滑らかな音声対話を実現するために設計されたAPIです。

従来の複数モデルを組み合わせる方式とは異なり、単一のモデルで音声の入力から出力までをテキスト化せず直接処理することで、驚異的な低遅延を達成しました。

これにより、ユーザーの発話に即座に反応し、途中で割り込んで話しても会話が成立するなど、よりリアルタイム性の高いインタラクションが可能になります。

2025年8月に正式版がリリースされ、最新モデル「gpt-realtime」の搭載や画像入力への対応など、その機能は大きく進化しています。

要点とできること

OpenAI Realtime APIの最大の要点は、極めて自然な音声対話を実現できる点にあります。

人間が会話するように、相手が話している最中でも割り込んで発話することが可能で、APIはそれを即座に認識して応答を調整します。

単に言葉をテキストに変換して応答を生成するだけでなく、笑い声のような非言語的なニュアンスを捉えたり、声のトーンを調整したりすることも可能です。

これらの機能により、まるで人と話しているかのような、スムーズでストレスのないユーザー体験を提供します。

従来のパイプラインとの違い

従来の音声AIは、「音声認識(STT)」「大規模言語モデル(LLM)」「音声合成(TTS)」という3つのモデルを順番に処理するパイプライン方式が一般的でした。

この方式では各工程で遅延が発生し、会話が途切れがちになるという課題がありました。

一方、Realtime APIは、これら一連の処理を単一の統合モデルで完結させます。

音声をテキストに変換することなく、音声のまま直接処理して応答音声を生成するため、パイプライン方式で生じていた遅延を大幅に削減しました。

このアーキテクチャによって、応答速度が向上するだけでなく、元の音声が持つ感情や抑揚といった微妙なニュアンスが失われにくく、より表現力豊かで自然な対話が実現されます。

接続方式の選び方

Realtime APIへの接続には、主にWebRTC、WebSocket、SIP接続の3つの方式が用意されており、用途に応じて最適なものを選択する必要があります。

Webアプリケーションやモバイルアプリといったクライアント側のアプリケーションを構築する場合は、低遅延のリアルタイム音声ストリーミングに特化したWebRTCが最も推奨されます。

バックエンドサーバー間で通信を行う場合や、安定した低遅延ネットワーク環境で利用する際にはWebSocketが適しています。

さらに、2025年8月のアップデートでSIPにも対応し、従来の電話網やPBXシステムと直接連携できるようになりました。

これにより、AI音声エージェントが電話を受けたり、発信したりすることが可能となり、コールセンターの自動応答システムなど、より専門的な用途への活用も広がっています。

モデルや機能の概観

Realtime APIの中核をなすのが、最新の音声対話モデルであるgpt-realtimeです。

このモデルは音声認識、言語処理、テキスト音声合成をすべて統合されたフレームワークで処理することで、遅延を軽減し、声の理解や指示への追従性において優れた性能を発揮します。

機能面では、外部のツールやデータベースと連携するための関数呼び出し(Function Calling)に対応しており、音声エージェントが情報検索やタスク実行を非同期で行います。

これにより、関数実行を待っている間も自然な会話が継続され、会話が止まってしまうことがなくなります。

また、MCP(Model Context Protocol)サーバーをサポートしたことで、外部ツールとの連携を手動で実装する手間なく、より高度な機能拡張が可能です。

あわせて読みたい
MCPサーバーおすすめ31選!AIエージェントと連携 Claudeなどの大規模言語モデル(LLM)を利用しており、ファイル管理やデータベース操作、クラウド運用などを自動化・効率化したい方は、MCPサーバーの導入がおすすめです。 MCPサーバーを導入することで、AIが外部サービスにアクセスしやすくなり、AIエージェントを構築しやすくなります。 この記事では、MCPサーバーやAIエージェントの概要、そしてカテゴリ別のおすすめMCPサーバーをご紹介します。

さらに、音声だけでなく画像入力にも対応し、ユーザーが提示した画像について質問するといった、マルチモーダルな対話も実現しています。

これらの多彩な機能により、単なる会話AIに留まらない、幅広い応用が期待されます。

OpenAIのRealtime APIの料金体系

次に、Realtime APIを使用する際に気になる料金体系をご説明します。

API料金体系

2025年9月時点での料金体系は以下のようになっています。

スクロールできます
種類モデル入力トークン
(100万トークンあたり)
キャッシュされた
入力トークン
(100万トークンあたり)
出力トークン
(100万トークンあたり)
テキストgpt-realtime$4.00$0.40$16.00
GPT-4o mini$0.60$0.30$2.40
音声gpt-realtime$32.00$0.40$64.00
GPT-4o mini$10.00$0.30$20.00
画像gpt-realtime$5.00$0.50
出典:API 料金

Realtime APIではモデルとしてgpt-realtimeのほか、GPT-4o miniも使用できます。

gpt-realtimeの方が高性能ですが、その分料金も高く設定されているため、用途と予算に合わせてどちらのモデルを選ぶか決めましょう。

なお、画像入力はgpt-realtimeにのみ対応しています。

料金の目安

料金の概算を把握するために、具体的な利用シーンで試算してみましょう。

今回はコールセンターでの活用を想定し、ユーザーとAIが1分間ずつ、合計2分間の会話を行った場合を考えます。

1分間の会話において、入力と出力で消費する音声トークンをそれぞれ600トークンと仮定すると、入力コストは600トークン÷100万トークン×$32=$0.0192、出力コストは600÷100万×$64=$0.0384。

よって、1分あたりの合計コストは$0.0576となります。

もし10本の通話を同時につないでいると、1分あたりの合計コストは$0.576となります。

当然これらはユーザーやAIの実際の発話量によって変動しますが、1つの目安としてご参考にしてください。

コストを最適化するためには?

APIの利用料金を最適化するには、いくつかの重要な手法があります。

最も効果的なのがキャッシュ入力の活用で、同じ内容の入力が繰り返される場合、キャッシュされたデータを利用することで料金を通常入力の10分の1以下に抑えることが可能です。

次に、会話履歴をすべてAPIに送るのではなく、適切なコンテキスト上限を設定し、不要なトークンを送信しないことも重要です。

また、無音部分を検知して送信データを削減したり、発話の切れ目で適切にセッションを管理したりするサンプリング設計も、無駄なデータ転送をなくしてコスト削減に直結します。

注意点

Realtime APIを安定して運用するためには、料金以外の技術的な注意点も理解しておく必要があります。

まず、リアルタイム通信であるため、安定したネットワーク帯域が不可欠であり、帯域が不足すると音声の途切れや遅延の原因となります。

使用時には、マイクがスピーカーの音を拾ってしまう音声ループを防ぐため、ユーザーはイヤホンの使用を推奨することが望ましいです。

さらに、APIが受け付けるサンプリングレートやフォーマットの仕様を遵守することも大切です。

また、APIには一定の期間内に使用されるリクエストまたはトークンにレート制限があります。

制限はAPIの使用量によって決まり、より多くの課金をすると自動的に増加します。

OpenAIのRealtime APIの使い方

続いて、Realtime APIの使い方を解説します。

最小構成:WebRTCとWebSocketの選び方

APIの接続方式は、ユースケースに応じて選択するのが基本です。

ユーザーがブラウザやモバイルアプリから直接AIと対話するアプリケーションを開発する場合、WebRTCを利用するのが最も一般的です。

WebRTCはリアルタイムの音声・映像ストリーミングに特化しており、低遅延な通信を実現します。

一方で、サーバー間で音声データをやり取りする場合や、録音済みの音声をリアルタイムで文字起こし(転写)するような用途では、より汎用的なWebSocketが適しています。

まずはこの2つのどちらを主軸にするかを決めると、システム構成が明確になります。

WebRTCを使用したRealtime APIの使い方

ここでは、接続方式としてWebRTCを使ってRealtime APIを使う方法をご紹介します。

STEP

フォルダ構成

具体的な手順の前に、今回作成するフォルダ構成を示します。

realtime-webrtc-app/
├── node_modules/   (npm install後に自動生成)
├── public/         (ブラウザからアクセスするファイル)
│   ├── index.html  (ページの骨格)
│   └── client.js   (ブラウザで動くJavaScript)
├── .env            (APIキーを保存するファイル)
├── package.json    (プロジェクト管理ファイル)
└── server.js       (サーバー側のNode.jsコード)
STEP

Ephemeral Keyの発行

まずは、Ephemeral Keyと呼ばれる一時キーの発行をリクエストします。

このキーは短時間のみ有効であるため、セキュリティを保つことができます。

APIの利用は「セッション」という単位で管理され、バックエンドサーバーから、OpenAIのAPIキーを使ってセッション作成をリクエストします。

セッションが作成されると、Ephemeral Keyが発行されます。

ここまでの一連の流れが以下のコードです。

// server.js
import express from "express";

const app = express();
const apiKey = process.env.OPENAI_API_KEY; // 環境変数からAPIキーを読み込む

// セッションの設定
const sessionConfig = JSON.stringify({
  session: {
    type: "realtime",
    model: "gpt-realtime",
    audio: {
      output: {
        voice: "marin", // AIの声を指定
      },
    },
  },
});

// "/token" エンドポイント
app.get("/token", async (req, res) => {
  try {
    const response = await fetch(
      "https://api.openai.com/v1/realtime/client_secrets",
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${apiKey}`, // ★サーバーの標準APIキーを使用
          "Content-Type": "application/json",
        },
        body: sessionConfig,
      }
    );

    const data = await response.json();
    res.json(data); // クライアントに一時キーを含む情報を返す
  } catch (error) {
    console.error("Token generation error:", error);
    res.status(500).json({ error: "Failed to generate token" });
  }
});

app.listen(3000, () => console.log("Server listening on port 3000"));
STEP

WebRTC接続の初期化

次に、ブラウザ側でWebRTC接続を確立します。

Ephemeral Keyを取得してRTCPeerConnectionオブジェクトを作成し、データチャネルを作成してSDP (Session Description Protocol) の交換を行います。

これにより、WebRTCのピア接続が確立されます。

コード例は以下の通りです。

// client.js

async function connectToRealtimeAPI() {
    // 1. サーバーから一時キーを取得
    const tokenResponse = await fetch("/token");
    const data = await tokenResponse.json();
    const EPHEMERAL_KEY = data.value; // 一時キー

    // 2. RTCPeerConnectionを作成
    const pc = new RTCPeerConnection();
    const audioElement = document.createElement("audio");
    audioElement.autoplay = true;

    // 3. AIからの音声トラックを受信したときの処理
    pc.ontrack = (e) => (audioElement.srcObject = e.streams[0]);

    // 4. ユーザーのマイク音声トラックを追加
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    pc.addTrack(stream.getTracks()[0]);

    // 5. イベント送受信用のデータチャネルを作成
    const dc = pc.createDataChannel("oai-events");

    // 6. SDPオファーを作成してOpenAI APIに送信
    const offer = await pc.createOffer();
    await pc.setLocalDescription(offer);

    const sdpResponse = await fetch("https://api.openai.com/v1/realtime/calls?model=gpt-realtime", {
        method: "POST",
        body: offer.sdp,
        headers: {
            Authorization: `Bearer ${EPHEMERAL_KEY}`, // ★一時キーを使用
            "Content-Type": "application/sdp",
        },
    });

    // 7. OpenAIからのアンサーSDPを設定
    const answer = {
        type: "answer",
        sdp: await sdpResponse.text(),
    };
    await pc.setRemoteDescription(answer);
    
    // これで接続が確立
    console.log("WebRTC connection established.");
}
STEP

イベントの送受信

接続の確立後、テキストの送信やセッションの状態変更などのやり取りは作成したデータチャネルを通じて行います。

サーバーからのイベント受信はデータチャネルのmessageイベント、クライアントからのイベント送信はdc.send()メソッドを使って行います。

// client.js (続き)

// データチャネルの準備
const dc = pc.createDataChannel("oai-events");

// サーバーからのイベントを受信
dc.addEventListener("message", (e) => {
    const event = JSON.parse(e.data);
    console.log("Received event from server:", event);
});

// クライアントからイベントを送信する例
dc.onopen = () => { // データチャネルが開通したら送信可能
    const event = {
        type: "conversation.item.create",
        item: {
            type: "message",
            role: "user",
            content: [
                {
                    type: "input_text",
                    text: "こんにちは!",
                },
            ],
        },
    };
    dc.send(JSON.stringify(event));
};
STEP

その他のコード例

フォルダ構成内にあるその他のコードを以下に示します。

これらをコピーすることで、実際の動きを試すことが可能です。

package.jsonファイルの中身はこちらです。

{
  "name": "realtime-webrtc-app",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "type": "module",
  "scripts": {
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^16.3.1",
    "express": "^4.18.2"
  }
}

.envファイルにはOpenAIのAPIキーを記述します。

OPENAI_API_KEY="sk-..."

index.htmlの例はこちらです。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Realtime API with WebRTC</title>
    <style>
        body { font-family: sans-serif; text-align: center; margin-top: 50px; }
        button { font-size: 1.2rem; padding: 10px 20px; cursor: pointer; }
        #status { margin-top: 20px; color: #555; }
    </style>
</head>
<body>
    <h1>Realtime API WebRTC Demo</h1>
    <button id="connectButton">接続して会話を開始</button>
    <p id="status">待機中...</p>

    <script src="client.js"></script>
</body>
</html>
STEP

依存ライブラリのインストール

ターミナル(コマンドプロンプト)を開き、プロジェクトのルートフォルダrealtime-webrtc-app に移動して、以下のコマンドを実行します。

npm install

これによって、package.jsonに書かれたexpressdotenvnode_modulesフォルダにインストールされます。

STEP

サーバーの起動

同じターミナルで、以下のコマンドを実行してサーバーを起動します。

npm start

成功すると、Server is running at http://localhost:3000 と表示されます。

STEP

ブラウザでアクセス

ターミナルでCtrlを押しながらURLをクリックするか、ブラウザで直接http://localhost:3000にアクセスしましょう。

今回のコードでは、以下のような画面が表示されます。

実際に動かしてみた動画がこちらです。

なお、実際の入力音声は字幕の通りです。

音声イベントの扱い:VADと割り込み制御

Realtime APIでは、VAD(Voice activity detection)を設定可能です。

VADとは、ユーザーが話し始めたときや話し終えたときを自動的に検出する機能です。

セッションではデフォルトでオンになっていますが、オプションでオフにすることも可能です。

VADには無音期間に基づいてオーディオを自動的にチャンク化するserver_vadと、ユーザーの発言に基づいて、発話が完了したとモデルが判断したときにオーディオをチャンク化するsemantic_vadの2モードがあります。

server_vadの構成例は以下の通りです。

{
  "type": "session.update",
  "session": {
    "turn_detection": {
      "type": "server_vad",
      "threshold": 0.5,
      "prefix_padding_ms": 300,
      "silence_duration_ms": 500,
      "create_response": true, // only in conversation mode
      "interrupt_response": true, // only in conversation mode
    }
  }
}

thresholdはモデルをアクティベートするのに必要な音量、prefix_padding_msは音声を検出する前に含めるオーディオの量、silence_duration_msは音声停止を検出する際の無音期間を表します。

また、semantic_vadの構成例は以下の通りです。

{
  "type": "session.update",
  "session": {
    "turn_detection": {
      "type": "semantic_vad",
      "eagerness": "low" | "medium" | "high" | "auto", // optional
      "create_response": true, // only in conversation mode
      "interrupt_response": true, // only in conversation mode
    }
  }
}

eagernessのプロパティは、autoがデフォルト値(mediumと同等)であり、lowにするとユーザーがよりゆっくり話せるようになり、highにするとできるだけ早くオーディオをチャンク化するようになります。

また、Realtime APIでは割り込み制御が可能です。

AIが話している最中にユーザーが話し始めても、それを検知してAIの発話を即座に停止させられます。

これにより、一方的な応答ではなく、人間同士のようなテンポの良い会話が実現できます。

ツールや関数呼び出しの基本

新しいgpt-realtimeモデルは、関数呼び出し(Function Calling)の性能が大幅に向上しました。

これにより、適切なタイミングで関連する関数をより正確な引数で呼び出すことが可能になります。

特に、非同期での関数呼び出しがネイティブでサポートされた点が大きな特徴です。

この機能によって、時間のかかる処理を実行している間もモデルはユーザーとの会話を続けることができ、よりスムーズな対話が実現します。

さらに、このRealtime APIはリモートのMCPサーバーをサポートしています。

セッション設定でMCPサーバーのURLを指定するだけで、AIエージェントの能力を簡単に追加・拡張できます。

この仕組みにより、開発者は用途に応じて異なる機能を持つサーバーへ接続を切り替えるだけで、エージェントのカスタマイズを柔軟に行うことが可能です。

よくある落とし穴(セッション失効・音漏れ・音量正規化・端末権限)

運用時にはいくつかの典型的な問題に注意が必要です。

まず、Ephemeral Keyを使用したセッションは一定時間で失効するため、接続が切れた際に再接続する処理を組み込んでおく必要があります。

次に、スピーカーから出たAIの音声をマイクが拾ってしまう音漏れは、会話がループする原因となるため、エコーキャンセレーション技術の導入やイヤホンの使用推奨が有効です。

また、ユーザーによってマイクの音量がバラバラだと認識精度が落ちるため、音量を一定に保つ正規化処理を入れると安定性が増します。

最後に、ブラウザでマイクを利用するにはユーザーからの端末権限の許可が必須であるため、許可を求めるUIフローを最初に実装し忘れないようにしましょう。

OpenAIのRealtime APIを使った文字起こし方法

続いて、Realtime APIを使って文字起こし(転写)を行う方法を解説します。

ここでは、接続方式としてWebSocketを選択します。

文字起こしのやり方

Realtime APIを使って文字起こしを行う手順は以下の通りです。

なお、今回はPythonを使用しています。

STEP
APIキーを設定

.envファイルを作成し、OpenAIのAPIキーを記述します。

OPENAI_API_KEY="sk-..."
STEP
WebSocketへの接続

まず、OpenAI Realtime APIのエンドポイント(wss://api.openai.com/v1/realtime?intent=transcription)に接続します。

接続時には、認証のためのAPIキーを含むヘッダー情報を付与します。

async with websockets.connect(WS_URL, additional_headers=HEADERS) as websocket:
    self._websocket = websocket
    logging.info("✅ WebSocket接続成功!")
STEP
セッション設定

WebSocket接続が確立したら、次にどのような文字起こしを行いたいかをサーバーに伝えます。

ここでは、使用するモデル(gpt-4o-transcribe)などをJSON形式で指定し、サーバーに送信します。

# 文字起こしセッションの設定を送信
session_config = {
    "type": "transcription_session.update",
    "session": {"input_audio_transcription": {"model": "gpt-4o-transcribe"}},
}
await self._websocket.send(json.dumps(session_config))
STEP
音声データ送信

マイクからの音声をリアルタイムで取得し、細切れのデータ(チャンク)に分割します。

各チャンクはBase64形式にエンコードされ、input_audio_buffer.appendというイベントタイプを指定したJSONメッセージとして、WebSocketを通じてサーバーに送信され続けます。

この一連の処理は_audio_senderメソッドが担当しています。

async def _audio_sender(self):
    """マイクからの音声データをWebSocket経由で送信するタスク。"""
    logging.info("音声送信タスクを開始します。")
    while True:
        try:
            # マイクから音声データを読み込む
            audio_data = await asyncio.get_event_loop().run_in_executor(
                None, self._stream.read, CHUNK, False
            )
            
            # Base64にエンコードしてJSONメッセージを作成
            message = {
                "type": "input_audio_buffer.append",
                "audio": base64.b64encode(audio_data).decode("utf-8"),
            }
            
            await self._websocket.send(json.dumps(message))
        except websockets.exceptions.ConnectionClosed:
            logging.warning("WebSocket接続が閉じられたため、音声送信を停止します。")
            break
        except Exception as e:
            logging.error(f"音声送信中にエラーが発生しました: {e}")
            break
        # CPUを過度に消費しないように、他のタスクに制御を譲る
        await asyncio.sleep(0.01)
STEP
文字起こしイベント受信

音声データを送信するのと並行して、サーバーからの応答を待ち受けます。

サーバーは文字起こしの進捗に応じて、中間結果(delta)と確定結果(completed)をJSONメッセージとして送り返してきます。

_transcript_receiverメソッドはこれらのメッセージを非同期で受信し、内容を解析してコンソールに出力します。

async def _transcript_receiver(self):
    """WebSocketから文字起こし結果を受信して表示するタスク。"""
    logging.info("文字起こし受信タスクを開始します。")
    full_transcript = []
    try:
        async for message_str in self._websocket:
            message = json.loads(message_str)
            msg_type = message.get("type")

            if msg_type == "conversation.item.input_audio_transcription.delta":
                # 中間結果をリアルタイムで表示
                print(message.get("delta", ""), end="", flush=True)
            elif msg_type == "conversation.item.input_audio_transcription.completed":
                # 発話の区切りで改行して表示
                transcript = message.get("transcript", "")
                full_transcript.append(transcript)
                print("\n>> ", end="", flush=True)
    except websockets.exceptions.ConnectionClosed as e:
        logging.info(f"WebSocket接続が正常に閉じられました: {e}")
    except Exception as e:
        logging.error(f"メッセージ受信中にエラーが発生しました: {e}")
STEP
コードの実行

ここまでのコードをまとめると以下のようになります。

import asyncio
import base64
import json
import logging
import os

import pyaudio
import websockets
from dotenv import load_dotenv

# --- ロギング設定 ---
logging.basicConfig(
    level=logging.INFO, format="[%(asctime)s] [%(levelname)s] - %(message)s"
)

# --- 設定値 ---
load_dotenv()
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
if not OPENAI_API_KEY:
    raise ValueError("環境変数 'OPENAI_API_KEY' が設定されていません。")

# WebSocket接続先URLとヘッダー情報
WS_URL = "wss://api.openai.com/v1/realtime?intent=transcription"
HEADERS = {
    "Authorization": f"Bearer {OPENAI_API_KEY}",
    "OpenAI-Beta": "realtime=v1",
}

# PyAudio設定
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 24000  # OpenAI Realtime APIは24kHzを推奨
CHUNK = 2048


class RealtimeTranscriber:
    """
    マイクからの音声をリアルタイムで文字起こしするクラス。
    """

    def __init__(self):
        self._pyaudio_instance = pyaudio.PyAudio()
        self._stream = None
        self._websocket = None

    async def _audio_sender(self):
        """マイクからの音声データをWebSocket経由で送信するタスク。"""
        logging.info("音声送信タスクを開始します。")
        while True:
            try:
                # マイクから音声データを読み込む (ブロッキング処理のためExecutorを使用)
                audio_data = await asyncio.get_event_loop().run_in_executor(
                    None, self._stream.read, CHUNK, False
                )
                
                # Base64にエンコードしてJSONメッセージを作成
                message = {
                    "type": "input_audio_buffer.append",
                    "audio": base64.b64encode(audio_data).decode("utf-8"),
                }
                
                await self._websocket.send(json.dumps(message))
            except websockets.exceptions.ConnectionClosed:
                logging.warning("WebSocket接続が閉じられたため、音声送信を停止します。")
                break
            except Exception as e:
                logging.error(f"音声送信中にエラーが発生しました: {e}")
                break
            # CPUを過度に消費しないように、他のタスクに制御を譲る
            await asyncio.sleep(0.01)

    async def _transcript_receiver(self):
        """WebSocketから文字起こし結果を受信して表示するタスク。"""
        logging.info("文字起こし受信タスクを開始します。")
        full_transcript = []
        try:
            async for message_str in self._websocket:
                message = json.loads(message_str)
                msg_type = message.get("type")

                if msg_type == "conversation.item.input_audio_transcription.delta":
                    print(message.get("delta", ""), end="", flush=True)
                elif msg_type == "conversation.item.input_audio_transcription.completed":
                    transcript = message.get("transcript", "")
                    full_transcript.append(transcript)
                    print("\n>> ", end="", flush=True)
        except websockets.exceptions.ConnectionClosed as e:
            logging.info(f"WebSocket接続が正常に閉じられました: {e}")
        except Exception as e:
            logging.error(f"メッセージ受信中にエラーが発生しました: {e}")

    async def run(self):
        """リアルタイム文字起こし処理を開始する。"""
        try:
            self._stream = self._pyaudio_instance.open(
                format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK,
            )

            async with websockets.connect(WS_URL, additional_headers=HEADERS) as websocket:
                self._websocket = websocket
                logging.info("✅ WebSocket接続成功!")

                # 文字起こしセッションの設定を送信
                session_config = {
                    "type": "transcription_session.update",
                    "session": {"input_audio_transcription": {"model": "gpt-4o-transcribe"}},
                }
                await self._websocket.send(json.dumps(session_config))

                print("\n🎤 リアルタイム文字起こしを開始します。話しかけてください。(Ctrl+Cで終了)")
                print(">> ", end="", flush=True)

                # 音声送信と文字起こし受信タスクを並行して実行
                sender_task = asyncio.create_task(self._audio_sender())
                receiver_task = asyncio.create_task(self._transcript_receiver())
                await asyncio.gather(sender_task, receiver_task)

        except websockets.exceptions.NegotiationError as e:
            logging.error(f"WebSocket接続に失敗しました (ステータスコード: {e.status_code})。")
            logging.error("APIキーが正しいか、または組織の利用制限を確認してください。")
        except Exception as e:
            logging.error(f"予期せぬエラーが発生しました: {e}")
        finally:
            # 使用したリソースをクリーンアップ
            if self._stream and self._stream.is_active():
                self._stream.stop_stream()
            if self._stream:
                self._stream.close()
            self._pyaudio_instance.terminate()
            logging.info("リソースを解放し、プログラムを終了します。")


async def main():
    """メイン関数。"""
    transcriber = RealtimeTranscriber()
    await transcriber.run()


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("\n👋 プログラムが中断されました。")

このコードを.envファイルと同じディレクトリで実行します。

python main.py

実際の文字起こしした結果が以下の通りです。

主要イベントの流れと整列のコツ

Realtime APIにおける文字起こしは、主に2つのイベントdeltacompletedによって構成されます。

deltaは、モデルが音声を処理する過程で生成される中間的な文字起こし結果を断片的に送信するイベントです。

これにより、ユーザーは発話しながらリアルタイムにテキストが生成されていく様子を確認できます。

一方、completedは、発話の区切りを検知した時点で、その一連の発話をまとめた最終的なテキストを返します。

whisper-1モデルでは中間結果が出力されず、completedのみが返されますが、gpt-4o系のモデルではdeltaによる逐次的な結果を得られます。

モデルの選択肢

現在、Realtime APIでは主に3つの文字起こしモデルが利用可能です。

whisper-1は、OpenAIの強力な音声認識モデルですが、リアルタイムの逐次的な文字起こしには最適化されていません。

よりリアルタイム性を重視するならgpt-4o-transcribeまたはgpt-4o-mini-transcribeが推奨されます。

gpt-4o-transcribeは最も高精度なフラッグシップモデルで、専門用語やノイズが多い環境でも優れた性能を発揮します。

対してgpt-4o-mini-transcribeは、精度を維持しつつ、より高速かつ低コストで処理できるように最適化されており、多くのリアルタイムアプリケーションにおいてバランスの取れた選択肢となります。

品質と遅延のトレードオフ

リアルタイム文字起こしでは、品質と遅延(レイテンシ)がトレードオフの関係にあります。

例えば、音声データのサンプルレートをAPIが推奨する24kHzなどに設定することで、モデルがより詳細な音声情報を得られるため品質が向上しますが、データ量が増えるため遅延につながる可能性があります。

また、VADにおける無音検知の閾値を短く設定すると、発話の区切りが素早く検知されるようになります。

これにより、文字起こし結果が返ってくるまでの反応速度は上がりますが、発話の途中でも区切りと誤認識されやすくなる可能性があります。

これらのパラメータをユースケースに応じて適切に調整することが、最適なユーザー体験を実現する鍵となります。

まとめ

OpenAIのRealtime APIは、リアルタイムの会話や文字起こしにおいて高い性能を発揮する強力なAPIです。

音声の入力に対し、テキスト化せずに直接処理できるため、これまでのモデルと比べて低遅延な対話を実現しました。

今後は音声チャットや通話botとして、コールセンターなどで業務を効率化できるようになるでしょう。

OpenAIのAPIさえあればだれでも使えるため、気になる方はぜひ試してみてください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次