笔者曾经开发过对话式机器人,包括软件及硬件。过去服务端用的是express,前端用的是vue。硬件用的是全志+乐鑫。现在,笔者重新开始写对话式机器人,前端用react(hooks),后端用laravel(PHP)。此次并不编写硬件方面的代码。
之所以后端用laravel,出于以下考虑:
1、出于对成本的考虑。python主机或node主机比php主机贵很多。而实际上这类应用,并不需要占用特别多的计算资源。因此laravel完全能胜任该应用场景。
如果你仅仅是为了做一个对话式机器人,甚至连后端都不需要,直接去连接各种微服务的API就可以了。
2、之前公司用的是express,因此用laravel全新开发,不会包含任何与之前公司相似的代码。
前端用react hooks,是出于以下几点:
1、笔者之前在公司的商用项目几乎都是vue,为了避免不小心把公司的商业代码混进来(虽然可能性很低),所以笔者直接使用react hooks。
2、过去笔者在公司的react项目都是16.4之前的版本的,所以能保证这次react hooks中没有任何之前的代码,从而杜绝了风险。
3、有一款UI专门是用来做聊天界面的,而它只支持react。采用一套完善的UI,可以节省很多时间。
4、react hooks 几乎不需要关注生命周期,只需要关注对某对个变量的依赖,因此复杂的逻辑从此变得简单。
5、阿里巴巴的开源ahooks ahooks.js.org/zh-CN 是对hooks的增强,使用起来非常方便。
一、什么是对话式机器人
对话式机器人主要是作为一种交互方式,用在受限屏幕上,做更多聚焦的功能性产品。
PC端的界面可以很丰富,比如图书馆的查询系统,比如宜家家居中的查询界面。它可以展示很多东西。
但对于小屏幕,很难装下那么多的东西。app或小程序,它的触摸式操作,也并不是万能的解决方式。触屏有局限性,很容易误操作。最常见的就是点餐机,有时候在上滑下滑的时候,就误操作把商品加入到购物车当中了。
对话式聊天机器人,以聊天的形态,去跑一个流程。通过抛出一个问题,提供相应的输入组件(单选、多选、在图片点选热区等),让用户不必滑动,只需要点击即可输入,降低了误操作带来的副作用,效果更聚焦。
在具有硬件的情况下,用户还可以通过硬件上的快捷键来选择,从而降低因为触屏灵敏度问题带来的卡顿体验。
对于具有选择困难症的用户,它还可以起到一个引导的作用。
二、对话式机器人能做什么?应用场景有那些?
对话式机器人主要用于提供帮助,应用领域是客服(售前问询,售中指引,售后服务)、导购、指引、伴学等。 应用场景很广泛,举例如下:
1、游乐场的项目选择
你是否恐高,你是否喜欢刺激,你身高体重多少,有无心脑血管基础病等。这些问题通过对话式让机器人问用户,用户通过选择,得到适合自己的游乐项目。后台需要把用户回答(点击)的这些关键字进行处理,筛选出适合用户的项目。比如用户恐高,那么过山车可能就不建议用户玩了。
2、商城推荐
买衣服,不仅仅是搭配,还得根据自身身材(体型,不是胖或瘦,具体是肩型、臀型、腿型等),皮肤肤质等。但还有一种场景,就是特定场景下的穿衣,是需要一定指南的。
比如,用户去面试,应该选购什么样的服装,用户去参加婚礼,或参加颁奖仪式,需要穿什么衣服。
这个可以做成一个流程,通过对话式让用户选择,并推荐一套适合的服装。
3、产品服务咨询
这是客服聊天机器人,几乎每个商业化的机构都包含这样的机器人。通常是自定义一些关键字,可以自动回复用户,给用户资料。也包括让用户输入联系方式,提交到后台,之后会有相关的销售人员打电话了解用户需求。
4、商场吃饭导购
不知道吃什么,你最近是否上火,想吃清淡的还是麻辣的。几个人吃,预算有多少钱。根据这些,做成对话式机器人,一问一答推荐商场内的餐厅。目前已经有很多商场有这样的硬件机器了,用户在硬件屏幕上与机器人机器人聊天,就可以得到推荐的餐厅。
5、评分、留言、反馈、建议等
笔者曾在一个自助柜员机上看到这样的聊天机器人界面,操作执行后,让你选择评分,是否对此次体验满意,是否有反馈建议等。
其实还有很多很多的应用场景。
下面的例子,是用一个简单的机器人,根据开发语言,硬件接口需求及应用场景,帮助用户选择开发版的一个demo:

