阿里云ASR测试功能开发调试实录

📌 关键要点

本文档记录阿里云paraformer-v1异步ASR的完整调试过程,重点解决API调用方式、请求头配置、transcription_url解析等关键问题。


🔗 在线测试

阿里云ASR测试页面:https://byp.com.cn/ping/ali-asr-test/


1. 项目背景

1.1 为什么做阿里云ASR测试页

1.2 参考对象

参考已有的Whisper测试页面: - 路由:/ping/whisper-test/ - 文件:whisper_test.py + whisper_test.html - 特点:紫色主题,本地模型识别

阿里云ASR测试页: - 路由:/ping/ali-asr-test/ - 文件:ali_asr_test.py + ali_asr_test.html - 特点:蓝色主题,云端API识别


2. 第一次错误:同步调用被拒绝

2.1 错误现象

{
  "code": "AccessDenied",
  "message": "current user api does not support synchronous calls"
}

2.2 错误原因

误以为可以使用SDK的同步调用方式,尝试了: - Recognition.call(temp_wav_path) - 失败 - Recognition(callback=None) - 失败

2.3 根本原因

阿里云ASR的paraformer-v1只支持异步调用,必须: 1. 提交任务获取task_id 2. 轮询任务状态 3. 获取识别结果

2.4 解决方案

改用HTTP API + file_urls方式,但这次仍然失败...


3. 第二次错误:缺少关键请求头

3.1 错误现象

同样的错误再次出现:

{
  "code": "AccessDenied", 
  "message": "current user api does not support synchronous calls"
}

3.2 排查过程

对比voice_handler.py的成功案例,发现关键差异:

# ❌ 错误的代码(缺少请求头)
headers = {
    'Authorization': f'Bearer {DASHSCOPE_API_KEY}',
    'Content-Type': 'application/json'
}

# ✅ 正确的代码(必须添加X-DashScope-Async)
headers = {
    'Authorization': f'Bearer {DASHSCOPE_API_KEY}',
    'Content-Type': 'application/json',
    'X-DashScope-Async': 'enable'  # 关键!启用异步调用
}

3.3 核心教训

⚠️ 阿里云ASR HTTP API必须添加 X-DashScope-Async: enable 请求头,否则返回AccessDenied!


4. 第三次错误:识别成功但文本为空

4.1 错误现象

日志显示识别成功,但返回空文本:

🔄 开始阿里云 paraformer-v1 识别...
📤 开始阿里云 ASR 识别,音频大小: 71400 bytes
✅ 阿里云 ASR 识别成功 (1.18秒): 
✅ 阿里云 ASR 识别成功 (1.40秒): 

前端显示: - ⏱️ 耗时: 1.4秒 - 🤖 模型: paraformer-v1 - 📦 大小: 69.7KB - 但是没有识别文字!

4.2 排查过程

添加调试日志打印完整返回结果:

print(f"📋 完整返回结果: {query_result}")

日志输出:

{
  "request_id": "1d70fb51-3464-9802-930f-0b2015af767b",
  "output": {
    "task_id": "06afea4a-43ca-429a-b523-6bb9b5a82197",
    "task_status": "SUCCEEDED",
    "results": [{
      "file_url": "https://byp.com.cn/static/audio/asr_1779109299_bc3f2387.wav",
      "transcription_url": "https://dashscope-result-bj.oss-cn-beijing.aliyuncs.com/...",
      "subtask_status": "SUCCEEDED"
    }]
  }
}

4.3 核心发现

⚠️ 阿里云返回的不是transcription文本,而是transcription_url!需要再次请求这个URL获取识别结果!

4.4 错误解析尝试

第一次尝试解析(错误):

# ❌ 错误:假设是result.sentences结构
sentences = trans_data.get('result', {}).get('sentences', [])
transcription = ' '.join([s.get('text', '') for s in sentences])

结果:仍然为空


5. 第四次错误:JSON结构解析错误

5.1 添加详细调试

trans_data = trans_resp.json()
print(f"📋 transcription JSON: {trans_data}")

5.2 真实的JSON结构

{
  "file_url": "https://byp.com.cn/static/audio/asr_1779109552_62ab6ef1.wav",
  "properties": {
    "channels": [],
    "original_sampling_rate": 16000,
    "original_duration_in_milliseconds": 3900
  },
  "transcripts": [
    {
      "channel_id": 0,
      "content_duration_in_milliseconds": 3518,
      "text": "今天是星期一。",
      "sentences": [...]
    }
  ]
}

