Dialogflow エージェントを使用してモバイルアプリにコマンドを追加する

このチュートリアルでは、Dialogflow エージェントで処理される音声コマンドとテキスト コマンドに応答するように iOS アプリを構成する方法を説明します。このチュートリアルのクライアント アプリは、コマンドを取得してエージェントに送信し、そのレスポンスを受信します。エージェントはコマンドを処理し、コマンドを事前に構成されたインテントと照合して、適切なレスポンスを返します。このチュートリアルでは、クライアント アプリが Google Cloud APIs 呼び出しの認証に使用できる、有効期間が短い認証情報を取得する方法も説明します。

ソリューションの概要

このソリューションは、次のコンポーネントで構成されています。

クライアント アプリ
クライアント コンポーネントは、音声コマンドとテキスト コマンドを取得してストップウォッチ エージェントに送信する iOS アプリです。このアプリは、トークン サービスから認証情報を取得し、その認証情報を含めた認証済みリクエストを使用してコマンドを送信します。
トークン サービス
トークン サービスは、Cloud Functions for Firebase を使用して実装されています。このサービスは有効期間が短い認証情報を提供します。クライアント アプリはこの認証情報を使用して、認証済みリクエストをストップウォッチ エージェントに送信できます。トークン サービスは Cloud IAM API から認証情報を取得して Cloud Firestore データベースに保管し、その認証情報の有効期限が切れていないことを検証します。
ストップウォッチ エージェント
Dialogflow がホストするストップウォッチ エージェントは、コマンドをストップウォッチの制御指示に変換する自然言語処理機能を提供します。

次の図は、コンポーネント間のやり取りを示しています。

ソリューション アーキテクチャの概要

費用

このチュートリアルのサンプルを実行すると、次の料金が発生する可能性があることを考慮してください。

  • Firebase の使用は特定条件のもとで無料です。これらのサービスの使用量が Spark プランで規定されている制限に達しなければ、Firebase の使用料はかかりません。詳しくは、Firebase の料金プランをご覧ください。
  • Firebase では、Cloud Functions の使用量について割り当てを定義しています。この割り当てにより、リソース、時間、レートに関する上限が指定されています。詳しくは、Firebase ドキュメントの割り当てと上限をご覧ください。
  • Dialogflow の料金は、エディション、プラン、リクエストの数と所要時間に基づいて月単位で請求されます。Standard Edition を使用する場合、リクエスト数の上限まで無料でリクエストを使用できます。詳しくは、Dialogflow の料金をご覧ください。

Google Cloud 料金計算ツールを使用して料金を見積もることもできます。

始める前に

このチュートリアルを完了するには、次のソフトウェアが必要です。

サンプルアプリは、Xcode バージョン 10.2.1、CocoaPods バージョン 1.5.2、Node.js バージョン 8.16.1 でテストされています。

サンプルコードのクローンの作成

クライアント アプリコードのクローンを作成します。

git clone https://github.com/GoogleCloudPlatform/ios-docs-samples.git
    

トークン サービスコードのクローンを作成します。

git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
    

Firebase プロジェクトを作成

  1. Firebase アカウントを作成するか、既存のアカウントにログインします。

  2. [プロジェクトを追加] をクリックします。

  3. [プロジェクト名] に「stopwatch」と入力します。プロジェクトに割り当てられたプロジェクト ID は、このチュートリアルの複数の手順で使用するのでメモしておきます。

  4. 残りの設定手順を行い、[プロジェクトを作成] をクリックします。

  5. ウィザードでプロジェクトがプロビジョニングされたら、[続行] をクリックします。

  6. プロジェクトの [概要] ページで設定の歯車をクリックし、[プロジェクトの設定] をクリックします。

  7. [iOS アプリに Firebase を追加] をクリックします。

  8. [iOS バンドル ID] に「com.sample.dialogflow」と入力します。

  9. [アプリの登録] をクリックします。

  10. [構成ファイルをダウンロード] セクションの手順に従い、プロジェクトの ios-docs-samples/dialogflow/stopwatch/ フォルダに GoogleService-Info.plist ファイルを追加します。

  11. [構成ファイルのダウンロード] で [次へ] をクリックします。

  12. CocoaPods の使用方法をメモして、プロジェクトの依存関係をインストールして管理します。サンプルコードでは、CocoaPods 依存関係マネージャーがすでに構成されています。

  13. 次のコマンドを実行して、依存関係をインストールします。コマンドが完了するまでに時間がかかる場合があります。

    cd ios-docs-samples/dialogflow/stopwatch/
        ./INSTALL-COCOAPODS
        

    CocoaPods のインストールで Firebase の依存関係が見つからない場合は、pod repo update を実行する必要があります。

    このステップが完了したら、この iOS アプリの以降のすべての開発に、新たに作成された .xcworkspace ファイルを .xcodeproj ファイルの代わりに使用します。

  14. [Firebase SDK の追加] で [次へ] をクリックします。

  15. プロジェクトで Firebase の初期化に必要なコードをメモします。

  16. [初期化コードの追加] セクションで、[次へ] をクリックします。

  17. [アプリを実行してインストールを確認] で [このステップをスキップ] をクリックします。

  18. ApplicationConstants.swift ファイルのyour-project-identifier プレースホルダを Firebase プロジェクト ID に置き換えます。

