溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何使用nodejs BOT SDK開發(fā)問答類技能模板

發(fā)布時(shí)間:2021-11-10 16:11:07 來源:億速云 閱讀:129 作者:柒染 欄目:云計(jì)算

這篇文章給大家介紹如何使用nodejs BOT SDK開發(fā)問答類技能模板,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

問答技能模板

問答技能模板是針對(duì)問答類技能設(shè)計(jì)的模板,如知識(shí)問答、生活常識(shí)問題等。本文從問答類技能交互、部署講述如何快速搭建問答類技能。

問答技能模板的交互模型

問答類技能與用戶的交互方式是技能從題庫列表中選出一道題,并提供四個(gè)選項(xiàng),其中有一個(gè)選項(xiàng)是正確的。用戶通過說出正確選項(xiàng)的順序來答題。

下面以古詩問答技能為例,描述問答類技能與用戶交互過程。技能從古詩列表中選取一首詩讓用戶說出詩的作者,并依次讀出四個(gè)作者選項(xiàng)。用戶說出正確的作者的選項(xiàng)順序,回答正確后技能會(huì)記錄用戶得分。技能交互過程如下:

用戶:打開古詩問答 

技能:[技能歡迎語]。開始答題。第一題:離離原上草 一歲一枯榮的作者是誰。 1,李白 2,白居易 3,杜甫 4,柳宗元 

用戶:第二個(gè) 

技能:回答正確,得一分。目前的積分是1分。第二題。。。

技能從題庫中選取問題,可以杜絕題目重復(fù)和答案排列順序重復(fù)的問題。開發(fā)者只需要更新題目列表和相應(yīng)的技能配置信息,即可生成新的技能并在DuerOS DBP平臺(tái)上發(fā)布。題庫的存儲(chǔ)方式如下,其中正確答案需要放在答案的第一個(gè)位置,格式如下。

{
  '題目': [
    '正確答案',
    '錯(cuò)誤答案',
    '錯(cuò)誤答案',
    '錯(cuò)誤答案',
  ],
},

上面古詩問答的例子在題庫中的展現(xiàn)形式是:

{
  '第一題:離離原上草 一歲一枯榮的作者是誰。': [
    '白居易',
    '李白',
    '杜甫',
    '柳宗元',
  ],
  '第二題:白日依山盡,黃河入海流,出自那首詩。': [
    '登鸛雀樓',
    '...',
    '...',
    '...',
  ],
},

其中正確答案“白居易”和“登鸛雀樓”放在第一個(gè)位置,但是在給用戶出題時(shí),選項(xiàng)的順序會(huì)調(diào)整。

模板的使用說明

答案中必須有一個(gè)正確答案。

每道題的答案選項(xiàng)可以是3個(gè),4個(gè),5個(gè),選項(xiàng)總數(shù)不收限制。

每個(gè)題目必須通過選項(xiàng)序號(hào)來作答,如用戶必須說“第一個(gè)”、“第二個(gè)”,不能使用“是”、“對(duì)”、“錯(cuò)”、“不是”等進(jìn)行回答。

使用模板開發(fā)技能的流程

請(qǐng)注意,下面的新建技能和配置意圖過程可以通過在技能平臺(tái)-->創(chuàng)建技能-->引用技能-->導(dǎo)入技能頁面導(dǎo)入 http://dbp-cfc.cdn.bcebos.com/download/trivia.zip 實(shí)現(xiàn)。

新建技能

新建技能詳情請(qǐng)參閱自定義技能創(chuàng)建

配置意圖

意圖配置詳情請(qǐng)參閱意圖、常用表達(dá)和槽位

問答技能模板需要?jiǎng)?chuàng)建兩個(gè)意圖,分別是回答問題意圖和重新開始問答意圖。 回答問題意圖如下圖所示:

重新開始問答意圖如下圖所示:

如何使用nodejs BOT SDK開發(fā)問答類技能模板

配置技能服務(wù)部署

問答技能模板使用CFC部署技能服務(wù)。使用CFC部署技能服務(wù)詳情請(qǐng)參閱 百度云CFC

