Qwen-Audio-3.1 第一次跑通一条音频,先把输入格式和请求字段对齐

文章导读
第一次调 Qwen-Audio-3.1 卡住,多数不是模型侧的问题,而是送进去的音频参数和请求字段没对齐:采样率、声道数、编码格式和接口真正接受的输入形式对不上,返回解析又写得含糊。稳妥的做法是先挑一条几秒钟、人声清晰的短音频,用命令行读出它的真实参数,转成单声道和统一采样率,构造一次最小请求,把请求参数和返回内容一起写进日志,再对同一段音频跑两次确认结果是否稳定。
📋 目录
  1. 壹 用命令行读出这条音频的真实参数
  2. 贰 把音频转成单声道并按需统一采样率
  3. 叁 用一条短音频发一次最小请求
  4. 肆 把请求参数和返回内容一起写进日志
  5. 伍 对同一段音频重复跑两次并对比结果
A A

第一次调 Qwen-Audio-3.1 卡住,多数不是模型侧的问题,而是送进去的音频参数和请求字段没对齐:采样率、声道数、编码格式和接口真正接受的输入形式对不上,返回解析又写得含糊。稳妥的做法是先挑一条几秒钟、人声清晰的短音频,用命令行读出它的真实参数,转成单声道和统一采样率,构造一次最小请求,把请求参数和返回内容一起写进日志,再对同一段音频跑两次确认结果是否稳定。

先把一条短音频跑通,再去调效果。适用场景是第一次接入音频模型、或换了音频来源后报错;操作动作是 ffprobe 读参数、ffmpeg 转单声道与采样率、发一次最小请求、记录请求与返回字段;验证方式是转换前后参数对照、两次调用结果对比;边界是官方接口的字段名和取值必须以官方文档为准,未确认前只写占位骨架,不要写死。

用命令行读出这条音频的真实参数

在写调用代码之前,先确认这条音频到底长什么样。凭印象判断“应该就是 16k 单声道”是最常见的误判来源。ffprobe 是比较通用的选择,可以直接拿到采样率、声道数、时长和编码格式。

ffprobe -v error -show_format -show_streams sample.wav

输出里需要重点看几个字段:audio 流下的 codec_name 是编码格式(如 pcm_s16le、mp3、aac),sample_rate 是采样率,channels 是声道数,duration 是时长;format 段的 format_name 是容器格式,bit_rate 是码率。这几个值决定了你后面要不要转格式、以及请求里该填什么。

如果命令直接报错,报错位置通常能帮你定位问题:找不到文件会在打开输入阶段提示路径不存在;容器能打开但音频流读不出来,会在解析流信息时提示 invalid data 或找不到 codec。这两种情况不要急着改代码,先把文件本身换成一条能正常播放的音频再试。

把音频转成单声道并按需统一采样率

格式转换建议放在本地做,而不是指望接口帮你兜底。转换的意义是:转换之后每次送测都用同一套参数,链路里少一个变量,出问题时容易判断是数据问题还是调用问题。

Qwen-Audio-3.1 第一次跑通一条音频,先把输入格式和请求字段对齐
ffmpeg -i sample.mp3 -ac 1 -ar 16000 -c:a pcm_s16le sample_16k_mono.wav

这里的 -ac 1 是单声道,-ar 16000 是采样率,-c:a pcm_s16le 是输出编码。采样率具体取多少、输出用 wav 还是别的容器,需要结合官方文档和你要做的是语音识别还是音频理解来确认,不要照搬。转换完再读一次参数做前后对照:

ffprobe -v error -show_entries stream=codec_name,sample_rate,channels -of default=nw=1 sample_16k_mono.wav

对照时要确认三个值都变了:声道数变成 1,采样率等于你设定的值,编码格式是你指定的那个。如果声道数还是 2,多半是输入音频本身是多轨或有异常声道布局,需要单独确认。

用一条短音频发一次最小请求

先把链路跑通,排除长音频和复杂格式的干扰。请求构造可以拆成三段:读取音频、组装并发送请求、解析返回。字段名以官方文档为准,下面只是通用骨架,未确认的地方用占位标注。

import base64, requests, json

# 1. 读取音频(base64 或本地路径,取决于接口要求)
with open("sample_16k_mono.wav", "rb") as f:
    audio_b64 = base64.b64encode(f.read()).decode()

# 2. 发送请求(字段名以官方文档为准,以下为占位)
payload = {
    "model": "<按官方文档填写模型名>",
    "input": {
        "audio": audio_b64,          # 或 audio_url / file_id,二选一
        "format": "wav",             # 占位,确认是否必填
        "sample_rate": 16000         # 占位,确认是否必填
    },
    "parameters": {"<按文档填写>": "<值>"}
}
headers = {"Authorization": "Bearer <API_KEY>", "Content-Type": "application/json"}
resp = requests.post("<请求地址>", headers=headers, json=payload, timeout=60)

# 3. 解析返回
print(resp.status_code)
print(resp.text[:500])

第一次先把 resp.text 原样打出来,不要急着写字段提取逻辑。返回结构没确认之前,任何 resp.json()["output"]["text"] 这类取值都可能因为字段名不同而抛异常,反而掩盖真正的服务端错误。

Qwen-Audio-3.1 第一次跑通一条音频,先把输入格式和请求字段对齐

把请求参数和返回内容一起写进日志

日志的作用是在出错时一眼分清是发送侧的问题还是解析侧的问题。建议至少记录:文件名、音频时长、请求时间、返回状态码、返回文本长度。这几项足以覆盖大多数第一次接入的失败场景。

{
  "file": "sample_16k_mono.wav",
  "duration_sec": 3.2,
  "sample_rate": 16000,
  "channels": 1,
  "request_time": "2026-01-01T10:00:00+08:00",
  "http_status": 200,
  "resp_text_len": 128,
  "resp_head": "{\"output\":..."
}

读这条日志时的判断顺序是:状态码非 2xx,问题在发送侧,优先看请求体里的音频编码和字段名;状态码 200 但 resp_text_len 为 0 或异常小,问题在参数或音频内容本身;状态码 200 且长度正常但取值报错,问题在解析侧,回头核对返回的真实字段结构。

对同一段音频重复跑两次并对比结果

同样的输入跑两次,是为了区分偶发失败和稳定失败。对比方式很直接:把两次日志里的状态码、返回文本长度、以及返回文本本身做比对。文本比较可以先比长度,再比内容是否完全一致;如果返回里带有非确定性的表述,就退一步比关键字段是否一致。

run1: status=200 text_len=128 text_sha1=<第一次哈希>
run2: status=200 text_len=131 text_sha1=<第二次哈希>
diff: 状态一致;文本长度差 3;内容哈希不同

两次结果一致,说明这条链路是稳的,可以拿它当基线,再去换更长的音频或更多样的格式。两次结果都不一样,先别改模型或参数,回头检查音频是不是每次读取时被改写了、请求里有没有随机字段。如果是同一状态码下的稳定失败(比如两次都返回同样的错误信息),下一步动作是拿这条错误信息去核对请求字段和音频参数,而不是反复重试。