エージェントに音声機能を与える
Mastra のエージェントは音声機能を追加することで、話すことも聞くこともできるようになります。以下の例では、音声機能を構成する2つの方法を示します。
- 入力ストリームと出力ストリームを分ける複合的な音声構成を使う方法
- 両方を扱う統合型の音声プロバイダーを使う方法
どちらの例でも、デモ用に OpenAIVoice
プロバイダーを使用しています。
前提条件
この例では openai
モデルを使用します。OPENAI_API_KEY
を .env
ファイルに追加してください。
OPENAI_API_KEY=<your-api-key>
インストール
npm install @mastra/voice-openai
ハイブリッド音声エージェント
このエージェントは、音声認識(STT)と音声合成(TTS)を分離した複合的な音声構成を使用します。CompositeVoice
を使うと、聞き取り(入力)と発話(出力)で別々のプロバイダーを設定できます。なお、この例ではどちらも同じプロバイダーである OpenAIVoice
が処理します。
import { Agent } from "@mastra/core/agent";
import { CompositeVoice } from "@mastra/core/voice";
import { OpenAIVoice } from "@mastra/voice-openai";
import { openai } from "@ai-sdk/openai";
export const hybridVoiceAgent = new Agent({
name: "hybrid-voice-agent",
model: openai("gpt-4o"),
instructions: "You can speak and listen using different providers.",
voice: new CompositeVoice({
input: new OpenAIVoice(),
output: new OpenAIVoice()
})
});
設定オプションの一覧については Agent を参照してください。
統合型ボイスエージェント
このエージェントは、音声認識(STT)と音声合成(TTS)の両方に同一のボイスプロバイダーを使用します。聞き取りと発話の両方で同じプロバイダーを使う場合、よりシンプルに構成できます。この例では、OpenAIVoice
プロバイダーが両方の機能を担います。
import { openai } from "@ai-sdk/openai";
import { Agent } from "@mastra/core/agent";
import { OpenAIVoice } from "@mastra/voice-openai";
export const unifiedVoiceAgent = new Agent({
name: "unified-voice-agent",
instructions: "You are an agent with both STT and TTS capabilities.",
model: openai("gpt-4o"),
voice: new OpenAIVoice()
});
設定オプションの一覧については Agent を参照してください。
エージェントの登録
これらのエージェントを使用するには、メインの Mastra インスタンスに登録します。
import { Mastra } from "@mastra/core/mastra";
import { hybridVoiceAgent } from "./agents/example-hybrid-voice-agent";
import { unifiedVoiceAgent } from "./agents/example-unified-voice-agent";
export const mastra = new Mastra({
// ...
agents: { hybridVoiceAgent, unifiedVoiceAgent }
});
関数
以下のヘルパー関数は、音声対話の例における音声ファイルの操作とテキスト変換を行います。
saveAudioToFile
この関数は、オーディオディレクトリに音声ストリームをファイルとして保存し、ディレクトリが存在しない場合は作成します。
import fs, { createWriteStream } from "fs";
import path from "path";
export const saveAudioToFile = async (audio: NodeJS.ReadableStream, filename: string): Promise<void> => {
const audioDir = path.join(process.cwd(), "audio");
const filePath = path.join(audioDir, filename);
await fs.promises.mkdir(audioDir, { recursive: true });
const writer = createWriteStream(filePath);
audio.pipe(writer);
return new Promise((resolve, reject) => {
writer.on("finish", resolve);
writer.on("error", reject);
});
};
convertToText
この関数は、string
または ReadableStream
をテキストに変換し、音声処理における両方の入力形式に対応します。
export const convertToText = async (input: string | NodeJS.ReadableStream): Promise<string> => {
if (typeof input === "string") {
return input;
}
const chunks: Buffer[] = [];
return new Promise((resolve, reject) => {
input.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
input.on("error", reject);
input.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
});
};
使用例
この例では、2つのエージェント間での音声対話を示します。ハイブリッド音声エージェントが質問を音声で発し、その音声がファイルとして保存されます。ユニファイド音声エージェントはそのファイルを再生して内容を取得し、質問を処理して応答を生成し、音声で返答します。両方の音声出力は audio
ディレクトリに保存されます。
作成されるファイルは次のとおりです。
- hybrid-question.mp3 – ハイブリッドエージェントが話した質問。
- unified-response.mp3 – ユニファイドエージェントが話した応答。
import "dotenv/config";
import path from "path";
import { createReadStream } from "fs";
import { mastra } from "./mastra";
import { saveAudioToFile } from "./mastra/utils/save-audio-to-file";
import { convertToText } from "./mastra/utils/convert-to-text";
const hybridVoiceAgent = mastra.getAgent("hybridVoiceAgent");
const unifiedVoiceAgent = mastra.getAgent("unifiedVoiceAgent");
const question = "What is the meaning of life in one sentence?";
const hybridSpoken = await hybridVoiceAgent.voice.speak(question);
await saveAudioToFile(hybridSpoken!, "hybrid-question.mp3");
const audioStream = createReadStream(path.join(process.cwd(), "audio", "hybrid-question.mp3"));
const unifiedHeard = await unifiedVoiceAgent.voice.listen(audioStream);
const inputText = await convertToText(unifiedHeard!);
const unifiedResponse = await unifiedVoiceAgent.generate(inputText);
const unifiedSpoken = await unifiedVoiceAgent.voice.speak(unifiedResponse.text);
await saveAudioToFile(unifiedSpoken!, "unified-response.mp3");