サービス アカウントの作成

トークン サービスは Cloud IAM API を使用して、有効期間が短い認証情報をリクエストします。クライアントはこの認証情報を使用して、Dialogflow API にアクセスします。詳細については、有効期間が短いサービス アカウント認証情報の作成をご覧ください。

必要な権限を持ったサービス アカウントを作成するには:

  1. Firebase コンソールの左側のメニューで、ストップウォッチ プロジェクトのホームの横にある設定 アイコン(歯車アイコン)を選択し、[プロジェクトの設定] を選択します。

  2. [サービス アカウント]、[サービス アカウント権限の管理] の順に選択します。

  3. [サービス アカウントを作成] をクリックします。

  4. 以下を構成します。

    1. [サービス アカウント名] に「dialogflow-client」と入力します。
    2. [役割] で、[プロジェクト] > [Dialogflow API クライアント] の順に選択します。
  5. [作成] をクリックします。

トークン作成者の権限を追加する

Cloud Functions for Firebase は、App Engine のデフォルト サービス アカウントで稼働します。このアカウントに、有効期間が短い認証情報の作成権限を割り当てる必要があります。必要な権限を追加するには、サービス アカウント トークン作成者のロールを App Engine のデフォルト サービス アカウントに追加します。

  1. Cloud Console で IAM ページを開きます。

    [IAM] ページを開く

  2. [名前] 列に App Engine のデフォルト サービス アカウントが表示されているエントリを見つけて、その行の終わりにあるメンバーの編集ボタンをクリックします。

  3. [別の役割を追加] をクリックします。

  4. [役割を選択] メニューから [Service Accounts] > [サービス アカウント トークン作成者] を選択します。

  5. [保存] をクリックします。

Google Cloud プロジェクトの課金と API を有効にする

このチュートリアルでは、Dialogflow API を使用する必要があります。API を有効にするには、次の手順に従います。

  1. Google Cloud Console でストップウォッチ プロジェクトを選択します。

    プロジェクト ページに移動

  2. Google Cloud プロジェクトに対して課金が有効になっていることを確認します。 プロジェクトに対して課金が有効になっていることを確認する方法を学習する

  3. Dialogflow API を有効にします。

    API を有効にする

トークン サービスを作成してデプロイする

トークン サービスを作成してデプロイするには、Firebase CLI をインストールし、Cloud Functions for Firebase のプロジェクト構造を作成して、デフォルトの関数をトークン サービス関数に置き換える必要があります。

  1. Firebase CLI をインストールします。

    npm install -g firebase-tools
        
  2. Firebase にログインします。

    firebase login
        
  3. Cloud Functions for Firebase のプロジェクト構造を作成します。

    firebase init functions
        
  4. プロジェクト構造に含まれるデフォルトの index.js ファイルを、nodejs-docs-samples リポジトリの functions/tokenservice/functions フォルダ内にある index.js ファイルで置き換えます。

  5. generateAccessToken() 関数に含まれる SERVICE-ACCOUNT-NAME プレースホルダと YOUR_PROJECT_ID プレースホルダを、それぞれ dialogflow-client と Firebase プロジェクト ID に置き換えます。これらのプレースホルダは、次の形式のサービス アカウント名の一部となります。

    SERVICE-ACCOUNT-NAME@YOUR_PROJECT_ID.iam.gserviceaccount.com
        
  6. トークン サービス関数をデプロイします。

    firebase deploy --only functions
        

ストップウォッチ エージェントをデプロイする

ストップウォッチ エージェントをデプロイするには、Dialogflow コンソールで新しいエージェントを作成します。

  1. Dialogflow コンソール内にエージェントが 1 つも表示されていない場合、左側のメニューで [新しいエージェントの作成] をクリックします。それ以外の場合は、左側のメニューで現在のエージェント名をクリックしてから [新しいエージェントの作成] を選択します。

  2. エージェントの名前として「stopwatch」と入力します。

  3. デフォルトの言語を選択します。

  4. デフォルトのタイムゾーンを選択します。

  5. [Google プロジェクト] メニューから [stopwatch] を選択します。

  6. [作成] をクリックします。

