資産管理のためのチャットボット

Oct 29 2020
この記事では、アセットコントロール用のチャットボットを楽しい小さなプロジェクトとしてまとめています。どうして?私はしばらくの間チャットボットを実装したいと思っていたので(その方法を理解するためだけに)、ACドメインで実装するのは自然なことのように思えます。

この記事では、アセットコントロール用のチャットボットを楽しい小さなプロジェクトとしてまとめています。どうして?私はしばらくの間チャットボットを実装したいと思っていたので(その方法を理解するためだけに)、ACドメインで実装するのは自然なことのように思えます。また、これをSlackと統合して、これが既存のツールやワークフローにどのように適合するかを確認します。

何をするチャットボット?

これまでに、複数の場所でチャットボットに出くわしたことでしょう。彼らは会社のウェブサイトにあり、よくある質問に答えたり、予約したりするのに役立ちます。

この場合、AssetControlと通信できるようにするSlackと統合するチャットボットのプロトタイプを実装したいと思います。AC環境のステータス情報を要求したり、データを要求したりすることができます。

実装をガイドするために、ボットが答えられるはずの次の質問を使用しました。

  • XYZの最終価格はいくらですか?
  • XYZの今月の高値は何でしたか?
  • 今日は何人の容疑者ですか?
  • レプリケーションは最新ですか?

それで、ボットを最初から実装していますか?もちろん違います。そこには多くの解決策があり、私はすぐにGoogleDialogflowに落ち着きました。これがどのように機能するか見てみましょう。

Dialogflowでボットを構築する

Google Dialogflowでボットを構築するには、いくつかの概念を理解する必要があります。

  • 意図
  • エンティティ
  • フルフィルメント

インテントは、ボットが処理する必要がある特定の狭く定義されたトピックに属するすべてのものをバンドルします。私たちの場合、それは私たちのシステムからの株式の最後の利用可能な価格の検索です。Dialogflowを使用して、ボットにトレーニングフレーズを与えることで、会話がそのような意図に関するものである場合を理解するようにボットをトレーニングします。すぐにわかります。

次に、エンティティは会話を構成するビルディングブロックであり、特に関心があるため、それらをパラメータとしてキャッチします。さて、私たちの場合、「AAPLの最終価格はいくらですか?」という質問をすると、それがティッカーシンボルAAPLになります。

最後に、フルフィルメントの概念により、Webhookを介してチャットボットの背後にあるサービスにアクセスできます。つまり、Asset Controlと通信して、指定された株式を照会し、最後に利用可能な価格を取得します。

それでは、ボットを作成しましょう。

ステップ1:ボットをセットアップします。

に行く https://dialogflow.cloud.google.com/#/login サインインします。次に、エージェントAcChatBotを作成します。

ステップ2:ストックエンティティを作成する

左側のメニューから、[エンティティ]の横にある[プラス]をクリックし、[ストック]と呼び[A-Z][A-Z0-9]{1,6}、正規表現として入力します。ティッカーシンボルと完全に一致するわけではありませんが、この例では十分であり、Dialogflow自体によって実行される正規表現のチェックで機能します(広すぎないなど)。次に、それを保存します。

ステップ3:最終価格インテントを作成する

私たちの最後の価格意図のための時間。ここでも、左側のメニューからLast price[インテント]の横にある[プラス]を選択して呼び出し、[トレーニングフレーズの追加]をクリックします。

を入力しWhat is the last price for AAPL?ます。次にAAPL、マウスでハイライトします。これにより、ポップアップが表示されます。を検索しStock、表示されたオプションを選択します。

正しく実行すると、DialogflowがStockエンティティを正しくキャプチャするように、結果は次のようになります。

重要!次に、下にスクロールして[フルフィルメント]セクションを開き、[フルフィルメントを有効にする]をクリックして、次のEnable webhook call for this intentように切り替えます。

これにより、独自のバックエンド関数を呼び出してAssetControlと通信できます。

先に進む前に、必ずインテントを保存してください。

ステップ4:資産管理データを取得するためのRESTサービスを実装する

繰り返しますが、これを行うには多くの方法があります。Spring Bootを使用するのは、すべての面倒な作業を行うためであり、Javaにとどまるため、Adettaが使用するのと同じAsset Control JavaAPIをすぐに使用できます。