修改CFC函數(shù)代碼

問答技能模板使用questions.js配置題庫。開發(fā)者需要下載技能CFC函數(shù)完整zip程序包到本地進(jìn)行開發(fā),開發(fā)完成后上傳函數(shù)zip包進(jìn)行發(fā)布。具體流程如下:

  • 在CFC控制臺(tái)通過模板創(chuàng)建函數(shù), 選擇node.js DuerOS Bot SDK模板

  • 函數(shù)生成后,在函數(shù)控制臺(tái)點(diǎn)擊點(diǎn)擊下載完整 ZIP 程序包鏈接下載程序包

  • 在本地解壓程序包

  • 將https://github.com/dueros/bot-sdk-node.js/blob/master/samples/trivia/questions.js文件拷貝到程序包文件夾中

  • 使用https://github.com/dueros/bot-sdk-node.js/blob/master/samples/trivia/index.js替換程序包文件夾中的index.js文件

  • 將程序包文件夾中的所有文件重新打包成zip文件

  • 在函數(shù)控制臺(tái)上傳zip程序包并保存

CFC操作說明請(qǐng)參閱函數(shù)計(jì)算 CFC

完整代碼

const Bot = require('bot-sdk');
const privateKey = require("./rsaKeys.js").privateKey;
const question_list = [
    {
      '遠(yuǎn)上還山石徑斜,白云深處有人家': [
        '正確的',
        '杜甫',
        '白居易',
        '李白',
      ],
    },
    {
      '離離原上草,一歲一枯榮': [
        '正確的',
        '杜甫',
        '白居易',
        '李白',
      ],
    },
    {
      '舉頭望明月,低頭思故鄉(xiāng)': [
        '正確的',
        '杜甫',
        '白居易',
        '李白',
      ],
    },    {
      '鋤禾日當(dāng)午 汗滴禾下土': [
        '正確的',
        '杜甫',
        '白居易',
        '李白',
      ],
    },
    {
      '白日依山盡,黃河入海流': [
        '正確的',
        '杜甫',
        '白居易',
        '李白',
      ],
    },
    {
      '李白乘舟將欲行,忽聞岸上踏歌聲': [
        '正確的',
        '杜甫',
        '白居易',
        '李白',
      ],
    },
    {
      '橫看成嶺側(cè)成峰,遠(yuǎn)近高低各不同': [
        '正確的',
        '杜甫',
        '白居易',
        '李白',
      ],
    },
    {
      '人生自古誰無死,留取丹心照汗青': [
        '文天祥',
        '杜甫',
        '白居易',
        '李白',
      ],
    }
  ];


//定義一輪問答中的問題數(shù)量
const GAME_LENGTH = 5;
//定義每個(gè)問題的答案數(shù)量
const ANSWER_COUNT = 3;

