するめごはんのIT日記

主にITネタを書いていくのさ

深夜テンションで創ってAmazonさんの審査員さんに協議させてしまった「ノリノリのサンタ」の話

ごきげんよう

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

Echo Showのために4つのスキルを作成した方々、お疲れ様です。

さて、今回は僕が完全に深夜テンションで作成したAlexaスキル「ノリノリのサンタ」について記載します。

相変わらず、AmazonJapanの方を大至急協議させてしまいましたので、それについても触れていきます。

Alexaスキル「ノリノリのサンタ」とは

f:id:surumegohan:20181221221530p:plain
ノリノリのサンタ

「なんかクリスマスのスキルを創るかー」
と思って、いらすとや ではない素材を探していたら、テンションが高そうなサンタクロースの画像素材を見つけました。

なので、サンタクロースとかクリスマスとかについての雑学をググって、まとめて、僕が深夜テンションで自分で声を収録しました

もちろん画面対応

Echo Showが来る前にリリースしたので、Echo Spotでの画面表示に対応しています。

テンション高めなサンタクロースの画像と、ノリノリな声がぞれぞれランダムに再生されます。

ソースコードの一部

めっちゃ一部ですが、たいしたことはしてないです。
ユーザーが初回起動か否かの判定と、操作説明をオーディオファイルで流しているだけです。
画像とオーディオファイルは別々にランダムで表示させています。

'use strict';

const Alexa = require('ask-sdk');
const AWS = require("aws-sdk");
const docClient = new AWS.DynamoDB.DocumentClient({region: 'ap-northeast-1'});


//音声を定義

//起動時
const smartmacchiato = '<audio src=\"https://s3XXXXXXX.mp3\" />';


//2回目以降、わしがのりのりサンタじゃあ
const washiganorinori = '<audio src=\"https://s3-XXXXXX.mp3\" />';

//初回ユーザー用説明
const first_user = '<audio src=\"https://s3-XXXXXXXXXXX.mp3\" />';

:
:
:

//トリビアの数
const norinori_01 = '<audio src=\"https://s3-XXXXXXXXXXX/norinori1.mp3\" />';
const norinori_02 = '<audio src=\"https://s3-XXXXXXXXXXX/norinori2.mp3\" />';
const norinori_03 = '<audio src=\"https://s3-XXXXXXXXXXX/norinori3.mp3\" />';

:
:
:

//トリビアのの音声配列
var norinori_speak_array = [
    norinori_01,
    norinori_02,
    norinori_03,
        :
        :
        :
];



//画像
const DisplayImg1 = {
      title: 'のりのりサンタ1',
      url: 'https://s3-XXXXXXXXXXXX/img/santa1.png'
    };
    
const DisplayImg2 = {
      title: 'のりのりサンタ2',
      url: 'https://s3-XXXXXXXXXXXX/img/santa2.png'
};

const DisplayImg3 = {
      title: 'のりのりサンタ3',
      url: 'https://s3-XXXXXXXXXXXX/img/santa3.png'
};

:
:
:


//サンタ画像の配列
var norinori_img_array = [
    DisplayImg1,
    DisplayImg2,
    DisplayImg3,
        :
        :
];


//////////////////////////////////////////////////////////////////////////


