关于 ASR
基于 z-ai-web-dev-sdk 的语音转文字技能,面向需要在应用或脚本中处理音频转录的开发者。支持通过 CLI 快速转录,也可在后端代码中集成 SDK。
主要功能
- 01将音频转录为文本
- 02通过文件或 Base64 输入音频
- 03支持流式转录输出
- 04批量处理音频文件
- 05使用 SDK 构建后端转录流程
适用场景
- 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,其中包含可运行的示例。
概述
语音转文本(ASR,即自动语音识别)可让你构建能够将音频文件中的口语转换为书面文本的应用,从而实现语音控制界面、转录服务和音频内容分析。
重要提示: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
- 为生产应用实现适当的错误处理
- 考虑音频质量,以获得最佳转录准确度
- 处理后清理临时文件
- 缓存经常转录的文件的结果
评论
支持 Markdown