まだご存じない方のために説明すると、AdettaはAssetControl用のテスト自動化ソフトウェアです。ここでAdettaの紹介をご覧ください。

興味深いのは、以下に示すクラスAcBotControllerとそのhandleIntentメソッドです。

@RestController
public class AcBotController {
private static JacksonFactory jacksonFactory = JacksonFactory.getDefaultInstance();
private AcBotIntents intents = new AcBotIntents();
@PostMapping(path = "/ac-bot", consumes = "application/json", produces = "application/json")
public ResponseEntity<?> handleIntent(@RequestBody String requestStr,
HttpServletRequest servletRequest) {
try {
GoogleCloudDialogflowV2WebhookRequest request =
jacksonFactory.createJsonParser(requestStr)
.parse(GoogleCloudDialogflowV2WebhookRequest.class);
AcBotIntentHandler intentHandler = intents.getHandlerFor(request);
GoogleCloudDialogflowV2WebhookResponse response =
intentHandler.handleIntent(request);
return new ResponseEntity<>(response, HttpStatus.OK);
} catch (Exception ex) {
return new ResponseEntity<Object>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
}

DialogflowフルフィルメントのWebhookがPOSTリクエストを処理していることがわかります。その入力と出力はどちらもJSONです。また、GoogleCloudDialogflowV2Webhook*クラスを使用して、リクエストとレスポンスの両方のJSONを処理します。

インテントの実際の処理は、呼び出しから取得したAcBotIntentHandlerに渡されAcBotIntents.getHandlerFor(request)ます。このクラスを見てみましょう:

public class AcBotIntents {
private static Logger log = Logger.getLogger(AcBotIntents.class);
private static final String LAST_PRICE = "Last price";
private AcConnection conn;
private AcQueryService ac;
private Map<String, AcBotIntentHandler> mapping;
public AcBotIntents() {
try {
conn = AcConnection.getDefaultConnection();
ac = new AcQueryService(conn);
} catch (AcException e) {
log.error("Could not initialise connection to AC!", e);
}
mapping = new HashMap<>();
mapping.put(LAST_PRICE, new AcBotLastPriceIntentHandler(ac));
}
public AcBotIntentHandler getHandlerFor(GoogleCloudDialogflowV2WebhookRequest request) {
String intent = request.getQueryResult().getIntent().getDisplayName();
return mapping.getOrDefault(intent, new AcBotDefaultIntentHandler());
}
}

インテントからハンドラーへのマッピングを構築し(インテントを覚えていますLast priceか?)、リクエストオブジェクトにエンコードされたインテントを指定して適切なハンドラーを取得するメソッドを実装します。また、実際のハンドラーが見つからなかった場合に備えて、デフォルトのハンドラーを定義します。

public class AcBotDefaultIntentHandler implements AcBotIntentHandler{
@Override
public GoogleCloudDialogflowV2WebhookResponse handleIntent(GoogleCloudDialogflowV2WebhookRequest request) {
GoogleCloudDialogflowV2WebhookResponse response = new GoogleCloudDialogflowV2WebhookResponse();
response.setFulfillmentText("I am not sure I understand. Sorry.");
return response;
}
}

さて、与えられた株の最後の価格を取得するために、見てみましょうAcBotLastPriceIntentHandler

public class AcBotLastPriceIntentHandler implements AcBotIntentHandler {
private final AcQueryService ac;
private final String TREE = "CONSOLIDATION_C0";
private final String CLOSE = "CLOSE";
public AcBotLastPriceIntentHandler(AcQueryService ac) {
this.ac = ac;
}
@Override
public GoogleCloudDialogflowV2WebhookResponse handleIntent(GoogleCloudDialogflowV2WebhookRequest request) {
GoogleCloudDialogflowV2WebhookResponse response = new GoogleCloudDialogflowV2WebhookResponse();
String stock = (String) request.getQueryResult().getParameters().getOrDefault("Stock", "");
try {
response.setFulfillmentText(getLastPriceMessageForStock(stock));
} catch (AcException e) {
response.setFulfillmentText("I am afraid something went wrong requesting the data from AC. Sorry.");
}
return response;
}
// see below
}

を使用してリクエストオブジェクトからStockエンティティパラメータを取得しrequest.getQueryResult().getParameters().getOrDefault("Stock", "")、次にgetLastPriceMessageForStock示すように呼び出す方法を確認します。

private String getLastPriceMessageForStock(String stock) throws AcException {
if (stock.isEmpty()) {
return "I am not sure which stock you mean. Sorry.";
}
List<Ado> ados = ac.adoBrowse(String.format("symbol like 'C0.EQY%%' and attribute('C0#SA001', now) = '%s'", stock));
if (ados.isEmpty()) {
return String.format("I am afraid we don't have any data for '%s'. Sorry.", stock);
} else {
return ados.stream().map(ado -> this.getLastPriceMessageForAdo(stock, ado)).collect(Collectors.joining("\n"));
}
}

私たちは、在庫の欠測値から身を守ります。それ以外の場合は、資産管理システムを参照して、指定されたティッカーの株式を探します。そのようなADOがない場合は、対応するメッセージを表示します。それ以外の場合は、見つかった各ADOの価格データを引き続き取得します(これは単一のADOである必要がありますが、実装は必要に応じて複数のメッセージを返すだけです)。

最終価格を見つける方法は次のとおりです。

private String getLastPriceMessageForAdo(String stock, Ado ado) {
try {
return ado.loadTimeseries(TREE, attributes(CLOSE)).getLast().map(
lastRecord ->
String.format("The last price for %s (%s) is from %s at $%.2f.",
stock, ado.getId(), lastRecord.getDateStr("yyyy-MM-dd"), lastRecord.get(0).toDouble())
).orElse(String.format("Could not retrieve a last price for %s (%s). Sorry.", stock, ado.getId()));
} catch (AcException e) {
return String.format("An error occurred for %s!", ado.getId());
}
}

ADOの時系列をロードし、最後のレコードを取得して、存在する場合は、として読み取られるメッセージを作成しますThe last price for AAPL (C0.EQY.100101) is from 2020-06-12 at $338.00。そしてまた、私たちは価格を見つけることができない場合から身を守ります。

Spring Bootを使用してこれを起動し、RESTサービスをlocalhost:8080 / ac-botで実行できます。次は何?

このサービスをどこにも展開していないので、明らかに少し不正行為をしているので、外部から利用できるようにするために、ngrokを使用してトンネルを設定します。

$> ngrok http 8080
grok by @inconshreveable (Ctrl+C to quit)
Session Status online
Session Expires 7 hours, 59 minutes
Version 2.3.35
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://3cb86ef0e935.ngrok.io -> http://localhost:8080
Forwarding https://3cb86ef0e935.ngrok.io -> http://localhost:8080
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
view raw ngrok.sh hosted with ❤ by GitHub

ステップ5:独自のフルフィルメントWebhookを設定する

次に、DialogflowにRESTサービスの場所を通知する必要があるため、そこに戻って左側のメニューから[フルフィルメント]をクリックします。

次にhttps://...、ngrokで指定されたアドレスを入力/ac-botし、最後にを追加してください。

わかりました、真実の瞬間。右上隅に「」とTry it now入力できWhat is the last price for AAPL?ます。すべてが正常に機能している場合は、次の回答が得られます。

これはエキサイティングです!

ステップ6:チャットボットをSlackと統合する

これで、Dialogflowコンソールを使用してボットを操作し続けることはしません。代わりに、Slackと統合します。したがって、左側のメニューから[統合]を選択し、Slackを見つけて、テストボットを開始します。

次に、Slackに移動して、ボットとのチャットを開始します。

次に、このサイクルを繰り返して、記事の冒頭で概説した他の質問/意図を実装できます。

閉会の言葉

楽しかったと思いました。そして、私も思ったより簡単です。もちろん、私はいくつかのショートカットを取りました:

  • サービスをローカルでのみ実行します。
  • セキュリティ、アクセス制御、および資産管理環境の選択については考慮されていません。
  • ボットの使用は非常に限られています。

この記事を楽しんでいただけたでしょうか。あなたの考えを共有したり、質問をしたりするために連絡を取ってください。私たちは助けるためにここにいます。