代码详解|如何快速从硬盘里找到小电影?
看电影还要找豆瓣?别人的喜好怎能左右你的欢心~
豆瓣评分不靠谱,关键时刻得自己动手!
本文将手把手教你打造一个专属电影机器人,它能根据你的要求来推荐电影。科幻悬疑恐怖还是爱情文艺小清新,统统hold住!
本文基于SAP Conversational AI 来构建模型,并通过电影数据库 来获取电影信息。和简单的Q / A聊天机器人相比,与第三方API交互能实现更多有趣的案例。通过Bot Skills,我们添加了直接从构建器调用webhooks的选项,非常简单。
今天的机器人学习,分为以下步骤:
1.在句子中提取关键信息
2.构建机器人流程(触发,需求,操作)
3.创建并连接能从电影数据库中获取数据的 API
你需要一个SAP Conversational AI 帐户 ,Node.js ,测试时可能还需要Ngrok 。
1.在句中提取关键信息
“意图机制”有助于确定句子的整体含义。对实际运用而言,仅仅知道用户想看“某些”是不够的,我们需要知道用户想看“什么”。
“实体”被设计出来,正是旨在解决这个问题:它们可以在句子中提取关键信息。“意图机制”让你明白你必须做什么,而“实体”帮助你怎么做。
假设你是一家提供电话和互联网接入的电信公司,而机器人的意图是了解人们何时抱怨断网:
“实体”就可以从句中提取出关键信息。提取出的字符有助于了解“什么、哪里、何时”出了问题。
对于电影机器人,我们将尝试提取3关键信息块:
1. 用户想看什么(电影还是电视节目)
2. 用户想看的电影类别
3. 语种
2.使用“黄金实体”
为了加速开发进程,SAP Conversational AI可以默认提取多个实体:日期、位置、电话号码等。
在“language”实体中,名称旁的小星星就是黄金实体与普通实体的区别。
我们可以用这个实体解决第三个要求:电影语种。
3.创建自定义实体
创建自定义实体,可以有效提取我们所需要的信息。与“意图”一样,训练非常重要:添加到机器人的示例越多,获得的样本准确性就越高。
你可以通过多种意图对实体进行训练:
对电影机器人而言,只需要1个意图discover和2个实体:
• recording并识别用户想看电影还是电视节目
• genre(观看类别)
打开你的意图“discover”,添加表达式,一定要确保表达式包含了所有可能情况,如:
• 无实体:“我男朋友今晚想看点东西”
• 一个实体:“我想看一部电影”
• 多个实体:“你能给我推荐一些法国戏剧电视节目吗?”
要想标记表达式,请选择要标记的文本,并输入实体名称。
15个示例虽然可以,但应该添加更多的示例。生产就绪的机器人需要至少50个示例,才能表现良好。为了加快过程,你可以把机器人 中构建的实体[记录实 体 ,派别 实体 ]进行分叉,然后在机器中发现意图 。
你可以发现,在这里“法国”被检测为国籍,而不是语种,因为这就是实体在这种情况下的含义。在构建bot流程时,我们将确保检查这两个实体。
4.添加自定义进行丰富
我们已经标记了实体,现在让它们更丰富一些吧! 在训练选项卡下打开机器人中的实体面板,如下所示:
现在,打开genre实体。如果你注意一下面板的右上角,你会看到一个切换:free - restricted and settings。打开它,以便我们对你可以访问的不同选项进行详细解释:
在实体面板中,你可以访问实体的不同选项:
• Free与Restricted——你没有严格的值列表,但你希望机器学习能检测到所有可能的值,这时使用免费的(Free)自定义实体。然而,如果你有严格的单词列表要检测,则使用受限制的自定义实体不需要自动检测实体。
• 模糊匹配——模糊匹配是0和1之间的索引,用于表示单词与实体值列表中单词的接近程度。如果单词在此索引之外,平台则会按照列表中最接近的值,对它进行标记。
• 值列表——你可以在此处添加实体的所有值列表,可以是不同的值,或者同义词。
在我们的例子中,我们的genre实体将受到限制,因为Movie Database API仅管理特定的类别列表,如下:
{ id: 28, name: 'Action' }, { id: 12, name: 'Adventure' }, { id: 16, name: 'Animation' }, { id: 35, name: 'Comedy' }, { id: 80, name: 'Crime' }, { id: 99, name: 'Documentary' }, { id: 18, name: 'Drama' }, { id: 10751, name: 'Family' }, { id: 14, name: 'Fantasy' }, { id: 36, name: 'History' }, { id: 27, name: 'Horror' }, { id: 10402, name: 'Music' }, { id: 9648, name: 'Mystery' }, { id: 10749, name: 'Romance' }, { id: 878, name: 'Science Fiction' }, { id: 53, name: 'Thriller' }, { id: 10752, name: 'War' }, { id: 37, name: 'Western' }
把所有的类型添加到我们的值列表中,但不要忘记添加同义词,如科幻小说(SF,Sci-Fi),浪漫主义(Romantic)或卡通动画(AnimatedCartoon)等。你会发现,正如JSON中那样,会有一系列ID与类型相关联,因为电影数据库无法根据英文名称搜索特定类型,而只能搜索自定义数字。我们可以做的,就是为每个类型值关联一个特定的id,它将在NLP API的JSON中执行返回,这样我们就可以将它传递给Movie Database API。这就是丰富自定义的目的:每当检测到实体时,从NLP API返回的JSON都会添加有关该实体的信息。
在自定义的面板中,我们需要创建3个键:
• name——在同一值下映射同义词
• id——丰富电影数据库的id
• article——添加该类型的文章(稍后我们将用到它)
要添加自定义,请单击add new key 并添加上面列出的三个键——关于article,将默认键值设置为“a”,因为大多数类型都使用“a”。在name中,你可以开始添加特定的内容并将所有不同的值映射到name、ID、article中,如下所示:
你可以从此页面开始,分叉整个实体,其中包括丰富的自定义部分。既然已经完成了,就可以在测试控制台中测试一下。假设发送句子“我想看动画电影”,你应该可以看到以下自定义内容:
"genre": [ { "value": "animated", "raw": "animated", "confidence": 0.99, "name": "animation", "id": 16, "article": "an" }
现在这些添加为我们提供了通用名称,ID和Article。我们会以同样的方法操作录音实体。返回实体面板并单击录音,然后进行限制,并为电视节目和电影添加所有可能的值和同义词(如tv shows, shows, motion picture, film, films, movies等等)。
现在转到自定义丰富界面并添加Key选项,赋2个特定值:
• movie -所有电影的同义词
• tv - 所有电视节目的同义词
就像这样:
发回我们的句子:“我想看一部动画电影”,我们便有了录音的丰富内容:
"recording": [ { "value": "movie", "raw": "movie", "confidence": 0.99, "type": "movie" } ]
5.建立你的机器人流程
由于我们只需要在调用Node.JS API之前确保填写所有条件,因此构建部分将非常简单。
我们只需要一种技能 —— Discover。
5.1触发器
如果意图@Discover已经存在,我们将触发它:
5.2要求
此选项卡可帮助你在操作之前收集数据。我们希望确保用户在继续之前指定录音、类别、语种以及意图的是否:
这些要求将被逐一检查,它们都可以在第一条消息上实现。例如,如果用户说“我观看英语犯罪电影”,会立即触发操作。
对于每个要求,你都可以选择在消息完成或缺失时发送消息。
在要求完成后发送消息,可以让机器人更生动:“一部犯罪电影?我也爱他们!”但是,当缺少要求时,它们几乎是强制性的:你需要让用户告诉你想要的内容。
例如,如果缺少#genre,我就会发送带有建议类型的快速回复:
为了确认,我们将使用内存来显示动态消息,以验证用户对意图的选择@yes还是@no:
(使用内存显示动态消息)
一旦4组实体的问题都设置完毕,你就可以进行Actions了。
5.3操作(Actions)
一旦满足要求,如果用户说是,我们将调用API来实际执行搜索,否则我们会重置内存并再次询问用户想观看什么。
如果_memory.no存在,请重置整个内存并发送消息,例如“让我们重新开始,你想看什么?”
如果_memory.yes存在,则创建一个CALL WEHBOOK操作。你可以输入完整的URL(例如:https://mydomainname.com/discover-movies)或相对URL(/ discover-movies)。当你输入相对URL时,SAP Conversational AI将在机器人设置中使用参数Bot Base URL。
接下来,添加操作(actions)UPDATE CONVERSATION> EDIT MEMORY> RESET ALL MEMORY,以在调用完成后清空内存。
(操作)
如果你没有公共服务器,或者你想在开发过程中测试你的机器人,那么ngrok是一个非常方便的工具,它会创建一个公共URL,并将请求转发给你的计算机。
安装后,运行...
ngrok http 5000
并将HTTPS中的转发URL(https://XXX.ngrok.io)复制到机器人设置(“Bot webhook基本URL”字段),对这些URL发出的所有请求都将转发到你计算机的端口5000。
现在,机器人只需要API来获取电影了!
6.创建Movie Bot API
机器人的NodeJS部分非常简单,它将作为SAP会话AI和电影数据库之间的HTTP代理。
当你的应用程序收到来自SAP Conversational AI的请求时,它会根据用户标准向电影数据库发送搜索查询,并将JSON答案格式化为SAP Conversational AI消息格式。
选择1:自动操作
你可以直接从Git存储库复制整个项目:https://github.com/plieb/movie-bot-skills-training .
选择2:手动操作
第一步:支持你的项目
mkdir movie-bot && cd movie-bot npm init npm install --save express body-parser axios touch index.js config.js mkdir discover-movies && cd discover-movies touch index.js movieApi.js cd..
第二步:获取TMDb API令牌
你将需要一个令牌来使用Movie Database API,然后编辑你的config.js文件:
module.exports = { MOVIEDB_TOKEN: process.env.MOVIEDB_TOKEN || 'PURYOURTOKENHERE', PORT: process.env.PORT || 5000, };
第三步:使用Express应用程序填充index.js
让我们创建一个Express应用程序来处理来自SAP Conversational AI的请求。为了更好地组织项目,如第1步所示,我们有一个文件夹/ discover-movies /,包含了我们机器人代码的核心(不是将所有文件放在同一个文件夹中),我们通过loadMovieRoute调用它。
const express = require('express'); const bodyParser = require('body-parser'); const config = require('./config'); const loadMovieRoute = require('./discover-movies'); const app = express(); app.use(bodyParser.json()); loadMovieRoute(app); app.post('/errors', function(req, res) { console.log(req.body); res.sendStatus(200); }); const port = config.PORT; app.listen(port, function() { console.log(`App is listening on port ${port}`); });
第四步:填写discover-movies / index.js
当用户填写搜索条件时,我们要求SAP Conversational AI向/ discover-movies发送POST请求。
控制器的主要目标是从内存中选择和格式化首选项,以将它们发送到电影数据库的API:
const config = require('../config'); const { discoverMovie } = require('./movieApi'); function loadMovieRoute(app) { app.post('/discover-movies', function(req, res) { console.log('[GET] /discover-movies'); const kind = req.body.conversation.memory['recording'].type; const genre = req.body.conversation.memory['genre'].id; const language = req.body.conversation.memory['language']; const nationality = req.body.conversation.memory['nationality']; const isoCode = language ? language.short.toLowerCase() : nationality.short.toLowerCase(); return discoverMovie(kind, genreId, isoCode) .then(function(carouselle) { res.json({ replies: carouselle, conversation: { } }); }) .catch(function(err) { console.error('movieApi::discoverMovie error: ', err); }); }); } module.exports = loadMovieRoute;
第五步:填写 discover-movies/movieApi.js
现在我们已经提取并格式化了请求的所有过滤器,我们需要将请求发送到电影数据库并把答案格式化:
const axios = require('axios'); const config = require('../config'); function discoverMovie(kind, genreId, language) { return moviedbApiCall(kind, genreId, language).then(response => apiResultToCarousselle(response.data.results) ); } function moviedbApiCall(kind, genreId, language) { return axios.get(`https://api.themoviedb.org/3/discover/${kind}`, { params: { api_key: config.MOVIEDB_TOKEN, sort_by: 'popularity.desc', include_adult: false, with_genres: genreId, with_original_language: language, }, }); } function apiResultToCarousselle(results) { if (results.length === 0) { return [ { type: 'quickReplies', content: { title: 'Sorry, but I could not find any results for your request :(', buttons: [{ title: 'Start over', value: 'Start over' }], }, }, ]; } const cards = results.slice(0, 10).map(e => ({ title: e.title || e.name, subtitle: e.overview, imageUrl: `https://image.tmdb.org/t/p/w600_and_h900_bestv2${e.poster_path}`, buttons: [ { type: 'web_url', value: `https://www.themoviedb.org/movie/${e.id}`, title: 'View More', }, ], })); return [ { type: 'text', content: "Here's what I found for you!", }, { type: 'carousel', content: cards }, ]; } module.exports = { discoverMovie, };
第六步:启动吧!
就这样! 准备好测试你的机器人。
运行:启动应用程序 —— node index.js
一切顺利,你应该会看到: App started on port 5000
电影推荐,天气,健康,交通...使用第三方API,一切皆有可能!