ASR について
z-ai-web-dev-sdk を使った音声テキスト化スキルで、アプリやスクリプトで音声を扱う開発者向けです。CLI による手軽な文字起こしと、バックエンドコードへの SDK 組み込みに対応します。
主な機能
- 01音声をテキストに変換
- 02ファイルまたは Base64 の入力
- 03文字起こし結果のストリーミング
- 04複数音声ファイルの処理
- 05SDK によるバックエンド処理の構築
活用シーン
- 01会議やインタビュー録音の文字起こし
- 02ポッドキャストのテキスト化
- 03ボイスメモのテキスト変換
- 04顧客対応通話の分析
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ answerzhao/ agent-skills --skill asrインストール後、Claude Code で/asrを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSE.txt.DS_Storescripts/asr.ts詳細
ASR (音声テキスト化) スキル
このスキルは、z-ai-web-dev-sdk パッケージを使用した音声テキスト化 (ASR) 機能の実装をガイドし、話された音声をテキストへ正確に文字起こしできるようにします。
スキルのパス
スキルの場所: {project_path}/skills/ASR
このスキルは、あなたのプロジェクト内の上記のパスに配置されています。
リファレンススクリプト: 簡単なテストと参照用のサンプルテストスクリプトが {Skill Location}/scripts/ ディレクトリに用意されています。動作する例については {Skill Location}/scripts/asr.ts を参照してください。
概要
Speech-to-Text (ASR - Automatic Speech Recognition) を使用すると、音声ファイル内の話し言葉を書き言葉のテキストに変換するアプリケーションを構築でき、音声制御インターフェース、文字起こしサービス、音声コンテンツ分析などが実現できます。
重要: z-ai-web-dev-sdk はバックエンドコードでのみ使用する必要があります。クライアント側のコードでは決して使用しないでください。
前提条件
z-ai-web-dev-sdk パッケージはすでにインストールされています。以下の例に示すようにインポートしてください。
CLI の使用方法 (簡単なタスク向け)
簡単な音声文字起こしタスクでは、コードを書く代わりに z-ai CLI を使用できます。これはクイックな文字起こし、音声ファイルのテスト、バッチ処理に最適です。
ファイルからの基本的な文字起こし
# Transcribe an audio file
z-ai asr --file ./audio.wav
# Save transcription to JSON file
z-ai asr -f ./recording.mp3 -o transcript.json
# Transcribe and view output
z-ai asr --file ./interview.wav --output result.json
Base64 からの文字起こし
# Transcribe from base64 encoded audio
z-ai asr --base64 "UklGRiQAAABXQVZFZm10..." -o result.json
# Using short option
z-ai asr -b "base64_encoded_audio_data" -o transcript.json
ストリーミング出力
# Stream transcription results
z-ai asr -f ./audio.wav --stream
CLI パラメータ
--file, -f <path>: 必須 (--base64 を使用しない場合) - 音声ファイルのパス--base64, -b <base64>: 必須 (--file を使用しない場合) - Base64 エンコードされた音声--output, -o <path>: オプション - 出力ファイルのパス (JSON 形式)--stream: オプション - 文字起こしの出力をストリーミングする
対応音声フォーマット
ASR サービスは、以下を含むさまざまな音声フォーマットに対応しています:
- WAV (.wav)
- MP3 (.mp3)
- その他の一般的な音声フォーマット
CLI と SDK の使い分け
CLI を使用する場合:
- 音声ファイルのクイックな文字起こし
- 音声認識精度のテスト
- 簡単なバッチ処理スクリプト
- 単発の文字起こしタスク
SDK を使用する場合:
- アプリケーションでのリアルタイム音声文字起こし
- 録音システムとの統合
- カスタム音声処理ワークフロー
- ストリーミング音声を扱う本番アプリケーション
基本的な ASR の実装
シンプルな音声文字起こし
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
async function transcribeAudio(audioFilePath) {
const zai = await ZAI.create();
// Read audio file and convert to base64
const audioFile = fs.readFileSync(audioFilePath);
const base64Audio = audioFile.toString('base64');
const response = await zai.audio.asr.create({
file_base64: base64Audio
});
return response.text;
}
// Usage
const transcription = await transcribeAudio('./audio.wav');
console.log('Transcription:', transcription);
複数の音声ファイルの文字起こし
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
async function transcribeBatch(audioFilePaths) {
const zai = await ZAI.create();
const results = [];
for (const filePath of audioFilePaths) {
try {
const audioFile = fs.readFileSync(filePath);
const base64Audio = audioFile.toString('base64');
const response = await zai.audio.asr.create({
file_base64: base64Audio
});
results.push({
file: filePath,
success: true,
transcription: response.text
});
} catch (error) {
results.push({
file: filePath,
success: false,
error: error.message
});
}
}
return results;
}
// Usage
const files = ['./interview1.wav', './interview2.wav', './interview3.wav'];
const transcriptions = await transcribeBatch(files);
transcriptions.forEach(result => {
if (result.success) {
console.log(`${result.file}: ${result.transcription}`);
} else {
console.error(`${result.file}: Error - ${result.error}`);
}
});
高度なユースケース
メタデータ付きの音声ファイル処理
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
import path from 'path';
async function transcribeWithMetadata(audioFilePath) {
const zai = await ZAI.create();
// Get file metadata
const stats = fs.statSync(audioFilePath);
const audioFile = fs.readFileSync(audioFilePath);
const base64Audio = audioFile.toString('base64');
const startTime = Date.now();
const response = await zai.audio.asr.create({
file_base64: base64Audio
});
const endTime = Date.now();
return {
filename: path.basename(audioFilePath),
filepath: audioFilePath,
fileSize: stats.size,
transcription: response.text,
wordCount: response.text.split(/\s+/).length,
processingTime: endTime - startTime,
timestamp: new Date().toISOString()
};
}
// Usage
const result = await transcribeWithMetadata('./meeting_recording.wav');
console.log('Transcription Details:', JSON.stringify(result, null, 2));
リアルタイム音声処理サービス
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
class ASRService {
constructor() {
this.zai = null;
this.transcriptionCache = new Map();
}
async initialize() {
this.zai = await ZAI.create();
}
generateCacheKey(audioBuffer) {
const crypto = require('crypto');
return crypto.createHash('md5').update(audioBuffer).digest('hex');
}
async transcribe(audioFilePath, useCache = true) {
const audioBuffer = fs.readFileSync(audioFilePath);
const cacheKey = this.generateCacheKey(audioBuffer);
// Check cache
if (useCache && this.transcriptionCache.has(cacheKey)) {
return {
transcription: this.transcriptionCache.get(cacheKey),
cached: true
};
}
// Transcribe audio
const base64Audio = audioBuffer.toString('base64');
const response = await this.zai.audio.asr.create({
file_base64: base64Audio
});
// Cache result
if (useCache) {
this.transcriptionCache.set(cacheKey, response.text);
}
return {
transcription: response.text,
cached: false
};
}
clearCache() {
this.transcriptionCache.clear();
}
getCacheSize() {
return this.transcriptionCache.size;
}
}
// Usage
const asrService = new ASRService();
await asrService.initialize();
const result1 = await asrService.transcribe('./audio.wav');
console.log('First call (not cached):', result1);
const result2 = await asrService.transcribe('./audio.wav');
console.log('Second call (cached):', result2);
ディレクトリの文字起こし
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
import path from 'path';
async function transcribeDirectory(directoryPath, outputJsonPath) {
const zai = await ZAI.create();
// Get all audio files
const files = fs.readdirSync(directoryPath);
const audioFiles = files.filter(file =>
/\.(wav|mp3|m4a|flac|ogg)$/i.test(file)
);
const results = {
directory: directoryPath,
totalFiles: audioFiles.length,
processedAt: new Date().toISOString(),
transcriptions: []
};
for (const filename of audioFiles) {
const filePath = path.join(directoryPath, filename);
try {
const audioFile = fs.readFileSync(filePath);
const base64Audio = audioFile.toString('base64');
const response = await zai.audio.asr.create({
file_base64: base64Audio
});
results.transcriptions.push({
filename: filename,
success: true,
text: response.text,
wordCount: response.text.split(/\s+/).length
});
console.log(`✓ Transcribed: ${filename}`);
} catch (error) {
results.transcriptions.push({
filename: filename,
success: false,
error: error.message
});
console.error(`✗ Failed: ${filename} - ${error.message}`);
}
}
// Save results to JSON
fs.writeFileSync(
outputJsonPath,
JSON.stringify(results, null, 2)
);
return results;
}
// Usage
const results = await transcribeDirectory(
'./audio-recordings',
'./transcriptions.json'
);
console.log(`\nProcessed ${results.totalFiles} files`);
console.log(`Successful: ${results.transcriptions.filter(t => t.success).length}`);
console.log(`Failed: ${results.transcriptions.filter(t => !t.success).length}`);
ベストプラクティス
1. 音声フォーマットの取り扱い
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
async function transcribeAnyFormat(audioFilePath) {
// Supported formats: WAV, MP3, M4A, FLAC, OGG, etc.
const validExtensions = ['.wav', '.mp3', '.m4a', '.flac', '.ogg'];
const ext = audioFilePath.toLowerCase().substring(audioFilePath.lastIndexOf('.'));
if (!validExtensions.includes(ext)) {
throw new Error(`Unsupported audio format: ${ext}`);
}
const zai = await ZAI.create();
const audioFile = fs.readFileSync(audioFilePath);
const base64Audio = audioFile.toString('base64');
const response = await zai.audio.asr.create({
file_base64: base64Audio
});
return response.text;
}
2. エラーハンドリング
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
async function safeTranscribe(audioFilePath) {
try {
// Validate file exists
if (!fs.existsSync(audioFilePath)) {
throw new Error(`File not found: ${audioFilePath}`);
}
// Check file size (e.g., limit to 100MB)
const stats = fs.statSync(audioFilePath);
const fileSizeMB = stats.size / (1024 * 1024);
if (fileSizeMB > 100) {
throw new Error(`File too large: ${fileSizeMB.toFixed(2)}MB (max 100MB)`);
}
// Transcribe
const zai = await ZAI.create();
const audioFile = fs.readFileSync(audioFilePath);
const base64Audio = audioFile.toString('base64');
const response = await zai.audio.asr.create({
file_base64: base64Audio
});
if (!response.text || response.text.trim().length === 0) {
throw new Error('Empty transcription result');
}
return {
success: true,
transcription: response.text,
filePath: audioFilePath,
fileSize: stats.size
};
} catch (error) {
console.error('Transcription error:', error);
return {
success: false,
error: error.message,
filePath: audioFilePath
};
}
}
3. 文字起こし結果の後処理
function cleanTranscription(text) {
// Remove excessive whitespace
text = text.replace(/\s+/g, ' ').trim();
// Capitalize first letter of sentences
text = text.replace(/(^\w|[.!?]\s+\w)/g, match => match.toUpperCase());
// Remove filler words (optional)
const fillers = ['um', 'uh', 'ah', 'like', 'you know'];
const fillerPattern = new RegExp(`\\b(${fillers.join('|')})\\b`, 'gi');
text = text.replace(fillerPattern, '').replace(/\s+/g, ' ');
return text;
}
async function transcribeAndClean(audioFilePath) {
const zai = await ZAI.create();
const audioFile = fs.readFileSync(audioFilePath);
const base64Audio = audioFile.toString('base64');
const response = await zai.audio.asr.create({
file_base64: base64Audio
});
return {
raw: response.text,
cleaned: cleanTranscription(response.text)
};
}
一般的なユースケース
- 会議の文字起こし: 録音された会議を検索可能なテキストに変換します
- インタビューの処理: 分析とドキュメント化のためにインタビューを文字起こしします
- ポッドキャストの文字起こし: ポッドキャストのエピソードのテキスト版を作成します
- ボイスメモ: 参照しやすいようにボイスメモをテキストに変換します
- コールセンター分析: カスタマーサービス通話を分析します
- アクセシビリティ: 音声コンテンツにテキストによる代替を提供します
- 音声コマンド: 音声制御アプリケーションを実現します
- 語学学習: 発音練習を文字起こしします
統合の例
Express.js API エンドポイント
import express from 'express';
import multer from 'multer';
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
const app = express();
const upload = multer({ dest: 'uploads/' });
let zaiInstance;
async function initZAI() {
zaiInstance = await ZAI.create();
}
app.post('/api/transcribe', upload.single('audio'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ error: 'No audio file provided' });
}
const audioFile = fs.readFileSync(req.file.path);
const base64Audio = audioFile.toString('base64');
const response = await zaiInstance.audio.asr.create({
file_base64: base64Audio
});
// Clean up uploaded file
fs.unlinkSync(req.file.path);
res.json({
success: true,
transcription: response.text,
wordCount: response.text.split(/\s+/).length
});
} catch (error) {
// Clean up on error
if (req.file && fs.existsSync(req.file.path)) {
fs.unlinkSync(req.file.path);
}
res.status(500).json({
success: false,
error: error.message
});
}
});
initZAI().then(() => {
app.listen(3000, () => {
console.log('ASR API running on port 3000');
});
});
トラブルシューティング
問題: 「SDK must be used in backend」
- 解決策: z-ai-web-dev-sdk がサーバー側のコードでのみインポートされていることを確認してください
問題: 文字起こしが空になる、または正しくない
- 解決策: 音声の品質とフォーマットを確認してください。音声に明瞭な発話が含まれているか確認してください
問題: 大きなファイルの処理が失敗する
- 解決策: 大きな音声ファイルをより小さなセグメントに分割することを検討してください
問題: 文字起こしの速度が遅い
- 解決策: 繰り返しの文字起こしに対するキャッシュを実装し、ファイルサイズを最適化してください
問題: 大きなファイルでメモリエラーが発生する
- 解決策: ファイルをチャンク単位で処理するか、Node.js のメモリ上限を増やしてください
パフォーマンスのヒント
- SDK インスタンスの再利用: 一度作成し、何度も使用する
- キャッシュの実装: 重複するファイルの文字起こしをキャッシュする
- バッチ処理: 適切なキューイングで複数のファイルを効率的に処理する
- 音声の最適化: 可能な場合は処理前に音声ファイルを圧縮する
- 非同期操作: 適切な場合は並列処理に Promise.all を使用する
音声品質のガイドライン
最良の文字起こし結果を得るには:
- サンプルレート: 16kHz 以上
- フォーマット: WAV、MP3、または M4A を推奨
- ノイズレベル: バックグラウンドノイズを最小限に抑える
- 音声の明瞭さ: 明瞭な発音と通常の話速
- ファイルサイズ: 個々のファイルは 100MB 未満を推奨
覚えておくこと
- z-ai-web-dev-sdk は常にバックエンドコードでのみ使用してください
- SDK はすでにインストールされています - 例に示すようにインポートしてください
- 音声ファイルは処理前に base64 に変換する必要があります
- 本番アプリケーションには適切なエラー処理を実装してください
- 最高の文字起こし精度のために音声品質を考慮してください
- 処理後に一時ファイルをクリーンアップしてください
- 頻繁に文字起こしするファイルの結果をキャッシュしてください