跳至主要内容

异步 HTTP 接口

异步 HTTP 接口是一个用于长时间音频转写的非阻塞 HTTP API。

要使用此 API,请执行以下步骤:

  1. 创建语音识别Job
  2. 轮询检查语音识别Job的状态并获取结果

使用方法

1. 创建语音识别Job

Job创建的 API 请求与同步 HTTP 接口相同,设置请求参数,并将请求发送到异步 HTTP 接口的 endpoint。

POST https://acp-api-async.amivoice.com/v1/recognitions

例如,要使用 curl 命令发送音频文件 test.wav 的语音识别请求,不保存日志,可以执行以下操作:

curl https://acp-api-async.amivoice.com/v1/recognitions \
-F u={APP_KEY} \
-F d="grammarFileNames=-a-general loggingOptOut=True" \
-F a=@test.wav

虽然 endpoint 与同步 HTTP 接口不同,但请求参数的设置方法相同。

备注

某些参数(如情绪分析)仅支持异步 HTTP 接口。

警告

与同步 HTTP 接口不同,日志保存的有无不是通过 endpoint 指定,而是通过请求参数指定。 默认情况下会保存日志。要不保存日志,请在 d 参数中指定 loggingOptOut=True

成功时

成功时的响应包含 sessionid。这是用户语音识别请求的Job ID,用于检查Job状态和获取结果。 text 始终返回 "..."。

示例

{"sessionid":"017ac8786c5b0a0504399999","text":"..."}
失败时

失败时的响应不包含 sessionid。可以通过 codemessage 判断失败原因。 请参阅响应代码和消息

示例

{
"results": [{ "tokens": [], "tags": [], "rulename": "", "text": "" }],
"text": "",
"code": "-",
"message": "received illegal service authorization"
}

2. 检查语音识别Job状态并获取结果

语音识别请求成功后,请检查Job状态并轮询直到 status 变为 completederror

获取Job状态

Job在服务器端按顺序执行。要检查Job状态或获取结果,请使用 endpoint GET /v1/recognitions/{session_id} 以获取结果。

sessionid 设置为Job创建时获得的Job ID。将请求参数中的认证信息(authorization)指定在 Authorization header 中。

使用 curl 执行时,可以执行以下操作。这里假设 sessionid017c25ec12c00a304474a999

curl -H "Authorization: Bearer {APPKEY}" \
https://acp-api-async.amivoice.com/v1/recognitions/017c25ec12c00a304474a999

queued 状态

发送请求后,status 最初处于 queued 状态。

{"service_id":"{YOUR_SERVICE_ID}","session_id":"017c25ec12c00a304474a999","status":"queued"}

started 状态

当Job从队列中取出时,status 变为 started 状态。

{"service_id":"{YOUR_SERVICE_ID}","session_id":"017c25ec12c00a304474a999","status":"started"}

processing 状态

实际开始语音识别处理时,status 变为 processing 状态。请参阅以下示例。为了提高可读性,这里对结果进行了换行。 可以使用 API 接收的音频大小(audio_size)和 MD5 校验和(audio_md5)来确认音频是否正确传输。

processing 到下一个 completed 状态所需的时间取决于音频长度,但通常大约是音频长度的 0.5 到 1.5 倍。

{
"audio_md5":"40f59fe5fc7745c33b33af44be43f6ad",
"audio_size":306980,
"service_id":"{YOUR_SERVICE_ID}",
"session_id":"017c25ec12c00a304474a999",
"status":"processing"
}

completed 状态

语音识别完成后,status 变为 completed 状态。此时,可以从响应的 resultssegments 中获取语音识别结果。结果会在语音识别服务器处理完成后在服务器上保存一段时间。保存期限请参阅异步 HTTP 接口的限制中的"语音识别结果保存期限"。

有关包含识别结果的响应详细信息,请参阅语音识别结果

备注

如果访问超过一定时间后删除的结果,将出现 404 NOT FOUND 错误。有关错误的信息,请参阅参考文档中的错误响应列表

error 状态

如果由于某种原因语音识别失败,status 将变为 error 状态。此时,error_messsage 将设置错误原因。

示例:

