Qwen3 ASR API 调用文档
版本:1.0.0 · 更新:2026-10-13 · 服务:Qwen3-ASR-0.6B / 中文
| 用途 | 地址 |
|---|---|
| HTTP API | https://asr.talent-map.com |
| 流式 WebSocket | wss://asr.talent-map.com/ws/asr |
| 在线文档 | https://asr.talent-map.com/api-docs |
| OpenAPI 3.1,可导入 Postman / Apifox | https://asr.talent-map.com/openapi.json |
| 本文 Markdown | https://asr.talent-map.com/api-reference.md |
| 已接入公网 API 的录音测试页 | https://asr.talent-map.com/ |
公网 URL 没有 /api 前缀。/api 只用于本机 localhost 开发代理。
Next.js 全栈项目与 ASR 部署在同一台服务器时,见同机部署与优化。该方案使用内部地址调用 ASR,浏览器仍访问业务站点的 HTTPS/WSS。
1. 认证与接入方式
HTTP 和非浏览器 WebSocket 客户端发送:
Authorization: Bearer YOUR_TOKEN
Token 为共享 API 密钥,当前不自动过期。不要放到 URL、查询参数或普通访问日志中。浏览器原生 WebSocket 不支持设置握手 Authorization,请在第一条 start JSON 内发送 api_token,详见流式接口。
当前测试页按用户要求已将部署 Token 写入网页配置并自动连接。这使该 Token 可被页面访问者读取;它不能再限制公网调用者身份。其他项目应明确是否也采用这种公开测试方式。若要恢复受限服务,需要更换 Token,并将凭据保留在项目后端。
推荐其他业务项目由自己的后端调用此 API,然后将文本返回前端。跨域浏览器 HTTP 目前未启用 CORS;WebSocket 的浏览器 Origin 仅允许 https://asr.talent-map.com、http://localhost:8088、http://127.0.0.1:8088。使用其他前端域名直连时,需要追加准确的允许 Origin / CORS 配置。服务端调用无需发送 Origin。
2. 输入与服务限制
| 项目 | 要求 |
|---|---|
| 采样率 | 16000 Hz |
| 声道 | 单声道 |
| 样本格式 | 有符号 16 位、小端 pcm_s16le |
| 流式传输 | 原始 PCM 字节,不含 WAV 头、不用 Base64 |
| 整段上传 | 包含 WAV 头的 PCM16 WAV,multipart file 字段 |
| 建议录音 | 每段 2–8 秒 |
| 最大录音 | 15 秒,即 240000 样本 / 480000 PCM 字节 |
| 单帧 | 2–3200 个偶数字节,建议 3200 字节 / 100ms;尾帧可短 |
| 并发 | 全服务仅一个活动识别任务;HTTP 与 WS 共用容量 |
| SDK 分块 | 当前 500ms,由服务端 ready 的 chunk_ms 告知 |
| 音频积压 | 等待推理的 PCM 超过两秒会报 overloaded |
| 等待输入 | 首条消息 / 相邻消息超过约 10 秒会超时 |
| WS 会话 | 就绪后的会话处理总时限约 40 秒 |
| 输出 | 中文全文快照,无逐字时间戳、说话人分离或翻译 |
浏览器经常实际采样 44.1kHz 或 48kHz。必须连续低通重采样到 16kHz 后再转换 PCM,不能只修改 WAV 头。仓库 web/audio.js 与 web/pcm-worklet.js 可作为采集参考。
WebM、Opus、MP3、立体声 WAV 或浮点 WAV 不属于当前接口支持范围。上传 WAV 文件体积还受 484096 字节上限限制,multipart 请求体受 545536 字节上限限制。
3. 整段转写
POST /v1/audio/transcriptions
Content-Type: multipart/form-data,必填文件字段 file。让 HTTP 库生成 multipart boundary,不要手动设置成 audio/wav 或固定 multipart Content-Type。当前不提供 model、language、prompt、stream、时间戳等参数;多余 multipart 字段不会改变识别行为。
curl --connect-timeout 5 --max-time 30 \
-H 'Authorization: Bearer YOUR_TOKEN' \
-F 'file=@sample.wav;type=audio/wav' \
https://asr.talent-map.com/v1/audio/transcriptions
成功 200 application/json,例如:
{"text":"今天下午我们一起去公园散步。","audio_ms":3080,"inference_ms":42.5}
| 字段 | 类型 | 含义 |
|---|---|---|
text |
string | 完整文本,可能有标点;可以为空 |
audio_ms |
integer | 提交的音频样本时长,毫秒,取整 |
inference_ms |
number | 服务端推理调用耗时,毫秒;不包含文件上传、公网往返或浏览器采集 |
示例中的文本和耗时只表示响应形状。业务延迟应从自己的请求链路计时。
4. 流式转写
每次连接处理一段录音。结束后服务器发送一次 final 并正常关闭连接;下一段重新建立 WebSocket。
4.1 连接和开始
连接 wss://asr.talent-map.com/ws/asr。后端客户端握手使用 Bearer 头,然后发送:
{"type":"start","sample_rate":16000,"channels":1,"encoding":"pcm_s16le"}
浏览器客户端第一条消息改为:
{"type":"start","sample_rate":16000,"channels":1,"encoding":"pcm_s16le","api_token":"YOUR_TOKEN"}
等待服务器回复 ready,不要在 ready 前发送音频:
{"type":"ready","session_id":"c806544765fd4d098cb237218604c6ee","chunk_ms":500}
session_id 是排查会话问题的标识。chunk_ms 是模型更新分块,并不要求客户端把网络帧也设为 500ms。
4.2 按序发送 PCM
每 100ms 左右发送 3200 字节原始 PCM 二进制帧。文件回放也应按实时速度发送;一次性把整段音频灌入 WS 可能触发两秒积压限制。已经录好的整段音频通常使用 HTTP 接口更合适。
服务器仅在文字发生变化时推送:
{"type":"partial","revision":1,"text":"今天下午"}
{"type":"partial","revision":2,"text":"今天下午我们一起去公园"}
text 是完整文本快照,末尾可能改写。UI 应替换上一份文字,不要拼接。revision 在本次会话中递增;并不代表词数。少于一个 SDK 分块的录音可能没有任何 partial,随后直接收到 final。
4.3 停止和最终结果
停止采集后先清空 AudioWorklet、重采样器和网络发送缓冲,把尾帧全部按序发完,再发送:
{"type":"end"}
继续监听,不要立即关闭 WebSocket。服务器处理完所有已收到的音频,并完成 SDK 尾帧推理后发送:
{"type":"final","revision":3,"text":"今天下午我们一起去公园散步。","audio_ms":3080,"finalize_ms":39.2}
| 字段 | 类型 | 含义 |
|---|---|---|
revision |
integer | 最终文本 revision,比先前 partial 大 |
text |
string | 最终全文快照 |
audio_ms |
integer | 服务端实际接收的 PCM 样本总时长 |
finalize_ms |
number | 服务端收到 end 到准备发送 final 的耗时,含已有音频积压与尾帧收尾;不含客户端尾帧清空和公网返回时间 |
成功后关闭码为 1000。一旦收到 final,业务可以提交文本;final 文本可能与最后一个 partial 相同,也可能修正。服务不会在流式结束时额外强制做一次整段识别。
4.4 失败和断开
通常先发送:
{"type":"error","code":"busy","message":"已有识别任务,请稍后再试"}
协议、认证、超时或 busy 错误通常以 1008 关闭;推理失败为 1011。握手时 IP / Origin 被拒绝,或提供了错误 Authorization 请求头,可能直接以 HTTP 403 拒绝升级,此时没有 JSON error 帧。
连接在 final 前关闭时,该段未完成;不要把最近一次 partial 当成成功结果。没有恢复同一会话的接口。重新连接意味着开始新任务;可保留本地 PCM,在连接故障后由用户选择整段重试。断开后,正在执行的同步 GPU 调用会先结束再释放容量,因此极短时间内仍可能返回 busy。
5. 健康与就绪
GET /healthz:无需 Token,成功 200 {"status":"alive"}。只说明 HTTP 进程可响应,不说明模型可识别。
GET /readyz:要求 Bearer Token。
{"status":"ready","busy":false,"error":null}
| HTTP | 状态 | 含义 |
|---|---|---|
| 200 | ready, busy=false |
模型可用,当前容量空闲 |
| 200 | ready, busy=true |
模型可用,但已有任务 |
| 503 | loading |
正在加载/预热,或加载失败;error 可能是异常类型 |
ready 查询不会预留容量;查询后发起识别仍可能遇到其他客户端抢先占用。
6. HTTP 错误与 WS 错误码
HTTP 业务错误一般为 {"error":"CODE","message":"可选说明"};message 可缺省。WS 对应 {"type":"error","code":"CODE","message":"说明"}。
| HTTP | 错误码 | 处理建议 |
|---|---|---|
| 400 | invalid_format |
转成合法 16kHz 单声道 PCM16 WAV |
| 400 | empty_audio |
提交至少一个音频样本 |
| 400 | audio_too_long |
按不超过 15 秒切段 |
| 401 | unauthorized |
检查 Token;响应附 WWW-Authenticate: Bearer |
| 403 | forbidden |
检查代理到后端的 peer 白名单;不是语言/文件错误 |
| 408 | timeout |
上传在时限内没有完成 |
| 409 | busy |
稍后重试;当前全服务只允许一路识别 |
| 413 | upload_too_large |
请求体或 WAV 体积超限 |
| 422 | FastAPI detail 数组 |
缺少 multipart file,或请求类型不匹配 |
| 500 | inference_failed |
本次失败,保留本地音频后决定是否重试 |
| 503 | not_ready |
模型未就绪,等待 readyz |
WS 另有 invalid_message(非法 JSON/未知类型/控制消息超过1024字符)、invalid_sequence(重复 start/end、音频先于 start 或在 end 后)、invalid_frame(空帧/奇数字节/超过3200字节)、overloaded(两秒积压)、timeout(首帧、输入间隔或会话超时)。
网络代理可能返回 HTML 格式的 502/504;客户端应检查状态码和 Content-Type,不应假定所有故障都是 JSON。重试建议只针对 busy、未就绪或瞬时网络故障,采用有限次数与退避;401、格式错误和超长输入需要修正后再调用。
7. Python 项目接入
依赖:Python 3.11+、httpx==0.28.1、websockets==16.0。仓库有可直接运行的 examples/python/asr_client.py,支持整段与实时速度流式回放;Token 从环境或私有文件读取,没有写死在示例里。
python examples/python/asr_client.py --token-file /private/asr-token --mode upload sample.wav
python examples/python/asr_client.py --token-file /private/asr-token --mode stream sample.wav
最小 HTTP 示例:
import os
import httpx
with open("sample.wav", "rb") as audio, httpx.Client(timeout=30, trust_env=False) as client:
response = client.post(
"https://asr.talent-map.com/v1/audio/transcriptions",
headers={"Authorization": "Bearer " + os.environ["ASR_API_TOKEN"]},
files={"file": ("sample.wav", audio, "audio/wav")},
)
response.raise_for_status()
print(response.json()["text"])
8. JavaScript / Node.js 项目接入
Node.js 20+ 可直接使用 fetch / FormData / Blob:
import {readFile} from 'node:fs/promises';
const form = new FormData();
form.append('file', new Blob([await readFile('sample.wav')], {type:'audio/wav'}), 'sample.wav');
const response = await fetch('https://asr.talent-map.com/v1/audio/transcriptions', {
method:'POST', headers:{Authorization:`Bearer ${process.env.ASR_API_TOKEN}`},
body:form, signal:AbortSignal.timeout(30000),
});
if (!response.ok) throw new Error(`ASR HTTP ${response.status}: ${await response.text()}`);
console.log((await response.json()).text);
浏览器已经获得 16kHz PCM 帧时,核心流式调用如下。音频必须通过真实采集/定时回放按序传入,且只在 ready 之后发送:
const ws = new WebSocket('wss://asr.talent-map.com/ws/asr');
let ready = false;
ws.onopen = () => ws.send(JSON.stringify({
type:'start', sample_rate:16000, channels:1, encoding:'pcm_s16le', api_token:token,
}));
ws.onmessage = ({data}) => {
const result = JSON.parse(data);
if (result.type === 'ready') ready = true;
if (result.type === 'partial') showTemporaryText(result.text); // 替换快照
if (result.type === 'final') { ready = false; saveFinalText(result.text); }
if (result.type === 'error') { ready = false; showError(result.code); }
};
function sendPCM(arrayBuffer) {
if (!ready || ws.readyState !== WebSocket.OPEN) throw new Error('ASR not ready');
if (ws.bufferedAmount + arrayBuffer.byteLength > 64000) throw new Error('audio backlog');
ws.send(arrayBuffer);
}
function finishAfterAllTailFrames() {
if (!ready) throw new Error('ASR not ready');
ws.send(JSON.stringify({type:'end'})); // 继续监听直到 final
}
web/api-client.js 可复用 HTTP、ready 与浏览器 WS 认证代码;web/app.js 是包括采集、停止尾帧和对照识别的完整示例。其他浏览器域名直连需先配置允许的 Origin / CORS,或由自己的后端代理。
9. 当前行为边界
- 采用用户手动结束录音,不会额外等待静音 VAD;不是永久在线的长会话服务。
- 无按用户分配的并发、自动切段、批量流式、音频时间戳或自动 Token 轮换。
- 识别结果可能修正,数字可输出为汉字,人名可能错写,纯静音也可能产生文字;业务端仍需确认文本。
- 服务不保存提交的录音到磁盘。测试页的示例 WAV 是明确生成的测试素材。
- OpenAPI 描述 HTTP 接口,WS 协议以本文为准;将
openapi.json导入工具不会自动创建 WS 采集客户端。
10. Next.js 与 ASR 同机部署
本节是其他业务项目的部署接入方案。下面的业务路由 /api/asr/transcriptions、/asr/ws 为建议路径,需要在业务项目或网关中实现;当前 ASR 服务没有新增这些路由。ASR 的音频格式、认证和消息协议仍按第 1–6 节执行。
10.1 调用链路与地址选择
“同机”指 Next.js 服务端与 ASR 在同一台物理服务器。用户浏览器运行在用户自己的设备上,浏览器里的 127.0.0.1 指向用户设备,不能用它访问服务器上的 ASR。
录完上传:浏览器 → 业务域名 HTTPS /api/asr/transcriptions
→ Next.js 服务端 → 内部 HTTP /v1/audio/transcriptions
实时识别:浏览器 → 业务域名 WSS /asr/ws
→ 验证业务登录的 WS 网关 → 内部 WS /ws/asr
| 部署方式 | Next.js 服务端使用的 ASR 地址 | 注意事项 |
|---|---|---|
| Next.js 与当前 systemd ASR 都直接运行在宿主机 | http://127.0.0.1:8123;WS 为 ws://127.0.0.1:8123/ws/asr |
当前 ASR peer 白名单已包含 loopback,内部调用仍需要 Token |
| 两者都在同一个 Compose 网络 | 例如 http://asr:8123;WS 为 ws://asr:8123/ws/asr |
asr 是示例服务名;使用容器端口,按实际连接来源配置 ASR 白名单 |
| Next.js 在 Docker,ASR 仍是宿主机 systemd 服务 | http://host.docker.internal:8123 |
Linux 需配置 host-gateway;ASR 和宿主机防火墙需允许实际容器来源 |
| WS 网关在另一台机器 | 使用可达的私网地址,例如现有代理使用 10.100.0.2:8123 |
网关的 127.0.0.1 指向网关自己;仅 Next.js 与 ASR 同机并不能让远程代理使用 loopback |
Docker Compose 的服务发现和容器端口规则见官方网络文档。同一物理机上的容器通常有独立网络命名空间,不能直接把容器的 localhost 当成宿主机。
当前 asr.talent-map.com 的代理在 10.100.0.1,上游是 10.100.0.2:8123。可保留这条公网链路,同时让同机 Next.js 后端走 loopback。保留现有远程代理时,ASR 继续监听 0.0.0.0:8123 并用白名单和防火墙控制访问;直接改为仅监听 127.0.0.1 会中断现有公网入口。
10.2 Next.js 后端整段上传
宿主机部署的 Next.js 配置以下服务端环境变量;容器部署只需按上表调整内部 URL:
ASR_INTERNAL_URL=http://127.0.0.1:8123
ASR_API_TOKEN=YOUR_TOKEN
通过 Next.js 运行环境或私有 .env.local 注入凭据。不要使用 NEXT_PUBLIC_ASR_API_TOKEN,也不要把凭据传入 Client Component props;NEXT_PUBLIC_ 变量会进入浏览器构建产物。见 Next.js 环境变量文档。
业务项目可使用 App Router Route Handler 接收 /api/asr/transcriptions,先验证登录和用户调用额度,再转发 multipart file 到 ASR。Route Handler 是公开 HTTP 入口,不能仅凭地址有 /api 就视为已认证;接入方式见 Next.js 后端代理指南。
下面是仅供服务端调用的 TypeScript 转写函数,可放在业务项目的 lib/asr.ts。它返回原始上游 Response,便于业务路由按状态码处理;它本身不实现业务登录验证。
import 'server-only';
export async function transcribeWav(file: File): Promise<Response> {
const base = process.env.ASR_INTERNAL_URL;
const token = process.env.ASR_API_TOKEN;
if (!base || !token) throw new Error('ASR server configuration missing');
const form = new FormData();
form.append('file', file, file.name);
return fetch(`${base.replace(/\/$/, '')}/v1/audio/transcriptions`, {
method: 'POST',
headers: {Authorization: `Bearer ${token}`},
body: form,
cache: 'no-store',
signal: AbortSignal.timeout(30000),
});
}
Route Handler 应使用 Node.js runtime,按以下顺序处理请求:
- 验证当前业务用户,再检查上传 Content-Type、请求体上限和读取超时;不能只依赖 Content-Length,也不能无界执行
request.formData()。 - 提取
file,确认 WAV 文件不超过484096字节;multipart 请求体按545536字节限制。边缘代理可先限制总请求体,业务后端仍需处理缺少或伪造 Content-Length 的情况。 - 调用上面的服务端函数。由 FormData 自动生成 multipart boundary,不手工设置 Content-Type。
- 保留 ASR 的成功字段和错误状态语义,尤其是
409 busy、413、503;不要把失败包装成200。内部401表示服务凭据配置问题,业务登录过期应在第一步处理。 - 将请求超时或上游连接失败明确返回为业务网关错误,保留音频供用户决定是否重试。上游可能返回非 JSON,解析前检查状态和 Content-Type。
浏览器只上传到自身业务域名,HTTP 无需新增 ASR 跨域配置。重采样仍在浏览器采集阶段完成:16kHz、单声道、PCM16 WAV,最长 15 秒;浏览器 MediaRecorder 默认的 WebM/Opus 不能直接上传。
10.3 实时 WebSocket 网关
实时识别使用能持续代理 WebSocket 的 Nginx 或专用 WS 网关。普通上传 Route Handler 用于 HTTP;部署流式接口时,应明确验证宿主环境对 WebSocket Upgrade 和长连接的支持,不能把它当成普通 POST 请求处理。Next.js 自部署可以放在反向代理后,见官方自部署指南。
推荐网关在握手阶段用业务 session/cookie 验证用户,再向 ASR 注入 Authorization: Bearer YOUR_TOKEN。例如 Nginx auth_request 可以调用业务的登录校验接口,验证通过才允许升级;该校验接口也需要业务项目实现。仅由代理自动添加 Token,并不会限制公网调用者身份。见 Nginx 认证子请求。
WS 网关需要满足以下条件:
- 上游路径为
/ws/asr,使用 HTTP/1.1 并传递 Upgrade / Connection;正确转发二进制 PCM 和文本控制帧。见 Nginx WebSocket 代理。 - 在同一台宿主机上运行的网关可连接
ws://127.0.0.1:8123/ws/asr;远程网关或容器网关按 10.1 节选址。 - 业务浏览器的首条
start可以省略api_token,因为网关已在握手中注入服务 Token。消息顺序仍为 start → ready → PCM → end → final。 - 保持原始浏览器 Origin,并在 ASR 的
ASR_ALLOWED_ORIGINS逗号分隔列表中追加准确业务 Origin,例如https://app.example.com;保留现有允许项,配置生效需要重启 ASR。不要用伪造 Origin 代替业务登录鉴权。 - ASR 的
ASR_ALLOWED_CLIENTS检查网关实际连接地址。当前配置按单个 IP匹配,不支持 CIDR;改动时还要检查宿主机防火墙。 - 按实时速度转发,保留顺序和背压;不要等整段录音结束再一次性灌入 WS。建议每 100ms 发 3200 字节,等待
ready,停止时清空尾帧再发送end,收到final后才算成功。 - 代理读取超时应覆盖 ASR 约 40 秒的会话总时限,例如 60 秒;业务自行设置握手、输入和发送超时,不能无限积压。
同源业务域名简化浏览器接入,但 WS 仍受 ASR Origin 校验。HTTPS 页面使用 WSS;内部 loopback 使用 HTTP/WS 不需要绕行公网 TLS 入口。
10.4 Next.js 容器访问当前宿主机 ASR
以下为 Linux Compose 配置片段,假设业务服务名为 web,ASR 继续使用当前宿主机 systemd 部署:
services:
web:
# 保留业务项目已有的 image/build、ports 等配置。
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
ASR_INTERNAL_URL: http://host.docker.internal:8123
ASR_API_TOKEN: ${ASR_API_TOKEN:?ASR_API_TOKEN must be set}
从容器实际连接后确认 ASR 看到的 peer 地址,再配置允许的单个来源 IP 和必要的防火墙规则。当前 loopback / WireGuard 白名单不会自动允许 Docker bridge 来源。若容器地址重建后变化,应使用受控网络和稳定地址或同步更新配置;不能把 172.x CIDR 直接写进当前只匹配单个 IP 的白名单。若 ASR 也迁入 Compose,则使用同一网络中的实际服务名,并单独配置其模型、GPU 与访问规则;本节不会自动迁移现有服务。
10.5 性能、容量与凭据边界
- 内部调用省去公网 DNS/TLS、远程反向代理和 WireGuard 绕行;实际收益以业务端端到端计时为准。模型推理耗时和当前 500ms SDK 分块等待不会因此消失。
- Next.js 与 ASR 保持独立常驻进程,业务构建或重启不应重新加载 GPU 模型。ASR 维持一个 Uvicorn worker 和一个推理线程;增加 worker 会各自加载模型,且不能保持当前全服务单路容量约束。
- 当前 HTTP 和 WS、所有接入项目共用一个活动识别名额。同机部署、多个 Next.js 实例或多个业务用户都不会增加 GPU 服务容量。优先展示 busy 并有限退避;如需排队,应限制长度与等待时间,多实例业务使用共享协调机制,不能假设各进程内存队列能统一调度。
- 业务登录、用户限频和调用统计放在 Next.js / 网关层;服务端 Token 负责上游认证。现有公开测试页按用户要求仍包含共享 Token,单独将 Next.js 凭据放在服务端不会让这把已经公开的 Token 恢复保密。若后续需要限制整个公网 ASR 的调用者,应另行调整公开测试入口和凭据策略。
10.6 部署验收
先从 Next.js 所在运行环境测试内部地址。下面适用于宿主机进程;容器内应替换为对应内部地址,ASR_API_TOKEN 通过私有运行环境注入:
curl --connect-timeout 3 --max-time 10 http://127.0.0.1:8123/healthz
curl --connect-timeout 3 --max-time 10 \
-H "Authorization: Bearer ${ASR_API_TOKEN}" \
http://127.0.0.1:8123/readyz
readyz 应返回 200 且 status=ready;busy=true 表示已有调用,不代表部署失败。完成业务项目接入后,再验证:未登录的业务请求被拒绝;合法 WAV 经业务 HTTP 路由成功转写;WSS 收到 ready / partial / final;另一并发请求按约定返回 busy;浏览器业务构建中没有注入服务端 Token。现有公网测试页的公开 Token 行为另按第 1 节理解。