# 讯飞 ASR 集成踩坑笔记 > demo 期间累积的所有讯飞接入坑点。建议入 TPS 知识库,作为未来接入新 ASR 供应商时的对照。 --- ## 选型坑 ### 坑 1:不要用"大模型版" - 讯飞开放平台有两个录音转写产品: - 「录音文件转写(**标准版**)」← **用这个** - 「录音文件转写(**大模型版**)」← 不要用 - **大模型版的免费包阉割了 `language` 参数**,调用时会报 `language verify fail` - 这个错误信息**严重误导**:看上去是语种没设对,实际是免费包没开通该参数权限 - 排查耗时:约 30 分钟 ### 坑 2:消费端和开发端密钥不通 - 「讯飞听见 App」属于**消费端**,给最终用户做人工转写用 - 「讯飞开放平台」属于**开发端**,给开发者调 API 用 - **两套密钥互不相通**——开发端的 APP_ID 在消费端没用,反之亦然 - 如果只在讯飞听见 App 注册了账号,**没有** APP_ID 和 SecretKey,必须再去开放平台注册一次 ### 坑 3:免费额度不会自动激活 - 开放平台首页显示"5 小时免费试用包",但**只有走 0 元购买流程**才会激活 - 看到额度但没下单 → 调用时会报余额不足 - 流程:商品页 → 0 元下单 → 支付(实际免费)→ 额度到账 --- ## 音频规格坑 ### 坑 4:必须是 16kHz / 单声道 / 16bit WAV - 其他规格讯飞会报错(且错误信息可能误导成"language verify fail"或"audio decode error") - MP3 在标准版**也支持**,但保险起见统一转 WAV - 转码命令(ffmpeg): ```bash ffmpeg -i input.mp3 -ac 1 -ar 16000 -sample_fmt s16 output_16k_mono.wav ``` - `-ac 1` = 单声道 - `-ar 16000` = 16kHz 采样率 - `-sample_fmt s16` = 16bit 位深 ### 坑 5:时长字段不严格校验但建议传准 - API 要求传 `duration`(毫秒) - 实测哪怕传 0 也能上传成功,但官方文档建议传准确值 - WAV 从文件头读:`n_frames / sample_rate * 1000` - MP3 用 `mutagen.mp3.MP3` 读 --- ## 接口坑 ### 坑 6:签名算法是老式 HmacSHA1,不是常见的 HmacSHA256 ```python # signa = base64(HmacSHA1(MD5(appid + ts), secretKey)) md5_str = hashlib.md5((appid + ts).encode("utf-8")).hexdigest() # 32 位小写 hex mac = hmac.new(secret_key.encode("utf-8"), md5_str.encode("utf-8"), digestmod=hashlib.sha1) signa = base64.b64encode(mac.digest()).decode("utf-8") ``` 注意: - MD5 输出是**32 位小写 hex 字符串**,不是 16 字节 raw bytes - HMAC 的 key 是 `secretKey`,data 是 MD5 字符串本身(不是其字节) - 最后做 base64 编码 ### 坑 7:参数全部走 URL query,不是 form-data 或 JSON body - 上传接口:`POST https://raasr.xfyun.cn/v2/api/upload?appId=...&signa=...&ts=...&...` - `Content-Type: application/json`,但**请求体是音频原始字节流**(不是 JSON) - 所有业务参数(`fileSize` `fileName` `duration` `language` `pd` `hotWord` …)都在 URL query 里 - 中文热词必须做 URL encode:`quote(hot_word_str, safe='')` ### 坑 8:热词用 `|` 分隔,不是逗号 ```python hot_word_str = "|".join(HOT_WORDS) # 萨德|爱国者|标准-6|... params["hotWord"] = hot_word_str ``` 限制: - 最多 200 个热词 - 每个热词 2–16 字 - 整个 hotWord 参数 URL encode 后不能超过某个长度(实测 35 个常规军事术语没问题) --- ## 异步查询坑 ### 坑 9:轮询用 POST 不是 GET - 查询结果接口:`POST https://raasr.xfyun.cn/v2/api/getResult?...` - 即便没有 body 也是 POST - 用 GET 会报方法不允许 ### 坑 10:status 状态码语义 | status | 含义 | |---|---| | 0 | 待处理 | | 1 | 处理中 | | 4 | **完成** | | -1 | 失败 | 完成是 `4`,不是 `2` 也不是 `200`。 ### 坑 11:轮询间隔与超时 - 26 分钟音频实测转写耗时约 5 分钟 - 推荐轮询间隔 30 秒(taskEstimateTime 返回的预估值仅供参考) - 超时上限设 30 分钟比较保险 --- ## 结果解析坑 ### 坑 12:返回结果是双层嵌套 JSON 字符串 ```python # 外层 data = response.json() order_result_str = data["content"]["orderResult"] # 这是 JSON 字符串 # 中间还有一层反斜杠转义 cleaned = re.sub(r"\\\\", r"\\", order_result_str) outer = json.loads(cleaned) # 句子在 outer["lattice"] 里 for item in outer["lattice"]: inner_str = item["json_1best"] # 又是 JSON 字符串 inner = json.loads(inner_str) # 句子时间在 inner["st"]["bg"] / inner["st"]["ed"] # 句子内容在 inner["st"]["rt"][..]["ws"][..]["cw"][..]["w"] ``` 每个 `cw` 是一个字 / 词;要拼接所有 `cw["w"]`(跳过 `wp == "g"` 的空白标记)。 ### 坑 13:时间戳单位是毫秒(不是秒、不是百分秒) - `bg` `ed` 都是毫秒(int) - 与 B 稿的 `[Nm Ns]` 对齐时记得换算 --- ## 业务参数坑 ### 坑 14:`pd=mil` 对军事术语显著加分 - 不开 `pd`:默认通用领域,军事术语会被识别成同音常见词 - 开 `pd=mil`:军事领域专用模型介入,配合热词效果叠加 ### 坑 15:`eng_smoothproc` + `eng_colloqproc` 让 ASR 接近书面 - `eng_smoothproc=true`:去掉"嗯""那个""呃" - `eng_colloqproc=true`:口语规整("咱们"→"我们"等) - 两个都开后,输出已经非常接近书面文稿 - 但**不会清理"那么""呢""啊""这个"**——这些保留与否需要在融合层决定 --- ## 凭证与配额(demo 阶段使用) ``` APP_ID: 84eff996 SECRET_KEY: d37fb0c77e7f4b4f4398e6a8834d1017 免费包: 5 小时(已激活),有效期至 2027 年 5 月 ``` > ⚠️ **正式上线时替换为生产凭证**,并把 SECRET_KEY 从代码移到环境变量 / 密钥管理服务。 --- ## 一句话总结 讯飞 ASR 标准版**能用、好用**,但接入文档稀少且错误信息误导性强;以上 15 个坑全部踩完后,单次调用的成功率接近 100%。后续接入新 ASR 供应商时,建议按本笔记的结构(选型 / 音频 / 接口 / 异步 / 解析 / 业务参数 / 凭证)做同样的整理。