const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  async handle(handlerInput) {


    //サンタ話のうち1つをランダムで選ぶ
    var factSpeakArr = norinori_speak_array;
    var factSpearkIndex = Math.floor(Math.random() * factSpeakArr.length);
    var randomSpearkFact = factSpeakArr[factSpearkIndex];


    //サンタ画像のうち1つをランダムで選ぶ
    var factImgArr = norinori_img_array;
    var factImgIndex = Math.floor(Math.random() * factImgArr.length);
    var randomImgFact = factImgArr[factImgIndex];


    // Template 6
    if (supportsDisplay(handlerInput)){
      const myImage1 = new Alexa.ImageHelper()
        .addImageInstance(randomImgFact.url)
        .getImage();

      const myImage2 = new Alexa.ImageHelper()
        .addImageInstance(randomImgFact.url)
        .getImage();

      const primaryText = new Alexa.RichTextContentHelper()
        .withPrimaryText('')
        .getTextContent();

        handlerInput.responseBuilder.addRenderTemplateDirective({
        type: 'BodyTemplate6',
        token: 'string',
        backButton: 'HIDDEN',
        backgroundImage: myImage2,
        image: myImage1,
        title: "",
        textContent: primaryText
      });
    }
     


    //JSONを扱う関連      
    let handlerInput_json = await JSON.stringify(handlerInput, null, 2);
          
    (略)

    let norinori_start = washiganorinori + randomSpearkFact;

    try{
        
        const queryItems = await docClient.query({
          TableName: "norinoriSantaTable", 
          KeyConditionExpression: "#userId = :userId",
          ExpressionAttributeNames: {"#userId": "userId"},
          ExpressionAttributeValues: {":userId": JSONのユーザーID}
        }).promise();
        
        try{
            
            console.log("queryItems.Items[0].userId: " + queryItems.Items[0].userId);
        
        } catch (err){ //よろしくない実装
            
            //初回ユーザー用のオーディオファイルにする
            norinori_start = first_user;
            
            console.log("user Nothing");
        }
        
    } catch(err){
        console.error(`[query Error]: ${JSON.stringify(err)}`);
    }
    

    //DynamoDBにputする情報
    var item = {
           userId: JSONからのユーザーID,
    };

    var params = {
        TableName: 'テーブル名',
        Item: item
    };

    //DynamoDBにPut
    await putDynamo(params);

    //sessionAttributeを、起動後であることを示すように格納
    var sessionAttribute = '';
    
        sessionAttribute = {
        "SHA_state": "after_start"
        };
    
    handlerInput.attributesManager.setSessionAttributes(sessionAttribute); 

    //しゃべる音声スキルと、間に0.7秒の待機を挟み、ノリノリの話とユーザーへの操作説明をする
    let speechText = smartmacchiato + '<break time="0.7s"/>' + norinori_start + ask_next;

    return handlerInput.responseBuilder
      .speak('<speak>' + speechText + '</speak>')
      .reprompt('<speak>' + speechText + '</speak>')
      .withShouldEndSession(false)
      .getResponse();
  }
};

    