5.3 正确的解析方式

⚠️ JSON结构是 transcripts[0].text,不是 result.sentences!

# ✅ 正确的解析方式
transcripts = trans_data.get('transcripts', [])
if transcripts and len(transcripts) > 0:
    transcription = transcripts[0].get('text', '')

6. 最终成功方案

6.1 完整调用流程

def ali_asr_transcribe(audio_data, session_id=''):
    """阿里云 paraformer-v1 异步 ASR 识别"""

    # 1. 转换音频格式(WebM → WAV)
    wav_data = _convert_webm_to_wav(audio_data)

    # 2. 保存到static/audio目录
    wav_save_path = os.path.join(static_dir, 'audio', filename)
    shutil.copy2(temp_wav_path, wav_save_path)

    # 3. 生成HTTP URL
    audio_url = f'https://byp.com.cn/static/audio/{filename}'

    # 4. 提交ASR任务(关键:X-DashScope-Async)
    submit_url = 'https://dashscope.aliyuncs.com/api/v1/services/audio/asr/transcription'
    headers = {
        'Authorization': f'Bearer {DASHSCOPE_API_KEY}',
        'Content-Type': 'application/json',
        'X-DashScope-Async': 'enable'  # 必须!
    }
    payload = {
        'model': 'paraformer-v1',
        'input': {'file_urls': [audio_url]},
        'parameters': {'format': 'wav', 'sample_rate': 16000}
    }

    response = req.post(submit_url, headers=headers, json=payload)
    task_id = response.json()['output']['task_id']

    # 5. 轮询任务状态
    while time.time() - start_time < 60:
        query_url = f'https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}'
        query_result = req.get(query_url, headers=auth_header).json()

        if query_result['output']['task_status'] == 'SUCCEEDED':
            # 6. 获取transcription_url
            transcription_url = query_result['output']['results'][0]['transcription_url']

            # 7. 请求transcription_url获取识别文本
            trans_data = req.get(transcription_url).json()

            # 8. 解析JSON:transcripts[0].text
            transcription = trans_data['transcripts'][0]['text']

            return transcription, None

6.2 关键技术点总结

问题 错误做法 正确做法
调用方式 SDK同步调用 HTTP API异步调用
请求头 缺少X-DashScope-Async 必须添加 X-DashScope-Async: enable
音频提交 直接传二进制 必须传HTTP URL(file_urls)
返回结果 直接获取transcription 获取transcription_url再请求
JSON解析 result.sentences transcripts[0].text

7. 与voice_handler.py对比

7.1 voice_handler.py为什么能成功

查看 app/ping/voice_handler.py 第228-232行:

headers = {
    'Authorization': f'Bearer {api_key}',
    'Content-Type': 'application/json',
    'X-DashScope-Async': 'enable'  # 必须
}

关键:voice_handler.py从一开始就使用了正确的请求头!

7.2 本地测试失败的原因

在本地Windows开发时,即使代码正确也会失败,因为:

⚠️ 阿里云ASR必须部署到服务器上测试,本地无法测试!


8. 文件清单

8.1 核心文件

8.2 技术对比

对比项 Whisper测试 阿里云ASR测试
路由 /ping/whisper-test/ /ping/ali-asr-test/
颜色主题 紫色 蓝色
识别方式 本地模型 云端API
需要ffmpeg ✅ ✅
需要联网 ❌ ✅
识别速度 ~2-3秒 ~1-2秒
准确率 ~85-90% ~95-98%
成本 免费 ¥0.02/分钟

9. 踩坑总结

9.1 必须记住的3个坑

  1. X-DashScope-Async请求头
  2. 没有这个头就报AccessDenied
  3. voice_handler.py有这个头所以能用
  4. 新建的测试页容易遗漏

  5. transcription_url二次请求

  6. 不是直接返回文本
  7. 需要再次GET请求OSS上的JSON文件
  8. 多一次网络请求

  9. JSON结构transcripts[0].text

  10. 不是result.sentences
  11. 不是result.text
  12. 是transcripts数组的第一个元素的text字段

9.2 调试技巧


10. 参考资料


📝 创建:2026-05-18
🔄 记录完整的4次错误调试过程

相关笔记