class InquiryBot extends Bot {
    constructor(postData) {
        super(postData);
        this.addLaunchHandler(() => {
        	this.waitAnswer();
			let speechOutput = '歡迎來到古詩問答。我將念兩句古詩并給你三個(gè)詩人的名字。需要你告訴我哪一個(gè)是正確的作者。';
			//初始化一輪中的問題列表和第一題的話術(shù)
			let repromptText = this.startNewGame();
			let card = new Bot.Card.TextCard(repromptText);
			return {
				card: card,
				outputSpeech: speechOutput + repromptText
			};
        });

        this.addSessionEndedHandler(() => {
            this.endSession();
            return {
                outputSpeech: '謝謝使用!'
            };
        });

        this.addIntentHandler('answer_intent', () => {
            this.waitAnswer();
            //確保獲取到了用戶的回答
            let theAnswer = this.getSlot('theAnswer');
            if (!theAnswer) {
                this.nlu.ask('theAnswer');
                return {
                    outputSpeech: '您的答案是哪個(gè)?'
                };
            }
            //獲取session中相關(guān)信息
            let questionsList = this.getSessionAttribute('questionsList');
            let score = this.getSessionAttribute('score');
            let currentQuestionIndex = this.getSessionAttribute('currentQuestionIndex');
            let correctAnswerIndex = this.getSessionAttribute('correctAnswerIndex');
            let gameQuestions = this.getSessionAttribute('gameQuestions');
            let correctAnswerText = this.getSessionAttribute('correctAnswerText');
            let speechOutput = '';
            if (theAnswer == correctAnswerIndex){
            	score += 1;
            	speechOutput = '回答正確,得一分。目前得分:' + score + '分。';
            }else{
            	speechOutput = '很遺憾,回答錯(cuò)誤。正確答案是' + correctAnswerText + '.目前得分:' + score + '分。';
            }
            //到達(dá)最后一題,用戶選擇重新開始一輪或者退出技能
            if (currentQuestionIndex == GAME_LENGTH - 1){
            	speechOutput += '已經(jīng)是最后一題了。您可以說重新開始來繼續(xù)答題,或者說退出來退出技能。'
            	return {
                	outputSpeech: speechOutput
            	};
            }
            //獲取下一題信息
			currentQuestionIndex += 1;
			correctAnswerIndex = Math.floor(Math.random() * (ANSWER_COUNT));
			let spokenQuestion = Object.keys(questionsList[gameQuestions[currentQuestionIndex]])[0];
			let roundAnswers = this.populateRoundAnswers(gameQuestions, currentQuestionIndex,correctAnswerIndex,questionsList);
			let questionIndexForSpeech = currentQuestionIndex + 1;
			let repromptText = '第' + questionIndexForSpeech + '題:\n' + spokenQuestion + '\n';
			for (let i = 0; i < ANSWER_COUNT; i += 1) {
				repromptText += `${i + 1}. ${roundAnswers[i]}. `;
			}
			speechOutput += repromptText;
			let currentQuestion = questionsList[gameQuestions[currentQuestionIndex]];
			this.setSessionAttribute('speechOutput',speechOutput);
			this.setSessionAttribute('currentQuestionIndex',currentQuestionIndex);
			this.setSessionAttribute('correctAnswerIndex',correctAnswerIndex + 1);
			this.setSessionAttribute('gameQuestions',gameQuestions);
			this.setSessionAttribute('questionsList',questionsList);
			this.setSessionAttribute('score',score);
			this.setSessionAttribute('correctAnswerText',currentQuestion[Object.keys(currentQuestion)[0]][0]);
			let card = new Bot.Card.TextCard(repromptText);
			return {
				card: card,
				outputSpeech: speechOutput
			};
        });
        
        //重新開始答題,得分清零
        this.addIntentHandler('newGame_intent', () => {
        	this.waitAnswer();
        	//初始化一輪中的問題列表和第一題的話術(shù)
			let repromptText =  this.startNewGame();
			let card = new Bot.Card.TextCard(repromptText);
			return {
				card: card,
				outputSpeech: '好的,重新開始。' + repromptText
			};
        });

        /*
        * 獲取沒有被意圖解析的用戶輸入,并進(jìn)行相關(guān)處理
        * 缺省意圖 https://developer.dueros.baidu.com/didp/doc/dueros-bot-platform/dbp-nlu/defaultIntent_markdown
        */
        this.addIntentHandler('ai.dueros.common.default_intent', () => {
            this.waitAnswer();
            return {
                outputSpeech: '您可以對(duì)我說第幾個(gè)來告訴我您的答案。您也可以說重新開始重新玩,或者說退出來退出游戲。'
            };
        });
    }
    
    
    /**
     *  獲取新一輪問題列表和相應(yīng)的信息,并將信息存入session中
     *
     *  @return 新一輪答題話術(shù)
     */
    startNewGame() {
		let questionsList = question_list;
		let gameQuestions = this.populateGameQuestions(questionsList);
		let correctAnswerIndex = Math.floor(Math.random() * (ANSWER_COUNT));
		console.log(correctAnswerIndex);
		let roundAnswers = this.populateRoundAnswers(gameQuestions, 0,correctAnswerIndex,questionsList);
		let currentQuestionIndex = 0;
		let spokenQuestion = Object.keys(questionsList[gameQuestions[currentQuestionIndex]])[0];
		let repromptText = '第1題:\n' + spokenQuestion + '\n';
		for (let i = 0; i < ANSWER_COUNT; i += 1) {
			repromptText += `${i + 1}. ${roundAnswers[i]}. `;
		}

		let currentQuestion = questionsList[gameQuestions[currentQuestionIndex]];

		this.setSessionAttribute('currentQuestionIndex',currentQuestionIndex);
		this.setSessionAttribute('correctAnswerIndex',correctAnswerIndex + 1);
		this.setSessionAttribute('gameQuestions',gameQuestions);
		this.setSessionAttribute('questionsList',questionsList);
		this.setSessionAttribute('score',0);
		this.setSessionAttribute('correctAnswerText',currentQuestion[Object.keys(currentQuestion)[0]][0]);
		return repromptText;
    }
    