实际上的应用中,问答会比这复杂很多。但这不是从技术方面考虑的事情。技术上只考虑实现,而具体问什么,需要输入什么,最终会提供一个编辑器,让用户自定义。
显然编辑器需要后台,这个功能会在laravel中去实现。
三、先造一个外壳,做个最简单的聊天机器人对话界面
从架构的角度上来讲,应该先把功能做出来,然后再做UI。但从前端的角度上来讲,要先把UI做出来,然后再去对接后端的API。
在做后端通信的时候,常常会先做一个echo,就是不论说什么,都给返回原文。
现在整个界面有几部分组成,上面是一个标题栏,在对方(机器人)没有输入完成的情况下,显示的是正在输入,输入完成后显示的是正常的标题。
下面是输入框,输入后,自己的消息会在右侧,机器人的消息会在左侧。 因为目前没有接入后端,所以机器人的echo效果完全是个延时。
四、关键代码
1、因为echo有一个延时,使用timeout会因为作用域导致每次机器人说的话会覆盖用户说的话。(timeout中,拿到的list并不是更新后的list,而是之前的,因此延时后的追加,等于在第一次追加前进行追加,从而把第一次追加的给抹掉了。)
我在这里专门做了一个demo,可以看到为什么: codesandbox.io/s/ahooks-te…
解决办法有很多,最简单的方式,是直接用阿里巴巴开源ahooks中的动态列表。
demo在这里:codesandbox.io/s/ahooks-te…
使用ahooks中的动态列表的代码:
const { list, remove, getKey, push } = useDynamicList([]);
之后可以用push()来实现对列表的增加,如:
const botSay=text=>{
push({
avatar:"/logo.jpeg",
name:'bot',
text:text,
time:Date.now(),
isOwn:false
});
}
2、对于输入后,标题框产生正在输入的提示,是在机器人输入的前后更改标题。
const send = text => {
push({
avatar:null,
name:'Guest',
text:text,
time:Date.now(),
isOwn:true
});
setTpying('typing...');
setTimeout(() => {
botSay(text);
setTpying('Chat test');
}, 1000);
}
3、输入后的滚动效果,使用了scrollIntoView的特效。
const messagesEndRef = useRef(null);
const scrollToBottom = () => {
messagesEndRef.current.scrollIntoView({ behavior: "smooth" })
}
当列表改变时,就可以出发滚动效果:
useEffect(scrollToBottom, [list]);
相应地,要在return中,加入 <div ref={messagesEndRef} /> :
return (
<ThemeProvider>
...
<div ref={messagesEndRef} />
...
</ThemeProvider>
);
}
4、因为用户输入的和机器人输入的在界面中显示位置不一样,所以机器人和自己的对话显示,也做成了函数组件。
function Bot(props){
return(
<Row>
<Avatar imgUrl={props.avatar}/>
<Message authorName={props.name}>
<MessageText>{props.text}</MessageText>
</Message>
</Row>
)
}
function Guest(props){
return(
<Message isOwn authorName={props.name}>
<MessageText>{props.text}</MessageText>
</Message>
)
}
五、效果,不足与改进
1、随着聊天内容的增加,内容并没有自动滚动到最下方,会导致一部分内容被遮盖(已经解决)。
2、UI还很粗糙,该对齐的没有对齐,比如文字输入框的文本内容没有上下居中,该调整的间距没有调整,比如右上角的x,太靠右了。
这些问题会等时间充裕的时候再去解决。
目前的效果是:

本示例的源代码位于 :github.com/botform/bot
下篇文章中,会介绍如何使用laravel,包括环境的搭建,框架的使用,以及和前端的交互等。