検索
特集

C#によるAlexaスキル実装の基礎特集:はじめてのAlexaスキル開発(2/3 ページ)

AlexaのスキルをVSとC#を使って実装してみよう。これを助けてくれる拡張機能とNuGetパッケージもあるので、とてもカンタンだ。

Share
Tweet
LINE
Hatena

コードの実装

 [Empty Function]テンプレートから自動生成されたコードは次のようになっている(コメントや空行を削除したもの)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Amazon.Lambda.Core;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

namespace InsiderNetAlexaSkillFunc
{
  public class Function
  {
    public string FunctionHandler(string input, ILambdaContext context)
    {
      return input?.ToUpper();
    }
  }
}

デフォルトのLambda関数

 FunctionクラスにはFunctionHandlerメソッドのみが存在し、これが何らかのトリガーによって呼び出される。一般にLambda関数は次のようなシグネチャを持つ

<ReturnType> <HandlerName>(<InputType> input, ILambdaContext context)

Lambda関数のシグネチャ

 Lambda関数が戻り値型(ReturnType)に指定した型のオブジェクトを返送すると、それはJSONへとシリアライズされる(このときに使用されるシリアライザーを指定するのが、名前空間の前に付加されている「LambdaSerializer」属性だ)。inputパラメーターには、呼び出し側(本稿であれば、Alexaスキル)から特定の処理を実行するために必要な引数が渡される。contextパラメーターにはLambda関数をAWS上のランタイムで実行する際のコンテキスト情報が格納される(が、本稿では取り扱わない)。

 上のスケルトンコードでは、文字列を受け取り、文字列を返送するようになっていたが、ここではこのコードを次のようにしている。やっていることは前回と同じで、メッセージを3つ用意して、そこからランダムに出力を返すだけだ。一番簡単な実装としては次のようになる。強調表示したusing宣言はAlexa.NETが提供する各種クラスを使用するためのものだ。

using System;
using Alexa.NET.Request;
using Alexa.NET.Response;
using Alexa.NET.Request.Type;
using Amazon.Lambda.Core;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]
namespace AWSLambda4
{
  public class Function
  {
    string[] messages =
    {
      "Hello Alexa From CSharp",
      "Hello Alexa From Insider.NET with CSharp",
      "C#で実装したLambdaコードからこんにちは!"
    };

    public SkillResponse FunctionHandler(SkillRequest input,
      ILambdaContext context)
    {
      string msg;
      switch (input.Request)
      {
        case LaunchRequest lr:
          msg = "こんにちは";
          break;
        case IntentRequest ir:
          msg = MakeResponse(ir);
          break;
        default:
          msg = "何か?";
          break;
      }

      var response = new SkillResponse
      {
        Version = "1.0",
        Response = new ResponseBody()
      };
      response.Response.OutputSpeech =
        new PlainTextOutputSpeech() { Text = msg };
      return response;
    }

    private string MakeResponse(IntentRequest ir)
    {
      string msg;
      switch (ir.Intent.Name)
      {
        case "HelloFromCSharpIntent":
          msg = messages[(new Random()).Next(3)];
          break;
        default:
          msg = "何か?";
          break;
      }
      return msg;
    }
  }
}

変更後のLambda関数

 スケルトンコードとの大きな違いは、FunctionHandlerメソッドのシグネチャだ。スケルトンコードでは「文字列を受け取り、文字列を返送する」ようになっていたのが、Alexa.NETパッケージが提供するSkillResponseクラス、SkillRequestクラスをやりとりするようになっている。

 Alexaのスキルから受け取ったリクエストの内容は以下に示すSkillRequestクラスのオブジェクトへとデシリアライズされる。

public class SkillRequest
{
  [JsonProperty("version")]
  public string Version { get; set; }

  [JsonProperty("session")]
  public Session Session { get; set; }

  [JsonProperty("context")]
  public Context Context { get; set; }

  [JsonProperty("request")]
  [JsonConverter(typeof(RequestConverter))]
  public Type.Request Request { get; set; }

  public System.Type GetRequestType()
  {
    return Request?.GetType();
  }
}

SkillRequestクラス
Alexe.NETのリポジトリより。

 FunctionHandler内では、このうちのRequestプロパティの値(Request型)が実際にはLaunchRequestクラスのインスタンスかIntentRequestクラスのインスタンスかによって処理を切り分ける。

switch (input.Request)
{
  case LaunchRequest lr:
    msg = "こんにちは";
    break;
  case IntentRequest ir: 
    // インテントなら、MakeResponseメソッドでインテントごとに処理を切り分け
    msg = MakeResponse(ir);
    break;
  default:
    msg = "何か?";
    break;
}

