音声文字起こしと音声生成
AIモデルを使用して、音声ファイルをテキストに文字起こしし、テキストから音声を生成します。
AI Audio を使用する理由
アプリケーションで音声を扱う必要がある場合があります。要約したい会議の録音、検索したいボイスメモ、または読み上げたい文章などです。これをゼロから構築するには、音声プロバイダーとの直接統合、APIキーの管理、ファイルアップロードの処理、フォーマット変換コードの記述が必要になります。
Squid AI Audio では、文字起こしと text-to-speech の両方を単一のバックエンド呼び出しで利用できます。
- TypeScript
- Python
// Transcribe an audio file to text
const text = await this.squid.ai().audio().transcribe(audioFile, {
modelName: 'whisper-1',
});
// Generate speech from text
const speechFile = await this.squid.ai().audio().createSpeech('Welcome to Squid', {
modelName: 'tts-1',
voice: 'nova',
});
# Transcribe an audio file to text
text = await self.squid.ai().audio().transcribe(
audio_bytes,
'recording.mp3',
'audio/mpeg',
options={'modelName': 'whisper-1'},
)
# Generate speech from text
speech_bytes = await self.squid.ai().audio().create_speech(
'Welcome to Squid',
{'modelName': 'tts-1', 'voice': 'nova'},
)
概要
Squid AI Audio は、単一の API の背後で OpenAI の Whisper(speech-to-text)モデルと TTS(text-to-speech)モデルをラップします。バックエンドからメソッドを呼び出すと、Squid が認証、ファイルアップロード、フォーマット変換、レスポンスのデコードを処理します。
AI Audio を使用する場面
| ユースケース | 推奨事項 |
|---|---|
| 音声を検索可能なテキストに変換する | transcribe() |
| 合成音声でテキストを読み上げる | createSpeech() |
| AI agent のチャット体験に音声入力を組み込む | AI chat widget の enable-transcription フラグを使用 |
| AI agent 用のカスタム音声 | voice options を持つ agents を使用 |
| agent を電話通話やブラウザでのライブ音声会話に組み込む | voice agents を使用 |
仕組み
- バックエンドサービスから
this.squid.ai().audio().transcribe()または.createSpeech()を呼び出します。 - Squid バックエンドがリクエストを認証し、アプリケーション設定から OpenAI APIキーを検索して、呼び出しを転送します。
- 文字起こしでは、音声ファイルがモデルにストリーミングされ、文字起こし結果がテキストとして返されます。
- 音声生成では、モデルがバイナリ音声を返し、Squid がそれを
Fileオブジェクト(TypeScript)またはbytes(Python)でラップします。
クイックスタート
前提条件
squid initで初期化された Squid バックエンドプロジェクト@squidcloud/backendパッケージ(TypeScript)またはsquidcloud-backendパッケージ(Python)- Squid Console でアプリケーションの AI provider として設定された OpenAI
ステップ 1: 音声呼び出しをラップする executable を作成する
音声操作には Squid リソースへの admin access が必要なため、バックエンドで実行する必要があります。クライアントが安全に呼び出せるよう、executable でラップします。
- TypeScript
- Python
import { executable, SquidFile, SquidService } from '@squidcloud/backend';
export class AudioService extends SquidService {
@executable()
async transcribeAudio(audio: SquidFile): Promise<string> {
this.assertIsAuthenticated();
// SquidFile carries the original filename and MIME type from the client.
// Convert it to a native File so the audio client can stream it to OpenAI.
const file = new File([audio.data], audio.originalName, { type: audio.mimetype });
return this.squid.ai().audio().transcribe(file, {
modelName: 'whisper-1',
});
}
}
from squidcloud_backend import SquidFile, SquidService, executable
class AudioService(SquidService):
@executable()
async def transcribe_audio(self, audio: SquidFile) -> str:
self.assert_is_authenticated()
# SquidFile is a TypedDict carrying the file bytes plus metadata.
return await self.squid.ai().audio().transcribe(
audio['data'],
audio['originalName'],
audio['mimetype'],
options={'modelName': 'whisper-1'},
)
ステップ 2: バックエンドをデプロイするか、ローカルで実行する
squid start
クラウドへのデプロイについては、バックエンドのデプロイを参照してください。
ステップ 3: クライアントから executable を呼び出す
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
const audioFile = fileInput.files![0];
const transcript = await squid.executeFunction('transcribeAudio', audioFile);
console.log(transcript);
認証と設定
音声メソッドには、admin access を持つ認証済み Squid クライアントが必要です。これは transcribe() と createSpeech() の両方に当てはまります。推奨されるパターンは 2 つあります。
- 呼び出しを executables でラップする。 標準的なパターンです。executable は admin context でバックエンド上で実行され、クライアントが基盤となる APIキーを見ることはありません。executablesを参照してください。
- 権限を持つバックエンドサービスから呼び出す。 triggers、schedulers、webhooks、その他のバックエンド専用エントリポイントはすでにバックエンド権限で実行されるため、これらから音声メソッドを直接呼び出せます。
認証されていないブラウザクライアントからの呼び出しは、UNAUTHORIZED エラーで拒否されます。
OpenAI は Squid app で external service として有効にする必要があります。APIキーは Squid Console に保存され、音声クライアントはリクエスト時にそれを検索します。
コアコンセプト
文字起こしモデル
Squid は 3 種類の OpenAI 文字起こしモデルをサポートしています。
| モデル | 備考 |
|---|---|
whisper-1 | デフォルト。最も幅広い response format をサポートし、最も低コストです。 |
gpt-4o-transcribe | 高精度。JSON のみを返します。 |
gpt-4o-mini-transcribe | gpt-4o-transcribe より小型かつ低コスト。JSON のみを返します。 |
文字起こしオプション
AiAudioTranscribeOptions は modelName による discriminated union です。すべてのバリアントで、以下の基本フィールドを共有します。
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
modelName | string | はい | サポートされる文字起こしモデルのいずれか |
temperature | number | いいえ | サンプリング温度 |
prompt | string | いいえ | 文字起こしを誘導する任意のテキスト(例: 固有名詞) |
whisper-1 は追加で以下をサポートします。
| フィールド | 型 | 説明 |
|---|---|---|
responseFormat | string | 'json'、'text'、'srt'、'verbose_json'、'vtt' のいずれか。デフォルトは 'json'。 |
gpt-4o-transcribe と gpt-4o-mini-transcribe は 'json' のみを返します。
モデルの基盤となる response format にかかわらず、メソッドは常にプレーンな文字列を返します。
音声生成モデル
| モデル | 備考 |
|---|---|
tts-1 | より高速、低レイテンシ、やや低い忠実度 |
tts-1-hd | より高い忠実度、低速 |
gpt-4o-mini-tts | 新しい GPT-4o ファミリーの TTS モデル |
音声生成オプション
AiAudioCreateSpeechOptions のフィールド:
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
modelName | string | はい | サポートされる TTS モデルのいずれか |
voice | string | いいえ | 'alloy'、'ash'、'ballad'、'coral'、'echo'、'fable'、'onyx'、'nova'、'sage'、'shimmer'、'verse' のいずれか。デフォルトは 'alloy'。 |
responseFormat | string | いいえ | 音声コンテナフォーマット。'mp3'、'opus'、'aac'、'flac'、'wav'、'pcm' のいずれか。デフォルトは 'mp3'。 |
instructions | string | いいえ | 音声スタイルに関する自由形式のガイダンス(例: 「ゆっくり、明瞭に話す」) |
speed | number | いいえ | 再生速度の倍率。デフォルトは 1.0。 |
戻り値の形式
| メソッド | TypeScript の戻り値 | Python の戻り値 |
|---|---|---|
transcribe() | Promise<string> | str |
createSpeech() | Promise<File> | bytes |
TypeScript では、ファイルの MIME type と拡張子は responseFormat から推論されます。たとえば、responseFormat: 'mp3' を指定したリクエストは、MIME type が audio/mpeg の audio.mp3 という名前のファイルを返します。
コード例
クライアントがアップロードした音声を文字起こしする
- TypeScript
- Python
import { executable, SquidFile, SquidService } from '@squidcloud/backend';
export class AudioService extends SquidService {
@executable()
async transcribeRecording(audio: SquidFile, languageHint?: string): Promise<string> {
this.assertIsAuthenticated();
const file = new File([audio.data], audio.originalName, { type: audio.mimetype });
return this.squid.ai().audio().transcribe(file, {
modelName: 'whisper-1',
// Use the prompt to bias the model toward correct spellings or terminology.
prompt: languageHint,
});
}
}
from squidcloud_backend import SquidFile, SquidService, executable
class AudioService(SquidService):
@executable()
async def transcribe_recording(
self,
audio: SquidFile,
language_hint: str | None = None,
) -> str:
self.assert_is_authenticated()
return await self.squid.ai().audio().transcribe(
audio['data'],
audio['originalName'],
audio['mimetype'],
options={
'modelName': 'whisper-1',
# Use the prompt to bias the model toward correct spellings.
'prompt': language_hint,
},
)
音声を生成してクライアントに返す
この例では、テキストから MP3 ファイルを作成し、クライアントが再生できるよう base64 として返します。
- TypeScript
- Python
import { executable, SquidService } from '@squidcloud/backend';
export class SpeechService extends SquidService {
@executable()
async narrate(text: string): Promise<{ base64: string; mimeType: string }> {
this.assertIsAuthenticated();
const audioFile = await this.squid.ai().audio().createSpeech(text, {
modelName: 'tts-1-hd',
voice: 'nova',
responseFormat: 'mp3',
speed: 1.0,
});
// Convert the File to base64 so it can travel back to the client over JSON.
const buffer = Buffer.from(await audioFile.arrayBuffer());
return {
base64: buffer.toString('base64'),
mimeType: audioFile.type,
};
}
}
生成した音声を長期保存する場合は、インラインで返すのではなく、結果を storage connector にアップロードしてください。
import base64
from squidcloud_backend import SquidService, executable
class SpeechService(SquidService):
@executable()
async def narrate(self, text: str) -> dict:
self.assert_is_authenticated()
audio_bytes = await self.squid.ai().audio().create_speech(
text,
{
'modelName': 'tts-1-hd',
'voice': 'nova',
'responseFormat': 'mp3',
'speed': 1.0,
},
)
# Return the audio as base64 so it can travel back to the client over JSON.
return {
'base64': base64.b64encode(audio_bytes).decode('ascii'),
'mimeType': 'audio/mpeg',
}
往復処理: 音声を生成してから再度文字起こしする
integration test または sanity check に役立つパターンです。
- TypeScript
- Python
const speechFile = await this.squid.ai().audio().createSpeech('The quick brown fox jumps over the lazy dog.', {
modelName: 'tts-1',
});
const transcript = await this.squid.ai().audio().transcribe(speechFile, {
modelName: 'whisper-1',
});
console.log(transcript); // "The quick brown fox jumps over the lazy dog."
audio_bytes = await self.squid.ai().audio().create_speech(
'The quick brown fox jumps over the lazy dog.',
{'modelName': 'tts-1'},
)
transcript = await self.squid.ai().audio().transcribe(
audio_bytes,
'speech.mp3',
'audio/mpeg',
options={'modelName': 'whisper-1'},
)
print(transcript) # "The quick brown fox jumps over the lazy dog."
エラー処理
一般的なエラー
| エラー | 原因 | 解決策 |
|---|---|---|
UNAUTHORIZED | 音声呼び出しが non-admin context から発生した(例: バックエンドなしのブラウザ) | 呼び出しを executable でラップするか、バックエンドコードから呼び出す |
Unsupported audio model | modelName がサポート対象リストにない | リストに記載された文字起こしモデルまたは TTS モデルを使用する |
OpenAI external services are disabled | アプリケーションで OpenAI が有効になっていない | Squid Console で OpenAI を有効にし、APIキーを設定する |
| File too large | アップロードされた音声が上流プロバイダーの制限を超えている | Whisper のアップロード上限は 25 MB です。このサイズを超える音声は分割または圧縮してください。 |
呼び出し前に入力を検証する
OpenAI Whisper API は 25 MB より大きいファイルを拒否します。上流のエラーを待つのではなく、executable 内で大きすぎるアップロードを拒否してください。
- TypeScript
- Python
@executable()
async transcribeAudio(audio: SquidFile): Promise<string> {
this.assertIsAuthenticated();
const MAX_BYTES = 25 * 1024 * 1024;
if (audio.size > MAX_BYTES) {
throw new Error('Audio file exceeds 25 MB. Split or compress before transcribing.');
}
if (!audio.mimetype.startsWith('audio/')) {
throw new Error('Only audio files are accepted');
}
const file = new File([audio.data], audio.originalName, { type: audio.mimetype });
return this.squid.ai().audio().transcribe(file, { modelName: 'whisper-1' });
}
@executable()
async def transcribe_audio(self, audio: SquidFile) -> str:
self.assert_is_authenticated()
MAX_BYTES = 25 * 1024 * 1024
if audio['size'] > MAX_BYTES:
raise ValueError('Audio file exceeds 25 MB. Split or compress before transcribing.')
if not audio['mimetype'].startswith('audio/'):
raise ValueError('Only audio files are accepted')
return await self.squid.ai().audio().transcribe(
audio['data'],
audio['originalName'],
audio['mimetype'],
options={'modelName': 'whisper-1'},
)
ベストプラクティス
- 音声呼び出しは常に executables でラップする。 音声メソッドには admin access が必要です。ブラウザから直接公開すると、APIキーが漏洩し、認証がバイパスされます。
transcribe()を呼び出す前に、ファイルサイズと MIME type を検証する。 上流プロバイダーにも独自の制限があり、不正な入力を早期に拒否することで、ユーザーによりよいエラーメッセージを提供できます。- 文字起こしでは
promptフィールドを使用する。 ドメイン固有の語彙、固有名詞、想定される言語を渡します。これは精度を向上させる最も低コストな方法です。 - **ストリーミングまたは低レイテンシ UX には
tts-1を選択し、**レイテンシより品質が重要なオフラインアセットにはtts-1-hdを選択します。 - 音声 executables に rate limiting を適用する。 音声生成と文字起こしはどちらも有料 API quota を消費します。
- 生成された音声をキャッシュする。 同じ入力に対する text-to-speech は、特定の voice では常に同じ出力を生成するため、キャッシュにより quota を節約できます。
関連項目
- Executables - クライアントから呼び出せるよう音声呼び出しをラップする
- AI agent - 音声入力と出力を備えた AI agent を構築する
- AI chat widget - chat widget は
enable-transcriptionを通じて音声入力を公開する - Rate and quota limiting - 音声 executables を不正利用から保護する