三番目に公開したAlexaスキル「IT業界の深い闇」でSDKをなぜいじったか


ごきげんよう

この記事は
「するめごはんのVUI・スマートスピーカー Advent Calendar 2018」
の3日目の記事です。

前日の
https://qiita.com/surumegohan/items/854a8f9cc7f79adf266c
にて記載した「IT業界の光闇」のActions on Google版について記載する前にこちらを先に記載します。

なお、この記事に記載している「IT業界の深い闇」というスキルは既にソースコードを以下に公開しています。

■VUIアプリケーションのフィードバック機能をAlexaスキルで公開した
https://qiita.com/surumegohan/items/af700c96df5ccf1a1a74

「IT業界の深い闇」のdeveloper console画面

このスキルでは、IT業界の深い闇の話を流した後に、ユーザーに対して「いいね」「いまいち」「ストップ」の判断を依頼します。

そのためにgoodIntentとbadIntentがあり、goodIntentは以下の7つにしています。

  1. 深い
  2. 最高
  3. いい
  4. よいですね
  5. いいですね
  6. よかった
  7. いいね

これらの言葉は「いいね」として見なすようにしています。

なぜ今またこの記事を書くか

まず、Alexaで「状態」を管理する方法はいくつかあり、大雑把に言うと

  1. リクエスト単位(話しかけるごと)
  2. セッション単位(一度の起動から終了まで)
  3. 永続化(スキルを終了しても保存しておく)

のレベルがそれぞれあります。

Alexaスキルでユーザーの起動が2回目以降の場合などにおいて、3の永続化 として、DynamoDBにユーザーIDを記録しておくのはよくあるやり方だと認識しています。

AlexaのSDKの場合、


handlerInput.attributesManager.setPersistentAttributes(attribute)

とすると、DynamoDBにPutされます。

けれど、このスキルは DynamoDBにPutする部分のSDKを改編している ので、そこについて少し触れようと思います。

ここでのユーザーIDとは

本記事でのユーザーIDとは、Alexaでやり取りする際に発生するJSONに含まれるuserIdを示します。

この記事を書いている12月頭時点で、このスキルを起動すると以下のようなJSONが流れます。

AlexaのuserIdはスキル内では同じユーザーは、いつ同じスキルを起動しても同じユーザーIDです。
このユーザーIDの人は、他のスキルを起動すると、そこでは別のユーザーIDになります。

※一部マスキングしているのと、略しています。