{
"status": "error",
"audio_md5":"40f59fe5fc7745c33b33af44be43f6ad",
"audio_size":306980,
"service_id":"{YOUR_SERVICE_ID}",
"session_id":"017c25ec12c00a304474a999",
"error_message": "ERROR: Failed to transcribe in recognition process - amineth_result=0, amineth_code='o', amineth_message='recognition result is rejected because confidence is below the threshold'"
}

error_message 可能包含 amineth_code='{响应代码}'amineth_message='{错误消息}'。详细信息请参阅响应代码和消息详细信息中的

特别是当错误消息包含 amineth_code='o' 时,表示客户端请求方法或音频文件有问题,重试也会得到相同的结果。详细信息请参阅"拒绝 (响应代码=o)"。对于 o 以外的错误,可能是 AmiVoice API 基础设施的问题,请稍等片刻后重试。

内容 ID

在请求时,可以在 d 参数的 contentId 中自由设置字符串。例如,可以设置应用程序生成的 ID、文件名、用户等信息,以便稍后作为识别结果的一部分获取这些信息。

例如,要使用 curl 命令将文件名设置为 contentId 并发送请求,可以执行以下操作:

curl https://acp-api-async.amivoice.com/v1/recognitions \
-F u={APP_KEY} \
-F d="grammarFileNames=-a-general loggingOptOut=True contentId=test.wav" \
-F a=@test.wav

获取Job状态或结果时,响应中将包含 content_id,如下所示:

{"content_id":"test.wav","service_id":"{YOUR_SERVICE_ID}","session_id":"017c25ec12c00a304474a999","status":"queued"}

示例代码

以下是使用 Python 的异步 HTTP 接口典型处理流程的示例代码。

请求参数

执行需要 AmiVoice API 的 APPKEY。请在以下行中设置您自己的 AmiVoice API APPKEY。

app_key = 'TODO: Please set APPKEY here'

决定要在 d 参数中设置的选项。这里我们设置如下:

  • 引擎:通用 (grammarFileNames=-a-general)
  • 日志保存:无 (loggingOptOut=True)
  • 内容 ID:文件名 (contentId=filename)
  • 说话人区分:启用 (speakerDiarization=True)
  • 说话人数:最大=最小=2 (diarizationMinSpeaker=2, diarizationMaxSpeaker=2)
  • 情绪分析:启用 (sentimentAnalysis=True)
domain = {
'grammarFileNames': '-a-general',
'loggingOptOut': 'True',
'contentId': filename,
'speakerDiarization': 'True',
'diarizationMinSpeaker': '2',
'diarizationMaxSpeaker': '2',
'sentimentAnalysis': 'True',
...

此外,我们还注册了两个词。profileId 被注释掉,因此注册的词仅在该会话中有效。有关详细信息,请参阅单词注册

    #'profileId': 'test',
'profileWords': 'wwww よんこだぶる|www2 とりぷるだぶる',
}

d 参数中设置的 key-value 的 value 需要进行 URL 编码。在 Python 中,我们使用 urllib.parse.quote

params = {
'u': app_key,
'd': ' '.join([f'{key}={urllib.parse.quote(value)}' for key, value in domain.items()]),
}
logger.info(params)

params 将如下所示:

{'u': 'XXXX', 'd': 'grammarFileNames=-a-general loggingOptOut=True contentId=www.wav profileWords=wwww%20%E3%82%88%E3%82%93%E3%81%93%E3%81%A0%E3%81%B6%E3%82%8B%7Cwww2%20%E3%81%A8%E3%82%8A%E3%81%B7%E3%82%8B%E3%81%A0%E3%81%B6%E3%82%8B speakerDiarization=True diarizationMinSpeaker=2 diarizationMaxSpeaker=2 sentimentAnalysis=True'}

发送语音识别Job创建请求

使用之前创建的 params 和音频文件通过 HTTP POST 发送请求。为了便于阅读 HTTP 通信代码,示例中使用了 HTTP 客户端库 requests

request_response = requests.post(
url=endpoint,
data={key: value for key, value in params.items()},
files={'a': (filename, open(filename, 'rb').read(), 'application/octet-stream')}
)