//起動直後、本アプリの継続に「はい」「次」「もっと」「のりのり」「きかせて」と答えた場合の処理
const continueHandler = {
    canHandle(handlerInput) {
      
        return handlerInput.requestEnvelope.request.type === 'IntentRequest'
            && ((handlerInput.requestEnvelope.request.intent.name === 'AMAZON.YesIntent')
                || (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.NextIntent')
                || (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.MoreIntent')
                || (handlerInput.requestEnvelope.request.intent.name === 'norinoriIntent')
                || (handlerInput.requestEnvelope.request.intent.name === 'kikitaiIntent')
                )
            && handlerInput.attributesManager.getSessionAttributes().SHA_state == 'after_start';
    },
    async handle(handlerInput,event) {


    //サンタ話のうち1つをランダムで選ぶ
    var factSpeakArr = norinori_speak_array;
    var factSpearkIndex = Math.floor(Math.random() * factSpeakArr.length);
    var randomSpearkFact = factSpeakArr[factSpearkIndex];


    //サンタ画像のうち1つをランダムで選ぶ
    var factImgArr = norinori_img_array;
    var factImgIndex = Math.floor(Math.random() * factImgArr.length);
    var randomImgFact = factImgArr[factImgIndex];
    
    
    // Template 6
    if (supportsDisplay(handlerInput)){
      const myImage1 = new Alexa.ImageHelper()
        .addImageInstance(randomImgFact.url)
        .getImage();

      const myImage2 = new Alexa.ImageHelper()
        .addImageInstance(randomImgFact.url)
        .getImage();

      const primaryText = new Alexa.RichTextContentHelper()
        .withPrimaryText('')
        .getTextContent();

        handlerInput.responseBuilder.addRenderTemplateDirective({
        type: 'BodyTemplate6',
        token: 'string',
        backButton: 'HIDDEN',
        backgroundImage: myImage2,
        image: myImage1,
        title: "",
        textContent: primaryText
      });
    }
    
        //ランダムに話して、ユーザー操作を促す
        const speechText = randomSpearkFact + ask_next;
        
        return handlerInput.responseBuilder
              .speak('<speak>' + speechText + '</speak>')
              .reprompt('<speak>' + speechText + '</speak>')
              .withShouldEndSession(false)
              .getResponse();
    
    }
};




//DynamoDBにputする関数
function putDynamo(params) {
    
    console.log("=== putDynamo function ===" + params);
    
    docClient.put(params, function (err, data) {
    
        console.log("=== put ===");
    
        if (err) {
            console.log(err);
        } else {
            console.log(data);
        }
    });

}


// returns true if the skill is running on a device with a display (show|spot)
function supportsDisplay(handlerInput) {
  var hasDisplay =
    handlerInput.requestEnvelope.context &&
    handlerInput.requestEnvelope.context.System &&
    handlerInput.requestEnvelope.context.System.device &&
    handlerInput.requestEnvelope.context.System.device.supportedInterfaces &&
    handlerInput.requestEnvelope.context.System.device.supportedInterfaces.Display;

  console.log("Supported Interfaces are" + JSON.stringify(handlerInput.requestEnvelope.context.System.device.supportedInterfaces));
  return hasDisplay;
}

リジェクト内容で協議させてしまうパターン

僕にとっては、もう慣れているので構わないのですが、リジェクト理由に納得がいかずに、進言したら協議の上、承認されました。

リジェクト理由は 「ノリノリ」は名詞ではないから。

日本語でスキルの呼び出し名を決める際には、原則として名詞を2つにする必要があります。

ヒロイン告白」みたいな。 「の」は無くてもOKの場合も多いです。

で、納得いかないので以下のように伝えたら承認されました。

1.「ノリノリ」は名詞でも使われる

goo辞書で検索すると、以下のように記載されてます。

[名・形動]《動詞「乗る」の連用形を重ねた語。「ノリノリ」と書くことも多い》調子がよくて気分が高揚していること。乗りがよくて、リズミカルであること。また、そのさま。いけいけ。「乗り乗りな曲で踊る」「乗り乗りムードで一気に勝ち進む」

[名・形動]ってあるじゃん。

2.テンションが高い場合はどう表現するのか

テンションが高い状態を示す場合、具体的にAmazonさんはどういう表現なら良いのか求めました。

そしてその際に、

仮に「ハイテンションなサンタ」にした場合、【ハイテンション】こそ日本語ではないと私は解釈します。 日本語のみで状況・状態を説明するための具体的なガイドラインをください。

と、伝えました。

そしたら通った

いつも通り?、「大至急協議します」のメールが飛んでくるので、しばし待ちます。 Amazonさんから「大至急協議します」のメールが来た場合、たいていその日のうちに返答がきます。

今回は 「協議の上、認められることになりましたので、再申請をお願いします。」

とのことで、再申請したら通りました。

その後のAlexaDevSummitでつかまる

AlexaDevSummitで会場をウロウロしてたら、中の人からお声がけがありました。

A「showさんですよね!この間の件ですが・・・」
僕「(やらかしまくっているので)どの件でしょうかごめんなさi・・・」
A「ノリノリの件です。ご指摘ありがとうございました。あれはたしかに認められないと表現できないですよね。フィードバックありがとうございました!」
僕「こちらこそ、ご対応ありがとうございましたぁぁぁ!!!」

全力で土下座する体制にしようとしましたが、むしろ感謝されました。

何が言いたいかというと、最近、スキル審査結果に対してフィードバックが画面ポチポチで選べるようになったんですけども、審査員側だって、ユーザーからのご意見が欲しいわけです。

明らかに開発者側のミスは置いておいて、リジェクトされたから黙って従うだと、AmazonさんのAlexaスキル審査員さんが独りよがりの神になってしまうわけです。

なので、ちゃんと根拠を示して、自分の意見を伝えると、AmazonのAlexa担当の方々はちゃんと対応してくれますよ。

リジェクトを頂いても、認定されても、フィードバックは送りましょう。 その方がお互いにハッピーだと思います。

以上。

Alexaデザインガイドの本は他のプラットフォームでも応用できる

ごきげんよう

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

先日のAlexaDevSummitで以下の本が配られました。

f:id:surumegohan:20181221113408p:plain
デザインガイドβ

中身は基本的に以下のWebページと同様です。

紙の物理本ですので、オンラインの方が最新であることは確かでしょう。

■Alexaデザインガイド
developer.amazon.com

もう1回振り返るのに最適

Alexaでスキルを作成している方々はすでに上記サイトは確認済みとは思いますが、画面が付いたりマルチモーダルになってきたAlexaについてのデザインガイドは再度読んでおくと、新しい発見があると思います。

もちろん、これからスキルを作成する方々にも極めて有用です。

他にも公式のこのドキュメントは目を通すべき

上記のデザインガイドはもちろん有用ですが、以下も併せて確認することをオススメします。

■音声デザインガイド
Amazon Alexa Voice Design Guide

特にチェックリストが載っているのが良いと思われます。

Alexa以外のプラットフォームでも共通部分は多い

もちろんこれらのドキュメントはAmazonのAlexa用のドキュメント類です。

とはいえ、音声デザインに関しては他社プラットフォームに共通している部分も多く、上記のドキュメントをしっかり読んでいれば他社プラットフォームでも応用が十分聞くと思います。

特にLINE系の場合はClovaに限らず、Messaging APIやLIFFなどに置き換えることもある程度できるのではないでしょうか。

以上

IT系イベント実施の会場設営チェックリストを作った

ごきげんよう

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

風邪ひいて遅れました。。。

さて、もともとアドベントカレンダーである程度の人が見てくれるようになってきたら投稿したいと思っていた記事を今回は投稿します。

直接的にはVUIも技術も関係ないですが、技術イベントを開催する際に会場設営で必要なチェックリスト をまとめました。

今年、様々なVUI・スマートスピーカー系のイベントの運営スタッフや主催をやらせてもらいまして、そこで会場設営時のノウハウがたまってきたのでまとめました次第です。

VUI系なので音を気にすることも多いですが、他のイベントでも応用できるリストであると考えておりますので、ガンガン共有します。

技術系イベントを開催時の AWS Well-Architected Framework みたいな感じのチェックリスト として、よしなに参考にしてください。

https://github.com/surumegohan/event_know-how

様々なIT系のイベントでは運営している人たちは上記のようなことを気を配って実施しています。
なので慢性的にスタッフが足りてないと思いますので、この記事を読んだ方が、イベント実施者に「こんなリストがあるよ」とか「ここらへんは手伝える」とか、そういう流れになってくれると嬉しいです。

また、他にも自分はこんなことを気を付けてるぜ!とか、こんなことがあったぜ!とかありましたら、コメントをくれるかプルをリクってください。

以上

【深堀版】スマートスピーカーを遊びたおす会で話した「ヒロインとデート」のVUI設計思想

ごきげんよう showです。

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

昨日、「スマートスピーカーを遊びたおす会」のVol.4を開催させて頂きました。

■イベントのconnpass

connpass.com

■デモ動画

www.youtube.com

そして、そこで流したデモ動画についての記事を先ほど「スマートスピーカー 2 Advent Calendar 2018」に投稿しました。

スマートスピーカー 2 Advent Calendar 2018

qiita.com

僕が考えているVUIデザインについて記載しています。

スマートスピーカーを遊びたおす会で話した「ヒロインとデート」のVUI設計思想

qiita.com

で、今この画面で開いているこの記事は僕のアドベントカレンダーなので、上記記事をもっと深堀します。

VUIデザインについて再掲

上記記事でも触れましたが、VUIのデザインはユーザーの想定外の対話や、1ショット起動のため、フラット型をベースにする方がいいと僕は考えています。

f:id:surumegohan:20181218122855p:plain
VUIデザインはワンショットも対応するべき

「ヒロインとデート」についてもう少し語ると

僕がヒロインシリーズを創っているのは、もっとも人間に近い対話設計にチャレンジできると考えているからです。

前作の「ヒロインの告白」は、告白なので基本的には「はい」「いいえ」で進むことがわかりやすくできます。

この次の対話設計としては、人間と想定する 結城琴葉ちゃん と、どれだけ自然に会話できるかを考えています。 そうなると「はい」「いいえ」だけで会話するのは無理です。 というか、それは会話や対話ではないです。

駅名と施設名を指定できる

デートをするシチュエーションを想定すると、現実には事前に行先を決めていると思われるのと、位置情報とリンクした際は、その位置情報に合わせた設計が必要になります。

とはいえ、僕がまだそこまで実装しきれていないので、デモ動画では駅名から聞かれます。

どこかで待ち合わせをしたとして、「じゃあどこ行こう」となった際に、例えばアメリカに行くとしたら 「グランドキャニオンに行こう」 などの観光スポットになると思います。 これは、グランドキャニオンの最寄り駅を指定するよりは、グランドキャニオンそのものを指定するはずです。

なので、声優さんに渡した脚本として、はじめから「動物園」「水族館」などの場所・施設名を話しかけた場合の動き も収録してもらっています。

動物園に入ったら、水族館には突然ワープさせない

そして、例えば動物園に行った場合、そこからいきなり水族館にワープすることは現実的には(たぶん)ないので、そこへのワンショットはさせないようにデザインしています。

その変わり、動物園内で「パンダ」「フラミンゴ」「カバ」などの動物をめぐるようにしており、それぞれ 「●●(動物)を見に行こう」とユーザーが話しかける場合と、結城琴葉ちゃんが「パンダが観たい」と話しかけてくるような設計にしています。

動物園内ではワンショットで対象の動物を選択し、また、「他の動物」や「次の動物」だとランダムで他の動物を観に行くことを想定しています。

LIFFで思い出をエモく

位置情報が使えるなら実世界とリンク、自宅にいながらでもClovaの操作によって、LINEさんのLIFFを使って、過去に廻った観光スポットとかをパーソナルに表示して、思い出としてエモく残すこともできると考えています。

応用すると

例えば、アメリカ人でバットマンが好きな人が日本にきたとして、そこでモバイルでVUIを活用して日本観光をするとなると、英語でバットマンが日本観光案内をしてくれることもできなくはないと思っています。 しかも場合により課金もできる。

というわけで

やりたいことが山ほどあります。 もうこうなると個人でやるのは限界なので「オラに元気をわけてくれ」状態です。

ともあれ、やりたいことがあるのにやらないという選択肢はとりたくないので、コツコツ頑張っていきます。

以上

AndroidユーザーならXperia Ear Duoすごいぞ

ごきげんよう

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

本日はXperia Ear Duoの話です。

SONYさんのサイト

www.sonymobile.co.jp

上記だと約3万円ですが、僕が秋葉原のヨドバシさんで買ったときは25000円しないくらい。 ヨドバシのポイントが溜まってたので1万円未満でした。

Xperia Ear Duoがスマートスピーカーなのか否かですが、音声アシスタントが呼び出せればスマートスピーカーに部類されると僕は考えています。 セットアップ時にGoogle Assistantもでてきました。

ただ、Xperia Ear Duoの僕にとっての最大の魅力はLINE社のClovaを呼び出すことができ、会話できます。 右耳側を押したら起動するのでその後は声で操作できます。

モバイル端末で外でClovaが呼べるのは非常に大きなアドバンテージです。

スマホGoogle、Alexaも呼び出すことはもちろん可能ですが、いちいちスマホを取り出す必要がないのは強いです。

使ってみた系の記事

様々なサイトで賛否両論に使ってみた系の記事があります。

e-earphone.blog

estpolis.com

実物

こんな感じです。

f:id:surumegohan:20181218122130j:plain
パッケージ

f:id:surumegohan:20181218122150j:plain
箱を開けてみる

f:id:surumegohan:20181218122210j:plain
ケース兼充電器

丸いケースが充電器も兼ねていて、USB接続で給電します。

iPhoneではClovaが使えないっぽい

僕はiPhoneを所有していないのでわからないですが、当初は以下のような記事でした。

■LINEのAIアシスタント「Clova」がソニーモバイル「Xperia Ear Duo」に対応

prtimes.jp

上記サイトに4月11日時点で LINEのAIアシスタント「Clova」に対応はAndroidのみ と記載されています。

ともあれセットアップガイドにはiPhoneでのセットアップマニュアルもあります。

f:id:surumegohan:20181218122244j:plain
解説書にiPhoneも記載されている

LINE BOOT AWARDがあったんだからiPhoneでも今ならClovaは動くだろう。 と思いましたが、公式サイトをみるとダメっぽいですね。。

Siriに引っ張られてるのでしょうか。。

他の部分は「対応予定」とはありますが、Clovaの対応予定がないので、Xperia Ear DuoではAndroidになりそうですね。

f:id:surumegohan:20181218122323p:plain
SONYさんのページ

ともあれ

僕個人が作っているClovaスキルを耳元で起動できています。 もちろん公開されているスキルも使えます。 ボイスアップラボさんの「ゾンビの街」とかすごいです。

AndroidのユーザーでClovaを屋外で呼び出すなら要検討かと。 ただ、下手したらEcho Showよりお高い ですけど。

以上

LINE ClovaでSDKなしでオーディオファイルを再生する

ごきげんよう

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

今回はClovaの話です。

LINE Clovaでスキルを作成する時、SDKを用いることは多いと思います。

けれども、SDKがなくとも自分でJSONを作成してしまえばスキルを作成できます。

このアドベントカレンダーの10日目の記事

surumegohan.hatenablog.com

にも記載している、この本からスタートするとClovaのスキルを動かすことが非常に素早くできます。

Clovaのテキストスピーチは本に書いてある

上記の本には既にAPI GatewayとLambdaを使って挨拶をするスキルの作り方、および、ソースコードが記載されておりダウンロード可能です。

ほぼ同様の記述でオーディオファイルを再生する

音声ファイルとしてmp3を作っておいて、例えばAWSならそのままS3に置いておくとします。

'use strict';

exports.handler = function (event, context, callback) {
    var response = {
        statusCode: 200,
        headers: {},
        body: ""
    };
    
    var speechText = "";
    var requestJson = JSON.parse(event.body).request;
    var endFlg = false;
    
    if (requestJson.type === 'LaunchRequest') {
        // 起動時処理
        speechText = 'https://s3-ap-northeast-1.amazonaws.com/起動時に再生したいオーディオファイル.mp3';

    } else if (requestJson.type === 'SessionEndedRequest') {
        // セッション切れ
        speechText = 'https://s3-ap-northeast-1.amazonaws.com/セッションが切れたら再生したいオーディオファイル.mp3';
        endFlg = true;
    
    } else if (requestJson.type === 'IntentRequest') {

        if (requestJson.intent.name === 'EndIntent' || requestJson.intent.name === 'Clova.NoIntent') {
            // 終了処理
            speechText = 'https://s3-ap-northeast-1.amazonaws.com/終了時に再生したいオーディオファイル.mp3';
            endFlg = true;
        
        } else if (requestJson.intent.name === 'Clova.GuideIntent') {
            // ヘルプ用
            speechText = 'https://s3-ap-northeast-1.amazonaws.com/ヘルプの時に再生したいオーディオファイル.mp3';
            
        } else if (requestJson.intent.name === 'HelloIntent') {
            
            speechText = 'https://s3-ap-northeast-1.amazonaws.com/ハローと言われたの時に再生したいオーディオファイル.mp3';

        } else if (requestJson.intent.name === 'MorningIntent') {
            
            (略)
    }

    var responseJson = JSON.stringify({
        "version": "1.0",
        "response": {
            "outputSpeech": {
                "type": "SimpleSpeech",
                "values": {
                    "type":"URL", //ここを「URL」にする
                    "lang":"ja", //ここはjaである必要すら本当はない
                    "value": speechText
                }
            },
            "card": {},
            "directives": [],
            "shouldEndSession": endFlg
        }
    });
    
    response.body = responseJson;
    
    callback(null, response);

};

要するに、JSONを組み立てられればそれでOKなわけです。

実際に公式のSDKのコードを読めばすぐに気づきます。

github.com

ド直球に言うと以下で気づくことができます。

github.com

まとめ

Clovaに限らず、要するにJSONが組み立てられればそれで良くて、SDKJSONの生成をわかりやすく補助するためのものでしかありません。

それは他のプラットフォームでも、もっというとどんなアプリやサービスでも、対応するプロトコルに合えばそれでいいのです。

後から気づいたおまけ

本の著者がそのまま書いてるじゃん・・w

qiita.com

VUIの3プラットフォームに「ほげほげ」と話しかけた場合の謝るメッセージ一覧

ごきげんよう

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

更新が遅れていた。。Qiitaにはあげていたんですが。。orz

先日、某イベントでこんなような会話がありました。

Aさん「~~~で、大丈夫ですか?」
僕「おっけーぐーぐる!」
Aさん「わかりませんでした。すみませんw」
僕「それはAlexaだろうが!w」

というわけで、VUIのスキルやアプリ作成時のエラーメッセージはバリエーションを持たせると機械っぽさが低減されて良いです。

なので、3プラットフォームに「ほげほげ」(hogehoge)と呼びかけた時、どういう返答をするかまとめました。 謝る系で、発見できたワードのみ掲載です。

これをどうでもいいと思うか、ユーザビリティと思うかは、その人次第です。

Alexa、ほげほげ

  1. うまく答えられません、ごめんなさい。
  2. わかりません、ごめんなさい。
  3. わかりませんでした、すみません。
  4. ごめんなさい、ちょっとわかりませんでした。
  5. すみません、ちょっと難しいです。
  6. すみません、よくわかりません。
  7. ちょっとよくわからなかったです。ごめんなさい。
  8. ちょっと難しいです。ごめんなさい。
  9. ごめんなさい、今はわかりません。
  10. すみません、わかりませんでした。

Clova、ほげほげ

  1. すみません、わかりませんでした。
  2. すみません、よくわかりませんでした。

※ たまに 「あら、そうですか」 などの相槌がでます。

OK,Google、ほげほげ

  1. 聞き取れませんでした。もう一度お願いします。
  2. すみません、お役に立てそうもありません。もっと頑張ります。
  3. すみません、よくわかりません。

※ たまに 「ぽけぽけを探しましたが現在利用できないようです」 になります。

まとめ

各社、様々な対応をしていますね。

Alexaが10種類も見つかったのは意外でした。 Clovaは相槌を話すことがあり、コミュニケーションを大事にしている様子が伺えます。 Google HomeActions on Google)は「ぽけぽけ」で必死に検索しようとするので、いかにも検索の会社らしいです。

エラーメッセージにも各社の社風がでてきて面白いと思います。 何かしらの参考になれば幸いです。

以上