メインコンテンツまでスキップ

非同期 HTTP インタフェース

非同期 HTTP 音声認識 API は長時間の音声を文字起こしするためのノンブロッキングな HTTP API です。

この API を利用するには以下のステップを実行します。

  1. 音声認識ジョブを作成する
  2. ポーリングして音声認識ジョブの状態をチェックし、結果を取得する

利用の方法

1. 音声認識ジョブを作成する

ジョブの作成の API リクエストは、同期 HTTP 音声認識 API と同じようにリクエストパラメータを設定し、非同期 HTTP 音声認識 API のエンドポイントにリクエストを送信します。

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

例えば、curl コマンドで、ログ保存なしで、test.wavファイルの音声認識のリクエストを送るには以下のようにします。

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

同期 HTTP 音声認識 API とはエンドポイントが異なりますが、リクエストパラメータの設定の方法は同じです。

注記

いくつかのパラメータは非同期 HTTP 音声認識 API にしか対応していません。例えば、2022年6月現在では、話者ダイアライゼーションや感情解析は非同期 HTTP にしか対応していないためです。

注意

同期 HTTP 音声認識 API とは異なり、ログ保存のあり、なしはエンドポイントではなく、リクエストパラメータで指定します。 デフォルトではログ保存ありとなります。ログ保存をしないようにするには、dパラメータにloggingOptOut=Trueを指定します。

成功した場合

成功時のレスポンスにはsessionidが含まれています。これは、ユーザの音声認識リクエストに対するジョブの ID でジョブの状態を確認したり、結果を得るために利用します。 textは常に"..."を返します。

{"sessionid":"017ac8786c5b0a0504399999","text":"..."}

失敗した場合

失敗時のレスポンスにはsessionidが含まれません。codemessageで失敗の原因を判断できます。 レスポンスコードとメッセージを参照ください。

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

2. 音声認識ジョブの状態をチェックし、結果を取得する

音声認識の結果のリクエストに成功したら、ジョブの状態を確認し、statuscompletedとなるかerrorとなるまでポーリングしてください。

ジョブの状態の取得

ジョブはサーバ側で順次実行されます。ジョブの状態の確認や、結果を取得するには結果取得用のエンドポイント GET /v1/recognitions/{session_id}に問い合わせます。

sessionidには、ジョブ作成時に得られたジョブ ID を設定します。リクエストパラメータの認証情報(authorization)は、Authorizationヘッダーに指定してください。

curl で実行する場合は以下のようにします。ここでは、sessionid017c25ec12c00a304474a999だとします。

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

queued 状態

リクエストを送った直後は、statusqueuedの状態になります。

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

started 状態

キューからジョブが取り出されるとstatusstarted状態になります。

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

processing 状態

実際に音声認識処理が始まるとstatusprocessing状態になります。以下のサンプルを参照ください。ここでは見やすさのために結果を改行しています。 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 状態

音声認識が完了すると、statuscompleted状態となります。このとき、レスポンスのresultssegmentsに音声認識結果を得ることができます。結果は音声認識サーバの処理が完了してから一定期間サーバに保存されます。保存期間は、非同期HTTP音声認識APIの制限の"音声認識結果の保存期間"を参照してください。

認識結果を含むレスポンスの詳細は音声認識の結果を参照してください。

注記

一定期間を過ぎて削除された結果にアクセスすると、404 NOT FOUNDエラーとなります。エラーについてはリファレンスのエラーレスポンス一覧を参照してください。

error 状態

なんらかの理由で音声認識が失敗した場合は、statuserror状態となります。errorの原因についてはレスポンスコードとメッセージを参照してください。

コンテンツ ID

リクエスト時にdパラメータのcontentIdには自由に文字列を設定できます。例えば、アプリケーションが発行した ID や、ファイル名、利用者などの情報を設定しておくことで、認識結果の一部として後からそれらの情報が得られます。

例えば、curl コマンドでcontentIdにファイル名を設定してリクエストを送るには以下のようにします。

curl -X POST 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

ジョブの状態や結果を取得する際、以下のようにcontent_idが含まれるようになります。

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

サンプルコード

非同期HTTP音声認識APIの典型的な処理の流れをPythonのサンプルコードで示します。

リクエストパラメータ

実行にはAmiVoice APIのAPPKEYが必要です。以下の行をに自身のAmiVoice APIのAPPKEYを設定します。

app_key = 'TODO: Please set APPKEY here'

dパラメータに設定するオプションを決めます。ここでは以下のようにします。

  • エンジン: 汎用 (grammarFileNames=-a-general)
  • ログ保存: なし (loggingOptOut=True)
  • コンテンツID: ファイル名 (contentId=filename)
  • フィラー: 出力する (keepFillerToken=1)
  • 話者ダイアライゼーション: 有効 (speakerDiarization=True)
  • 話者数: 最大=最小=2 (diarizationMinSpeaker=2, diarizationMaxSpeaker=2)
  • 感情解析: 有効 (sentimentAnalysis=True)
domain = {
'grammarFileNames': '-a-general',
'loggingOptOut': 'True',
'contentId': filename,
'keepFillerToken': '1',
'speakerDiarization': 'True',
'diarizationMinSpeaker': '2',
'diarizationMaxSpeaker': '2',
'sentimentAnalysis': 'True',
...

また、2つの単語を登録しています。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 keepFillerToken=1 speakerDiarization=True diarizationMinSpeaker=2 diarizationMaxSpeaker=2 sentimentAnalysis=True'}

音声認識ジョブ作成のリクエスト

さきほどの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が存在するかどうかで確認します。

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)