通过 HTTP 状态码检查调用是否成功。此外,通过检查响应中是否存在 sessionid 来确认Job是否成功创建。

if request_response.status_code != 200:
logger.error(f'Failed to request - {request_response.content}')
exit(1)

request = request_response.json()

if 'sessionid' not in request:
logger.error(f'Failed to create job - {request["message"]} ({request["code"]})')
exit(2)

logger.info(request)

Job创建成功时,将获得如下响应。使用响应中包含的 sessionid 来检查Job状态和获取结果。

{'sessionid': '01838d9535080a304474a07f', 'text': '...'}

检查语音识别Job状态

通过 HTTP GET 向 recognitions/{sessionid} 发送请求。轮询直到响应中包含的 status 变为 completederror。这里我们每 10 秒检查一次结果。

while True:
# 通过 HTTP GET 向 `recognitions/{sessionid}` 发送请求
result_response = requests.get(
url=f'{endpoint}/{request["sessionid"]}',
headers={'Authorization': f'Bearer {app_key}'}
)
if result_response.status_code == 200:
result = result_response.json()
if 'status' in result and (result['status'] == 'completed' or result['status'] == 'error'):
# 当响应的 `status` 变为 `completed` 或 `error` 时,格式化并输出结果
print(json.dumps(result, ensure_ascii=False, indent=4))
exit(0)
else:
# 如果响应的 `status` 不是 `completed` 或 `error`,则Job仍在执行中
# 因此,在再次检查状态之前稍等片刻(这里等待 10 秒)
logger.info(result)
time.sleep(10)
else:
# 如果 HTTP 响应代码不是 200,则退出
logger.error(f'Failed. Response is {result_response.content} - {e}')
exit(3)

代码

以下是完整的 Python 代码。

async-http-sample.py
import time
import json
import urllib
import logging

import requests


logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(message)s")


endpoint = 'https://acp-api-async.amivoice.com/v1/recognitions'
app_key = 'TODO: Please set APPKEY here'
filename = 'www-2.wav'

# 请求参数
domain = {
'grammarFileNames': '-a-general',
'loggingOptOut': 'True',
'contentId': filename,
'speakerDiarization': 'True',
'diarizationMinSpeaker': '2',
'diarizationMaxSpeaker': '2',
'sentimentAnalysis': 'True',
#'profileId': 'test',
'profileWords': 'wwww よんこだぶる|www2 とりぷるだぶる',
}
params = {
'u': app_key,
'd': ' '.join([f'{key}={urllib.parse.quote(value)}' for key, value in domain.items()]),
}
logger.info(params)

# 发送Job请求
request_response = requests.post(
url=endpoint,
data={key: value for key, value in params.items()},
files={'a': (filename, open(filename, 'rb').read(), 'application/octet-stream')}
)

if request_response.status_code != 200:
logger.error(f'Failed to request - {request_response.content}')
exit(1)

request = request_response.json()

if 'sessionid' not in request:
logger.error(f'Failed to create job - {request["message"]} ({request["code"]})')
exit(2)

logger.info(request)

# 每 10 秒检查一次状态,直到结果出来
while True:
# 向 `recognitions/{sessionid}` 发送 HTTP GET 请求
result_response = requests.get(
url=f'{endpoint}/{request["sessionid"]}',
headers={'Authorization': f'Bearer {app_key}'}
)
if result_response.status_code == 200:
result = result_response.json()
if 'status' in result and (result['status'] == 'completed' or result['status'] == 'error'):
# 当响应的 `status` 变为 `completed` 或 `error` 时,格式化结果并输出
print(json.dumps(result, ensure_ascii=False, indent=4))
exit(0)
else:
# 当响应的 `status` 不是 `completed` 或 `error` 时,说明Job仍在执行中
# 因此,在再次检查状态之前稍等片刻(这里等待 10 秒)
logger.info(result)
time.sleep(10)
else:
# 当 HTTP 响应代码不是 200 时退出
logger.error(f'Failed. Response is {result_response.content} - {e}')
exit(3)

执行方法

请确保系统已安装 Python3。

安装库:

pip install requests

下载示例音频文件(www-2.wav),并复制到与程序相同的目录中。