{
    "version": "1.0",
    "session": {
        "new": true,
        "sessionId": "amzn1.echo-api.session.955d0538-a464-4beb-b2ac-AAAAAAAAAAA",
        "application": {
            "applicationId": "amzn1.ask.skill.76274913-c2f8-4159-8d0e-XXXXXXXX"
        },
        "user": {
            "userId": "amzn1.ask.account.AECCNHCZ53P2CLATS4ZQZKHWBT3ZCOOZKISGGPP4KTEBFEUANDGZ4OL4WPLBHYRMIHYVIWVYTBHJ6MNRMJJ3YN7NSGKMY3L4TL3XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
        }
    },
    "context": {
        "System": {
            "application": {
                "applicationId": "amzn1.ask.skill.76274913-c2f8-4159-8d0e-XXXXXXXX"
            },
            "user": {
                "userId": "amzn1.ask.account.AECCNHCZ53P2CLATS4ZQZKHWBT3ZCOOZKISGGPP4KTEBFEUANDGZ4OL4WPLBHYRMIHYVIWVYTBHJ6MNRMJJ3YN7NSGKMY3L4TL3XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
            },
            "device": {
                "deviceId": "amzn1.ask.device.AEFHK3CB56PBTMR6NHFZ3MNIJ5RPZLKWIG3DKSOV4PDM3UWNKN553JXDJF2FOF7NPZMCWHUSWYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
                "supportedInterfaces": {}
            },

以降、略

今のところ、日本のAlexaSDKのV2だと、上記のようなJSON形式であり、userIdはsessionの中でも、contextの中でも同一となっています。
※僕が認識している限り。

SDKの中身

再掲ですが、SDKで僕がいじった箇所は以下となります。
これを読むと上記のJSONのcontextの方のuserIdがPutされる対象になることがわかります。

本来はそうなるはずですが、「IT業界の深い闇」では何がなんでもuser1という文字列をPKとしてDynamoDBにPutするコードにしました。
ちなみにdeviceIdも同じようにしています。

ask-sdk-dynamodb-persistence-adapter/dist/attributes/persistencePartitionKeyGenerators.js
'use strict';

/*
 * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
var AskSdkUtils_1 = require("../../utils/AskSdkUtils");
/**
 * Object containing implementations of {@link PartitionKeyGenerator}.
 */
exports.PartitionKeyGenerators = {
    /**
     * Gets attributes id using user id.
     * @param {RequestEnvelope} requestEnvelope
     * @returns {string}
     */
    userId: function (requestEnvelope) {
        if (!(requestEnvelope
            && requestEnvelope.context
            && requestEnvelope.context.System
            && requestEnvelope.context.System.user
            && requestEnvelope.context.System.user.userId)) {
            throw AskSdkUtils_1.createAskSdkError('PartitionKeyGenerators', 'Cannot retrieve user id from request envelope!');
        }

        //何がなんでもキー値を user1 という文字列で返すように書き換え
        return 'user1';

        //本来書いてあった戻り値
        //return requestEnvelope.context.System.user.userId;
    },
    /**
     * Gets attributes id using device id.
     * @param {RequestEnvelope} requestEnvelope
     * @returns {string}
     */
    deviceId: function (requestEnvelope) {
        if (!(requestEnvelope
            && requestEnvelope.context
            && requestEnvelope.context.System
            && requestEnvelope.context.System.device
            && requestEnvelope.context.System.device.deviceId)) {
            throw AskSdkUtils_1.createAskSdkError('PartitionKeyGenerators', 'Cannot retrieve device id from request envelope!');
        }

        //何がなんでもキー値を device1 という文字列で返すように書き換え
        return 'device1';

        //本来書いてあった戻り値
        //return requestEnvelope.context.System.device.deviceId;
    },
};
//# sourceMappingURL=PartitionKeyGenerators.js.map


なぜこんなことをしたのか

SDKなどのライブラリを読む人と読まない人は別にどちらでも構わないのですが、それはおいておいて、これでDynamoDBにどのようにPutされるのか。

答えは以下となります。

DynamoDBで、SDKの通りに何も考えないで永続化を試みると、userIdがidとしてPKになります。

上記に記載しましたが、もう一度、記載します。

AlexaのuserIdはスキル内では同じユーザーは、いつ同じスキルを起動しても同じユーザーIDです。
このユーザーIDの人は、他のスキルを起動すると、そこでは別のユーザーIDになります。

つまり、僕のLambdaのコードの書き方だと、ユーザーごとにidが異なるので、DynamoDBのデータ量がユーザー単位でどんどん増えていきます。

このスキルの目的は、IT業界の深い闇を伝えることよりも、「いいね」「いまいち」などを集計することが主目的です。
なので、ユーザーのIDはどうでもよくて、「いいね」「いまいち」の数が管理できればそれで良いのです。

DynamoDBはトランザクションに対応していないので、必ずしも正確な数字が記録されるとは限らないかもしれないですが、そこまでの厳密性は求めていませんでした。

※ 12月2日追記 トランザクション対応が発表されたみたいですね。。

なので、ユーザーのIDはそもそも管理したくない、かつ、数字の集計がめんどくさいという2つの理由から、本来は自分のスキルのLambdaを書き直せばよいものを、あえてSDKをいじっても審査が通るのかもチャレンジ してみました。

実はかなり審査員とバトルやりとりした

このスキル、Amazonの方々とものすごくやりとりしました。

当初、実はこのスキルは当初「スキルフィードバック」という名前のスキルで、ユーザーにいきなり「ストップと話しかけてください」と問うスキルで、その後に「いいね」「いまいち」を伝えてもらうモノだったためです。

もちろん、何度もリジェクトを頂きました。

・テスト目的のスキルにみえる
・ユーザーを困惑させる
・ストップの後で終了しないのはなぜなのか

これらのようなコメントをガンガンもらいました。
今までのスキルはせいぜい2~3営業日で初回の審査結果が来ていましたが、このスキルは当初で約5営業日かかりました。

Amazonの中でもいろいろあったと思います。

そして、僕としては
・開発者がユーザーのフィードバックを何も得られないのはおかしい
・テスト目的ではない、スキル作成者全体に関わることだ
などなどと、激しく抵抗した結果・・・

何か他のスキルのようなやりとりをした後に、このスキルの機能を組み込むなら承認します

というコメントを頂きました。

なので、「IT業界の光闇」の際に、ボツにしていた、闇としては深いネタをランダムで選び、そして「ストップ」と話しかけたら、ユーザーに「いいね」「いまいち」を伝えてもらうようになっています。

結果的にSDKの改編については、一切の言及がなかったです。

このスキルはAxel Gadgets様に特集された

このスキルはAxel Gadgets様にリリース後すぐに特集記事を書いていただきました。
※サイト画像の提示は以前許可をもらってます。

気づいた方は気づいたかも

ここまできて、気づいた方は気づいたかもしれません。

僕がこのスキルの後にリリースした「ヒロインの告白」として美少女から告白されるスキルは、この「いいね」「いまいち」を★の数のレビューとフリーコメントの機能として継承されることになります。

そしてさらに、そこから特許出願に至る「「音声レビュー・ログデータ収集・分析システム」へと進化していきます。

はい、3日目はここでおしまいです。
以上!