テクノロジー

2020.12.17

Alexa for Appsを利用したユーザー体験向上への挑戦

Yahoo! JAPAN Advent Calendar 2020の17日目の記事です。

こんにちは。サイエンス統括本部スマートデバイス部スキルチームでプロダクトマネージャーを担当している藤田 和秀です。

本記事では、今年の7月に開催されたAlexa Live 2020で発表された新機能「Alexa for Apps」をサービスに導入した背景、導入方法をご紹介したいと思います。

Alexa for Appsとは

Alexa for Appsは、一言で言うとスマホのAmazon Alexaアプリ(以降は、Alexaアプリ)から別のアプリへディープリンクさせる機能です。ユーザーは音声をフックに、アプリ内の機能をトリガーしたりできます。

現在は開発者プレビューとなっております。(2020年12月2日現在)

リクエストフロー

リクエストフロー図

ステップ 概要
1 「アレクサ、ヤフー天気で新宿の天気を教えて」で起動
2 Alexaアプリから、デバイスがディープリンク可能か、OSの種別のデータを積んでリクエスト送信
3 ディープリンク可能デバイスなら、ディープリンクの情報を積んでレスポンスを返す
4 Alexaアプリは、ディープリンク情報を利用して、ヤフー天気アプリを開く
5 Alexaアプリは、バックエンドシステムにディープリンクが成功したかどうかの結果を返す

実際の挙動

導入した背景

さて、どうして私たちのプロジェクトが本機能を採用したのか経緯を説明します。

音声で伝える情報量は、難しい

結論から言いますと、音声でお客様に伝える情報量には限界があるからです。

弊社の乗換案内スキルを例にあげます。「アレクサ、ヤフー乗換案内で新宿から成田空港まで」のように経路案内を聞いたとき、丁寧に案内をすると以下のようになります。

「成田空港に行くには、新宿駅18時10分発のJR中央線快速、東京行きに乗ります。東京駅で18時33分発のJR特急成田エクスプレス49号、成田空港行きに乗り換えし、19時33分に空港第2ビル駅で降ります。駅から約10分歩いて19時44分に到着します。」

この文章を読み上げられたと想像してみてください。どういった経路で向かえば良いか把握できましたでしょうか? 実は、上記の案内でも東京駅には何時に着くのか? といった情報が不足しており、乗り換えするまでにどれくらいの余裕があるのかわかりません。また、何番線のホームに到着するのか? 料金はいくらなのか? と不足している情報をあげればきりがありません。

文章を視覚で読めば、内容を理解し、情報を取捨選択し、記憶する事が可能だと思います。しかし、音声で読み上げられたときに大半の人は全てを理解する事が難しく、冒頭の内容を忘れてしまいます。社内でユーザーテストを実施した際も同様のフィードバックがありました。

つまり音声UIのプロダクトでは、常に以下のジレンマを抱える事になります。

  • 情報過多にならないシンプルな応答が要求される
  • お客様に価値のある(有意義な)情報は提供したい

ちなみに乗換案内スキルの既存機能では、情報過多にならないように、お客様が最初に行うアクションに絞って応答を返しています。詳細の経路情報はメールでご案内、またはAlexaアプリのホームから「その他→アクティビティ」でご確認できるようにしております。

そこで、アプリ/Webへ遷移する選択

しかし、続きをメールやAlexaアプリで確認するのは以下の2点に課題があります。

  1. お客様に数ステップのアクションを強制してしまう
  2. アクティビティで確認する場合、関連情報へのアクセスが難しい(例えば乗換案内結果を見て、次の電車を利用したい場合に再度、聞き直す必要が生じる)

アプリなどへディープリンクできれば、シームレスに遷移して、関連情報を容易に取得可能になります。遷移先から、乗換案内は一本前後の経路案内結果、天気であれば翌日の詳細な降水確率の推移情報にもアクセスしやすくなります。

これらを踏まえて私たちのプロジェクトでは本機能を採用するに至りました。

導入方法

導入にあたっては、事前準備とスキル設定反映、コードの実装と3アクションを行う必要があります。

事前準備

※本記事では、ask cliはversion2を利用しております

注意事項

  • 開発プレビューのため、導入方法などが変更される可能性がありますので、導入する際は最新の情報を参照ください
  • こちらは既に構築されているスキルに導入する方法となります