クライアント アプリ プロジェクトには StopwatchAgent.zip ファイルが含まれています。このファイルを使用してエージェントをインポートできます。

  1. 左側のメニューで、エージェント名の横にある歯車アイコン settings をクリックします。
  2. [エクスポートとインポート] タブをクリックします。
  3. [Zip からのインポート] をクリックします。
  4. StopwatchAgent.zip ファイルを選択します。
  5. テキスト フィールドに「IMPORT」と入力して、この操作を確定します。
  6. [インポート] をクリックします。

コードの確認

クライアント アプリで有効期間が短い認証情報を取得するには、トークン サービスの getOAuthToken() 関数を呼び出します。getOAuthToken() メソッドは、次のタスクを実行します。

  1. 現在のリクエストが認証されていることをチェックします。リクエストが認証されていない場合は、エラーをスローします。
  2. データベースからのトークンの取得を試みて、次のいずれかのアクションを実行します。
    1. データベース内にトークンがあり、トークンの有効期限が 5 分以内に切れない場合は、そのトークンをクライアントに返します。
    2. データベース内にトークンがない場合、またはトークンがあっても有効期限が 5 分以内で切れる場合、Cloud IAM API からトークンを取得して、そのトークンをクライアントに渡します。また、以降のクライアント リクエストに対応するために、そのトークンをデータベース内に保存します。

次のコードサンプルは、getOAuthToken() 関数を示しています。

exports.getOAuthToken = functions.https.onCall(async (data, context) => {
      // Checking that the user is authenticated.
      if (!context.auth) {
        // Throwing an HttpsError so that the client gets the error details.
        throw new functions.https.HttpsError(
          'failed-precondition',
          'The function must be called ' + 'while authenticated.'
        );
      }
      // Retrieve the token from the database
      const docRef = db.collection('ShortLivedAuthTokens').doc('OauthToken');

      const doc = await docRef.get();

      try {
        if (doc.exists && isValid(doc.data().expireTime)) {
          //push notification
          pushNotification(
            data['deviceID'],
            doc.data().accessToken,
            doc.data().expireTime
          );
          return doc.data();
        } else {
          const result = await retrieveCredentials(context);
          console.log('Print result from retrieveCredentials functions');
          console.log(result);
          pushNotification(
            data['deviceID'],
            result['accessToken'],
            result['expireTime']
          );
          return result;
        }
      } catch (err) {
        console.log('Error retrieving token', err);
        pushNotification(data['deviceID'], 'Error retrieving token', 'Error');
        // return 'Error retrieving token';
        return 'Error retrieving token';
      }
    });

トークン サービスは認証情報を取得するために、次のコードに示されている、Cloud IAM API の /computeMetadata/v1/instance/service-accounts/default/token エンドポイントにリクエストを送信します。

const retrieveCredentials = (context) => {
      return new Promise((resolve) => {
        // To create a new access token, we first have to retrieve the credentials
        // of the service account that will make the generateTokenRequest().
        // To do that, we will use the App Engine Default Service Account.
        const options = {
          host: 'metadata.google.internal',
          path: '/computeMetadata/v1/instance/service-accounts/default/token',
          method: 'GET',
          headers: {'Metadata-Flavor': 'Google'},
        };

        const get_req = http.get(options, (res) => {
          let body = '';

          res.on('data', (chunk) => {
            body += chunk;
          });

          res.on('end', async () => {
            const response = JSON.parse(body);
            const result = await generateAccessToken(
              context,
              response.access_token,
              response.token_type
            );
            return resolve(result);
          });
        });
        get_req.on('error', (e) => {
          //console.log('Error retrieving credentials', e.message);
          return `Error retrieving token${e.message}`;
        });
        get_req.end();
      });
    };
    exports.retrieveCredentials = retrieveCredentials;

トークン サービスの詳細については、Cloud Functions for Firebase を定義する index.js ファイルをご覧ください。

クリーンアップ

このチュートリアルで使用したリソースの Google Cloud アカウントへの課金を回避するには、Google Cloud プロジェクトと Firebase プロジェクトを削除します。Firebase コンソールでプロジェクトを作成しましたが、このプロジェクトは Cloud Console でも削除できます。

  1. Cloud Console で [リソースの管理] ページに移動します。

    [リソースの管理] ページに移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。