Lexでインテント間の情報共有とコンテキストの管理にセッションを利用する

2018.06.06

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

渡辺です。 札幌は夏になりました。 夏は1ヶ月ほどで終わるので、8月には雪が降るかも知れません。

Amazon Lexは、ユーザの発話(入力)に対し、レスポンス(出力)を返す、対話型のインターフェイスです。 REST APIのようなステートレスな設計にすることも可能ですが、会話の流れを考慮するため、一般的にはステートフルに設計します。 Lexでは、アプリケーションサーバと同様に、対話がはじまるとセッションを作成します。 セッションデータを利用することで、コンテキスト間で情報共有を行えます。

セッションデータの保存

Lambda関数でセッションデータを保存するには、レスポンスオブジェクトにsessionAttributesフィールドを追加します。

レスポンスオブジェクトは次のような形式です。

{
  "dialogAction": {
  },
  "sessionAttributes": { 
     "key": "value",
     "key": "value"
  }
}

dialogActionは、Lexの返答するメッセージなどを含むフィールドで必須項目です。 sessionAttributesは、オプション項目となっていて、セッションデータとして保存する情報をKey-Value形式で設定します。

例えば、スロットにSign(星座名)が入力された時、入力されたSignをセッションデータとして保存するならば、次のようになります。

exports.handler = function(event, context, callback) {
    let sign = event.currentIntent.slots['Sign']; // ex) "Aries"    
    let result = {
        "dialogAction": {}, // 略
        "sessionAttributes": {
            "Sign": sign
        }
    };
    callback(null, result);
}

セッション情報は、Lexで暗号化されて保存されます。

セッションデータの取得

Lambda関数では入力イベントからセッションデータを取得できます。

一部を抜粋した入力イベントは次のような構造です。

{
  "currentIntent": {
    "name": "intent-name"
  },
  "bot": {
    "name": "bot name",
    "alias": "bot alias",
    "version": "bot version"
  },
  "userId": "User ID specified in the POST request to Amazon Lex.",
  "sessionAttributes": { 
     "key": "value",
     "key": "value"
  }
}

入力イベントにセッションデータが含まれる場合、sessionAttributesから取得できます。 セッションデータが含まれない場合は、空オブジェクトではなく、sessionAttributesキー自体が存在しないので注意してください。

Lambdaでは、セッションデータを次のようにして取得できます。

exports.handler = function(event, context, callback) {
    let attributes = event.sessionAttributes;
    let sign = attributes['Sign'];
    // 以下略
}

ユーザが入力したパラメータなどをセッションに保存すれば、「XXに関して会話している」という流れを踏まえた応答をボットが行えます。

コンテキストをセッションに保存する

コンテキスト(文脈)は、チャットボットなどで会話の流れを表す概念です。 あるボットに幾つかの機能があったとして、「どの機能を利用しているのか?」といった情報は会話の流れから決定します。 例えば、お店の予約や案内をするボットがあったとして、「予約をしている」のか「近くの店舗を探している」のか「メニューを確認している」のかといったことが想定されるでしょう。 このような状態をコンテキストと定義することで、処理時に適切なハンドリングを行えます。

残念ながら、Lexではデフォルトでコンテキストを扱う機能はありません。 現状では、セッションデータの一部としてコンテキストを保存します。

例えば、コンテキストとして、「RESEVATION(予約)」「STORE_INFO(店舗情報)」「STORE_MENU(メニュー)」がある場合、次のようなイメージでコンテキストを管理します。

exports.handler = function(event, context, callback) {
    let context = event.sessionAttributes['_context'];
    if (context === 'STORE_INFO') {
        // 略
    } else {
        // 略
    }
    let result = {
        "dialogAction": {}, // 略
        "sessionAttributes": {
            "_context": 'STORE_INFO'
        }
    };
    callback(null, result);
}

同じインテントであってもコンテキストが異なる場合に処理を変えなければならないケースでは必要です。

まとめ

Lexでは状態をセッションデータで管理することができます。 特に、今どのような会話をしているのかをコンテキストとして保持すれば、複雑な会話も実現出来るでしょう。

しかし、 ユーザの発話を識別するインテントでは、コンテキストは考慮されません 。 あまり複雑でネストしたコンテキストを持つボットを設計すると、破綻しやすくなるので注意してください。 可能であれば、ひとつのボットはひとつの機能と割り切った方が良いでしょう。 Connectのバックエンドで動かすのであれば、機能毎にボットは分割するほうが良いと思います。