Bluemix Node-REDを使ってWatson APIを呼び出す

# はじめに
以前、[BluemixのWatson APIを駆使して日本語質問応答システムを作る～その2～](http://qiita.com/VegaSato/items/1fa437667815a20a5624)で作った質問応答アプリを、Node-REDで作り直してみたが、Node-REDからWatson APIを呼ぶには一工夫必要なので、そこを整理してみる。
~~Speech to Textの組み込みはどうしたの？という意見は受け付けない。~~

# NLCノードの使い方
NLCを呼ぶ場合は、IBM_WatsonのNLCノードを使う。
フローエディターの左側のIBM_Watsonグループ(下の方)にNLCノードがある。
![NLCnode_00.png](https://qiita-image-store.s3.amazonaws.com/0/102519/b1f203e1-cb3d-9485-55ce-fb96933ddaf4.png)


これをドラッグ&ドロップする。
![NLCnode_01.png](https://qiita-image-store.s3.amazonaws.com/0/102519/9202df3b-cb99-f6f1-0a1f-3f933e49b274.png)

ドロップしたNLCノードをダブルクリックすると設定画面が開く。
![NLCnode_02.png](https://qiita-image-store.s3.amazonaws.com/0/102519/6064f576-5d45-a387-69c4-0be174640edd.png)
Nameはフロー上の名前、UsernameとPasswordはNLCのサービス呼び出しをする際のユーザーIDとパスワード、Classifier IDはクラス分類に使うインスタンスのIDを指定する。
ModeはClassifyとTrainingが選択できるが、ここではトレーニング済みのNLCを使うのでClassifyでいい。

NLCノードは、msg.payloadの値を入力とし、NLCの結果をmsg.payloadに出力してくれる。

## NLCノードへの入力
msg.payloadへの値のセットはchangeノード![2016-02-10 18_00_33-Node-RED _ satohdai-nodered.mybluemix.net.png](https://qiita-image-store.s3.amazonaws.com/0/102519/1fa8d2ac-d584-48ae-beac-ab74dc74a505.png)を使うのがいいだろう。
HTTPリクエストのパラメーターとして質問文を渡す場合(例えば */ask?q=質問文* というケース)はmsg.payload.qに質問文が保管されているので、そこから取り出せばよい。
msg.payloadは何度か上書きすることになるので、msg.payload.qをmsg.questionなど、payload配下ではないところに最初に退避させておくと後々便利だ。

話が横道にそれたので戻すとする。
質問文がmsg.questionに保存されているならば、以下のように設定すればよい。
![2016-02-10 18_01_32-Node-RED _ satohdai-nodered.mybluemix.net.png](https://qiita-image-store.s3.amazonaws.com/0/102519/e0317ebe-a58a-3d14-4270-2f687cbd6aba.png)

これでNLCノードの入力準備は完了だ。

## NLCノードの出力

NLCノードの出力例は以下の通りだ。

```json:msg.payload
{ "classes": [ { "class_name": "Android", 
                 "confidence": 0.9951245879757794 }, 
               { "class_name": "iPhone", 
                 "confidence": 0.0048754120242205535 } ], 
  "top_class": "Android" }
```
応答は、クラス名とConfidence(確信度)のリストを返す"classes"と、一番確信度が高いクラスを返す"top_class"がある。見てわかるように、top_classはあるがtop_confidenceが無い。なんでだろう。使い勝手をあげるには、top_confidenceを作るのもいい。functionノードの中で以下のような命令を書くと実現できる。

```js:functionノード
msg.payload.top_confidence = msg.payload.classes[0].confidence;
return msg;
```
msg.payload.classesがリストであり1番目の要素にアクセスするには、msg.payload.classes[0]と記述すればよい。
ここで注意が必要なのが、**changeノードやdebugノードでは配列が使えない**ことだ。なぜか使えない。配列にアクセスしたければfunctionノードを間に入れる必要がある。めんどい。

こうすると、以下のようにtop_confidenceが追加されるので、色々便利になる。

```json:msg.payload
{ "classes": [ { "class_name": "Android", 
                 "confidence": 0.9951245879757794 }, 
               { "class_name": "iPhone", 
                 "confidence": 0.0048754120242205535 } ], 
  "top_class": "Android",
  "top_confidence": 0.9951245879757794 }
```

## サンプル・フロー

ということで、Node-REDでNLCを呼び出して結果を取得しようとしたら、次のようなフローを作ればよいことになる。
![NLCnode_03.png](https://qiita-image-store.s3.amazonaws.com/0/102519/19d60ca9-dfb6-1f84-3af2-c0cc33fa0740.png)

HTTPリクエストの場合は以下のようなフローを作ればよい。
![NLCnode_04.png](https://qiita-image-store.s3.amazonaws.com/0/102519/fb8040cb-6a00-d39c-0fd3-acdda92be689.png)

なお、msgの中にHTTP req,resが格納されているので、msg本体を引き継がないといけないのにも注意。


# R&Rの呼び出し方

R&RはまだNode-REDのノードが無い(2016年2月13日現在)。そのため、functionノードで値をセットし、httpリクエストノードを使って呼び出してあげないといけない。

## R&Rへの入力
functionノードを使い、msg.urlにWebサービスのURLをセットする。
msg.questionに質問文が入っている場合、以下のようにセットしてあげればよい。

```javascript:functionノード
var param = "ranker=<<ランカーID>>&wt=json&fl=id,title,body&q=" + msg.question;
msg.url = encodeURI("https://gateway.watsonplatform.net/retrieve-and-rank/api/v1/solr_clusters/<<クラスターID>>/solr/<<コレクション名>>/fcselect" + "?" + param);
return msg;
```

ランカーID、クラスターID、コレクション名は各自の環境に合わせて欲しい。
質問文が日本語(全角)の場合、URIエンコードを行わないと正しく送信できない。そのため、msg.urlは**encodeURI関数**を使ってエンコードを行う。

## R&Rの呼び出し
呼び出しはhttpリクエストノードを使用する。
ベーシック認証の使用にチェックを入れて、ユーザーID、パスワードの入力欄を出し、R&Rの呼び出し時に使用するユーザーIDとパスワードを入力する。
また、Returnはデフォルトで「UTF-8 String」が選択されているが、出力結果を処理するためにJSONパースが必要になってしまうので、最初からJSON形式で返すようにしておこう。
![node-red_httprequest_01.png](https://qiita-image-store.s3.amazonaws.com/0/102519/a3dee9f0-5334-5d53-2957-8aa88a9f6c9f.png)


## R&Rの出力
出力は以下のようなJSON形式で返される。

```json:R&Rの出力
{"responseHeader":{"status":0,
                   "QTime":6},
 "response":{"numFound":11,
             "start":0,
             "maxScore":1.3636142,
             "docs": [ { "id": "40", 
                         "body": [ "Android 4.4.4以降でVoLTEに対応しています。" ] }, 
                       { "id": "39",
                         "body": [ "メールアプリケーションのバージョンが4.1.1以上か確認してください。バージョンが古い場合は、メールアプリケーションの更新をお願いいたします。" ] }, 
                       { "id": "28", 
                         "body": [ "機種変更後の機種が「アニメ放題」対応機種であれば、引き続きご利用いただけます。機種変更後の機種で、再度アプリケーションをインストールしてください。ダウンロードしたコンテンツは引き継がれません。" ] }, ・・・・
```

docsの中身がランクの高い順に並んでいるので、docs配列の一番最初の要素を取り出せばいい。取り出しにはfunctionノードを使う。httpレスポンスとして返したい情報は、**msg.payload**に格納する。

```javascript:functionノード
msg.payload = msg.payload.response.docs[0].body[0];
return msg;
```

最後にこれをhttpレスポンスノードに入れてあげればよい。

## サンプル・フロー
ということで、サンプル・フローはこちら。httpリクエスト/レスポンス版は割愛。
ハマりやすいところは、R&R(httpリクエスト)の返りの型の指定だろう。StringにしてしまうとJSONパースノードを使ってパースしてあげないとエラーになるので注意。
![node-red_r&r_flow1.png](https://qiita-image-store.s3.amazonaws.com/0/102519/9dfb26a5-1740-cd50-0d38-b6f446a8132c.png)


# TTSの呼び出し方

TTSは、Node-REDのノードはある。が、日本語に対応していない(2016年2月13日現在)。そのためR&Rと同様、functionノードで値をセットし、httpリクエストノードを使って呼び出す。
TTSの応答は音声データになるため、ブラウザー等のコードを使って再生してあげる必要がある。

## TTSへの入力
msg.payload.textにしゃべらせたい文章が入っている場合、以下のようにセットする。
voiceの指定を変えれば、いろいろな音声でしゃべらせることが可能だ。使える音声に何があるかは、[こちらの記事](http://qiita.com/VegaSato/items/0b7ab85dde97a9d1f4a9)を参考に調べてみて欲しい。

```javascript:functionノード
var param = "voice=ja-JP_EmiVoice&accept=audio/wav&text=" + msg.payload.text;
msg.url = encodeURI("https://stream.watsonplatform.net/text-to-speech/api/v1/synthesize" + "?" + param);
return msg;
```

## TTSの呼び出し
呼び出しはhttpリクエストノードを使用する。
ベーシック認証の使用にチェックを入れて、ユーザーID、パスワードの入力欄を出し、TTSの呼び出し時に使用するユーザーIDとパスワードを入力する。
また、Returnはデフォルトで「UTF-8 String」が選択されているが、音声データなので**a binary buffer**を選択しておく。
![node-red_tts_request.png](https://qiita-image-store.s3.amazonaws.com/0/102519/53152e63-8869-5d0c-c25d-7e4de597966f.png)

## TTSの出力
結果は、そのままhttpレスポンスノードで返せばよい。
むしろ問題は再生方法だ。
音声の再生にはクライアントが必要。curlで出力した場合は結果をファイルに落としてプレイヤーで再生できるし、ブラウザーで直接URLを入力した場合は結果をプレイヤーで開いて再生ができる。テストするだけならそれでもいいが、アプリケーションに組み込むならちゃんと再生させないといけない。
ということでクライアント側のHTMLファイルにjavascriptでこんな感じのコードを書いておくと再生可能だ。

```javascript:クライアント側
var audio = document.createElement("audio");
audio.src = "./t2s?text=" + "こんにちは"; 
audio.play();

```

## サンプル・フロー
ということで、TTSのサンプル・フローはこちら。ノードを使った方が色々簡単のはず。早く日本語化が待ち望まれる。
![node-red_tts_flow.png](https://qiita-image-store.s3.amazonaws.com/0/102519/1263aac5-e480-acb2-a2a7-9f5a39023dda.png)


# 終わりに
これらを全て組み合わせて、[BluemixのWatson APIを駆使して日本語質問応答システムを作る～その2～](http://qiita.com/VegaSato/items/1fa437667815a20a5624)で作った質問応答アプリを、Node-REDで作り直してみた。
が、今回かなり長くなってしまったので、次回にまわそうと思う。