    /**
     *  從問題列表中隨機(jī)抽取問題。問題個(gè)數(shù)由變量GAME_LENGTH定義
     *  @param {list} translatedQuestions 所有問題列表
     *  @return 問題id列表
     */
	populateGameQuestions(translatedQuestions) {
	  let gameQuestions = [];
	  let indexList = [];
	  let index = translatedQuestions.length;
	  if (GAME_LENGTH > index) {
		throw new Error('Invalid Game Length.');
	  }

	  for (let i = 0; i < translatedQuestions.length; i += 1) {
		indexList.push(i);
	  }

	  for (let j = 0; j < GAME_LENGTH; j += 1) {
		let rand = Math.floor(Math.random() * index);
		index -= 1;

		let temp = indexList[index];
		indexList[index] = indexList[rand];
		indexList[rand] = temp;
		gameQuestions.push(indexList[index]);
	  }
	  return gameQuestions;
	}
	
    /**
     *  從問題列表中隨機(jī)抽取問題。問題個(gè)數(shù)由變量GAME_LENGTH定義
     *  @param {list} gameQuestionIndexes 一輪問答中問題id列表
     *  @param {int} currentQuestionIndex 當(dāng)前問題Index
     *  @param {int} correctAnswerTargetLocation 當(dāng)前問題答案Index
     *  @param {list} translatedQuestions 所有問題列表
     *  @return 當(dāng)前問題答案選項(xiàng)列表
     */
	populateRoundAnswers(gameQuestionIndexes,currentQuestionIndex,correctAnswerTargetLocation,translatedQuestions) {
	  const answers = [];
	  const translatedQuestion = translatedQuestions[gameQuestionIndexes[currentQuestionIndex]];
	  const answersCopy = translatedQuestion[Object.keys(translatedQuestion)[0]].slice();
	  let index = answersCopy.length;

	  if (index < ANSWER_COUNT) {
		throw new Error('Not enough answers for question.');
	  }

	  // 打亂當(dāng)前問題答案列表順序
	  for (let j = 1; j < answersCopy.length; j += 1) {
		const rand = Math.floor(Math.random() * (index - 1)) + 1;
		index -= 1;

		const swapTemp1 = answersCopy[index];
		answersCopy[index] = answersCopy[rand];
		answersCopy[rand] = swapTemp1;
	  }

	  // 將正確答案放置到correctAnswerTargetLocation的位置
	  for (let i = 0; i < ANSWER_COUNT; i += 1) {
		answers[i] = answersCopy[i];
	  }
	  const swapTemp2 = answers[0];
	  answers[0] = answers[correctAnswerTargetLocation];
	  answers[correctAnswerTargetLocation] = swapTemp2;
	  return answers;
	}
}

exports.handler = function(event, context, callback) {
    try {
        let b = new InquiryBot(event);
        // 0: debug  1: online
        b.botMonitor.setEnvironmentInfo(privateKey, 0);
        b.run().then(function(result) {
            callback(null, result);
        }).catch(callback);
    } catch (e) {
        callback(e);
    }
}

至此,問答技能就開發(fā)完成了。開發(fā)者可以在技能開放平臺(tái)的模擬測(cè)試頁面對(duì)技能進(jìn)行測(cè)試。

關(guān)于如何使用nodejs BOT SDK開發(fā)問答類技能模板就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI