March 2017

Volume 32 Number 3

Bot Framework - ボットをさらにインテリジェントにする

Kevin Ashley | March 2017

最近、Microsoft Bot Framework、LUIS、Azure Bot Service、Azure Functions などで「ボット」という言葉をよく耳にするようになりました。ボットは新しいアプリです。ボットが話題になる 1 つの理由は、ボットが一般的な作業にもたらす容易性と効率性にあります。たとえば、 航空便の予約やレストランの予約を Web サイトで行う場合、通常、Bing や Google から 2、3 個以上のサイトを移動することになります。ところが、これがなんらかの種類のデジタル アシスタントに依頼するだけで済むのです。ですが、アプリ開発者がこうした傾向を受け入れるのには、もっと大きな理由があります。 モバイル アプリの作成コストはますます高くなっています。たった 1 つのモバイル アプリをビルドする場合でも、プラットフォーム (iOS、Android、Windows) ごとに 3 万~ 50 万ドルかかります。

確かに、クロスプラットフォーム テクノロジ (Unity や Xamarinn など) もいくつかあり、複数のプラットフォームを対象に開発することでコストを軽減できます。ただし、ボットはこれとは別のオプションとして登場していて、会話インターフェイスを採用することでパラダイムを変えています。このアプローチは、数千年にわたって人類が使ってきたコミュニケーション手段を利用し、その手段を音声やテキストにまで広げます (Skype や Messenger を想像してください)。この会話 UI (CUI) は、画像、ビデオ、Button や CardAction のような従来型のコントロールを使って拡張することもできます。ボットでは、従来型のコントロールを備えた UI を作成することではなく、入力のシンプルさと自然さに重点を置きます。

では、ボットのメリットとは何でしょう。 画面パラダイムごとに個別の UI をビルドするのをやめ、画面を用意するかどうかにかかわらず、すべてのデバイス、すべてのフォーム ファクターで機能する会話インターフェイスを採用すれば、総額にして数十億ドルの節約になる可能性があります。今回例として、近くのランニング、サイクリング、ハイキング、スキーなどのコースを検索できる Active Fitness ボットをビルドしました (bit.ly/2knZVbr にアクセスして Skype に追加できます)。 このアプリはリッチな UI やコントロールを備えていますが、会話入力を理解するように設計したボットになっています (図 1 参照)。

コースを表示する Active Fitness ボット
図 1 コースを表示する Active Fitness ボット

ボットの影響は、アプリ開発だけにはとどまりません。古く、時代遅れの Web フレームワークや Web 標準を多く使用し、ナビゲーションがますます難しくなっている Web サイトの課題にもボットで対処できます。たとえば、どこかのカスタマー サービス Web サイトでタスクを完了してみてください。Web サイト メニューのナビゲーションにおそらく 30 分は必要になります。

他にもボットが活躍できる独特の分野があります。Solitaire などのカード ゲーム アプリを考えてみます。ボットが登場する前は、プラットフォームごとに別のバージョンをビルドしなければなりませんでした。ところが、今では、各プラットフォーム固有の問題点はボットが解決します。作成した Windows ベース アプリが、すべての iPhone や Android 上でボットとして機能します。その理由は、 ボットがチャネル内 (Skype、Telegram、Facebook Messenger、Slack などのユビキタス アプリ プラットフォーム) で機能するためです。

そこで今回、世界で最も人気のあるゲームを Microsoft Bot Framework を使ってビルドすればすばらしいのではないかと考えました。今回は Solitaire を図 2 に示すようにすぐにプレイできるようにします (bit.ly/2jrzP7S にアクセスして Skype に追加できます)。

1 週間あたり 50 万件のリクエストを処理する Skype の Solitaire ボット
図 2 1 週間あたり 50 万件のリクエストを処理する Skype の Solitaire ボット

ボットの作成を開始するには、Microsoft Bot Framework サイト (dev.botframework.com) に移動します。ボットは Microsoft .NET Framework でも Node.js でも好みのテクノロジを使ってビルドできます。最近、マイクロソフトはボットを実装するためのさらに便利な方法として Azure Bot Service (bit.ly/2knEtU6) を追加しました。Azure Bot Service は使いやすいテンプレートからボットをビルドするためのブラウザベースのエクスペリエンスを提供します。このサービスは Azure Functions を使用します。Azure Functions はイベント ベースのサーバーレス コード アーキテクチャで、コストを抑え、ボットを最適にスケーリングできる「関数」を備えています。 

Azure Functions が優れている点はいくつかあります。 ボットのビルドに着手すると、ボットが必要になるたびに、新しい Web アプリを作成してアプリ コンテナーを提供しなければならないことがわかります。アプリが 1 つなら問題ありませんが、ボットが複数になると問題で、それぞれ独立したインフラストラクチャが必要になります。しかし、Azure Functions では、アプリケーション インフラストラクチャ全体を気にかけることなく、少量のコードをクラウド内でホストできます。

インテリジェンスとボット開発

アプリと同様、ボットは、特定のタスクについて、最善を尽してユーザーを助けるために存在します。たとえば、今回の Solitaire ボットは Solitaire をプレイできるだけでなく、ゲームのルールやテクニックを教えることもできます。確かに、チューリング テスト (1950年に Alan Turing が開発した機械のインテリジェンスを測定する方法) に合格しないかもしれませんが、大半のボットにはおそらくチューリング テストはやりすぎでしょう。

同じように考えると、地方陸運局用ボットなら、ライセンス登録の支払申請の決まり事、違反切符の支払いや運転免許証更新申請を支援する方法を把握する必要があります。天気ボットなら、天気関連の質問に答える必要があるため、ボットのインテリジェンスとしては、場所と、具体的な天気データについてのユーザーのニーズを考慮する方法などが含まれます。

ボットは会話インターフェイスを使って多くのことを自動化しますが、画像、ボタンなど、モバイル デバイス備え付けのもっと便利な対話コントロールを使用して強化することもできます。自由に会話できるようになれば、ボットのインテリジェンスが実際に機能し始めます。一連の回答をあらかじめ定義した対話を用意するのが標準です。これはプログラミング作業としては比較的簡単です。一方、人間が話しかけることに反応するには、インテリジェンスが必要になります。さいわい、Microsoft Bot Framework によってどちらも可能になります。 LuisDialogs が自然言語ベースの会話をサポートできるようにします。これに対し、従来型のコントロールは、ユーザーがオプションを選択するためにボタン、カルーセルなどのコントロールを使用できるようにします。

インテリジェンスは、多くのボットが採用している会話 UI の心臓部で、人間との直感的な対話を可能にします。マイクロソフトの Cognitive Services (bit.ly/2jx1kMQ) はその数を増やしていますが、このサービスを利用すれば、ボットにインテリジェント機能をすぐにプラグインすることができます。以下のサービスがあります (ただし、これに限定されるわけではありません)。

  • 視覚
  • 音声
  • 言語
  • 知識
  • 検索
  • 場所

Cognitive Services について Alessandro Del Sole が執筆した素晴らしい記事 (msdn.com/magazine/mt742868) を確認してください。 

ボットのインテリジェンスを高める場合は、チューリング テストには合格しないと思われる一般的な手段をいくつか使用する必要があります。ただし、ボットとの会話は確実に向上します。以下がその例です。

  • 正規化したデータを集め、メッセージを伝え、会話を単純にするダイアログや画像などの手段
  • ローカライズ
  • ボットの基礎的な機能を改善するヘルプとダイアログのパイプライン
  • 重複する質問を避けるためのユーザー データ (場所など)

野心的になりすぎないようにします。ダウラス・アダムスが書いた「究極の答え」を計算する都市サイズのスーパー コンピューター (『銀河ヒッチハイク ガイド』より) のようなボットを作成しようとしてはいけません。今回のボットの機能を、最初は主な作業に必須の機能 (カード ゲームをプレイすることや、ランニングに最適なコースを提案することなど) に限定しました。これを実現した後、インテリジェンスを使って機能を拡張します。

LUIS による言語インテリジェンス

会話インターフェイスへの追加を検討する最初のインテリジェンス コンポーネントは Microsoft Language Intelligence Service (LUIS) です。このサービスは、サービスに送信した会話や文章を解析し、実際のインテント (Intent) とアプリケーション固有のエンティティ (Entity) を抽出します。LUIS ダイアログをボットに追加するには、LuisDialog を継承するクラスを作成し、その LUIS アプリを luis.ai で登録して、アプリ ID とキーをダイアログに提供します。

[LuisModel("<YOUR_LUIS_APP_ID>", "<YOUR_LUIS_APP_KEY>")]
[Serializable]
public class IntelligentLanguageDialog : LuisDialog<object>

LUIS の詳細ガイダンスについては、Ashish Sahu が執筆した MSDN Magazine 2017 年1 月号のコラム (msdn.com/magazine/mt745095) を参照してください。

今回の Active Fitness Trails ボット (図 3) では、フィットネス アクティビティに関連するインテントを定義しました。インテリジェンス ボットの会話の一環として、利用者から場所とフィットネス アクティビティが提供されることを想定しています。たとえば、利用者が「コロラドのスキー コースを表示」と発話すると、ボットがこれを理解して、一連のコースを表示します。Active Fitness アプリの利用者たちは、毎日数百万ものコースをランニングし、Active Fitness に問い合わせて、LUIS モデルの GetActivityLocation インテントに対応付けることができます。LUIS モデルは地理とアクティビティのエンティティをボットに返します。

LUIS のモデル
図 3 LUIS のモデル

ユーザーの入力が正規化されていないとしたら、そこからすべてのデータを抽出するのに、どの程度のコーディングが必要になるかを想像してみてください。 人間が話す短い文章でも、通常ある程度の複雑さがあり、機械学習ツールでしか理解して処理することはできません。この単純な文章に考えられるすべてのバリエーションを追加することを考えると、ボットのインテリジェンスが簡単な作業ではないことがわかります。さいわい、LUIS がインテリジェントな会話の分析ツールと抽出ツールの役割を果たし、開発者にとって困難な作業をすべて引き受けます。必要なのは、LUIS をボットに追加することだけです。

モデルを定義したら、発話することで LUIS をトレーニングし、LUIS がボットのインテントと、エンティティの取得方法を理解するのを助けます。GetActivity­Location インテントについてモデルをトレーニングするには、 「近くのサイクリング コースを表示」「オーストリアの最新のスノーボード コースを表示」「ユタで一番長いスキー コースはどこ」のような発話を行います。 LUIS がエンティティを処理するときは、モデルで定義したエンティティが強調表示されます (図 4 参照)。LUIS がエンティティを認識しない場合はどうすればよいでしょう。 その場合は、エンティティを手動でマッピングすることによってトレーニングします。今回の場合は、「スノーボード」をアクティビティとして認識するようボットをトレーニングする必要がありました。

LUIS でのモデル トレーニング
図 4 LUIS でのモデル トレーニング

次に、LUIS モデルから返されるインテントを処理するメソッドを定義します。None インテントを処理するメソッドや、モデルの他の任意のインテント処理するメソッドも用意します (図 5 参照)。

図 5 インテントの処理

[LuisIntent("None")]
  public async Task NoneIntent(IDialogContext context, LuisResult result)
  {
    await context.PostAsync(
      $"You have reached the none intent. You said: {result.Query}"); //
    context.Wait(MessageReceived);
  }
  // Go to https://luis.ai and create a new intent,
  // then train/publish your luis app.
  // Finally, replace "GetActivityLocation" with the name of your newly
  // created intent in the following handler.
  [LuisIntent("GetActivityLocation")]
  public async Task GetActivityLocation(IDialogContext context, LuisResult result)
  {
    await context.PostAsync(
      $"You have reached the GetActivityLocation intent. You said:
      {result.Query}"); //
    context.Wait(MessageReceived);
  }

入力と Get­ActivityLocation インテントが一致しているとモデルが判断するたびに、モデルがボットのメソッドを呼び出します。LUIS から返される結果は LuisResult オブジェクトです。これには、場所やアクティビティなどのエンティティが含まれます。たとえば、エンティティに国が含まれているかどうかをチェックするとします。これを行うには、LuisResult オブジェクトの TryFindEntity メソッドを使用して、“builtin.geography.country” 型のエンティティを検索します。LUIS は、事前に構築されたエンティティを数多く用意しています。これにより、開発者の作業はかなり単純になります。このようなエンティティの完全な一覧についてはこちら (bit.ly/2kWgCHR) で確認できます。

builtin.geography エンティティには、country や city などのサブエンティティがあるのがわかります。サブエンティティを含むエンティティを複合エンティティと呼びます。今回具体的に扱うサブエンティティは country です (図 6 参照)。

図 6 Country の検索

// Go to https://luis.ai and create a new intent, then train/publish your luis app.
  // Finally, replace "GetActivityLocation" with the name of your
  // newly created intent in the following handler.
  [LuisIntent("GetActivityLocation")]
  public async Task GetActivityLocation(IDialogContext context, LuisResult result)
  {
    await context.PostAsync(
      $"You have reached the GetActivityLocation intent. You said:
      {result.Query}"); //
    EntityRecommendation country;
    if(result.TryFindEntity("builtin.geography.country", out country))
    {
      await context.PostAsync($"Country: {country.Entity}");
    }
    context.Wait(MessageReceived);
  }

ボットの実用的ヒント

マイクロソフトは、ボット開発に力を入れていて、Language Intelligence Service (LUIS) や Azure Bot Service のようなツールをリリースして、新しいクラスのソフトウェアの作成と管理の効率化を支援しています。さいわい、最近のアプリの大半は、複数のタスクや複数の種類のアプリに適合する会話 UI (CUI) を備えたボットに簡単に変換できます。ボット開発を最大限に活用するには、考慮すべき点がいくつかあります。

サポートする範囲を広げる: ボットを最も魅力的にするには、あらゆる PC、タブレット、スマートフォンで利用されているチャネルでボットを実行します。たとえば、Skype、Facebook、Messenger、Telegram のようなチャネルです。ボットを複数のチャネルに公開すると、サポートするユーザーの範囲が最大限に広がります。また、入手可能なサービスを利用して、ボットを複数の言語にローカライズします。

話し上手にする: 会話インターフェイスをうまく使うことで、複数のタスクや複数の種類のアプリに適合するようになります。最善の結果を得るには、ボタンのような従来型の UI コントロールではなく、チャット、スピーチ、言語のような会話インターフェイスに重点を置きます。

サービスを利用する: 新しい Azure Bot Service は、インフラストラクチャのオーバーヘッドを最小限に抑え、ボット プロジェクトのスケール変換を可能にするクラウドベースのプラットフォームです。この機能を支えているのが Azure Functions で、これにより、開発者はクラウドで少量のコードをホストできるようになります。

専念する: ボットをビルドするときは、明確なタスクまたは各ボットが解決する必要のあるタスクに専念させます。必要以上に複雑にしません。AI 化することは必ずしも最善の方法ではありません。ボットが解決する必要のある主要タスクから始め、インテリジェンスを加えます。

はっきり話す: LUIS に組み込まれている情報を利用して、自由な会話を可能にし、ボットに入力を提供します。最善の結果を得るには、LUIS モデルをトレーニングし、エンティティとインテントが簡潔なセットになることに重点を置きます。モデルの「あいまいさ」が少ないほど、AI は適切に機能します。

当然、アクティビティのエンティティも認識する必要があります (Active Fitness では、ランニング、サイクリング、スキー、スノーボードなどの 50 以上のアクティビティを追跡できます)。従って、 「オーストリアで快適にスキーできるのはどこ」とたずねたら、 Active Fitness ボットがアクティビティ エンティティとして「スキー」を返すことを考えます (図 7 参照)。

Bot Emulator で動作中の GetActivityLocation インテント
図 7 Bot Emulator で動作中の GetActivityLocation インテント

そのため、GetActivityLocation メソッドを更新して、アクティビティを返すようにしました。builtin.geography.country とは異なり、アクティビティは組み込みのエンティティではありません。それでも、モデルのトレーニング中にいくつか会話を提供すると LUIS の理解度があがります (図 8 参照)。

図 8 会話によるモデルのトレーニング

// Go to https://luis.ai and create a new intent, then train/publish your luis app.
  // Finally, replace "GetActivityLocation" with the name of your newly
  // created intent in the following handler.
  [LuisIntent("GetActivityLocation")]
  public async Task GetActivityLocation(IDialogContext context, LuisResult result)
  {
    await context.PostAsync(
      $"You have reached the GetActivityLocation intent. You said:
      {result.Query}"); //
    EntityRecommendation country;
    if(result.TryFindEntity("builtin.geography.country", out country))
    {
      await context.PostAsync($"Country: {country.Entity}");
    }
    EntityRecommendation activity;
    if (result.TryFindEntity("activity", out activity))
    {
      await context.PostAsync($"Activity: {activity.Entity}");
    }
    context.Wait(MessageReceived);
  }

Bot Framework は、汎用の詳細コード サンプルを C# (bit.ly/2gHupjg) と Node.js (bit.ly/2kWolWx) で提供しています。

場所に関する情報の取得

多くのボットは場所に基づく応答を提供します。ですが、場所に対応する応答をコーディングするのは面倒な作業になりがちです。前の例では、LUIS のインテリジェンスを使用して、会話から LUIS が解析した組み込みの地理エンティティを取得しました。さいわい、Microsoft Bing の位置情報コントロールが Bot Framework に含まれるようになりました。このコントロールにより、開発者は場所に基づくデータを簡単に収集できるようになります。

Bot Framework の位置情報コントロールは、地図を含むビジュアル インターフェイスも提供します (図 9 参照)。ボットが Skype、Facebook、Messenger などのチャネルで動作する使いやすいまたはネイティブのインターフェイスから、郵便番号、都市、地域、および局所データを含む住所を提供する必要がある場合、このサービスは非常に便利です。

Bing Maps のビジュアル インターフェイスを使用する位置情報コントロール
図 9 Bing Maps のビジュアル インターフェイスを使用する位置情報コントロール

Bing の位置情報コントロールを使用するには、.NET プロジェクト (NuGet 経由) または Node.jp (npm 経由) のどちらかの Bing Maps API キーと LocationDialog コンポーネントを取得して、コード内で初期化し、LocationDialog のインスタンスを呼び出します。

var options = LocationOptions.UseNativeControl | LocationOptions.ReverseGeocode;
var requiredFields = LocationRequiredFields.Locality |
                     LocationRequiredFields.Region |
                     LocationRequiredFields.Country;
var prompt = "Where are you looking for trails?";
var locationDialog = new LocationDialog(
  apiKey, this.channelId, prompt, options, requiredFields);
context.Call(locationDialog, this.ResumeAfterLocationDialogAsync);

LocationDialog は、さまざまな住所フィールドの要求とプロンプトのカスタマイズのオプションを提供します。このダイアログから結果が返されるときに、再開メソッド内で結果を処理できます (図 10 参照)。

図 10 再開メソッドでの位置情報の結果の処理

private async Task ResumeAfterLocationDialogAsync(
  IDialogContext context, IAwaitable<Place> result)
  {
    var place = await result;
    if (place != null)
    {
      var address = place.GetPostalAddress();
      var formattedAddress = string.Join(", ", new[]
      {
         address.Locality,
         address.Region,
         address.Country
      }.Where(x => !string.IsNullOrEmpty(x)));
      await context.PostAsync(
        "Where are you looking for trails " + formattedAddress);
    }
    context.Done<string>(null);
  }

LUIS 以外の機能

Cognitive Services では、LUIS 以外にも多くの API を提供しています。こうした API を利用すれば、個人の開発者や企業が実装するには理解できない複雑に見えるスキルを使って、ボットを強化できます。ありがたいことに、こうした実装を独自に行う必要はありません。

  • Recommendations API では、商品にお勧め (一緒に購入されることが多い高い商品など) や個人に合わせたレコメンデーションを表示できます。
  • Vision API では、詳細画像やビデオのスキルがボットに追加され、物体や人の顔、年齢、性別、感情などを認識できるようになります。
  • Academic Knowledge API では、ボットへの学術的知識の追加、Q&A の作成、特定の知識ベースのスキルの追加などが可能になります。

さらに、ボットはサードパーティ製の API や サービスに対してオープンになっています。独自のサービスや API を使って、ボットに独自性を持たせたり、以前に解決されていない問題を解決させることができます。実際、Bot Framework は、シンプルな会話インターフェイスと使いやすい API によって、人間の知識の進化とアクセシビリティだけでなく、新しいチャンスを切り開きます。

新たな魅力的サービス

.NET Framework と Node.js 向けの Bot Framework と REST API のいつもの更新以外に、マイクロソフトは最近ボット開発を新たなレベルに引き上げる機能を追加しました。

Bot Framework への最新の追加の 1 つが Azure Bot Service です。これにより、クラウドの利便性とスケーラビリティを利用する簡単な方法がボット プロジェクトにもたらされるようになります (bit.ly/2knEtU6)。Bot Service (図 11) は Azure Function とクイックスタート テンプレートのコレクションを使って、ボット コードを最大限に活して任意のレベルにスケール変換できるようにします (bit.ly/2kuo9Bb)。選択できるボット テンプレートには、「Basic」、「Form」、「Proactive」、「LUIS」、「Question and Answer」などがあります。こうしたテンプレートからそのままボットを実行することも、必要に応じて拡張することもできます。

Bot Service コードのブラウザーでの直接編集
図 11 Bot Service コードのブラウザーでの直接編集

Azure Bot Service を背後で支えるテクノロジが Azure Functions です。この Azure Functions は、サーバーレス アーキテクチャとオンデマンド コンピューティングに基づくイベントドリブン型のエクスペリエンスを提供します。つまり、ボットごとにアプリをホストする高価なインフラストラクチャは必要なくなります。そのため、ボットはより軽量になり、スケール変換が容易になり、開発作業や開発時間が削減されます。同時に、Cognitive Service との接続が簡単になります。

最後に QnA Maker (qnamaker.ai) を紹介しておきます。QnA Maker は Azure Bot Service からテンプレート ボット プロジェクトとして入手できます。QnA Maker には、既存の知識ベースの一般的な質問に答える、ボットにとってごく一般的なシナリオが含まれています。

まとめ

Microsoft Bot Framework はボットのビルドに着手するための迅速かつ容易な方法を提供します。このフレームワークは、新規、既存を問わず、アプリやサービスに会話インターフェイスを提供します。新しい体験を生み出し、Skype、Facebook Messenger、Telegram などのチャネルを使って、数十億人のユーザーにアプリやサービスを届けることができます。Microsoft Cognitive Services には、ボットに追加できる多くのインテリジェント API が含まれています。

Bot Framework エコシステムは急速に成長しています。Cognitive Services に含まれるサービスを追加したり、サードパーティ プロバイダーの API を使用して、ボットに磨きをかけ、機能を向上することができます。ごく簡単なボットから始めても、多くの発見が待ち構えています。ボットは、実際には新しいアプリ (または既存アプリの拡張) です。これにインテリジェンスが加われば、まさに非常に強力なボットになります。また、ボットは開発の時間、作業量、および費用も削減します。

サンプル ボット

ここ数か月をかけて数個のボットを開発しました。せっかくなので、ここでこれらを共有できるようにします。図 A の一覧をご覧ください。試してみてください。このボットがきっかけになり自分でもボットを作成してみようと考えていただければさいわいです (リンクによって各ボットが Skype に追加されます)。

図 A ボットのコレクション

ボット  説明
Solitaire (bit.ly/2jrzP7S) Solitaire は世界で最も人気のあるカード ゲームです。このボットにより、アプリを起動しなくても、Skype のようなお気に入りのチャット クライアントを使って、ゲームを楽しむことができます。
Active Fitness (bit.ly/2knZVbr) 世界有数のフィットネス ソーシャル ネットワークから、ランニング、サイクリング、ウォーキング、スキーに適した世界中のコースを検索します。
UNO (bit.ly/2k4AzyH) 世界で有名なこのカード ゲームを Skype、Messenger、Telegram 上でプレイします。
Freecell (bit.ly/2l0NM9b) Freecell はさまざまな難易度がある優秀で難しいカード ゲームです。簡単なレベルから初めて、プレイ方法を学びます。
Crazy Eights (bit.ly/2kY7EdN) この覚えやすいカード ゲームを試してください。
Card Games Chest (bit.ly/2klXOCV) Solitaire、UNO、Crazy Eights、101、Freecell、Mau-Mau など、人気のあるカード ゲームを習得してください。

Kevin Ashley (@kashleytwit) はマイクロソフトのアーキテクト エバンジェリストです。彼は『Professional Windows 8 Programming』(Wrox、2012 年) の共著者であり、人気アプリ、ボットやゲームの開発者でもあります。代表的なアプリは Active Fitness (activefitness.co) です。Kevin はさまざまなイベント、産業展覧会や Web キャストで技術発表をよく行っています。新興企業やパートナーに協力し、ソフトウェア設計、ビジネスとテクノロジ戦略、アーキテクチャ、および開発に関するアドバイスを行っています。Kevin のブログは kevinashley.com (英語) で Twitter は @kashleytwit (英語) です。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Mat Velloso に心より感謝します。