LoginSignup
3
5

More than 5 years have passed since last update.

ServiceNowでWatson Assistantを使ってChatOpsを実現する

Last updated at Posted at 2018-05-02

概要

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(チャットボット用ユーザー)に対してチャットを開始し、下記の通り入力する。

adduserdialog.png

sys_userテーブルを開いて、userhogeが追加されていることが確認できる。

userhoge.png

コンテキスト保持テーブル(watson_conversation)の確認

watson_conversation_record.png

コンテキストの内容は下記の通り。


{
    "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を使ったチャットボット機能は、この他にもユーザーに問い合わせに関する回答実施といった使い方や、またユーザーの問い合わせについて、チャットボット自身が回答を見つけれない場合は、サポートデスク担当者に対してチャットボットが会話を開始し、サポートデスク担当者から回答を得て、ユーザーに回答するといった使い方も考えられる。

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5