スキル設定反映

  1. スキルマニフェストファイルを取得
    $ ask smapi get-skill-manifest -s <スキルID> -g development > skill.json
  2. スキルマニフェストファイル(skill.json)に設定を追加
    // 本機能の利用に必要な部分のみ抜粋しております
    {
    "manifest": {
     "apis": {
       "custom": {
         "appLink": {
           "linkedApplications": [
             // iOSアプリで利用されたときに利用する設定
             {
               "friendlyName": {
                 "default": "ヤフー天気",
                 "localizedNames": [{ "locale": "ja-JP", "name": "ヤフー天気" }]
               },
               "catalogInfo": {
                 "type": "IOS_APP_STORE",
                 "identifier": "id521974902" <--- ストアでアプリを検索した時のアプリID(URLの末尾)
               },
               "customSchemes": ["<カスタムスキーマ>"],
               "domains": ["<ドメイン>"]
             },
             // Androidアプリで利用されたときに利用する設定
             {
               "friendlyName": {
                 "default": "ヤフー天気",
                 "localizedNames": [{ "locale": "ja-JP", "name": "ヤフー天気" }]
               },
               "catalogInfo": {
                 "type": "GOOGLE_PLAY_STORE",
                 "identifier": "jp.co.yahoo.android.weather.type1" <--- ストアでアプリを検索した時のアプリID(idパラメータの値)
               },
               "customSchemes": ["<カスタムスキーマ>"],
               "domains": ["<ドメイン>"]
             }
           ]
         },
         "interfaces": [
           {
             "type": "APP_LINKS"
           }
         ]
       }
     }
    }
    }
  3. ask cliを用いて設定反映
    $ ask smapi update-skill-manifest -s <スキルID> -g development --manifest file:skill.json

実装

  1. 本機能を利用できるデバイスからのアクセスか判定
    // 判定は、Alexaからのリクエストデータに「handlerInput/requestEnvelope/context/Systen/device/supportedInterfaces/Applink」のキーが存在しているか否かで判定できます。
    // デバイスのOSもリクエストデータから判定できるようにしております。
    
    export function useApplink(handlerInput: HandlerInput): boolean {
      try {
        // スキル無効化の場合、例外が発生するため try catch で正常に処理させる
        const interfaces = getSupportedInterfaces(handlerInput.requestEnvelope);
        return !!interfaces?.AppLink;
      } catch (error) {}
      return false;
    }
    
    export function isIosDevice(handlerInput: HandlerInput): boolean {
      const context = handlerInput.requestEnvelope.context;
      const catalogTypes = context?.AppLink?.supportedCatalogTypes ? context.AppLink.supportedCatalogTypes : [];
      return catalogTypes.includes('IOS_APP_STORE');
    }
    
    export function isAndroidDevice(handlerInput: HandlerInput): boolean {
      const context = handlerInput.requestEnvelope.context;
      const catalogTypes = context?.AppLink?.supportedCatalogTypes ? context.AppLink.supportedCatalogTypes : [];
      return catalogTypes.includes('GOOGLE_PLAY_STORE');
    }
  2. 利用できる場合、本機能用のレスポンスを追加する
    const params: Directive = {
     type: 'Connections.StartConnection',
     uri: 'connection://AMAZON.LinkApp/1',
     input: {
       catalogInfo: isIos
         ? {
             identifier: 'id521974902',
             type: 'IOS_APP_STORE'
           }
         : {
             identifier: 'jp.co.yahoo.android.weather.type1',
             type: 'GOOGLE_PLAY_STORE'
           },
       actions: {
         primary: {
           type: 'CUSTOM_SCHEME',
           link: `<カスタムスキーマ>` <--- スキルマニフェストに設定したもの以外を設定するとエラーになります
         },
         fallback: {
           type: 'UNIVERSAL_LINK',
           link: <リンク> <--- スキルマニフェストに設定したもの以外を設定するとエラーになります
         }
       },
       prompts: {
         onAppLinked: {
           prompt: {
             ssml: '<speak>天気の詳細情報を開きます</speak>', <-- ディープリンクする際の発話内容
             type: 'SSML'
           },
           defaultPromptBehavior: 'SUPPRESS'
         },
         onScreenLocked: {
           prompt: {
             text: '詳細情報はこちらから',
             type: 'PlainText'
           }
         }
       }
     }
    }
    
    // レスポンスに追加
    handlerInput.responseBuilder.addDirective(params);
  3. ディープリンク結果のフィードバックリクエストに備える
    // ディープリンク結果のフィードバックリクエストを受信する設定を追加
    
    const sessionResumeIntentHandler = {
      canHandle(handlerInput: HandlerInput): boolean {
        return getRequestType(handlerInput.requestEnvelope) === 'SessionResumedRequest';
      },
      handle(handlerInput: HandlerInput): Response {
        const request = handlerInput.requestEnvelope.request;
        const statusCode = request.cause?.status?.code;
    
        // ログ出力するとディープリンクした回数の集計やエラー時のデバックがしやすくなるのでおすすめします
        statusCode === '200' || statusCode === '204'
          ? console.info(JSON.stringify(request))
          : console.error(JSON.stringify(request));
    
        return handlerInput.responseBuilder.getResponse();
      }
    };

