音频转写出错时,很多人第一反应是换模型或怀疑识别能力,但多数故障其实发生在文件、鉴权、网络和限流这四层。转写接口是 multipart/form-data 上传,链路比纯文本接口长,排查顺序也应该更长:先看 HTTP 状态码和错误类型,再看请求本身,最后才调整音频和提示词。

本文按 401、400 格式错误、413 文件过大、429 限流、超时,以及返回空文本或乱码这几类拆开,给出排查路径和一段可复用的处理代码。文件限制、模型名和错误格式可能随版本调整,以官方文档和站内文档为准,价格以模型价格为准。

说明:OpenAI、Whisper 是其权利人的商标,本文仅作技术接入说明。接口限制、支持格式和模型名称可能调整,请以官方文档与站内文档为准。

一、先建立排查顺序

现象 常见状态码 高频原因 处理方向
鉴权失败 401、403 Key 错误、未带 Bearer、无音频接口权限 修凭证,不要重试
格式不支持 400 编码格式不在支持列表、扩展名与实际编码不符 转码后重传
文件过大 413 或 400 超过单文件大小上限 压缩或切分
请求过频 429 每分钟请求数、并发或额度超限 退避、排队、拆 Key
服务端错误 500、502、503 上游异常或过载 退避后有限重试
超时 无响应或连接中断 文件大、上传慢、处理时间长 切分、调整超时
返回空文本 200 静音、音量低、语言误判、编码异常 检查音频而非重试

响应头或错误体中如果有 request-id,务必保留。找服务方排查时,一个请求 ID 加一段可复现的最小音频,比十张截图有用。

二、401:先查请求头,再查 Key

401 表示身份验证没有通过,和余额、模型能力通常无关。按下面顺序排查:

  1. 鉴权头是否为 Authorization: Bearer <key>,不要漏掉 Bearer 和空格。
  2. Key 前后是否有空格、换行或引号。从聊天窗口复制时最容易带入。
  3. 环境变量是否真的加载。可以只打印前缀确认,不要把完整 Key 写进日志。
  4. Key 是否被吊销、是否属于当前项目,账户是否有音频接口权限。
  5. Base URL 是否为 https://ai.zhongshangqihang.com/v1,路径是否为 /v1/audio/transcriptions。
  6. 如果是网关,确认该入口是否开放音频转写,不要假设文本接口可用,音频接口就一定可用。

还有一个常见错误:把文件塞进 JSON 请求体。转写接口要求 multipart/form-data,命令行要用 curl -F "file=@meeting.mp3",而不是 -d。请求体格式错了,有时会表现成 400,也可能因为没读到有效凭证而返回 401。

三、400 与 413:格式和大小问题

400 里最常见的是格式不支持。官方文档列出的常见音频格式包括 mp3、mp4、mpeg、mpga、m4a、wav、webm 等,完整列表和编码要求以当前文档为准。改扩展名不等于转码:把 .amr 重命名成 .mp3,服务端读到的仍是原编码,照样报错。

413 通常表示文件超过单文件大小上限。官方常见上限是 25 MB,具体以文档为准。处理方式有两种:

  • 压缩:统一转成 16 kHz 单声道,语音场景不需要高码率。可用 ffmpeg -i input.mp3 -ac 1 -ar 16000 -b:a 64k output.mp3。
  • 切分:按 10 分钟一段切开,逐段转写后合并。切点尽量选静音处,段间留一点重叠,避免把词切成两半。

长音频不要只靠压缩硬塞。文件越大,上传越慢,超时概率越高,失败后重传的成本也越高。切分并行更稳,但要控制并发,否则会从 413 变成 429。

四、429:限流不等于余额不足

429 表示请求超过了速率、并发或额度限制。同样是 429,错误体里的类型可能不同:有的表示频率超限,有的表示额度不足。先看错误类型,再决定是等待、降并发,还是充值。

常见触发场景包括:批量任务同时开太多并发;多个服务共用一把 Key;失败后立即重试,形成重试风暴;长音频占用连接时间过久;免费或低档位账号的每分钟额度较小。

处理顺序是:先看响应里有没有 Retry-After,再按指数退避加随机抖动重试;把批量任务放进队列,限制同时在跑的请求数;按项目拆 Key,避免互相挤占;给重试设置上限和告警,不要无限循环。转写按音频时长计费,重复提交还可能带来重复计费,重试前先确认上一次是否已经成功。

五、超时:区分上传慢还是处理慢

超时可能发生在三个阶段:连接建立、文件上传、服务端处理。小文件也超时,优先查网络、代理和 Base URL;大文件稳定超时,优先切分和压缩。

