(2019/06/12) 当記事にあるpromisifyを使う方法はWatson SDK V3までの古い情報です。2019/3月に出たSDK V4ではもっと簡単になったので、最新のV4を使う予定の方は記事 「 小ネタ: Watson SDK V4(Node.js)では、もっと楽にコールバック(やPromise)を意識せずAPIを呼び出せるようになりました」のほうをご参照くださいませ。
たとえば「Assistantの応答の確信度が基準値以下の場合は、Discoveryでロングテールの検索を行って回答を提示する」など複数のWatson APIを同期的に/シーケンシャルに呼び出したい場合があります。
釈迦説ですがNode.js環境は非同期実行の環境なので、こういうことをしたい場合のコードではコールバックとかPromiseを使う必要があります。でもPromiseって直感的でなくて、なんか面倒だなあと思ってました1。で、StackOverflowで以下のエントリーを発見しました。
Using Async/Await in WATSON Nodejs SDK
How can I promisify Watson Assistant functions to allow async/await in node?
要は**Node.jsのV8からはpromisifyが標準で搭載されて簡単に非同期を意識しないコードが書ける2そうですが、この機構がWatson APIでも利用できます。**例えば以下の感じでWatsonAssistantのインスタンスの”message”APIをpromisifyでくるんでawait付きで呼び出すと、Watson側で処理が完了するまで制御は待ち状態になり、完了してからプログラムに制御が戻る=同期的なコーディングができます。(非同期のややこしいコードが不要になります)
const messagePromise = util.promisify(assistant.message);
var response = await messagePromise.call(assistant, params);
以下は「Assistantに問いかけて確信度が基準値を下回ったらDiscoveryに改めて問いかける」シナリオの簡単なサンプルですが、同期的な=わかりやすいコードが実現できているのが見て取れると思います。3
'use strict';
/*
Watson Assistant & Watson Discoveryをpromisifyを使って逐次実行
*/
/* ibmcloud上のnode.jsで実行するならここをコメントアウト
const express = require('express');
const app = express();
const port = process.env.PORT || 8080;
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(port, () => console.log('Example app listening on port %d', port));
*/
const util = require('util');
console.log('==== start=======');
const assistant_iam_apikey ='xxxxxxxxxxxxxx';
const assistant_url ='https://gateway-syd.watsonplatform.net/assistant/api';
const assistant_workspace_id = 'xxxxxxxxxxxx';
const discovery_iam_apikey ='xxxxxxxxxxxxxx';
const dicovery_iam_url ='https://gateway-syd.watsonplatform.net/discovery/api';
const discovery_collection_id ='xxxxxxxxxxxxxx';
const discovery_configuration_id ='xxxxxxxxxxxxxx';
const discovery_environment_id = 'xxxxxxxxxxxxxx';
var AssistantV1 = require('watson-developer-cloud/assistant/v1');
var assistant = new AssistantV1({
version: '2018-09-20',
iam_apikey: assistant_iam_apikey,
url: assistant_url
});
var DiscoveryV1 = require('watson-developer-cloud/discovery/v1');
const discovery = new DiscoveryV1({
url: dicovery_iam_url,
version: '2018-10-15',
iam_apikey: discovery_iam_apikey
});
// Assistant
async function message(text) {
console.log("message Start:[" + text + "]")
var params = {
input: { text: text },
workspace_id: assistant_workspace_id
};
const messagePromise = util.promisify(assistant.message);
var response = await messagePromise.call(assistant, params);
return response;
}
// Discovery
async function query(text) {
console.log("query Start:[" + text + "]")
var params = {
environment_id: discovery_environment_id,
collection_id: discovery_collection_id,
passages: true,
passages_count: 1,
natural_language_query: text
};
const queryPromise = util.promisify(discovery.query);
var response = await queryPromise.call(discovery, params);
return response;
}
//***********************************************
// Promise/非同期を意識したコードを書かずに済んでます
//***********************************************
async function main() {
var min_confidence = 0.6; // 求める確信度。これを下回ったらDiscoveryに聞きなおす
var text = 'Excelで表の必要な部分だけを印刷するにはどうすればいいでしょうか';
var response = await message(text);
console.log('confidence of Assistant:',
JSON.stringify(response.intents[0].confidence, null, 2));
if (response.intents[0].confidence < min_confidence ){
// Assistantの回答の確信度が所定の基準を下回ったらDiscoveryにて再度検索
var response = await query(text);
console.log('message response from Discovery:',
JSON.stringify(response.passages, null, 2));
} else {
// Assistantの回答の確信度が所定の基準以上ならAssistantの回答を提示
console.log('message response from Assistant:',
JSON.stringify(response.output.text[0], null, 2));
}
}
main()
以下は実行結果例です
==== start=======
message Start:[Excelで表の必要な部分だけを印刷するにはどうすればいいでしょうか]
confidence of Assistant: 0.504277801513672
query Start:[Excelで表の必要な部分だけを印刷するにはどうすればいいでしょうか]
message response from Discovery: [
{
"document_id": "673b0e32529bf4c614c15acaebb47bab",
"passage_score": 40.21290429830693,
"passage_text": "Excelでは、印刷対象を選択して、必要な部分だけを印刷することができます。ここでは
、ご使用のExcelのバージョンに応じた参照先を案内します。\n\n対処方法\n\nExcelで表の必要な部分だけを印
刷する方法については、以下の情報を参照してください。\n※ ご使用のExcelのバージョンに応じた項目をクリ
ックしてください。\n\nExcel 2013の場合\nExcel 2013で表の必要な部分だけを印刷する方法\n\nExcel 2010の
場合\nExcel 2010で表の必要な部分",
"start_offset": 762,
"end_offset": 998,
"field": "text"
}
]
以上です。