音声エージェントの構築
音声の取り扱い
Section titled “音声の取り扱い”デフォルトの OpenAIRealtimeWebRTC
のような一部のトランスポートレイヤーは、音声の入力と出力を自動で処理します。OpenAIRealtimeWebSocket
のような他のトランスポート機構では、セッション音声を自分で扱う必要があります:
import { RealtimeAgent, RealtimeSession, TransportLayerAudio,} from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'My agent' });const session = new RealtimeSession(agent);const newlyRecordedAudio = new ArrayBuffer(0);
session.on('audio', (event: TransportLayerAudio) => { // play your audio});
// send new audio to the agentsession.sendAudio(newlyRecordedAudio);
セッション設定
Section titled “セッション設定”RealtimeSession
の構築時、または connect(...)
を呼び出すときに追加オプションを渡すことで、セッションを設定できます。
import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.',});
const session = new RealtimeSession(agent, { model: 'gpt-4o-realtime-preview-2025-06-03', config: { inputAudioFormat: 'pcm16', outputAudioFormat: 'pcm16', inputAudioTranscription: { model: 'gpt-4o-mini-transcribe', }, },});
これらのトランスポートレイヤーでは、session に一致する任意のパラメーターを渡せます。
RealtimeSessionConfig に対応するパラメーターがまだない新しいパラメーターについては、providerData
を使用できます。providerData
に渡したものは session
オブジェクトの一部としてそのまま渡されます。
通常のエージェントと同様に、ハンドオフを使用してエージェントを複数のエージェントに分割し、それらをオーケストレーションすることで、エージェントのパフォーマンスを向上させ、問題の範囲を適切に絞り込めます。
import { RealtimeAgent } from '@openai/agents/realtime';
const mathTutorAgent = new RealtimeAgent({ name: 'Math Tutor', handoffDescription: 'Specialist agent for math questions', instructions: 'You provide help with math problems. Explain your reasoning at each step and include examples',});
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.', handoffs: [mathTutorAgent],});
通常のエージェントと異なり、Realtime Agents におけるハンドオフはやや挙動が異なります。ハンドオフが行われると、進行中のセッションは新しいエージェント設定で更新されます。このため、エージェントは進行中の会話履歴に自動的にアクセスでき、入力フィルターは現在適用されません。
加えて、ハンドオフの一部として voice
や model
を変更することはできません。また、接続できるのは他の Realtime Agents のみです。別のモデル、例えば o4-mini
のような推論モデルを使用する必要がある場合は、ツールによる委譲 を利用できます。
通常のエージェントと同様に、Realtime Agents はツールを呼び出してアクションを実行できます。通常のエージェントで使用するのと同じ tool()
関数を使ってツールを定義できます。
import { tool, RealtimeAgent } from '@openai/agents/realtime';import { z } from 'zod';
const getWeather = tool({ name: 'get_weather', description: 'Return the weather for a city.', parameters: z.object({ city: z.string() }), async execute({ city }) { return `The weather in ${city} is sunny.`; },});
const weatherAgent = new RealtimeAgent({ name: 'Weather assistant', instructions: 'Answer weather questions.', tools: [getWeather],});
Realtime Agents で使用できるのは関数ツールのみで、これらのツールは Realtime Session と同じ場所で実行されます。つまり、Realtime Session をブラウザで実行している場合、ツールもブラウザで実行されます。より機密性の高い操作を行う必要がある場合は、ツール内でバックエンド サーバーへの HTTP リクエストを行うことができます。
ツールの実行中、エージェントはユーザーからの新しいリクエストを処理できません。体験を改善する方法の一つは、エージェントに対してツールを実行しようとしていることを告知させたり、ツール実行のための時間を稼ぐ特定のフレーズを話すように指示することです。
会話履歴へのアクセス
Section titled “会話履歴へのアクセス”エージェントが特定のツールを呼び出したときの引数に加えて、Realtime Session が追跡している現在の会話履歴のスナップショットにもアクセスできます。これは、会話の現在の状態に基づいてより複雑なアクションを実行する必要がある場合や、委譲のためのツール を使う予定がある場合に有用です。
import { tool, RealtimeContextData, RealtimeItem,} from '@openai/agents/realtime';import { z } from 'zod';
const parameters = z.object({ request: z.string(),});
const refundTool = tool<typeof parameters, RealtimeContextData>({ name: 'Refund Expert', description: 'Evaluate a refund', parameters, execute: async ({ request }, details) => { // The history might not be available const history: RealtimeItem[] = details?.context?.history ?? []; // making your call to process the refund request },});
ツール実行前の承認
Section titled “ツール実行前の承認”needsApproval: true
でツールを定義すると、エージェントはツールを実行する前に tool_approval_requested
イベントを発行します。
このイベントを監視することで、ツール呼び出しを承認または拒否するための UI をユーザーに表示できます。
import { session } from './agent';
session.on('tool_approval_requested', (_context, _agent, request) => { // show a UI to the user to approve or reject the tool call // you can use the `session.approve(...)` or `session.reject(...)` methods to approve or reject the tool call
session.approve(request.approvalItem); // or session.reject(request.rawItem);});
ガードレール
Section titled “ガードレール”ガードレールは、エージェントの発話が一連のルールに違反したかを監視し、即座に応答を遮断する方法を提供します。これらのガードレール検査はエージェントの応答の文字起こしに基づいて行われるため、モデルのテキスト出力が有効になっている必要があります(デフォルトで有効)。
提供したガードレールは、モデルの応答が返されるのと非同期に実行され、例えば「特定の禁止語を言及した」といった事前定義の分類トリガーに基づいて応答を遮断できます。
ガードレールが作動すると、セッションは guardrail_tripped
イベントを発行します。このイベントは、ガードレールを作動させた itemId
を含む details
オブジェクトも提供します。
import { RealtimeOutputGuardrail, RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.',});
const guardrails: RealtimeOutputGuardrail[] = [ { name: 'No mention of Dom', async execute({ agentOutput }) { const domInOutput = agentOutput.includes('Dom'); return { tripwireTriggered: domInOutput, outputInfo: { domInOutput }, }; }, },];
const guardedSession = new RealtimeSession(agent, { outputGuardrails: guardrails,});
デフォルトでは、ガードレールは 100 文字ごと、または応答テキストの生成が完了した時点で実行されます。音声での読み上げは通常それより時間がかかるため、たいていの場合、ユーザーが聞く前に違反を検知できます。
この動作を変更したい場合は、outputGuardrailSettings
オブジェクトをセッションに渡せます。
import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.',});
const guardedSession = new RealtimeSession(agent, { outputGuardrails: [ /*...*/ ], outputGuardrailSettings: { debounceTextLength: 500, // run guardrail every 500 characters or set it to -1 to run it only at the end },});
ターン検出 / 音声活動検出
Section titled “ターン検出 / 音声活動検出”Realtime Session は、ユーザーが話しているタイミングを自動的に検出し、組み込みの Realtime API の音声活動検出モード を使用して新しいターンを開始します。
音声活動検出モードは、turnDetection
オブジェクトをセッションに渡すことで変更できます。
import { RealtimeSession } from '@openai/agents/realtime';import { agent } from './agent';
const session = new RealtimeSession(agent, { model: 'gpt-4o-realtime-preview-2025-06-03', config: { turnDetection: { type: 'semantic_vad', eagerness: 'medium', createResponse: true, interruptResponse: true, }, },});
ターン検出設定を調整することで、不要な割り込みの調整や無音への対処に役立ちます。各種設定の詳細は Realtime API ドキュメント を参照してください
組み込みの音声活動検出を使用している場合、エージェントの発話中に話し始めると、自動的にエージェントがそれを検出し、発話内容に基づいてコンテキストを更新します。また、audio_interrupted
イベントを発行します。これは、すべての音声再生を即時停止するために使用できます(WebSocket 接続にのみ適用)。
import { session } from './agent';
session.on('audio_interrupted', () => { // handle local playback interruption});
手動で割り込みを行いたい場合、例えば UI に「停止」ボタンを用意したいときは、interrupt()
を手動で呼び出せます:
import { session } from './agent';
session.interrupt();// this will still trigger the `audio_interrupted` event for you// to cut off the audio playback when using WebSockets
いずれの場合も、Realtime Session はエージェントの生成の割り込み、ユーザーに対して話された内容の切り詰め、履歴の更新を処理します。
エージェントに接続するのに WebRTC を使用している場合は、音声出力もクリアされます。WebSocket を使用している場合は、キューに入って再生予定の音声の再生停止などを自分で処理する必要があります。
テキスト入力
Section titled “テキスト入力”エージェントにテキスト入力を送信したい場合は、RealtimeSession
の sendMessage
メソッドを使用できます。
ユーザーがエージェントと両方のモダリティでやりとりできるようにしたい場合や、会話に追加のコンテキストを提供したい場合に有用です。
import { RealtimeSession, RealtimeAgent } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Assistant',});
const session = new RealtimeSession(agent, { model: 'gpt-4o-realtime-preview-2025-06-03',});
session.sendMessage('Hello, how are you?');
会話履歴の管理
Section titled “会話履歴の管理”RealtimeSession
は、history
プロパティで会話履歴を自動管理します:
これを使用して、顧客に履歴をレンダリングしたり、追加の処理を行ったりできます。この履歴は会話の進行に伴って継続的に変化するため、history_updated
イベントを監視できます。
履歴を変更したい場合、例えばメッセージを完全に削除したり、その文字起こしを更新したりするには、updateHistory
メソッドを使用できます。
import { RealtimeSession, RealtimeAgent } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Assistant',});
const session = new RealtimeSession(agent, { model: 'gpt-4o-realtime-preview-2025-06-03',});
await session.connect({ apiKey: '<client-api-key>' });
// listening to the history_updated eventsession.on('history_updated', (history) => { // returns the full history of the session console.log(history);});
// Option 1: explicit settingsession.updateHistory([ /* specific history */]);
// Option 2: override based on current state like removing all agent messagessession.updateHistory((currentHistory) => { return currentHistory.filter( (item) => !(item.type === 'message' && item.role === 'assistant'), );});
- 現在、関数ツールの呼び出しを後から更新・変更することはできません
- 履歴でのテキスト出力には、文字起こしとテキストモダリティが有効である必要があります
- 割り込みにより切り詰められた応答には文字起こしがありません
ツールによる委譲
Section titled “ツールによる委譲”会話履歴とツール呼び出しを組み合わせることで、会話を別のバックエンド エージェントに委譲して、より複雑なアクションを実行し、その結果をユーザーに返すことができます。
import { RealtimeAgent, RealtimeContextData, tool,} from '@openai/agents/realtime';import { handleRefundRequest } from './serverAgent';import z from 'zod';
const refundSupervisorParameters = z.object({ request: z.string(),});
const refundSupervisor = tool< typeof refundSupervisorParameters, RealtimeContextData>({ name: 'escalateToRefundSupervisor', description: 'Escalate a refund request to the refund supervisor', parameters: refundSupervisorParameters, execute: async ({ request }, details) => { // This will execute on the server return handleRefundRequest(request, details?.context?.history ?? []); },});
const agent = new RealtimeAgent({ name: 'Customer Support', instructions: 'You are a customer support agent. If you receive any requests for refunds, you need to delegate to your supervisor.', tools: [refundSupervisor],});
以下のコードは サーバー 上で実行されます。この例では Next.js の server actions を通じて実行します。
// This runs on the serverimport 'server-only';
import { Agent, run } from '@openai/agents';import type { RealtimeItem } from '@openai/agents/realtime';import z from 'zod';
const agent = new Agent({ name: 'Refund Expert', instructions: 'You are a refund expert. You are given a request to process a refund and you need to determine if the request is valid.', model: 'o4-mini', outputType: z.object({ reasong: z.string(), refundApproved: z.boolean(), }),});
export async function handleRefundRequest( request: string, history: RealtimeItem[],) { const input = `The user has requested a refund.
The request is: ${request}
Current conversation history:${JSON.stringify(history, null, 2)}`.trim();
const result = await run(agent, input);
return JSON.stringify(result.finalOutput, null, 2);}