Tips

返すレスポンスにprimaryfallbackの設定がありますが、挙動としてはprimaryを試みて、条件が整わない場合はfallbackの設定を試みるようです。以下に設定の組み合わせと挙動を表にまとめました。注意が必要なのは、パターン3のアプリ未インストール時の挙動です。ストアへ遷移し、アプリのインストールを訴求する挙動になります。本機能を利用する際に、何を目的にしているか考えて設定していただければと思います。弊社はお客様に情報を提供する事を最も重要視しているので全てパターン1の設定にしております。

パターン primary fallback 挙動(アプリインストール済み/未インストール)
1 カスタムスキーム url アプリへ遷移/Webへ遷移
2 url カスタムスキーム Webへ遷移/Webへ遷移
3 カスタムスキーム 設定なし アプリへ遷移/ストアへ遷移
4 url 設定なし Webへ遷移/Webへ遷移

Alexa for Appsを導入したスキル一覧

Alexa Quick Linkを利用しておりますので、リンク先からスムーズにスキルを体験する事が可能になります。ぜひスマホでアクセスしてみてください。

今後の導入シーン

本機能は、ユーザーが設定変更したいシーンでも強力なサポートが可能だと考えており、対応を予定しております。

例えば、路線スキルはアカウントリンク機能を実装しております。アカウントリンクしていただければ、「アレクサ、ヤフー路線を開いて」と発話するだけでYahoo! JAPAN IDに登録されている路線の運行情報を返す事が可能になります。わざわざ路線名を指定する必要がありません。

そこで、「アレクサ、ヤフー路線で登録路線を編集したい」、「アレクサ、ヤフー路線で今の登録路線は?」のような発話に対応し、設定画面へディープリンクさせる事ができれば、お客様はスムーズに編集が行えると考えております。

最後に

今回は、Alexa for Appsを利用したユーザー体験向上について紹介いたしました。ここまで読んでいただいた皆様は、「スマホを使うのであれば、Alexaを経由せずに最初からそのアプリを開くのでは?」と感じる方もいらっしゃると思います。Alexaなどの音声プロダクトはハンズフリーで動かす事が利点です。またアメリカではワイヤレスイヤホン Amazon Echo Buds(現在、アメリカおよびイギリスのみで入手可能)とスマホを連携する事で、モバイル環境で以下のような利用が可能となっております。

  • Amazon Echo Budsに話しかけると、Alexaアプリが開いていなくてもBluetooth経由で起動、連動してディープリンク
  • 仮にスマホがロックされている場合でも、アンロック時にディープリンク

これは音声で家電操作するのと同様の体験になっていると思います。

この分野はスマートスピーカー、スマホにとどまらず、さまざまな家電(イヤホン、ウエアラブルデバイス、照明器具など)に導入されつつあり、いつでもどこでも欲しい情報を取得したり、行いたい操作をサポートしてくれる世界が来ると考えております。私たちのプロジェクトではそういった世界を想像しながら、新たな技術をいち早く検証し、プロダクトに組み込む事でユーザー体験を向上し、その世界を実現するように日々、格闘しております。いつか人が何かをする時のインプットの手段として音声が当たり前になり、便利になる世界を夢見て、精進してまいります!

最後まで読んでくださり、ありがとうございました。

※Alexaは、Amazon.com, Inc.の商標です


藤田 和秀
プロダクトマネージャー
TechLeadとしてVUIプロダクトの開発も行っています。

Yahoo! JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。

関連記事

このページの先頭へ