Amazon Lexは、Lambdaのコールドスタートのように、一定時間リクエストがないとボットの応答に数秒の遅延が発生します
困ったこと
Amazon ConnectからAmazon Lexボットを呼び出した際、ボットの応答に時間がかかることがあります。
呼び出す頻度は1日1回程度です。この現象の原因を教えてください。
結論
同様の環境で検証した結果、Amazon Lexでは AWS Lambdaのコールドスタートのように、ボットへのリクエストがしばらく行われなかった場合、その後の最初のリクエストでは応答に時間がかかることが分かりました。
Lambdaのコールドスタートとは、関数が呼び出されてから実行環境が準備され、実際に処理が開始されるまでの遅延時間のことを指します。
Amazon Lex ボットでの今回の現象を、本記事ではAWS Lambdaの類似概念になぞらえて便宜的に『コールドスタート』と呼びます。
さらに、ドラフトバージョンと番号付きバージョンのボットでは、リクエストの間隔によって応答時間が異なることが分かりました。
- ドラフトバージョン(エイリアス名:TestBotAlias)のボットでは、1時間以上リクエストがない場合、コールドスタートが発生する
- 番号付きバージョンのボットでは、24時間以上リクエストがない場合、コールドスタートが発生する
ドラフトバージョンは主に開発環境で利用されますが、応答に時間がかかると感じる場合は、番号付きバージョンのボットの利用をおすすめします。
Amazon Lex のコールドスタートについては、AWS の公式ドキュメントには記載がありませんが、AWS re:Post で一部言及されています。ドラフトバージョンに関しては言及されていません。
If the Lex bot is not used for a period longer than 24h, the first time is initialize after this it will suffer a cold start (in this case it will take some seconds to reply).
If cold start is a problem, you may want to pre warm the bot regularly, (you can use Eventbridge + lambda for this.
https://repost.aws/questions/QUu9_RU4P0TbyIOperajwz7g/amazon-connect-get-customer-input-takes-too-long-to-initialize-amazon-lex-bot
本番環境において24時間以上リクエストがない場合、かつ、応答遅延の影響を軽減したい場合は、Amazon EventBridgeとAWS Lambdaを使用して定期的にボットにリクエストを送ることで解決できます。
Lambda 関数
ここでは、定期的にボットにリクエストを送るための AWS Lambda 関数を作成します。
関数のタイムアウトは、ボットの応答時間を考慮して20秒に設定しています。
Lambda 関数には、以下の IAM ポリシーを追加します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "lex:RecognizeText",
"Resource": "arn:aws:lex:*:012345678901:bot-alias/*/*"
}
]
}
Lambda関数のコードは以下の通りです。このコードはAmazon Lexボットへのリクエストを送信し、リクエストから応答までの時間も計測します。
import boto3
import json
import time
def lambda_handler(event, context):
lex_client = boto3.client('lexv2-runtime')
bot_id = "XXXXXXXXXX" # ボットID
bot_alias_id = "TSTALIASID" # エイリアスID
locale_id = "ja_JP"
session_id = 'test-session-001' # セッションIDは適当
start_time = time.time() # 開始時間を記録
try:
# RecognizeText APIを呼び出し
response = lex_client.recognize_text(
botId=bot_id,
botAliasId=bot_alias_id,
localeId=locale_id,
sessionId=session_id,
text='Hello'
)
end_time = time.time() # 終了時間を記録
execution_time = end_time - start_time # 実行時間を計算
print(f'execution_time:{execution_time:.4f} seconds')
return {
'statusCode': 200,
'message': 'Success',
'execution_time': f'{execution_time:.4f} seconds'
}
except Exception as e:
print(f"Error: {str(e)}")
return {
'statusCode': 500,
'message': 'Error'
}
今回、リクエスト時の発話は、Hello
にしています。
上記のLambda関数をEventBridgeで定期的に実行することで、Amazon Lexボットの応答時間を短縮できます。
注意点
- この関数が実行されると、Lexボットや関連付けられたLambda関数のログもロググループに出力されます。
- Lexボットに関連付けているLambda関数のコードの見直しが必要になる可能性があります(例:この関数からのリクエスト時にログを出力しないようにするなど)。
上記の Lambda 関数を使用して検証した結果、以下のことが分かりました。
- 通常のLexボットの応答時間:多くの場合2〜3秒、変動範囲は1〜5秒
- コールドスタート時のLexボットの応答時間:多くの場合8〜12秒、変動範囲は5〜14秒
- ボットの種類による応答時間の顕著な差は確認できない
- ドラフトバージョンにおいて、ボットへのリクエスト間隔(例:1時間後と4時間後)と応答時間の間に比例関係は見られない
- 番号付きバージョンにおいて、ボットへのリクエスト間隔(例:24時間後と72時間後)と応答時間の間に比例関係は見られない
- ボットにリクエスト直後、ボットをビルドした場合、その後の最初のリクエストではコールドスタートは発生しない。
- ボットにリクエスト直後、番号付きバージョンを作成しエイリアスに関連付けた場合、その後の最初のリクエストではコールドスタートは発生しない。
ただし、これらの結果は特定の環境での検証によるものであり、すべての状況に当てはまるとは限りませんのでご注意ください。
参考になれば幸いです。
CloudShellでの実行
CloudShellで実行する場合、以下のコマンドになります。
$ aws lexv2-runtime recognize-text \
--bot-id XXXXXXXXXX \
--bot-alias-id TSTALIASID \
--locale-id ja_JP \
--session-id test-session-001 \
--text "Warm up request" \
--region ap-northeast-1
// 出力結果
{
"sessionState": {
"dialogAction": {
"type": "Close"
},
"intent": {
"name": "FallbackIntent",
"slots": {},
"state": "Failed",
"confirmationState": "None"
},
"sessionAttributes": {},
"originatingRequestId": "83925dd1-628e-4cf8-8fef-0372fb93ca33"
},
"interpretations": [
{
"intent": {
"name": "FallbackIntent",
"slots": {},
"state": "Failed",
"confirmationState": "None"
},
"interpretationSource": "Lex"
},
{
"nluConfidence": {
"score": 0.47
},
"intent": {
"name": "dummy",
"slots": {}
},
"interpretationSource": "Lex"
}
],
"sessionId": "test-session-001"
}
参考