まずはリクエストによって処理を切り分け

 最初に届くリクエストではRequestプロパティの型は「LaunchRequest」であり(Requestクラスの子クラス)、その場合には「こんにちは」と返し、その後にIntentRequest(同じくRequestクラスの子クラス)を受け取ったら、MakeResponseメソッドを呼び出して、そこで今度はインテントの種類(名前)に応じて、処理を切り分けている。

private string MakeResponse(IntentRequest ir)
{
  string msg;
  switch (ir.Intent.Name)  // インテントの名前で処理を切り分ける
  {
    case "HelloFromCSharpIntent":
      msg = messages[(new Random()).Next(3)];
      break;
    default:
      msg = "何か?";
      break;
  }
  return msg;
}

次にIntentRequestがきたら、インテントの名前でどんな処理をするかを切り分ける

 ユーザーとAlexaとの対話によりインテントが送信されると、そのインテントに関連する情報がIntentRequestクラスのインスタンスに格納される。例えば、インテントの名前はIntentRequest.Intent.Nameプロパティで参照できる。そこで、この情報を利用して、処理を切り分ければよい。上ではインテント名が「HelloFromCSharpIntent」であれば、3つのメッセージのどれかを、他のインテントでは「何か?」と返すようにしている。他のインテントを会話モデルに追加した場合には、MakeResponseメソッドのswitch文に対応する処理を追加していけばよい。ただし、このままでは組み込みインテントすら処理されてしまうので、実際にはきちんとそれらの処理を記述すべきだ。

 最後に、Alexaに返送するレスポンスは次のようにして作成している。

var response = new SkillResponse
{
  Version = "1.0",
  Response = new ResponseBody()
};
response.Response.OutputSpeech =
  new PlainTextOutputSpeech() { Text = msg };
return response;

Alexaに返送するメッセージの作成

 SkillResponseクラスではVersionプロパティとResponseプロパティが必須の要素となっている。後は、Response.OutputSpeechプロパティに返送するメッセージをセットしているだけだ。

 取りあえず、ここまでのコードの動作を確認してみよう。

Lambda関数の公開と動作確認

 VSで作成したLambda関数は、ソリューションエクスプローラーからAWS上に公開できる。これには、ソリューションエクスプローラーでプロジェクトを右クリックして、コンテキストメニューから[Publish to AWS Lambda]を選択する。すると、次のようなウィンドウが表示される。

[Function Name]欄への入力は必須(赤枠内)
[Function Name]欄への入力は必須(赤枠内)

 ここで必要ならRegionなどを適宜設定し(上では[Asia Pacific (Tokyo)]となっている)、[Function Name]に関数名を入力する(ここでは「HelloAlexaFromCSharpSample」とした)。AWSマネジメントコンソールには、これが作成したLambda関数の名前として表示される。他の設定は取りあえずそのままで[Next]ボタンをクリックすると、詳細な設定を行う画面が表示される。

Lambda関数を実行するロールの設定
Lambda関数を実行するロールの設定

 ここではLambda関数を実行するロールを指定する必要がある。ここでは前回に作成したロール(lambda_basic_execution)を選択する。その他にも実行マシンのメモリサイズの選択などが行えるが、ここでは特に変更をしていない。最後に[Upload]ボタンをクリックすると、AWSへの公開が開始される。

 公開が終わると、VSの内部に次のようなタブが表示される。

動作確認に使えるタブ
動作確認に使えるタブ

 この画面は前回に見たLambda関数側でのテスト実行に相当する(前回に使ってみた[Alexa Start Session]がドロップダウンにあるのが分かるだろうか)。つまり、このタブの内部でLambda関数の簡単な動作確認が可能だ。実際にテストをしてみたところを以下に示す。

公開したLambda関数を呼び出すには、サンプルのリクエストを選択して、[Invoke]ボタンをクリックする
公開したLambda関数を呼び出すには、サンプルのリクエストを選択して、[Invoke]ボタンをクリックする

 上の画像は前回と同じ[Alexa Start Session]を選んだもの。興味のある方は[Example Requests]ドロップダウンから[Alexa Intent - MyColors]を選択して、そのnameプロパティを「HelloFromCSharpIntent」に変更し、動作を確認するなどしてみよう。

 動作確認ができたので、VSでの作業はこれで終了だ。後はAWSコンソールでのLambda関数の設定と、Alexaスキル側での会話モデルの作成とLambda関数との対応付けが必要になる。これについては基本的に前回に説明したのと同じことをすればよいだけなので、駆け足で見ていこう。

Copyright© Digital Advantage Corp. All Rights Reserved.

ページトップに戻る