跳至主要內容

Mastra 的語音轉語音功能

簡介
「簡介」的直接連結

Mastra 的語音轉語音(STS)提供標準化介面,可透過多個 Provider 進行即時互動。 STS 會監聽 Realtime 模型的事件,實現持續的雙向音訊通訊。不同於各自獨立的 TTS 與 STT 操作,STS 會維持開放連線,持續處理雙向語音。

設定
「設定」的直接連結

  • apiKey:你的 OpenAI API 金鑰。若未提供,則使用 OPENAI_API_KEY 環境變數。
  • model:即時語音互動要使用的模型 ID(例如 gpt-5.1-realtime)。
  • speaker:語音合成的預設聲音 ID。你可以指定語音輸出要使用的聲音。
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

在即時工作階段中使用 Tool
「在即時工作階段中使用 Tool」的直接連結

Realtime Voice Provider 可使用 Agent 上設定的 Tool。請將 Tool 加入 Agent 定義,接著透過 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、播放助理音訊、記錄轉錄內容及處理錯誤:

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)
})

事件名稱與 payload 會因 Provider 而異。完整事件清單請查看下方的 Provider 章節或 Provider 參考文件。

每個工作階段各自使用 Voice 執行個體
「每個工作階段各自使用 Voice 執行個體」的直接連結

靜態 voice 執行個體會由所有請求共用。此方式適用於單次文字轉語音,但即時與語音轉語音 Provider 會儲存 WebSocket 連線、Tool、instructions 及 request context 等工作階段狀態。若一個 Agent 同時處理多個即時工作階段,共用執行個體可能會讓其中一個工作階段覆寫另一個工作階段的狀態。

當每個即時工作階段都需要自己的 Voice 執行個體時,請以 resolver 形式提供 voice。每次呼叫 getVoice() 時,Mastra 都會執行 resolver,並為該 request context 傳回新的執行個體:

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()

使用 resolver 時:

  • 每次呼叫 getVoice() 都會傳回新的執行個體,因此並行工作階段不會共用狀態。
  • Mastra 不會將 Tool 或 instructions 加入 resolver 執行個體。請在 resolver 內或 Provider 上進行設定。
  • 你必須管理傳回執行個體的生命週期,因此請在工作階段結束時呼叫 disconnect()close()

agent.voice getter 沒有 request context,因此當 voice 是 resolver 時會擲回錯誤。請改用 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 則需要 project/location 與服務帳戶憑證。
  • 事件: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 憑證 Provider chain 進行身分驗證。傳入 credentials 即可覆寫。
  • 事件:speaking(Int16Array 音訊)、writing(含 generationStage 的文字)、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。模型是透過初始 session.update 設定,而非在 URL 中設定。
  • Inworld 的 wire protocol 採用 OpenAI Realtime GA 規格,因此事件名稱與 @mastra/voice-openai-realtime 相同。
  • 具型別的 Inworld 即時控制項(MCP Tool 路由、語意 VAD eagerness、播放速度、轉錄模型、輸出模態等)會透過 session constructor 欄位公開。系統也會深層合併未具型別的 providerData escape hatch,以便向前相容於 Inworld 新功能。
  • 事件:speaker(PCM 音訊串流)、speaking(每個 delta 的音訊 Buffer)、writing(文字)、conversation.item.addedconversation.item.donefunction_call.argumentstool-call-starttool-call-resulterror