Python 标准库调用 Audio Transcriptions API 的超时与异常处理

把会议录音交给语音识别模型,最容易忽略的不是"识别准不准",而是输入与故障:文件可能为空、格式不支持、网络长时间无响应,或录音本身含个人信息。一个入门但可复用的转写入口应该先拒绝明显错误,再给网络请求时间上限,并把失败变成可处理的状态。本文用 Python 标准库演示 multipart 上传;你可以把它接到上传页面、定时任务或客服质检流程。

语音识别(Automatic Speech Recognition,ASR)会把音频波形转换为文本。模型负责听写,应用仍须负责文件白名单、上传授权、重试策略和文本后处理。官方 Speech-to-Text 文档列出音频转写能力与接口;请在部署时以账号控制台和最新文档确认模型 ID、大小限制、价格及数据政策。

flowchart LR F[用户授权的音频文件] --> S{大小/扩展名检查} S -->|不通过| E[提示重新上传] S -->|通过| M[multipart 请求] M --> A[转写 API] A --> T{超时或错误?} T -->|否| X[保存文本与最少审计信息] T -->|是| R[可追踪失败/人工处理]

环境与运行

Python 3.10+ 无第三方依赖。只在有录音所有者授权的前提下使用;示例不上传任何本地文件。

bash 复制代码
export OPENAI_API_KEY='你的密钥'
python3 transcribe.py meeting.mp3

完整代码

本例构造 multipart 请求并将总请求限制为 45 秒。为让代码聚焦流程,使用当前官方转写文档中的 gpt-4o-transcribe 作为示例模型;接口或权限变化时应以官方文档为准。

python 复制代码
import json, mimetypes, os, pathlib, sys, uuid
import urllib.error, urllib.request

MAX_BYTES = 20 * 1024 * 1024
def part(boundary, name, value, filename=None, content_type=None):
    head = f"--{boundary}\\r\\nContent-Disposition: form-data; name=\\\"{name}\\\""
    if filename: head += f"; filename=\\\"{filename}\\\"\\r\\nContent-Type: {content_type}"
    return head.encode() + b"\\r\\n\\r\\n" + (value if isinstance(value, bytes) else value.encode()) + b"\\r\\n"

def transcribe(path: pathlib.Path):
    key = os.getenv("OPENAI_API_KEY")
    if not key: raise RuntimeError("缺少 OPENAI_API_KEY")
    if not path.is_file() or path.stat().st_size == 0 or path.stat().st_size > MAX_BYTES:
        raise ValueError("文件不存在、为空或超过教学上限 20MB")
    mime = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
    if mime not in {"audio/mpeg", "audio/wav", "audio/mp4", "audio/x-m4a"}:
        raise ValueError("请使用 mp3、wav、m4a 或 mp4 音频")
    boundary = "----snail" + uuid.uuid4().hex
    data = part(boundary, "model", "gpt-4o-transcribe")
    data += part(boundary, "file", path.read_bytes(), path.name, mime)
    data += f"--{boundary}--\\r\\n".encode()
    req = urllib.request.Request("https://api.openai.com/v1/audio/transcriptions", data=data,
        headers={"Authorization": "Bearer " + key, "Content-Type": "multipart/form-data; boundary=" + boundary})
    with urllib.request.urlopen(req, timeout=45) as resp:
        if resp.status != 200: raise RuntimeError(f"HTTP {resp.status}")
        return json.load(resp)["text"]

if __name__ == "__main__":
    try: print(transcribe(pathlib.Path(sys.argv[1])))
    except (IndexError, ValueError, RuntimeError, urllib.error.URLError, TimeoutError) as e:
        print(f"TRANSCRIBE_FAILED: {e}", file=sys.stderr); raise SystemExit(2)

如何理解输出与失败

成功时标准输出是一段文本;失败时进程返回码为 2,并以 TRANSCRIBE_FAILED 开头,便于任务队列识别。mimetypes 只是初筛,恶意上传仍可能伪装扩展名,生产环境还应检测文件头、扫描恶意文件。45 秒并不是最佳值:短音频可更短,长音频应采用拆分、异步任务与可取消状态,而不是无止境等待。

本次用 python3 -m py_compile transcribe.py 完成语法检查,未设置真实密钥、未上传或调用线上 API。常见失败包括:一,密钥权限或余额不足;二,音频编码与扩展名不符;三,网络代理导致超时。分别处理为:不记录密钥、用音频工具规范化编码、将超时任务标为可重试并设置最大次数。

何时采用与如何工程化

适合会议纪要初稿、客服质检、用户明确授权的访谈检索;不适合把未审阅文本当法律、医疗或人事结论。下一步可增加说话人分离、术语表、敏感信息打码、对象存储临时链接和删除策略。五分钟实践:给代码加一个 --dry-run 参数,只打印文件大小与 MIME 类型,确保 CI 不会意外把测试音频传上云。

语音转写失败时,你会优先重试、拆分音频还是转人工?

关注「蜗牛聊AI」,一起看懂技术变化背后的真正机会。


本文首发于 java4u.cn,转载请注明出处。

相关推荐
znx9391 小时前
因子分析:量化交易的底层核心与盈利逻辑基石
人工智能·python·机器学习·期魔方
沉默王二1 小时前
轻量开源版 Muse 来了!CopilotKit 开源 OpenMuse,Personal Agent 的工程细节全摊开了
人工智能·openai·agent
Dawson Zhu1 小时前
《Agentic Design Patterns》第 10 章导读:模型上下文协议(MCP)
人工智能·语言模型·架构·aigc·agi
IvorySQL1 小时前
VACUUM FULL 之后 ROWID 就废了? IvorySQL 兼容性实测
数据库·人工智能·ai·postgresql·开源
GPUStack1 小时前
一张 A800 80GB,跑通 Qwen-Image-2.1:GPUStack 部署、生成与图像编辑实战
人工智能·开源·github·vllm·大模型部署·gpustack
高洁011 小时前
AI软件工程:大模型赋能软件研发全流程革新
人工智能·深度学习·机器学习·transformer·tornado
吴文周1 小时前
让大模型在本地持续进化:YoungAi 如何用 Sidecar 和后训练重新思考本地 AI
人工智能
浪子明X1 小时前
AI 日报自动化:先把事实、时间和引用做成可检查的编辑流水线
运维·人工智能·自动化