信息

这是一个录音的音频文件,内容是"トリプル・ダブルは、バスケットボールの記録に関する用語です。"在示例代码中,我们为"とりぷるだぶる"这个读音注册了"www2"这个单词,所以可以通过这个内容来确认这个注册是否生效。

要执行示例程序,请从命令行运行以下命令:

python async-http-sample.py

执行结果如下:

$ python sample.py
2022-12-06 15:01:03,336 {'u': 'XXXX', 'd': 'grammarFileNames=-a-general loggingOptOut=True contentId=www-2.wav speakerDiarization=True diarizationMinSpeaker=2 diarizationMaxSpeaker=2 sentimentAnalysis=True profileWords=wwww%20%E3%82%88%E3%82%93%E3%81%93%E3%81%A0%E3%81%B6%E3%82%8B%7Cwww2%20%E3%81%A8%E3%82%8A%E3%81%B7%E3%82%8B%E3%81%A0%E3%81%B6%E3%82%8B'}
2022-12-06 15:01:03,345 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-12-06 15:01:04,117 https://acp-api-async.amivoice.com:443 "POST /v1/recognitions HTTP/1.1" 200 55
2022-12-06 15:01:04,119 {'sessionid': '0184e605ff170a306b8f9c96', 'text': '...'}
2022-12-06 15:01:04,122 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-12-06 15:01:04,309 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/0184e605ff170a306b8f9c96 HTTP/1.1" 200 112
2022-12-06 15:01:04,312 {'status': 'queued', 'session_id': '0184e605ff170a306b8f9c96', 'service_id': 'amiyamamoto', 'content_id': 'www-2.wav'}
2022-12-06 15:01:14,328 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-12-06 15:01:14,517 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/0184e605ff170a306b8f9c96 HTTP/1.1" 200 112
2022-12-06 15:01:14,519 {'status': 'queued', 'session_id': '0184e605ff170a306b8f9c96', 'service_id': 'amiyamamoto', 'content_id': 'www-2.wav'}
2022-12-06 15:01:24,523 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-12-06 15:01:24,718 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/0184e605ff170a306b8f9c96 HTTP/1.1" 200 112
2022-12-06 15:01:24,721 {'status': 'queued', 'session_id': '0184e605ff170a306b8f9c96', 'service_id': 'amiyamamoto', 'content_id': 'www-2.wav'}
2022-12-06 15:01:34,728 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-12-06 15:01:34,886 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/0184e605ff170a306b8f9c96 HTTP/1.1" 200 112
2022-12-06 15:01:34,888 {'status': 'queued', 'session_id': '0184e605ff170a306b8f9c96', 'service_id': 'amiyamamoto', 'content_id': 'www-2.wav'}
2022-12-06 15:01:44,940 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-12-06 15:01:45,114 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/0184e605ff170a306b8f9c96 HTTP/1.1" 200 112
2022-12-06 15:01:45,118 {'status': 'queued', 'session_id': '0184e605ff170a306b8f9c96', 'service_id': 'amiyamamoto', 'content_id': 'www-2.wav'}
2022-12-06 15:01:55,124 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-12-06 15:01:56,735 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/0184e605ff170a306b8f9c96 HTTP/1.1" 200 112
2022-12-06 15:01:56,736 {'status': 'queued', 'session_id': '0184e605ff170a306b8f9c96', 'service_id': 'amiyamamoto', 'content_id': 'www-2.wav'}
2022-12-06 15:02:06,743 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-12-06 15:02:06,940 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/0184e605ff170a306b8f9c96 HTTP/1.1" 200 113
2022-12-06 15:02:06,942 {'status': 'started', 'session_id': '0184e605ff170a306b8f9c96', 'service_id': 'amiyamamoto', 'content_id': 'www-2.wav'}
2022-12-06 15:02:16,948 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-12-06 15:02:17,108 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/0184e605ff170a306b8f9c96 HTTP/1.1" 200 113
2022-12-06 15:02:17,109 {'status': 'started', 'session_id': '0184e605ff170a306b8f9c96', 'service_id': 'amiyamamoto', 'content_id': 'www-2.wav'}
2022-12-06 15:02:27,114 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-12-06 15:02:27,281 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/0184e605ff170a306b8f9c96 HTTP/1.1" 200 183
2022-12-06 15:02:27,283 {'status': 'processing', 'session_id': '0184e605ff170a306b8f9c96', 'service_id': 'amiyamamoto', 'content_id': 'www-2.wav', 'audio_size': 270444, 'audio_md5': 'fd7d144824e8a5982d3aaa4cda5358a8'}
2022-12-06 15:02:37,290 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-12-06 15:02:37,476 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/0184e605ff170a306b8f9c96 HTTP/1.1" 200 2481
{
"status": "completed",
"session_id": "0184e605ff170a306b8f9c96",
"service_id": "amiyamamoto",
"content_id": "www-2.wav",
"audio_size": 270444,
"audio_md5": "fd7d144824e8a5982d3aaa4cda5358a8",
"segments": [
{
"results": [
{
"tokens": [
{
"written": "www2",
"confidence": 1,
"starttime": 1620,
"endtime": 2548,
"spoken": "とりぷるだぶる",
"label": "speaker0"
},
{
"written": "は",
"confidence": 1,
"starttime": 2548,
"endtime": 2788,
"spoken": "は",
"label": "speaker0"
},
{
"written": "バスケットボール",
"confidence": 1,
"starttime": 2916,
"endtime": 3956,
"spoken": "ばすけっとぼーる",
"label": "speaker0"
},
{
"written": "の",
"confidence": 0.99,
"starttime": 3956,
"endtime": 4052,
"spoken": "の",
"label": "speaker0"
},
{
"written": "記録",
"confidence": 1,
"starttime": 4052,
"endtime": 4404,
"spoken": "きろく",
"label": "speaker0"
},
{
"written": "に",
"confidence": 1,
"starttime": 4404,
"endtime": 4532,
"spoken": "に",
"label": "speaker0"
},
{
"written": "関する",
"confidence": 1,
"starttime": 4532,
"endtime": 5060,
"spoken": "かんする",
"label": "speaker0"
},
{
"written": "用語",
"confidence": 1,
"starttime": 5060,
"endtime": 5412,
"spoken": "ようご",
"label": "speaker1"
},
{
"written": "です",
"confidence": 0.96,
"starttime": 5412,
"endtime": 5940,
"spoken": "です",
"label": "speaker0"
},
{
"written": "。",
"confidence": 0.8,
"starttime": 5940,
"endtime": 6196,
"spoken": "_",
"label": "speaker0"
}
],
"confidence": 1,
"starttime": 1300,
"endtime": 6196,
"tags": [],
"rulename": "",
"text": "www2はバスケットボールの記録に関する用語です。"
}
],
"text": "www2はバスケットボールの記録に関する用語です。"
}
],
"utteranceid": "20221206/15/0184e60741170a30522339d0_20221206_150225[nolog]",
"text": "www2はバスケットボールの記録に関する用語です。",
"code": "",
"message": "",
"sentiment_analysis": {
"segments": [
{
"starttime": 1680,
"endtime": 2860,
/* 情绪参数 */
},
{
"starttime": 3520,
"endtime": 4900,
/* 情绪参数 */
}
]
}
}

text是结果文本,得到的结果为"www2はバスケットボールの記録に関する用語です。",可以看出语音内容被正确识别。另外,"トリプルダブル"被转换为"www2",可以确认单词注册也有效生效。关于结果的详细信息,请参考语音识别的结果

故障排除

received illegal service authorization

如果显示以下内容,可能是没有设置AmiVoice API的APPKEY。

2022-10-11 10:10:44,928 Failed to create job - received illegal service authorization (-)

请检查代码中以下部分是否设置了APPKEY,以及是否有错误。

app_key = 'TODO: Please set APPKEY here'

也请参考本页中的请求参数部分。

No such file or directory: 'www-2.wav'

如果显示以下内容,说明执行目录中不存在音频文件。

FileNotFoundError: [Errno 2] No such file or directory: 'www-2.wav'

请下载样本音频文件(www-2.wav)并复制到执行命令的目录中。确认文件存在后再次执行。

其他文档