ASRService 测试台与 API 文档
同一端口测试一句话识别、实时 WebSocket、长录音和词库,并查看完整请求与返回格式。
测试音频
一句话音频约 4.571 秒;长录音为医患对话,约 196.996 秒。两者均为 16 kHz 单声道 PCM WAV,下载后可直接用于下面三个测试。
词库管理
每行一个词;强制替换词也可写成 原词[TAB]目标词。创建后点击词库标签即可填入识别请求。
热词
敏感词
强制替换
一句话识别
调用 POST /api/v1,建议使用 60 秒以内的 WAV。
长录音任务
调用 POST /request,自动轮询 /getResult。
实时 WebSocket
选择 WAV 后,浏览器会提取 PCM16 数据并分块发送到 /ws/v1;这不依赖麦克风权限,适合远程 HTTP 页面。
在线 API 文档
以下内容与当前 ASRService 实现同步,示例中的 $BASE 表示本页 HTTP 地址。
接口总览
zh-cmn-Hans-CN,模型采样率为 16 kHz。HTTP 错误会同时使用对应的 HTTP 状态码和 JSON status 业务状态码。/health
GET/ready
POST/api/v1
WS/ws/v1
POST/request
GET/getResult
GET/getTasks
POST/deleteTask
CRUD/hotwords
CRUD/forbidden_words
CRUD/correction_words
GET/demo-audio/*
| 通用业务状态码 | 含义 |
|---|---|
| 00000 | 成功 |
| 20001 | 词库请求或词条格式错误 |
| 20114 | 音频请求体为空 |
| 20191 | 请求参数无效、音频超过限制或词库 ID 不存在 |
| 20193 | 识别或服务处理失败 |
| 20302 | 长录音任务正在排队或处理中 |
| 20326 | 任务 ID 不存在 |
GET/health 与 /ready
/health 返回模型、设备和预热状态;/ready 仅在本地模型完整且预热完成后返回 HTTP 200。
curl "$BASE/health" curl "$BASE/ready"
/health 返回示例
{
"status": "ok",
"version": "0.1.0",
"offline": true,
"device": "cpu",
"models_dir": "/opt/asrservice/models",
"missing_models": [],
"preload_enabled": true,
"models_loaded": ["offline+vad+punc+speaker", "streaming", "streaming-punctuation"],
"models_warmed_up": true
}
/ready 返回示例
{"status":"ready","offline":true}
预热尚未完成时返回 HTTP 503:{"status":"warming_up", ...};模型缺失时返回 HTTP 503:{"status":"not_ready", ...}。
POST/api/v1 一句话识别(≤ 60 秒)
音频放在原始 HTTP body 中,Content-Type 为 application/octet-stream。整个请求等待识别完成后返回。
| Query 参数 | 类型/默认值 | 说明 |
|---|---|---|
| lang_type | string / zh-cmn-Hans-CN | 当前只支持普通话 |
| format | string / pcm | pcm、pcm_16000 或 WAV/FLAC 等可解码格式 |
| sample_rate | int / 16000 | 当前必须为 16000 |
| bit_depth | int / 16 | 当前必须为 16 |
| hotwords_id | string / 空 | 已创建的热词库 ID |
| hotwords_list | string / 空 | 本次请求热词,以 | 或逗号分隔 |
| hotwords_weight | float / 0.4 | 范围 0.1~1.0;临时热词优先与词库词合并 |
| correction_words_id | string / 空 | 强制替换词库 ID,多个 ID 用 | 分隔,all 表示全部 |
| forbidden_words_id | string / 空 | 敏感词库 ID,支持多个 ID 或 all |
| clusters | int / 0 | 0 关闭说话人分离;1 开启自动说话人分离;不接受其他值 |
WAV 请求示例
curl -X POST \ "$BASE/api/v1?lang_type=zh-cmn-Hans-CN&format=wav&clusters=1" \ -H "Content-Type: application/octet-stream" \ --data-binary @audio.wav
成功返回
{
"task_id": "5bf51482cdd64c1c9b31b07920a184d8",
"result": "对方正在通话中,请尝试其他方式。",
"status": "00000",
"message": "SUCCESS",
"segments": [{
"lang_type": "zh-cmn-Hans-CN",
"paragraph": 1,
"begin": "00:00:00,120",
"end": "00:00:04,400",
"begin_time": 120,
"end_time": 4400,
"seg_num": 1,
"transcript": "对方正在通话中,请尝试其他方式。",
"confidence": 1.0,
"cluster_id": 1
}]
}
segments。confidence 缺失时当前实现会填 1.0,不能视作经过校准的真实置信度。失败返回示例
{
"task_id": "...",
"result": "",
"status": "20191",
"message": "一句话识别音频不能超过 60 秒"
}
WS/ws/v1 实时语音识别
连接地址为当前页面同主机的 。音频必须是 16 kHz、单声道、16-bit little-endian PCM;建议每个二进制帧 7680 字节。
1. 首条文本消息:启动
{
"header": {
"namespace": "SpeechTranscriber",
"name": "StartTranscription"
},
"payload": {
"lang_type": "zh-cmn-Hans-CN",
"format": "pcm",
"sample_rate": 16000,
"enable_intermediate_result": true,
"enable_punctuation_prediction": true,
"enable_speaker_label": false,
"hotwords_id": "HW12345678",
"hotwords_list": ["魔搭", "FunASR"],
"hotwords_weight": 0.4,
"correction_words_id": "CW12345678",
"forbidden_words_id": "FW12345678",
"user_id": "conversation_001"
}
}
| 模式 | namespace | 启动/停止消息 | 约束 |
|---|---|---|---|
| 一句话流式 | SpeechRecognizer | StartRecognition / StopRecognition | 最长 60 秒 |
| 实时长连接 | SpeechTranscriber | StartTranscription / StopTranscription | 客户端主动结束 |
2. 循环发送二进制 PCM 数据
// JavaScript 示例 socket.send(pcm16Chunk); // ArrayBuffer/Uint8Array,建议 7680 bytes
3. 停止消息
{"header":{"namespace":"SpeechTranscriber","name":"StopTranscription"}}
服务端事件
| 事件 | 说明 |
|---|---|
| TranscriptionStarted / RecognitionStarted | 会话建立成功 |
| SentenceBegin | 实时长连接检测到首个非空结果 |
| TranscriptionResultChanged / RecognitionResultChanged | 中间识别结果,是否返回由 enable_intermediate_result 控制 |
| SentenceEnd | 实时长连接最终句子结果 |
| TranscriptionCompleted / RecognitionCompleted | 最终结果,随后连接关闭 |
| Pong | 响应 Ping 或 Heartbeat 控制消息 |
| TaskFailed | 参数或识别失败,header.status 为 20193 |
事件返回示例
{
"header": {
"namespace": "SpeechTranscriber",
"name": "TranscriptionCompleted",
"status": "00000",
"status_text": "success",
"task_id": "...",
"message_id": "...",
"user_id": "conversation_001"
},
"payload": {
"index": 1,
"time": 4571,
"begin_time": 0,
"speaker_id": "",
"result": "对方正在通话中,请尝试其他方式。",
"confidence": 1,
"volume": 0,
"words": null
}
}
enable_speaker_label=true 时,结束阶段会把整段 PCM 再走一次离线识别与 CAM++ 分离,并在最终 payload 中返回 segments;这不是实时增量说话人分离。POST/request 长录音异步转写
请求类型为 multipart/form-data。服务接收文件后立即返回任务 ID,客户端使用 /getResult 轮询。上传缓存会在成功或失败后立即删除,结果仍可查询。
| Form 字段 | 类型/默认值 | 说明 |
|---|---|---|
| file | File / 必填 | 上传录音文件 |
| lang_type | string / zh-cmn-Hans-CN | 当前只支持普通话 |
| format | string / wav | 文件格式 |
| sample_rate | int / 16000 | 当前必须为 16000 |
| hotwords_id | string / 空 | 热词库 ID |
| hotwords_list | string / 空 | 临时热词,以 | 或逗号分隔 |
| hotwords_weight | float / 0.4 | 范围 0.1~1.0 |
| correction_words_id | string / 空 | 强制替换词库 ID |
| forbidden_words_id | string / 空 | 敏感词库 ID |
| clusters | int / 0 | 0 关闭说话人分离;1 开启自动说话人分离;不接受其他值 |
curl -X POST "$BASE/request" \ -F "file=@meeting.wav" \ -F "lang_type=zh-cmn-Hans-CN" \ -F "format=wav" \ -F "clusters=1"
创建成功
{
"status": "00000",
"message": "",
"data": {"duration": 197, "task_id": "2aad1d7d835d4a68b28a7b74d4946a22"}
}
轮询结果
curl "$BASE/getResult?task_id=2aad1d7d835d4a68b28a7b74d4946a22"
处理中返回:
{
"status": "20302",
"message": "",
"data": {
"task_id": "...", "desc": "音频转写中", "file_name": "meeting.wav",
"insert_time": "2026-09-07 10:00:00", "process_time": "2026-09-07 10:00:01",
"finish_time": "", "progress": 1, "duration": 0, "statistics": null
}
}
完成后返回:
{
"status": "00000",
"message": "",
"data": [{
"lang_type": "zh-cmn-Hans-CN", "paragraph": 1,
"begin": "00:00:00,150", "end": "00:00:00,950",
"begin_time": 150, "end_time": 950, "seg_num": 1,
"transcript": "医生您好。", "confidence": 1.0, "cluster_id": 1
}],
"statistics": {
"keywords": [], "speed": 276, "word_count": 906,
"insert_time": "...", "process_time": "...", "finish_time": "...", "duration": 197
}
}
GET/getTasks 与 /deleteTask
| /getTasks Query | 说明 |
|---|---|
| time | 只查询最近 N 小时,省略表示不限 |
| type | 0 失败、1 完成、2 处理中、3 排队中 |
| count | 最多返回多少条 |
curl "$BASE/getTasks?time=24&type=1&count=20" curl -X POST "$BASE/deleteTask?task_id=<task_id>"
/deleteTask 只能删除已完成或失败的任务元数据;排队或处理中返回 HTTP 409、业务状态 20193。
{"status":"00000","message":"success","data":null}
CRUD热词、敏感词与强制替换词库
| 词库 | 根地址 | ID 前缀 | 词条格式 |
|---|---|---|---|
| 热词 | /hotwords | HW | 普通字符串,单条最多 20 字符 |
| 敏感词 | /forbidden_words | FW | 普通字符串,单条最多 10 字符 |
| 强制替换 | /correction_words | CW | 原词[TAB]目标词,单条最多 100 字符 |
| 方法与地址 | 请求 | 作用 |
|---|---|---|
| POST /{kind} | JSON 或 form-data 文件 | 创建词库,返回词库 ID |
| GET /{kind} | 无 | 列出词库 |
| GET /{kind}/{id} | 无 | 获取详情和 words |
| PUT /{kind}/{id} | JSON 或 form-data | 修改名称或覆盖词条 |
| DELETE /{kind}/{id} | 通常无 body | 删除整个词库 |
| POST /hotwords/{id} | JSON words | 追加热词;其他两类不支持追加 |
| DELETE /hotwords/{id} | 可带 JSON words | 有 body 时仅删除指定热词,无 body 删除词库 |
JSON 创建热词
curl -X POST "$BASE/hotwords" \
-H "Content-Type: application/json" \
-d '{"name":"医疗术语","lang_type":"zh-cmn-Hans-CN","words":["房颤","射频消融"]}'
JSON 创建敏感词
curl -X POST "$BASE/forbidden_words" \
-H "Content-Type: application/json" \
-d '{"name":"敏感词","lang_type":"zh-cmn-Hans-CN","words":["秘密","机密"]}'
JSON 创建强制替换
curl -X POST "$BASE/correction_words" \
-H "Content-Type: application/json" \
-d '{"name":"纠错词","lang_type":"zh-cmn-Hans-CN","words":["无忌\t5G"]}'
UTF-8 文件上传
curl -X POST "$BASE/forbidden_words" \ -F "name=敏感词" -F "lang_type=zh-cmn-Hans-CN" -F "file=@forbidden.txt" curl -X POST "$BASE/correction_words" \ -F "name=纠错词" -F "lang_type=zh-cmn-Hans-CN" -F "file=@corrections.txt"
通用返回
{"status":"00000","message":"success","data":"HW50D67839"}
详情中的 data 包含 id、name、lang_type、status、sample_rate、update_time、amount 和 words。
GET测试页面与演示音频
| 地址 | 说明 |
|---|---|
| / | 当前测试台和在线接口文档 |
| /docs | FastAPI 自动生成的 OpenAPI/Swagger 文档 |
| /openapi.json | OpenAPI JSON 描述 |
| /demo-audio/sentence.wav | 4.571 秒、16 kHz 单声道 PCM WAV |
| /demo-audio/long_demo.wav | 196.996 秒、16 kHz 单声道 PCM WAV |