客户端要显式设置超时,不要依赖默认值。转写耗时和音频时长、文件大小、排队情况都有关,给一个偏宽松但有限的上限,例如两到三分钟,再用任务记录区分“已提交未返回”和“根本没发出去”。超时后的自动重试要谨慎:如果服务端其实已经处理完成,重试会产生第二次计费和重复结果。

六、200 但空文本或乱码

这两种情况没有 HTTP 错误,排查方向完全不同。

空文本常见于:整段静音或音量过低;语言参数和音频不符;声道异常,主声道没有声音;文件编码能上传但解码异常。先做音量检测,显式传 language=zh,再统一转成 16 kHz 单声道试一次。

乱码或大段错字常见于:语种误判、方言口音重、背景音乐和人声混在一起、多人抢话、录音距离过远。可以在请求里用 prompt 传入人名、产品名和专业术语,转写后用词表做替换。若音频本身不可用,换模型也救不回来,应该回到录音和预处理环节。

七、重试与切分:一段可复用的代码

重试只适用于瞬时错误。401、400、413 改请求才有意义,盲目重试只会放大问题。

import os
import random
import time
from openai import (
    OpenAI,
    APIConnectionError,
    APIStatusError,
    APITimeoutError,
    RateLimitError,
)

client = OpenAI(
    api_key=os.environ["WHISPER_API_KEY"],
    base_url="https://ai.zhongshangqihang.com/v1",
    timeout=180,
)

def transcribe(path: str, model: str = "whisper-1", retries: int = 4) -> str:
    for attempt in range(retries):
        try:
            with open(path, "rb") as f:
                result = client.audio.transcriptions.create(
                    model=model,
                    file=f,
                    language="zh",
                    response_format="verbose_json",
                )
            text = (result.text or "").strip()
            if not text:
                raise ValueError("空转写:先检查音量、语言和声道")
            return text
        except RateLimitError:
            wait = min(2 ** attempt, 30) + random.random()
            print(f"429,等待 {wait:.1f}s 后重试")
            time.sleep(wait)
        except (APITimeoutError, APIConnectionError):
            time.sleep(min(2 ** attempt, 30) + random.random())
        except APIStatusError as exc:
            if exc.status_code >= 500:
                time.sleep(min(2 ** attempt, 30) + random.random())
            else:
                raise  # 400/401/413 应该改请求,不要重试
    raise RuntimeError("重试次数已用完")

print(transcribe("meeting.mp3"))

模型名以站内文档为准;whisper-1 只是常见示例。超过大小限制的音频,先用 ffmpeg 切分,再对每一段调用这个函数,合并时记得把各段起始时间加回去。

八、日志与找服务方排查的清单

  1. 记录发生时间、时区、HTTP 状态码和错误类型。
  2. 记录 request-id,以及请求打的是官方入口还是网关入口。
  3. 记录文件名、实际编码、时长、大小、采样率和声道数。
  4. 记录 model、language、response_format 等关键参数。
  5. 记录重试次数、每次等待时间和最终结果。
  6. 记录是否为共享 Key、当时的并发数和批量任务规模。
  7. 准备一段 10–30 秒、可公开的最小复现音频,去掉个人信息。

提交给服务方时,不要发送完整 Key,也不要在未获授权的情况下发送客户录音。能稳定复现的最小样本,比“有时候会报错”的描述有效得多。

常见问题

413 和 400 都是文件问题,怎么区分?

413 一般表示体积超过上限,处理方式是压缩或切分;400 更多是格式、参数或编码问题,处理方式是转码和检查字段。先看错误类型和消息,不要只看状态码。

429 一直不恢复怎么办?

先看错误类型是频率超限还是额度不足。频率问题降并发、排队、拆 Key;额度问题需要充值或换入口。持续 429 时不要继续加密重试,否则只会延长恢复时间。

超时该不该直接重试?

先判断请求是否可能已经在服务端完成。转写可能已计费,直接重试会产生重复结果和重复费用。用任务记录或幂等设计确认状态,再决定是否重发。

转写为空是不是模型坏了?

多数不是。优先检查音量、静音段、语言参数、声道和实际编码。用同一段音频换一个入口测试,可以快速区分是音频问题还是服务问题。

网关报错和官方报错怎么区分?

保留 request-id 和错误类型,用同一条请求分别打两个入口。若官方入口正常、网关入口报错,问题多半在网关配置或转发;两边都报错,再查 Key、网络和音频文件。

下一步

先注册中商起航 AI 网关,用一段一分钟音频把 401、413、429 和超时各演练一次,再接入生产监控。模型可用性与价格以模型价格为准。

相关阅读: