前ページで見たJSONデータを取得し、C#で利用可能なLuisResponse型のオブジェクトを作成しているのがGetLuisResponseAsyncメソッドだ(ステップ1に相当するコード)。
private async Task<LuisResponse> GetLuisResponseAsync(string text)
{
var url = "LUISアプリのURLとクエリ" + HttpUtility.UrlEncode(text);
var textdata = await CallRestAPIAsync(url);
var result = JsonConvert.DeserializeObject<LuisResponse>(textdata);
return result;
}
このメソッドではCallRestAPIAsyncヘルパーメソッドに実際のAPI呼び出しを任せ、返送されたテキストをJSON.NETのDeserializeObjectメソッドでLuisResponseオブジェクトにデコードしている。LuisResponseクラスは上で見たJSONデータを基にその構造を素直に書き下したクラスだ。
public class LuisIntent
{
public string intent { get; set; }
public double score { get; set; }
}
public class LuisEntity
{
public string entity { get; set; }
public string type { get; set; }
public int startIndex { get; set; }
public int endIndex { get; set; }
public double score { get; set; }
}
public class LuisResponse
{
public string query { get; set; }
public LuisIntent topScoringIntent { get; set; }
public List<LuisIntent> intents { get; set; }
public List<LuisEntity> entities { get; set; }
}
ロケスマWebとGoogle GeocodingのREST APIでもやっていることは同様なので、上で述べたステップ3と4に相当するこれらのコード(GetLocaSmaResponseAsyncメソッド呼び出しとこの後で見るGetLocationAndCategoryAsyncメソッド内でのREST API呼び出しと、そこで使われるモデルクラス)についての説明は省略する。実際のREST API呼び出しを行っているコードは次のようになる。
private async Task<string> CallRestAPIAsync(string url)
{
var wc = new WebClient();
var response = await wc.DownloadDataTaskAsync(url);
var enc = System.Text.Encoding.GetEncoding("utf-8");
return enc.GetString(response);
}
これについても見ての通り、特別なことは何もしていない。
最後に取得した情報からURLを作成するまでの手順を見ておこう。
ステップ4と5についてはまとめてコードを紹介する。
private async Task<LocationAndCategory> GetLocationAndCategoryAsync(
LuisResponse luisres, List<LocaSmaData> locasmadata)
{
var location = luisres.entities.Find(x => x.type == "Location")?.entity;
var categoryname = locasmadata[0].items[0].name;
var categoryid = locasmadata[0].items[0].id;
double lat = 0.0;
double lon = 0.0;
if (location != null)
{
var url = "Google Geocoding APIのURLおよびクエリ" + location;
var text = await CallRestAPIAsync(url);
var tmp = JsonConvert.DeserializeObject<GeoData>(text);
if (tmp.results.Count != 0)
{
lat = tmp.results[0].geometry.location.lat;
lon = tmp.results[0].geometry.location.lng;
}
}
return new LocationAndCategory()
{
CategoryId = categoryid,
CategoryName = categoryname,
Location = location,
Lat = lat,
Lon = lon
};
}
private string MakeMessage(LocationAndCategory locandcat)
{
var message = "";
if (locandcat.Location != null && !locandcat.Location.Contains("近く"))
{
message = $"{locandcat.Location}で{locandcat.CategoryName}をお探しですか?" +
$"https://www.locationsmart.org/map.html?id={locandcat.CategoryId}" +
$"&lat={locandcat.Lat}&lon={locandcat.Lon}";
}
else
{
message = $"{locandcat.CategoryName}をお探しですか?" +
$"https://www.locationsmart.org/map.html?id={locandcat.CategoryId}";
}
return message;
}
GetLocationAndCategoryAsyncメソッドでは、LUISアプリが抽出した地名(Locationエンティティ)を利用してGoogle Geocoding APIを呼び出し、緯度経度情報を取得している。そしてそこから得たデータ(緯度経度情報)、ロケスマWebのREST APIから得たデータ(ロケスマWebのURL作成で利用するカテゴリIDとその日本語表記)、LUISアプリから得たデータ(地名=Locationエンティティ)をまとめて、LocationAndCategoryオブジェクトを作成している。地名がない場合には緯度経度情報は0.0としている(が、その下のMakeMessageメソッドでは使用していない)。
MakeMessageメソッドではこれらのデータを基に、地名の有無に合わせてロケスマWebで地図と店舗を表示するためのURLを含んだ応答メッセージを作成している。
ざっくりとした説明だが、ここで作成したBotではおおよそこのような処理を行っている。実際にLINE BotをAzueに展開して、友人として登録し、使用しているところを以下に示す。
最後に少し考察をしておくと、このBotはユーザーから得たメッセージを全てLUISへと送り込んでいる(スリープしていない限り)。LUISの公式サイトでは現在のところ「LUIS is in beta and free to use」と書いてあり、本稿ではこちらを利用しているが、一方でMicrosoft Azureでは無料の「LUIS API - Free」レベル(10,000トランザクション/月まで)と有料の「LUIS API - Basic」レベル(76.5円/1000トランザクション)の2レベルのサービスが提供されている。Azureを用いてプロダクトベースでLUISを使用する場合には、何でもかんでもLUISにデータを送り込むのはいろいろな意味でコストを無駄にする可能性がある。
そのため、実際にはユーザーからの何らかの入力をトリガーとして(例えば、「ロケスマさん」という文言が含まれていたら)、そこからデータをLUISに送り込むようにするといった対応が必要になるだろう。
もう1つ、本稿で作成したLINE BotはLUISアプリの精度があまりよろしくない。これに加えて、ロケスマWebのREST APIの呼び出しの結果の扱いでも手を抜いている。例えば「スタバ」と入力しても「カフェ」のマップを表示するためのURLが表示されるといった具合だ。
「焼肉」と「焼き肉」の違いでBotが対応したり対応しなかったりするのも、ユーザーにとっては使い勝手が悪いところだ。こうした部分を改善していくことで、このBotの使い勝手が向上するはずだ。
本稿ではC#でLINE Botを開発するために必要な手順を見て、簡単なサンプルプログラムを作成した。LUISを使用することで、Botがそれなりの知性を持ち(もちろん、学習を深めていく必要がある)、さらにさまざまなWebサービスを組み合わせることで、BotがSNSで対話しながらユーザーに役立つ情報を提供できるようになることが何となく実感できたのではないだろうか。
Copyright© Digital Advantage Corp. All Rights Reserved.