13. SubAgent¶
13.1. 概要¶
サブエージェント(SubAgent)は、sraix要素を使用して呼び出す外部のサービスです。 sraixで指定する外部サービスの呼び出し方法を説明します。
外部サービスの呼び出しの方法には、次の4種類があります。
- 汎用RESTインタフェース
- 属性に、”template”を指定した場合、または、属性で、”botName”、”nlu”、”service”が未指定の場合、子要素のhost、header、query、body等を利用して、REST APIとして外部サービスを呼び出します。
- 対話プラットフォームで、公開されているbot呼び出し
- 属性に、”botName”を指定した場合、対話プラットフォームで公開されているbotを呼び出します。
- NLU通信インタフェース
- 属性に、”nlu”を指定した場合、意図解釈エンジン(NLUサーバ)を呼び出します。
- カスタム外部サービス実装
- 属性に、”service”を指定した場合、カスタム実装を行なった処理を利用して、外部サービスを呼び出します。
- 属性
パラメータ | タイプ | 必須 | 説明 |
---|---|---|---|
未指定 | No | 子要素による汎用REST API呼び出し。 | |
template | string | No | 汎用REST通信用のテンプレート名。 |
botName | string | No | 公開Bot通信用のエイリアス名。 |
nlu | string | No | NLU通信用のエイリアス名。 |
service | string | No | カスタム外部サービスのサービス名。 |
default | string | No | 外部呼び出し失敗時の応答文。 |
timeout | string | No | 通信タイムアウト時間(秒単位)を ‘1’ 以上の整数で指定。未指定時は ‘10’。 |
13.2. 共通仕様¶
各外部サービス呼び出しに共通する機能として、通信失敗の検出と要因解析のための機能があります。
13.2.1. 通信失敗時の応答文(default)¶
通信失敗時には、sraixの戻り値として””(空文字)が設定されますが、”default”属性を指定した場合、指定された文字列をsraixの戻り値として返します。
以下は、カスタム外部サービス実装を利用する場合の例です。
<category>
<pattern>botステータスチェック *</pattern>
<template>
<star />のステータスは、<sraix service="someBot" default="通信失敗"><star /></sraix>です。
</template>
</category>
13.2.2. タイムアウト指定(timeout)¶
以下は、カスタム外部サービス実装を利用しタイムアウトが発生した場合の例です。
<category>
<pattern>botステータスチェック *</pattern>
<template>
<star />のステータスは、<sraix service="someBot" timeout="10"><star /></sraix>です。
</template>
</category>
13.2.3. HTTPステータスコードの取得¶
通信失敗の要因には、パラメータの指定異常などを含め各種ありますが、通信処理の結果として、
ローカル変数(var): __SUBAGENT_STATUS_CODE__
に、HTTPステータスコードの値が文字列として設定されます。
- 取得失敗 : 通信処理が行われなかった。
- 000 : 通信要求に問題があり通信が行われなかった、または、通信結果として、ステータスコードが取得できなかった。
- 001 : 通信タイムアウトが発生した。
- 200 : 通信が正常に行われた。
- その他 : 通信結果として、異常を示すステータスコードが通知された。
以下は、REST通信のhostに接続できないURLを指定した場合の例です。
<category>
<pattern>不正RESTサーバ指定</pattern>
<template>
<think>
<sraix><host>https://otherhost.com:5000</host><body>data</body></sraix>
</think>
ステータスコードは、<get var="__SUBAGENT_STATUS_CODE__" />です
</template>
</category>
13.2.4. レイテンシの取得¶
__SUBAGENT_LATENCY__
に、送受信時間を秒単位の小数点付き数値で設定します。以下は、REST通信のhostに通信できないURLを指定してタイムアウトが発生した場合の例です。
<category>
<pattern>通信タイムアウト</pattern>
<template>
<think>
<sraix timeout="1"><host>https://anyhost.com</host><body>data</body></sraix>
</think>
通信時間は、<get var="__SUBAGENT_LATENCY__" />秒です
</template>
</category>
13.3. 汎用RESTインタフェース¶
sraixの属性に”template”を指定した場合、または、属性で、”botName”、”nlu”、”service”が未指定の場合、 子要素のhost, header, query, body等を利用して、REST APIとして外部サービスを呼び出します。
13.3.1. 送信¶
- 子要素
パラメータ | タイプ | 必須 | 説明 |
---|---|---|---|
host | string | No | 接続先のURLを指定します。属性に’template’を指定しない場合は必須です。 |
method | string | No | HTTPのメソッド。未指定時はGETで、その他、POST/PUT/DELETE/PATCHに対応します。 |
query | string | No | queryとして指定するパラメータを連想配列で指定します。 |
header | string | No | ヘッダに指定するキーと値を連想配列で指定します。 |
body | string | No | ボディに設定する内容を指定します。 |
exception-response
で指定できます。)汎用RESTインタフェースでのリクエストは、以下のように指定します。 bodyには文字列を設定します。
<category>
<pattern>XXX</pattern>
<template>
<sraix>
<host>https://www.***.com/ask</host>
<method>POST</method>
<query>"userid":"1234567890","q":"question"</query>
<header>"Authorization":"yyyyyyyyyyyyyyyyy","Content-Type":"application/json;charset=UTF-8"</header>
<body>{"question": "Ask this question"}</body>
</sraix>
</template>
</category>
送信内容
POST /ask?userid=1234567890&q=question HTTP/1.1
Host: www.***.com
Content-Type: application/json;charset=UTF-8
Authorization: yyyyyyyyyyyyyyyyy
{
"question": "Ask this question"
}
対話APIで指定された metadata
をボディに指定する場合は、jsonタグで __USER_METADATA__
を取得し、子要素”body”に設定します。
<category>
<pattern>XXX</pattern>
<template>
<sraix>
<host>https://otherhost.com/ask</host>
<method>POST</method>
<query>"userid":"1234567890","q":"question"</query>
<header>"Authorization":"yyyyyyyyyyyyyyyyy","Content-Type":"application/json;charset=UTF-8"</header>
<body><json var="__USER_METADATA__" /></body>
</sraix>
</template>
</category>
13.3.2. テンプレートを使用した送信¶
rest_templatesファイルで以下の定義を行った場合のテンプレートの使用例を示します。
rest:
テンプレート:
host: 'https://otherhost.com/ask'
method: POST
query: '"item":"1234"'
header: '"Content-Type": "applicaton/json"'
body: '{"key": "Send Data"}'
<category>
<pattern>XXX</pattern>
<template>
<sraix template="テンプレート">
<query></query>
<sraix>
</template>
</category>
送信内容
POST /ask HTTP/1.1
Host: otherhost.com
Content-Type: application/json
{
"key": "Send Data"
}
<category>
<pattern>XXX</pattern>
<template>
<sraix template="テンプレート">
<host>https://otherhost.com/ask</host>
<method></method>
<query>"item": None, "userid":"1234567890"</query>
<header>"Authorization":"yyyyyyyyyyyyyyyyy"</header>
<body>{"key2": "added data"}</body>
<sraix>
</template>
</category>
送信内容
GET /ask?userid=1234567890 HTTP/1.1
Host: otherhost.com
Content-Type: application/json
Authorization: yyyyyyyyyyyyyyyyy
{
"key": "Send Data", "key2": "added data"
}
13.3.3. 受信¶
__SUBAGENT_BODY__
にも展開します。 getで、<get var=”__SUBAGENT_BODY__” />を指定することで、ボディの文字列を取得できます。__SUBAGENT_BODY__
は上書きされるため、必要なレスポンス内容は他の変数に代入してください。sraixの結果と、ローカル変数の格納値は同じ形式なので、以下の2つの記述の結果は同じになります。
<category>
<pattern>XXX</pattern>
<template>
<sraix template="テンプレート" />
</template>
</category>
<category>
<pattern>XXX</pattern>
<template>
<think>
<sraix template="テンプレート" />
</think>
<get var="__SUBAGENT_BODY__" />
</template>
</category>
受信結果のボディ内容がJSONの場合、 json タグでJSON内部のパラメータを取得できます。
{
"transportation": {
"station": {
"departure": "東京",
"arrival": "京都"
},
"time": {
"departure": "2018/11/1 11:00",
"arrival": "2018/11/1 13:30"
}
},
"facility": ["鹿苑寺", "清水寺", "伏見稲荷大社"]
}
というボディ内容の場合、以下の記述で、JSONタグによりボディの内部情報を取得することができます。
<json var="__SUBAGENT_BODY__.transportation.station.departure" /> <!-- 取得結果: 東京 -->
<json var="__SUBAGENT_BODY__.facility" function="len" /> <!-- 取得結果: 3 -->
<json var="__SUBAGENT_BODY__.facility"><index>1</index></json> <!-- 取得結果: 清水寺 -->
13.4. 対話プラットフォームで、公開されているbot呼び出し¶
13.4.1. 送信¶
- 子要素
パラメータ | タイプ | 必須 | 説明 | |
---|---|---|---|---|
userId | string | No | ユーザID。未指定の場合、対話APIの指定値を引き継ぎます。 | |
locale | string | No | ロケール。言語指定ISO-639 言語コードとISO-3166 国コードをハイフン繋いだ組み合わせを指定します。 | |
time | string | No | 時間情報。ISO8601(RFC3339)形式で指定します。 | |
topic | string | No | トピックID。topic情報を変更する場合にのみ指定します。 | |
deleteVariable | boolean | No | タスク変数削除指定。’true’が指定された場合のみに送信します。 | |
metadata | string | No | メタデータ。Text形式、または、JSON形式で指定します。 | |
config | No | コンフィグ指定。JSON形式で指定します。 | ||
logLevel | string | No | ログレベル。’none’,’error’,’warning’,’info’,’debug’のいずれかを指定します。 |
exception-response
で指定できます。)以下の例は、userId, topic、deleteVariable、metadata、configをシナリオで指定し、locale、timeは、指定しない場合の例です。 尚、bot_names ファイルでは、URLとapikeyのみを指定した場合になります。
bot:
someBot:
url: https://somebot.com/bots/botId_1/ask
apikey: test_apikey
<category>
<pattern>botステータスチェック *</pattern>
<template>
<think>
<json var="askSubagent.郵便番号">222-0033</json>
<json var="config.logLevel">debug</json>
</think>
<sraix botName="someBot">
<star />
<userId>someUser</userId>
<topic>test</topic>
<deleteVariable>true</deleteVariable>
<metadata><json var="askSubagent"/></metadata>
<config><json var="config"/></config>
</sraix>
</template>
</category>
送信内容
POST /bots/botId_1/ask HTTP/1.1
Host: somebot.com
Content-Type: application/json;charset=UTF-8
x-api-key: test_apikey
{
"userId": "someUser",
"topic": "test",
"deleteVariable": true,
"metadata": {"郵便番号": "222-0033"},
"config": {"logLevel": "debug"},
"utterance": "郵便番号検索"
}
13.4.2. bot_namesでパラメータを設定した場合の送信¶
bot_namesファイルで以下の定義を行った場合の送信例を示します。
bot:
someBot:
url: https://somebot.com/bots/botId_1/ask
locale: ja-JP
time: 2018-07-01T12:18:45+09:00
topic: test
deleteVariable: false
config: '("loglevel": "info"}'
metadata: Send Data
__USER_USERID__
の値を使用します。)<category>
<pattern>XXX</pattern>
<template>
<sraix botName="someBot">
<userId>someUser</userId>
<time></time>
<topic></topic>
こんにちは
<sraix>
</template>
</category>
送信内容
POST /bots/botId_1/ask HTTP/1.1
Host: somebot.com
Content-Type: application/json;charset=UTF-8
x-api-key:
{
"userId": "someUser",
"locale": "ja-JP",
"config": {"logLevel": "info"},
"utterance": "こんにちは",
"metadata": "Send Data"
}
metadata: '{"key1":"value1", "key2": "value2", "key3": "value3"}'
<category>
<pattern>XXX</pattern>
<template>
<sraix botName="someBot">
<userId>someUser</userId>
<topic>test</topic>
<deleteVariable>true</deleteVariable>
<metadata>{"key1": null, "key2": {"modify": "data"}, "key4": "added"}</metadata>
こんにちは
</sraix>
</template>
</category>
送信内容
POST /bots/botId_1/ask HTTP/1.1
Host: somebot.com
Content-Type: application/json;charset=UTF-8
x-api-key:
{
"userId": "someUser",
"locale": "ja-JP",
"time": "2018-07-01T12:18:45+09:00",
"topic": "test",
"deleteVariable": true,
"config": {"logLevel": "info"},
"utterance": "こんにちは",
"metadata": {"key2":{"modify": "data"}, "key3": "value3", "key4": "added"}
}
13.4.3. 受信¶
以下のシナリオで、
<category>
<pattern>*</pattern>
<template>
<sraix botName="someBot"><star /></sraix>
</template>
</category>
公開Bot:someBotからの受信データが
HTTP/1.1 200 Ok
Content-Type: application/json;charset=UTF-8
{
"response": "こんにちは、今日もいい天気ですね。",
"topic": "greeting"
}
だった場合、結果は、
になります。
__SUBAGENT_EXTBOT__.エイリアス名
にJSON形式で展開され、jsonタグで取得することができます。__SUBAGENT_EXTBOT__
は上書きされるため、必要なレスポンス内容は他の変数に代入してください。<category>
<pattern>*</pattern>
<template>
<think>
<sraix botName="someBot"><star /></sraix>
</think>
<json var="__SUBAGENT_EXTBOT__.someBot" />
</template>
</category>
getタグで取得する場合には、変数名のみを指定する必要があり、第一階層のキーとしてエイリアス名が設定されたJSON全体を取得することになります。 前述のjsonタグをgetタグに変更すると、以下の様になります。
<get var="__SUBAGENT_EXTBOT__" />
公開Botからの受信ボディの内容はJSONのため、 json タグでJSON内部のパラメータを取得できます。
また、metadata
の内容がJSONである場合、JSONタグで metadata
内のパラメータも取得できます。
公開Botからのボディの内容が、
{
"utterance": "こんにちは",
"response": "こんにちは、今日もいい天気ですね。",
"topic": "greeting"
"metadata":{"broadcaster":"OBS","title":"午後のニュース"}
}
の場合、
<json var="__SUBAGENT_EXTBOT__.someBot.response" /> <!-- 取得結果: こんにちは、今日もいい天気ですね。 -->
<json var="__SUBAGENT_EXTBOT__.someBot.utterance" /> <!-- 取得結果: こんにちは -->
<json var="__SUBAGENT_EXTBOT__.someBot.topic" /> <!-- 取得結果: greeting -->
<json var="__SUBAGENT_EXTBOT__.someBot.metadata" /> <!-- 取得結果: {"broadcaster":"OBS","title":"午後のニュース"} -->
<json var="__SUBAGENT_EXTBOT__.someBot.metadata.broadcaster" /> <!-- 取得結果: OBS -->
<json var="__SUBAGENT_EXTBOT__.someBot.metadata.title" /> <!-- 取得結果: 午後のニュース -->
として、公開botからの戻り値、及び、metadataの情報を取得することができます。
__SUBAGENT_EXTBOT__.エイリアス名
に格納します。13.5. NLU通信インタフェース¶
以降の説明では、NLUサーバから以下のJSON形式のデータが返却された例として説明します。
{
"intents": [
{"intent": "transportation", "score": 0.9}
],
"slots": [
{"slot": "departure", "entity": "東京", "score": 0.85}
]
}
また、nlu_serversの定義は以下のもの使用するものとします。
servers:
someNlu:
url: https://***.com/run
apikey: test_key
13.5.1. 送信¶
<category>
<pattern>nlu通信 *</pattern>
<template>
<sraix nlu="someNlu"><star /></sraix>
</template>
</category>
送信内容
POST /run HTTP/1.1
Host: someNlu.com
Content-Type: application/json;charset=UTF-8
x-api-key: test_key
{
"utterance": "お出かけは"
}
13.5.2. 受信¶
__SUBAGENT_NLU__.エイリアス名
にJSON形式で展開され、jsonタグで取得することができます。__SUBAGENT_NLU__
は上書きされるため、必要なレスポンス内容は他の変数に代入してください。<category>
<pattern>*</pattern>
<template>
<think>
<sraix nlu="someNlu"><star /></sraix>
</think>
<json var="__SUBAGENT_NLU__.someNlu" />
</template>
</category>
getタグで取得する場合には、変数名のみを指定する必要があり、第一階層のキーとしてエイリアス名が設定されたJSON全体を取得することになります。 前述のjsonタグをgetタグに変更すると、以下の様になります。
<get var="__SUBAGENT_NLU__" />
NLUからの受信ボディの内容はJSONのため、 json タグでJSON内部のパラメータを取得できます。
<json var="__SUBAGENT_NLU__.someNlu.intents" /> <!-- 取得結果: [{"intent": "transportation", "score": 0.9}] -->
<json var="__SUBAGENT_NLU__.someNlu.intents" index="0" /> <!-- 取得結果: {"intent": "transportation", "score": 0.9} -->
<json var="__SUBAGENT_NLU__.someNlu.slots" /> <!-- 取得結果: [{"slot": "departure", "entity": "東京", "score": 0.85}] -->
<json var="__SUBAGENT_NLU__.someNlu.slots" index="0" /> <!-- 取得結果: {"slot": "departure", "entity": "東京", "score": 0.85} -->
__SUBAGENT_NLU__.エイリアス名
に格納します。13.5.3. nluintent / nluslotでのデータ取得¶
NLUサーバからの受信したデータを、template要素の nluintent 、nluslot を利用して、
マッチ処理で取得したNLUデータ: __SYSTEM_NLUDATA__
に対する処理と同様の操作を行うことができます。
以下に、NLUサーバから受信したデータに対して、nluintentで内容を取得する例を示します。
<nluintent name="*" item="count" target="__SUBAGENT_NLU__.someNlu" /> <!-- 取得結果: 1 -->
<nluintent name="*" item="intent" index="0" target="__SUBAGENT_NLU__.someNlu" /> <!-- 取得結果: transportation -->
<nluintent name="transportation" item="score" target="__SUBAGENT_NLU__.someNlu" /> <!-- 取得結果: 0.9 -->
同様に、nluslotで内容を取得する例は以下の様になります。
<nluslot name="*" item="count" target="__SUBAGENT_NLU__.someNlu" /> <!-- 取得結果: 1 -->
<nluslot name="*" item="slot" index="0" target="__SUBAGENT_NLU__.someNlu" /> <!-- 取得結果: departure -->
<nluslot name="departure" item="entity" target="__SUBAGENT_NLU__.someNlu" /> <!-- 取得結果: 東京 -->
尚、 nluintent 、nluslot で、JSON型の変数名(区切り文字:’.’ を利用)を指定できるのは、
ローカル変数(var) の __SUBAGENT_NLU__.xxx
の形式の場合のみです。
13.6. カスタム外部サービス実装¶
属性に、”service”を指定した場合、カスタム実装を行なった処理を利用して、外部サービスを呼び出すことができます。 カスタム外部サービスは、利用するサービス(SubAgent)毎に実装が必要な呼び出し方法があるため、以下の基底クラスを継承して個別に実装します。
programy.services.service.Service
処理クラスの実装では、基底クラスを継承したクラスを作成し、ask_question()関数として、発話データに相当する”question”引数を利用して、結果の文字列を返す処理を実装します。 外部サービスとの連携を行う場合、ask_question()内に、REST通信機能を実装することになります。
from programy.services.service import Service
class StatusCheck(Service):
__metaclass__ = ABCMeta
def __init__(self, config: BrainServiceConfiguration):
self._config = config
@property
def configuration(self):
return self._config
def load_additional_config(self, service_config):
pass
@abstractmethod
def def ask_question(self, client_context, question: str):
return "OK"
次に、Brainコンフィグレーションの services に、次のようにカスタム外部サービスのエントリを追加することで、sraixのサービス名として利用できるようになります。
myService:
classname: programy.services.myService.StatusCheck
url: https://myService.com/api/statuscheck
AIMLで利用する場合には、以下の例のように、sraixの属性”service”に、カスタム外部サービスのエントリ名を指定します。 sraixのカスタム外部サービスの処理としては、カスタム外部サービスのエントリのclassnameで定義されたクラスをロードし、関数:ask_question()が呼び出します。 関数:ask_question()の戻り値が、sraixの結果になります。
<category>
<pattern>ステータスチェック *</pattern>
<template>
<star />のステータスは、<sraix service="myService"><star /></sraix>です。
</template>
</category>
13.6.1. カスタム外部サービスへの引数および戻り値¶
13.6.1.1. 引数¶
sraix service=”myService”がカスタム外部サービス呼び出しで、sraix要素内を引数として扱います。 引数の定義は個々のカスタム外部サービスの引数I/Fに依存しており、個々のサービスに合わせ実装を行う必要があります。
<aiml>
<!-- sub agent execute -->
<category>
<pattern>subagent *</pattern>
<template>
<set var="text">
<sraix service="myService">
<star />
<space />
<json var="__USER_METADATA__.arg1" />
<space />
<json var="__USER_METADATA__.arg2" />
<space />
<json var="__USER_METADATA__.arg3" />
</sraix>
</set>
<think>
<set name="departure"><json var="__SUBAGENT__.myService.transportation.station.departure" /></set>
<set name="arrival"><json var="__SUBAGENT__.myService.transportation.station.arrival" /></set>
</think>
<get name="departure">から<get name="arrival">までを検索します。
</template>
</category>
</aiml>
13.6.1.2. 戻り値¶
__SUBAGENT__.サービス名
に展開されます。__SUBAGENT__
は上書きされるため、必要なレスポンス内容は他の変数に代入してください。変数に格納される形式は、テキスト、または、JSON形式となり、カスタム実装でもバイナリは使用できません。
以下の例では、myServiceに対する処理の戻り値が __SUBAGENT__.myService
に展開されていますが、その内容がJSON形式で、
{
"transportation": {
"station": {
"departure" :"東京",
"arrival" : "京都"
},
"time": {
"departure": "2018/11/1 11:00",
"arrival": "2018/11/1 13:30"
},
"facility": ["鹿苑寺", "清水寺", "伏見稲荷大社"]
}
}
の場合、
<json var="__SUBAGENT__.myService.transportation.station.departure" />
<json var="__SUBAGENT__.myService.transportation.station.arrival" />
として、jsonタグを利用して、ボディの内部情報を取得することができます。
__SUBAGENT__.myService
を変数名として、getタグで取得することになります。<get var="__SUBAGENT__.myService />