Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8898

前々回の記事で作成したオウム返しチャットボットにQ&Aエンジンを実装する

$
0
0

前々回の記事で作成したオウム返しチャットボットにQ&Aエンジンを実装する

 
※本記事は最終的にQ&Aチャットボットを構築するための一部分となります。
本編はこちら

Q&Aナレッジを格納するベースファイルを作成します

テストの為に気象庁のQ&Aを元にQ&Aエクセルを作成します
QANDA.xlsxというファイル名で保存します

出典:https://www.jma.go.jp/jma/kishou/know/faq/faq10.html

image.png

 

 
 
knowledgeBaseというフォルダを作成し、QANDA.xlsxを配置します
image.png

libというフォルダを作成し、knowledgeBase.jsというファイルを作成します
これはエクセルのQA一覧をナレッジベースとして読み込むためのリソースです。

参考用ソースは以下

knowlegeBasex.js ※新規作成(クリックして展開)
/***********************************
 エクセルからナレッジベースを取得
***********************************/exports.getKnowledgeBase=function(){vardocList=[];constXLSX=require("xlsx");constUtils=XLSX.utils;// Workbookの読み込みconstworkbook=XLSX.readFile("knowledgeBase/QANDA.xlsx");// シート読み込みletworksheet=workbook.Sheets['Sheet1'];// 有効なセルを取得letrange=worksheet['!ref'];constdecodeRange=Utils.decode_range(range);// シートからQ&A情報を取得するvarcount=0;for(letrowIndex=decodeRange.s.r;rowIndex<=decodeRange.e.r;rowIndex++){constq=Utils.encode_cell({r:rowIndex,c:0});consta=Utils.encode_cell({r:rowIndex,c:1});constcellq=sheet1[q];constcella=sheet1[a];if(typeofcellq!=='undefined'&&typeofcellq.v!=='undefined'&&cellq.v!=='Question'&&typeofcella!=='undefined'&&typeofcella.v!=='undefined'&&cella.v!=='Answer'){docList.push({id:count,'title':cellq.v,'body':cella.v});count++;}}console.log('Knowledge base');console.log(docList);returndocList;}


  
 
 

 
検索エンジンを日本語に対応するのに必要なリソースを手に入れてlib配下にコピーします
https://github.com/MihaiValentin/lunr-languages
上記ページから以下のファイルを取得してlib配下にコピー
lunr.stemmer.support.js
lunr.jp.js

日本語形態素解析に必要なリソースもlib配下にコピーします
http://chasen.org/~taku/software/TinySegmenter/tiny_segmenter-0.2.js

  
 
同じくlibフォルダ配下に、elasticlunrsearch.jsというファイルを作成します
これはナレッジベースとして読み込んだナレッジからQ&A抽出を行う為の検索エンジンコントローラです。

参考用ソースは以下

elasticlunrsearch.js ※新規作成(クリックして展開)
// ******************************************************************//// ** 全文検索エンジン関連 ここから                                  **//// ******************************************************************//constelasticlunr=require('elasticlunr');require('./lunr.stemmer.support.js')(elasticlunr);require('./lunr.jp.js')(elasticlunr);// ドキュメントライブラリを取得varlibdocs=require('./knowledgeBase.js');vardocs=libdocs.getKnowledgeBase();// 質問のブースト値constBOT_qes_boost=process.env.BOT_qes_boost;// 回答のブースト値constBOT_ans_boost=process.env.BOT_ans_boost;// インデックス構築constindex=elasticlunr(function(){this.use(elasticlunr.jp);this.addField('body');this.addField('title');this.setRef('id');for(vari=0;i<docs.length;i++){this.addDoc(docs[i]);}});/***********************************
 エクセルからナレッジベースを取得
***********************************/exports.indedxsearch=asyncfunction(keyword){constresult=awaitindex.search(keyword,{fields:{title:{boost:BOT_qes_boost},body:{boost:BOT_ans_boost},},expand:true,});returnresult;}/***********************************
 ナレッジIDをキーにナレッジを取得
***********************************/exports.searchById=function(id){for(vari=0;i<docs.length;i++){if(id==docs[i].id){returndocs[i];}}returnnull;}

 

同じくlibフォルダ配下に、bot.jsというファイルを作成します
これは現在のindex.jsのBOT関連リソースを集約したファイルになります。
これに合わせてindex.jsのソースも修正します

参考用ソースは以下

bot.js ※新規作成(クリックして展開)
/***********************************
 BOTが回答できない場合のソーリーメッセージ
***********************************/exports.BOT_sorrymsg=process.env.BOT_sorrymsg;/***********************************
 BOTが理解不能と判断する際の閾値(下回り)
***********************************/constBOT_Sorry_threshold=process.env.BOT_Sorry_threshold;/***********************************
 LINEのエンドポイント設定
***********************************/constChannel_endpoint=process.env.Channel_endpoint;/***********************************
 LINEのチャネルアクセストークン設定
***********************************/constChannel_access_token=process.env.Channel_access_token;/***********************************
 BOTの回答が有効なものである場合の定数値
***********************************/constBOT_ANSWER_TRUE="BOT_ANSWER_TRUE";exports.BOT_ANSWER_TRUE=BOT_ANSWER_TRUE;/***********************************
 BOTの回答が有効だが一定の確信度に満たない場合の低数値
***********************************/constBOT_ANSWER_TRUE_ANY="BOT_ANSWER_TRUE_ANY";exports.BOT_ANSWER_TRUE_ANY=BOT_ANSWER_TRUE_ANY;/***********************************
 BOTの回答が無効なものである場合の定数値
***********************************/constBOT_ANSWER_FALSE="BOT_ANSWER_FALSE";exports.BOT_ANSWER_FALSE=BOT_ANSWER_FALSE;// ---- ************************************************** -------// ---- BOTの回答が有効であるか判断した結果を返す             -------// ---- ************************************************** -------exports.isAnswerEnabled=function(result){if(isNullOrUndefined(result)||result.lentgh==0||isNullOrUndefined(result[0])){// 回答がない場合returnBOT_ANSWER_FALSE;}elseif(isNullOrUndefined(result[0].ref)||result[0].score<BOT_Sorry_threshold){// 回答はあるが最低限の確信度に満たない場合console.log("first score(score ng)");console.log(result[0].score);returnBOT_ANSWER_FALSE;}else{// 最低限の確信度を満たした回答が存在する場合console.log("first score");console.log(result[0].score);returnBOT_ANSWER_TRUE;}}// ---- ************************ -------// ---- 一答形式の回答を作成する   -------// ---- ************************ -------exports.createAnsMessage=function(req,message){varoptions={method:"POST",uri:Channel_endpoint,body:{replyToken:req.body.events[0].replyToken,messageNotified:0,messages:[// 基本情報{contentType:1,type:"text",text:message,}]},auth:{bearer:Channel_access_token},json:true};returnoptions;}// ---- ************************************************** -------// ---- 空チェック  -------// ---- ************************************************** -------functionisNullOrUndefined(o){return(o===undefined||o===null);}

 

  

 
 
index.jsを以下のように修正します
lib配下に移動した処理を削除
BOTの回答を制御するための処理を追加

参考用ソースは以下

index.js ※修正(クリックして展開)
// ******************************************************************//// ** 初期設定関連 ここから                                          **//// ******************************************************************//varexpress=require("express");varapp=express();varcfenv=require("cfenv");require('dotenv').config();varrequest=require("request");varbodyParser=require("body-parser");app.use(bodyParser.urlencoded({extended:true}));app.use(bodyParser.json());// 検索エンジンコントローラ読み込みvarelasticlunrsearch=require('./lib/elasticlunrsearch.js');// BOTコントローラ読み込みvarbot=require('./lib/bot.js');// ******************************************************************//// ** メッセージ処理 ここから                                      **//// ******************************************************************//constasyncwrap=fn=>(req,res,next)=>fn(req,res,next).catch(next);app.post("/api",asyncwrap(async(req,res)=>{// 受信テキストvaruserMessage=req.body["events"][0]["message"]["text"];console.log("user -> "+userMessage);// 問い合わせ内容をキーにQAデータを検索varresult=awaitelasticlunrsearch.indedxsearch(userMessage);// 取得したQAデータを元に応答メッセージを作成varoptions=null;// 回答が有効化どうかを判断した結果を取得するswitch(bot.isAnswerEnabled(result)){// BOTが質問を理解できない場合はソーリーメッセージcasebot.BOT_ANSWER_FALSE:options=bot.createAnsMessage(req,bot.BOT_sorrymsg);break;  // BOTが質問を理解し、回答が可能な場合casebot.BOT_ANSWER_TRUE:// ナレッジIDをキーにナレッジを抽出するconstknowledge=elasticlunrsearch.searchById(result[0].ref);options=bot.createAnsMessage(req,knowledge.body);break;}// メッセージを返すrequest(options,function(err,res,body){});res.send("OK");}));// サーバ起動varappEnv=cfenv.getAppEnv();app.listen(appEnv.port,"0.0.0.0",function(){console.log("server starting on "+appEnv.url);});

 

 

.envファイルに以下の設定を追記します

# ---------------------#
# BOTのチューニング設定         
# ---------------------#
# BOTが理解不能と判断する際の閾値(下回り)
BOT_Sorry_threshold=0.25# BOTが1問1答する為の確信度の閾値(上回り)
BOT_Confidence=0.9# 質問のブースト値
BOT_qes_boost=0.3# 回答のブースト値
BOT_ans_boost=0.1# 複数回答を返す際に最大いくつの候補を提示するか
BOT_any_ans_count=5# BOTが質問を理解できないときのソーリーメッセージ
BOT_sorrymsg=申し訳ありません。質問の意味が理解できませんでした。

# BOTが複数回答を投げかけるときのメッセージ
BOT_anyansmsg=この中にお役に立てる情報はございますでしょうか。

 
 
 
現時点でソースコードは以下のような構成になっているはずです

image.png

  

 

動作確認

BOTを起動して動作確認します。

image.png

 
 

シュミレータ上で期待通りの動きをすることを確認したら、Gitにコミット&プッシュします

git add .
git commit -am "Q&Aエンジンの追加"
git push heroku master

 
 
LINEで動きを確認してみましょう

image.png


Viewing all articles
Browse latest Browse all 8898

Trending Articles