概要
ChatOpsとはChatによってシステム運用を行う仕組みを指す。
Chatの相手先は人ではなく、ChatBotを使用する。 ChatBotを使ってシステム運用を自動化することにより、システム運用の効率化、コスト削減、レスポンス向上の実現が可能だ。例えばシステムの状態を監視し、負荷が高い場合に自動的にChatBotからシステム運用者にチャットで状況報告するとともに、クラウドのCPUの追加、メモリの追加などの対応の選択肢をChatBotがシステム運用者とのチャットにより聞き出し、ChatBotがCPUの追加、メモリの追加を自動的に行ったり、ユーザーからのシステムの利用状況の問い合わせに対して、ChatBotが利用状況の調査結果をユーザーに回答したりといたことが実現できる。
ServiceNowにはチャット機能が用意されているが、ChatOpsのための専用の仕組みはKINGSTONEではまだ用意されていない。
そこで、IBMのWatson Assistantと連携することにより、ChatOpsを実現する。
既に、ServiceNowでボットチャットを作成する方法、Watson Assistantで対話(Dialog)を作成する方法、ServiceNowのOutbound IntegrationsのREST Messageを使ってWatson AssistantのMessage APIを起動する方法についてはこちらで解説済みだ。
今回は、システム運用者がユーザーまたは部門の追加をChatBotに依頼して、ChatBotがユーザーまたは部門のアカウント追加を行う仕組みを作ってみた。
参考
ServiceNowでチャットボットを作成する(オーム返し版)
Watson Assistantでユーザーまたは部門を追加するチャットボットを作成する
ServiceNowのOutbound REST MessageでWatson Assistant APIを起動する
環境
ServiceNow : KINGSTONE
Watson Assistant API : 2018-02-16
前提条件
上記参考資料で作成したWatson AssistantのWorkspace、ServiceNowのOutbound IntegrationsのREST Messsage、オーム返しボット用のBusiness Rule、Script Includeを作成済みであること。
手順
コンテキスト保持テーブルの作成
Watson AssistantのMessage APIはユーザー入力のメッセージを送信すると、Watson Assistantの応答メッセージと共に、対話を持続するためのコンテキスト情報が一緒に応答メッセージとして返ってくる。次回以降のユーザーのメッセージ送信時は、このコンテキスト情報をつけて送信する必要がある。このコンテキスト情報を保持するために、ユーザーのsysIDをキーとしてコンテキストを保持するテーブルを作成する。
属性 | 値 |
---|---|
Label | watson_conversation |
Name | x_211750_angular_a_watson_conversation |
NameはLabelの入力値を元にシステムによって自動生成される。
後ほど、GlideRecordでテーブルを開く場合は、ここで生成されたNameを使用するので控えておく。
また、テーブルに下記の2つの列を追加する。
Column Label | Column Name | 型 | 最大長 |
---|---|---|---|
Profile | profile | String(Full UTF-8) | 128 |
Context | context | String(Full UTF-8) | 4000 |
Context列は大きめに確保している。
Script Includeの作成
参考資料で作成したScript Includeに関数を追加し下記のScriptを設定する。
var LiveChatUtility = Class.create();
LiveChatUtility.prototype = {
initialize: function() {
},
getProfileFromName:function(name) {
var gr = new GlideRecord('live_profile');
gr.addQuery('name', name);
gr.query();
if(gr.next()) {
return gr.getUniqueValue();
}
},
isLiveGroupMember:function(group, profile) {
var gr = new GlideRecord('live_group_member');
gr.addQuery('group', group);
gr.addQuery('member.name', profile);
gr.query();
return gr.next();
},
hasContext:function(profile) {
var gr = new GlideRecord('x_211750_angular_a_watson_conversation');
gr.addQuery('profile', profile);
gr.query();
return gr.next();
},
getContext:function(profile) {
var gr = new GlideRecord('x_211750_angular_a_watson_conversation');
gr.addQuery('profile', profile);
gr.query();
if (gr.next()) {
return gr.getValue('context');
}
},
insertContext:function(profile, context) {
gs.info('context追加処理開始');
gs.info('insert profile = ' + profile);
gs.info('insert context = ' + context);
try {
// 追加
var gr = new GlideRecord('x_211750_angular_a_watson_conversation');
gr.initialize();
gr.setValue('profile', profile);
gr.setValue('context', context);
gr.insert();
}
catch(ex) {
gs.error(ex.getMessage());
}
},
updateContext:function(profile, context) {
gs.info('context 更新処理開始');
try {
// 更新
var gr = new GlideRecord('x_211750_angular_a_watson_conversation');
gr.addQuery('profile', profile);
gr.query();
if (gr.next()) {
gr.setValue('context', context);
gr.update();
}
}
catch(ex) {
gs.error(ex.getMessage());
}
},
addUserOrGroup:function(context) {
var addtarget = context.addtarget;
var objectname = context.objectname;
gs.info('addUserOrGroup:addtarget = ' + addtarget);
gs.info('addUserOrGroup:objectname = ' + objectname);
if ('user' == addtarget) {
this.addUser(objectname);
} else if ('group' == addtarget) {
this.addGroup(objectname);
}
},
addUser:function(username) {
var gr = new GlideRecord('sys_user');
gr.initialize();
gr.first_name = username;
gr.name = username;
gr.insert();
},
addGroup:function(groupname) {
var gr = new GlideRecord('sys_user_group');
gr.initialize();
gr.name = groupname;
gr.insert();
},
type: 'LiveChatUtility'
};
今回追加した関数を説明していく。
hasContext:function(profile)
引数で指定されたユーザープロファイル(ユーザーのsysID)をキーとするレコードが「watson_conversation」テーブルに存在すればtrueを返却する。
getContext:function(profile)
引数で指定されたユーザープロファイル(ユーザーのsysID)をキーとするwatson_conversationテーブルのレコードのコンテキスト情報を返す。
insertContext:function(profile, context)
引数で指定されたユーザープロファイル(ユーザーのsysID)とコンテキストのレコードをwatson_conversationに追加する。
updateContext:function(profile, context)
引数で指定されたユーザープロファイル(ユーザーのsysID)をキーとするwatson_conversationテーブルのレコードのコンテキスト情報を引数のcontextで更新する。
addUserOrGroup:function(context)
このコードは今回の最も重要な部分だ。
var addtarget = context.addtarget;
var objectname = context.objectname;
このコードでcontextからaddtargetとobjectnameを取得している。
このaddtargetとobjectnameをどこで設定しているかと言うと、Watson AssistantのDialogの設定だ。参考資料で設定内容を確認して欲しい。
addUser、addGroupは、それぞれsys_userテーブル、sys_user_groupテーブルにユーザー、部門を追加する。なお、それぞれのテーブルはスクリプトからのレコードCreateの許可をオンにしておく必要がある。
Dialogの設定でWatson Assistantが認識したIntentとEntityを元にコンテキストに値を設定している。このコンテキストの値を元にユーザーまたは部門の追加をスクリプトで実施する。
Business Rule(チャット自動応答スクリプト)の拡張
(function executeRule(current, previous /*null when async*/) {
var userDislayName = gs.getUserDisplayName();
var chatUtil = new LiveChatUtility();
var currentUserProfile = chatUtil.getProfileFromName(userDislayName);
var profilefromrecord = current.getValue('profile');
if (currentUserProfile != profilefromrecord) {
gs.info('プロファイルが現在のユーザーで無いため処理を抜けた。');
return;
}
// Live Messageのgroupidを取得
var convId = current.getValue('group');
var CHATBOTUSERNAME = 'Service Desk';
// BOTのprofileを取得
var botprofile = chatUtil.getProfileFromName(CHATBOTUSERNAME);
if (chatUtil.isLiveGroupMember(convId, CHATBOTUSERNAME) == false) {
gs.info('現在のチャットグループにボットユーザーが含まれないために処理を抜けた。');
return;
}
gs.info('チャットボット 自動応答処理開始...');
var message = current.getValue('message');
var context = '{}';
var hasContext = false;
if (chatUtil.hasContext(currentUserProfile) == true) {
hasContext = true;
context = chatUtil.getContext(currentUserProfile);
}
var watson = new WatsonAssistantApi();
var response = watson.send(message, context);
var lm = new global.LiveFeedMessage();
var data = {
"message": response.output.text,
"group_id": convId,
"from_profile":botprofile
};
lm.postMessage(data);
chatUtil.addUserOrGroup(response.context);
var parser = new global.JSON();
var contextstring = parser.encode(response.context);
if (hasContext == true) {
chatUtil.updateContext(currentUserProfile, contextstring);
} else {
chatUtil.insertContext(currentUserProfile, contextstring);
}
gs.info('自動応答処理完了.');
})(current, previous);
今回はコンテキストをRest MESSAGEの応答から取得し、「watson_conversation」テーブルにユーザープロファイル(sysID)をキーとして保存または取得する処理と、
Watson AssistantのRest MESSAGEにユーザーの入力メッセージとコンテキストを設定して送信処理を追加している。
var watson = new WatsonAssistantApi();
var response = watson.send(message, context);
これは参考資料で作成したOutbound IntegrationsのREST Messageを起動するためのScript Includeだ。次の節で説明する。
REST Message起動用のScript Includeの作成
「WatsonAssistantApi」の名前でScript Includeでを作成しScriptに下記を入力する。
var WatsonAssistantApi = Class.create();
WatsonAssistantApi.prototype = {
initialize: function() {
},
send:function(message, context){
gs.info('Watson Assistant起動処理開始...');
try {
var r = new sn_ws.RESTMessageV2('x_211750_angular_a.WatsonAssistant', 'Message');
gs.info('message = ' + message);
gs.info('context = ' + context);
r.setStringParameterNoEscape('context', context);
r.setStringParameterNoEscape('message', message);
var response = r.execute();
var responseBody = response.getBody();
var httpStatus = response.getStatusCode();
gs.info('Watson Assistant http status = ' + httpStatus);
gs.info('Watson Assistant responseBody = ' + responseBody);
var parser = new global.JSON();
var parsed = parser.decode(responseBody);
return parsed;
}
catch(ex) {
gs.error(ex.getMessage());
}
},
type: 'WatsonAssistantApi'
};
動作確認
ServiceNowのブラウザに「chat」と入力してエンターでチャットを起動する。
Service Desk(チャットボット用ユーザー)に対してチャットを開始し、下記の通り入力する。
sys_userテーブルを開いて、userhogeが追加されていることが確認できる。
コンテキスト保持テーブル(watson_conversation)の確認
コンテキストの内容は下記の通り。
{
"addtarget": "group",
"conversation_id": "a8a957b2-6bfd-4df2-881e-2a73f9e8c7c2",
"objectname": "g1010101",
"system": {
"_node_output_map": {
"node_11_1524953286671": [
0
],
"node_4_1524918503718": [
0
],
"node_5_1524918589117": [
0
],
"node_5_1524951184739": [
0,
0
]
},
"branch_exited": true,
"branch_exited_reason": "completed",
"dialog_request_counter": 6,
"dialog_stack": [
{
"dialog_node": "root"
}
],
"dialog_turn_counter": 6
}
}
Watson Assistantの設定が不十分な点について
Watson Assistantの設定が不十分なため、「ユーザー名を入力してください」の後に、「追加」を入力すると「追加」ユーザーが追加されるという変な動作となる。
今回はWatson AssistantとServiceNowのチャットを連携する方法の説明に主眼を置いているため、Watson Assistantの設定については、不十分なままでとりあえずは良しとしている。実際に実用にするにはさらに複雑な設定が必要になるだろう。
最後に
Watson Assistantを使ったチャットボット機能は、この他にもユーザーに問い合わせに関する回答実施といった使い方や、またユーザーの問い合わせについて、チャットボット自身が回答を見つけれない場合は、サポートデスク担当者に対してチャットボットが会話を開始し、サポートデスク担当者から回答を得て、ユーザーに回答するといった使い方も考えられる。