メインコンテンツへ移動

Mastra の Speech-to-Speech 機能

はじめに
はじめにへの直接リンク

Mastra の Speech-to-Speech(STS)は、複数の Provider でリアルタイム対話を行うための標準化されたインターフェースを提供します。 STS は Realtime モデルのイベントをリッスンし、継続的な双方向音声通信を実現します。TTS と STT を個別に実行する場合とは異なり、STS は接続を維持し、双方向の音声を継続的に処理します。

設定
設定への直接リンク

  • apiKey: OpenAI API キー。未指定の場合は OPENAI_API_KEY 環境変数を使用します。
  • model: リアルタイム音声対話に使用するモデル ID(例: gpt-5.1-realtime)。
  • speaker: 音声合成に使用するデフォルトの Voice ID。音声出力に使用する Voice を指定できます。
const voice = new OpenAIRealtimeVoice({
apiKey: 'your-openai-api-key',
model: 'gpt-5.1-realtime',
speaker: 'alloy', // Default voice
})

// If using default settings the configuration can be simplified to:
const voice = new OpenAIRealtimeVoice()

STS を使用する
STS を使用するへの直接リンク

import { Agent } from '@mastra/core/agent'
import { OpenAIRealtimeVoice } from '@mastra/voice-openai-realtime'
import { playAudio, getMicrophoneStream } from '@mastra/node-audio'

const agent = new Agent({
id: 'agent',
name: 'OpenAI Realtime Agent',
instructions: `You are a helpful assistant with real-time voice capabilities.`,
model: 'openai/gpt-5.6-sol',
voice: new OpenAIRealtimeVoice(),
})

// Connect to the voice service
await agent.voice.connect()

// Listen for agent audio responses
agent.voice.on('speaker', ({ audio }) => {
playAudio(audio)
})

// Initiate the conversation
await agent.voice.speak('How can I help you today?')

// Send continuous audio from the microphone
const micStream = getMicrophoneStream()
await agent.voice.send(micStream)

Agent で利用できる Voice Provider の概要については、Mastra の Voiceを参照してください。

リアルタイムセッションで Tools を使用する
リアルタイムセッションで Tools を使用するへの直接リンク

Realtime Voice Provider は、Agent に設定された Tools を使用できます。Agent の定義に Tools を追加し、Voice Provider を通じて接続して音声を送信します。

import { Agent } from '@mastra/core/agent'
import { OpenAIRealtimeVoice } from '@mastra/voice-openai-realtime'
import { calculate, search } from '../tools'

export const agent = new Agent({
id: 'speech-to-speech-agent',
name: 'Speech-to-Speech Agent',
instructions: 'You are a helpful assistant with speech-to-speech capabilities.',
model: 'openai/gpt-5.6-sol',
tools: {
search,
calculate,
},
voice: new OpenAIRealtimeVoice(),
})

リアルタイムイベントをリッスンする
リアルタイムイベントをリッスンするへの直接リンク

Realtime Voice Provider は、UI の更新、assistant 音声の再生、文字起こしの記録、エラー処理に使用できるイベントを発行します。

agent.voice.on('speaking', ({ audio }) => {
playAudio(audio)
})

agent.voice.on('writing', ({ text, role }) => {
console.log(`${role}: ${text}`)
})

agent.voice.on('error', error => {
console.error('Voice error:', error)
})

イベント名とペイロードは Provider によって異なります。すべてのイベントについては、以下の Provider セクションまたは各 Provider のリファレンスを確認してください。

セッションごとの Voice インスタンス
セッションごとの Voice インスタンスへの直接リンク

静的な voice インスタンスは、すべてのリクエストで共有されます。単発の Text-to-Speech では問題ありませんが、Realtime および Speech-to-Speech Provider は、WebSocket 接続、Tools、instructions、RequestContext などのセッション状態を保持します。1 つの Agent が複数のライブセッションを同時に処理すると、共有インスタンスにより、あるセッションが別のセッションの状態を上書きする可能性があります。

各ライブセッションに固有の Voice インスタンスが必要な場合は、voice をリゾルバーとして指定します。Mastra は getVoice() の呼び出しごとにリゾルバーを実行し、その RequestContext 用の新しいインスタンスを返します。

import { Agent } from '@mastra/core/agent'
import { RequestContext } from '@mastra/core/request-context'
import { OpenAIRealtimeVoice } from '@mastra/voice-openai-realtime'

export const agent = new Agent({
id: 'support-line',
name: 'Support Line',
instructions: ({ requestContext }) => `Help user ${requestContext.get('user')}.`,
model: 'openai/gpt-5.6-sol',
voice: ({ requestContext }) =>
new OpenAIRealtimeVoice({
apiKey: requestContext.get('apiKey'),
}),
})

const requestContext = new RequestContext()
requestContext.set('user', 'user-123')
requestContext.set('apiKey', process.env.OPENAI_API_KEY)

const voice = await agent.getVoice({ requestContext })
await voice.connect()

リゾルバーを使用する場合は、次の点に注意してください。

  • getVoice() を呼び出すたびに新しいインスタンスが返されるため、同時セッション間で状態は共有されません。
  • Mastra はリゾルバーのインスタンスに Tools や instructions を追加しません。リゾルバー内または Provider で設定してください。
  • 返されたインスタンスのライフサイクルは呼び出し側で管理するため、セッション終了時に disconnect() または close() を呼び出してください。

agent.voice ゲッターには RequestContext がないため、voice がリゾルバーの場合はエラーをスローします。代わりに agent.getVoice({ requestContext }) を使用してください。

Google Gemini Live(Realtime)
Google Gemini Live(Realtime)への直接リンク

import { Agent } from '@mastra/core/agent'
import { GeminiLiveVoice } from '@mastra/voice-google-gemini-live'
import { playAudio, getMicrophoneStream } from '@mastra/node-audio'

const agent = new Agent({
id: 'agent',
name: 'Gemini Live Agent',
instructions: 'You are a helpful assistant with real-time voice capabilities.',
// Model used for text generation; voice provider handles realtime audio
model: 'openai/gpt-5.6-sol',
voice: new GeminiLiveVoice({
apiKey: process.env.GOOGLE_API_KEY,
model: 'gemini-2.0-flash-exp',
speaker: 'Puck',
debug: true,
// Vertex AI option:
// vertexAI: true,
// project: 'your-gcp-project',
// location: 'us-central1',
// serviceAccountKeyFile: '/path/to/service-account.json',
}),
})

await agent.voice.connect()

agent.voice.on('speaker', ({ audio }) => {
playAudio(audio)
})

agent.voice.on('writing', ({ role, text }) => {
console.log(`${role}: ${text}`)
})

await agent.voice.speak('How can I help you today?')

const micStream = getMicrophoneStream()
await agent.voice.send(micStream)

注:

  • Live API には GOOGLE_API_KEY が必要です。Vertex AI にはプロジェクト、ロケーション、サービスアカウントの認証情報が必要です。
  • イベント: speaker(音声ストリーム)、writing(テキスト)、turnCompleteusageerror

AWS Nova Sonic(Realtime)
AWS Nova Sonic(Realtime)への直接リンク

import { Agent } from '@mastra/core/agent'
import { NovaSonicVoice } from '@mastra/voice-aws-nova-sonic'
import { playAudio, getMicrophoneStream } from '@mastra/node-audio'

const agent = new Agent({
id: 'agent',
name: 'Nova Sonic Agent',
instructions: 'You are a helpful assistant with real-time voice capabilities.',
// Model used for text generation; voice provider handles realtime audio
model: 'openai/gpt-5.6-sol',
voice: new NovaSonicVoice({
region: 'us-east-1',
speaker: 'matthew',
// Static credentials are optional. The default AWS credential provider
// chain is used when none are passed.
}),
})

await agent.voice.connect()

// Assistant audio is emitted as 16-bit PCM on the `speaking` event
agent.voice.on('speaking', ({ audioData }) => {
if (audioData) playAudio(audioData)
})

agent.voice.on('writing', ({ role, text }) => {
console.log(`${role}: ${text}`)
})

await agent.voice.speak('How can I help you today?')

const micStream = getMicrophoneStream()
await agent.voice.send(micStream)

注:

  • 利用可能なリージョン: us-east-1us-west-2ap-northeast-1
  • 標準の AWS credential provider chain で認証します。上書きするには credentials を渡します。
  • イベント: speaking(Int16Array 音声)、writinggenerationStage を含むテキスト)、toolCallinterruptturnCompleteusagesessionerror

Inworld Realtime
Inworld Realtimeへの直接リンク

import { Agent } from '@mastra/core/agent'
import { InworldRealtimeVoice } from '@mastra/voice-inworld'
import { playAudio, getMicrophoneStream } from '@mastra/node-audio'

const agent = new Agent({
id: 'agent',
name: 'Inworld Realtime Agent',
instructions: 'You are a helpful assistant with real-time voice capabilities.',
// Model used for text generation; voice provider handles realtime audio
model: 'openai/gpt-5.6-sol',
voice: new InworldRealtimeVoice({
apiKey: process.env.INWORLD_API_KEY,
model: 'inworld/models/gemma-4-26b-a4b-it',
speaker: 'Sarah',
// Typed Inworld realtime knobs (semantic VAD, playback speed, etc.)
// session: {
// audio: {
// output: { speed: 1.1 },
// input: { turn_detection: { type: 'semantic_vad', eagerness: 'high' } },
// },
// },
}),
})

await agent.voice.connect()

agent.voice.on('speaker', stream => {
playAudio(stream)
})

agent.voice.on('writing', ({ role, text }) => {
console.log(`${role}: ${text}`)
})

await agent.voice.speak('How can I help you today?')

const micStream = getMicrophoneStream()
await agent.voice.send(micStream)

注:

  • INWORLD_API_KEY が必要です。Inworld API キーは事前に Basic エンコードされているため、そのまま貼り付けてください。
  • WebSocket URL には、クライアントが生成した ?key=...&protocol=realtime が追加されます。モデルは URL ではなく、最初の session.update で設定します。
  • Inworld のワイヤープロトコルは OpenAI Realtime GA 仕様に準拠しているため、イベント名は @mastra/voice-openai-realtime と一致します。
  • 型付きの Inworld Realtime 設定(MCP Tool のルーティング、semantic VAD の eagerness、再生速度、文字起こしモデル、出力モダリティなど)は、session コンストラクターフィールドで指定できます。型なしの providerData エスケープハッチもディープマージされ、新しい Inworld 機能との前方互換性を確保します。
  • イベント: speaker(PCM 音声ストリーム)、speaking(delta ごとの音声 Buffer)、writing(テキスト)、conversation.item.addedconversation.item.donefunction_call.argumentstool-call-starttool-call-resulterror