ジョブの作成に成功すると以下のようなレスポンスが得られます。レスポンスに含まれるsessionidを使ってジョブの状態の確認や結果の取得を行います。

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

音声認識ジョブの状態の確認

HTTP GETでrecognitions/{sessionid}にリクエストします。レスポンスに含まれるstatuscompletederrorになるまでポーリングします。ここでは、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`以外の場合は、ジョブの実行中
# なので、再度状態をチェックする前に少し待つ(ここでは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.wav'

# リクエストパラメータ
domain = {
'grammarFileNames': '-a-general',
'loggingOptOut': 'True',
'contentId': filename,
'keepFillerToken': '1',
'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)

# ジョブのリクエストの送信
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:
# 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`以外の場合は、ジョブの実行中
# なので、再度状態をチェックする前に少し待つ(ここでは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.wav)をダウンロードして、プログラムと同じディレクトリにコピーしてください。

サンプルプログラムを実行するにはコマンドラインから以下を実行します。

python async-http-sample.py

実行結果は以下のようになります。

2022-09-30 17:48:29,269 {'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 keepFillerToken=1 speakerDiarization=True diarizationMinSpeaker=2 diarizationMaxSpeaker=2 sentimentAnalysis=True'}
2022-09-30 17:48:29,286 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-09-30 17:48:29,845 https://acp-api-async.amivoice.com:443 "POST /v1/recognitions HTTP/1.1" 200 55
2022-09-30 17:48:29,848 {'sessionid': '01838d9535080a304474a07f', 'text': '...'}
2022-09-30 17:48:29,850 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-09-30 17:48:29,985 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/01838d9535080a304474a07f HTTP/1.1" 200 110
2022-09-30 17:48:30,001 {'content_id': 'www.wav', 'service_id': 'amivoice-test-user-01', 'session_id': '01838d9535080a304474a07f', 'status': 'queued'}
2022-09-30 17:48:40,016 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-09-30 17:48:40,145 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/01838d9535080a304474a07f HTTP/1.1" 200 110
2022-09-30 17:48:40,145 {'content_id': 'www.wav', 'service_id': 'amivoice-test-user-01', 'session_id': '01838d9535080a304474a07f', 'status': 'queued'}
2022-09-30 17:48:50,148 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-09-30 17:48:50,298 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/01838d9535080a304474a07f HTTP/1.1" 200 110
2022-09-30 17:48:50,298 {'content_id': 'www.wav', 'service_id': 'amivoice-test-user-01', 'session_id': '01838d9535080a304474a07f', 'status': 'queued'}
2022-09-30 17:49:00,313 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-09-30 17:49:00,469 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/01838d9535080a304474a07f HTTP/1.1" 200 110
2022-09-30 17:49:00,471 {'content_id': 'www.wav', 'service_id': 'amivoice-test-user-01', 'session_id': '01838d9535080a304474a07f', 'status': 'queued'}
2022-09-30 17:49:10,481 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-09-30 17:49:10,576 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/01838d9535080a304474a07f HTTP/1.1" 200 111
2022-09-30 17:49:10,579 {'content_id': 'www.wav', 'service_id': 'amivoice-test-user-01', 'session_id': '01838d9535080a304474a07f', 'status': 'started'}
2022-09-30 17:49:20,592 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-09-30 17:49:20,678 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/01838d9535080a304474a07f HTTP/1.1" 200 111
2022-09-30 17:49:20,680 {'content_id': 'www.wav', 'service_id': 'amivoice-test-user-01', 'session_id': '01838d9535080a304474a07f', 'status': 'started'}
2022-09-30 17:49:30,709 Starting new HTTPS connection (1): acp-api-async.amivoice.com:443
2022-09-30 17:49:30,957 https://acp-api-async.amivoice.com:443 "GET /v1/recognitions/01838d9535080a304474a07f HTTP/1.1" 200 591
{
"audio_md5": "6125024102697d46f1bb624c250afb37",
"audio_size": 82022,
"code": "",
"content_id": "www.wav",
"message": "",
"segments": [
{
"results": [
{
"confidence": 0.979,
"endtime": 2036,
"rulename": "",
"starttime": 500,
"tags": [],
"text": "www2",
"tokens": [
{
"confidence": 0.98,
"endtime": 1764,
"label": "speaker1",
"spoken": "とりぷるだぶる",
"starttime": 788,
"written": "www2"
}
]
}
],
"text": "www2"
}
],
"sentiment_analysis": {
"segments": []
},
"service_id": "amivoice-test-user-01",
"session_id": "01838d9535080a304474a07f",
"status": "completed",
"text": "www2",
"utteranceid": "20220930/17/01838d9610d90a3044a939d0_20220930_174925[nolog]"
}

以下のように表示される場合は、AmiVoice APIのAPPKEYを設定していない可能性があります。リクエストパラメータを参照して設定してください。

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

以下のように表示される場合は、音声ファイルが実行ディレクトリに存在しません。www.wavをダウンロードしてコマンドを実行するディレクトリにコピーしてください。ファイルが存在することを確認して再度実行してください。

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