整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

B端设计指南:“按钮”究竟应该如何设计?

钮是最常使用的组件之一,但是在与人交流时,还是会觉得大家存在很多误区,所以本文将围绕如何使用按钮展开分析,希望能给大家带来一些新的思考。

按钮是任何用户界面当中(无论是桌面还是移动用户界面)必备的交互元素:甚至可以说,如果页面中没有一个按钮,整个页面设计将是不完整的。在日常生活中,按钮也是随处可见的,一个电灯开关、遥控器按钮,现实生活中按钮反复的出现也可以帮助我们不断去理解屏幕世界中按钮应该如何操作,从而衍生出屏幕世界中的按钮的具体形态。

人机交互中最重要的就是把我们从小到大对于这个世界的认识映射到屏幕世界中,比如苹果最为经典iOS的滑动解锁。

而到了屏幕世界中,控件的设计更应该与物理世界保持相对的一致,这也是按钮设计中,尤为重要的一个环节。

最近常问身边的朋友,按钮究竟是什么?

他们多数的回答:“按钮就是按钮呗,还能是什么~”

因为对于他们而言,按钮不就是一个操作区域加上文字,没什么特别的,也正因如此,对于按钮的具体构造也不太了解。这篇文章主要是根据自身的工作经验,结合当下对于按钮设计的理解,去分析如何进行更合理的按钮设计。

聊聊按钮的痛点

对于每个设计师来说按钮并不陌生,在每天的设计中,都会使用按钮进行页面设计;但又不是每一个设计师都能够将按钮设计好,因为它存在三个方面的复杂性:

  • 它使用的场景过多:不是每个场景都需要相同的按钮,比如在登录页当中,登录和注册就是使用不同的按钮形式。这些情况特别在B端产品中,业务在每个步骤中需要突出和强调的点不同,导致设计师很难通过具体场景进行按钮设计;
  • 按钮出现的频率太高:B端产品中,每个页面当中都会有按钮不停的重复,而高频的出现会让我们感到麻木,导致很多设计师都将其忽略;
  • 按钮形式太多:在我总结的按钮形式当中,一共分为10类,且情况多种多样,很多时候都没有意识到不同形式之间的差距,导致乱用混用。

因此一个看上去小小的按钮,其实经常会困扰着我们,如果刚开始没有将按钮进行整体的梳理,那么在你的设计过程中,按钮会经常打断自己的设计思维,为了让大家能够对按钮有更深的理解,我将按钮进行系统的拆解,结合实际案例,能够使按钮更浅显易懂。

在文章按钮类型的分析中,我将按钮分为两大类、十小类,将其分类也是为了更好的为大家去分析每一个按钮所涉及到的状态,当我们理解按钮之前,你需要了解它的内部构造。

一、按钮类型

首先,抛出一个问题给大家,下图中,共有几种按钮形式?分别是什么?给大家五秒钟时间思考。

如果你对上图的按钮形式并不完全了解,建议你拿好小本本,做好笔记~

在开始讲常见按钮类型前,我们必须要搞清楚一个重要的知识点:按钮状态。

1.1 什么是按钮状态?

按钮状态,可以让用户知道这个按钮当前是在进行哪一种操作,方便帮助用户进行判断。

常见的按钮状态分为:正常状态(Normal)、聚焦状态(Focus)、悬停状态(Hover)、激活状态(Active)、加载状态(Loading)、禁用状态(Disabled),下面分别对这六大状态进行拆解。

正常状态 (Normal):除了其他五种状态外的情况都是正常状态,它是按钮最为常规的状态形式,也是设计师必须设计的一种状态。

聚焦状态 (Focus):主要是用于展示当前电脑光标所在的具体位置。听起来有点玄乎,我来讲他背后的原理,主要是方便一些键盘使用的用户,可以使用Tab键或者方向键来对网页进行访问输入。

比如在Mac OS 以及 Windows的使用中,通过点击Tab,便展示出文件的Focus状态。

Focus状态是一个非常重要的交互形式,但是很多设计师都会忽略,甚至在很多网站,直接就是将这个样式所去除,导致使用Tab键无法获取聚焦的反馈,比如常见的百度、Google再到各大操作系统都会有这类反馈,去除这类反馈,会导致用户无法用方向键控制光标位置,在很大程度上降低用户使用的可能性。

悬停状态 (Hover):在桌面端的设计当中,即使用户知道它是可以点击的,但是你还是需要设计悬停状态,表明鼠标现在正在按钮上。平板电脑和移动端的设备上用永远不会展示悬停状态,因为你的手指是无法在屏幕上进行悬停的(虽然IPad OS 更新了13.4版本后,会有Hover态的出现,但是是通过鼠标进行操作,因此这里不予以讨论)。

激活状态 (Active):激活状态因为叫法不同,在有的地方也称之为Press状态,它的表现就是将按钮按压下,将颜色变深同时会涉及到内阴影等效果的出现。

禁用状态 (Disable):按钮禁用状态作为最难设计的状态之一,主要在于禁用状态的表现形式以及禁用状态与激活状态之间如何的切换,我具体来分析一下两个难点。

难点一:禁用状态在颜色上首先会给人灰色块的感觉,行业里也称之为置灰,在设计上,也需要注意光标移动时需要展示禁用光标,即让前端大哥将Cursor的hover状态更改为not-allowed,你可以优雅的在前端面前装个X(之后会出一期,简单聊聊我与前端如何协作如何装X)。

难点二:禁用与激活状态的切换,比如在一个注册页面中,需要姓名与电话必填,当用户没有填写完成姓名与电话时,应该将按钮置灰,提示用户不可以点击,当用户填写完成必填字段后,将禁用按钮转变为激活按钮,提示用户可以点击登录。

加载状态/Loading:按钮需要时间进行加载的一种状态,通常会被用户忽略,但是在B端产品中,Loading状态尤为重要,这里有很多细节和小技巧,展开讲篇幅太大,在文中4.3(按钮细节)会详细解答。

1.2 主按钮 (Primary Button)

主按钮为页面中按钮区最为核心的操作,在日常场景中,主要按钮一般都为新建、编辑、保存这一类正向的操作,强调页面中最为核心的功能,能够让用户一看到主按钮就明白大致在页面中需要如何操作。

但在主按钮的使用中,要遵循以下两个原则:

1)在页面当中,按钮区域的主按钮最好只有一个,否则会对页面的主要功能造成干扰。

2)并不是每一个页面都需要有主按钮,不要因为页面缺失主按钮而强行加上。因为在实际使用中,时常遇到按钮之间为平级的关系,强行添加,容易造成页面层级混乱。

在主按钮中,按钮状态的设计也会跟随物理世界进行相应的映射,因此在设计时需考虑现实生活中的状态。

比如用户的Hover时,一般都将按钮抬起并亮度增加,其目的是为了提示用户可以点击,而用户在按下时,用加黑来表示用户按下的状态,与现实生活中按钮的状态类似,因此按钮状态应该映射物理世界。

1.3 次按钮/标准按钮(Default Button)

次按钮在页面中出现最为频繁,在日常使用中,如果你不太确定使用何种按钮时,那使用它,大概率没有错。因为它运用广泛,出现频率也最高,因此也被人们称为标准按钮。

在次按钮的设计形式中,我们团队将设计形式分为两类:

第一类 字线型

此按钮整体以文字+边框的形式,这类形式在视觉层面上感知较弱,适合几个按钮同时展示,在B端项目中,字线型也是在使用中最为频繁的形式。

第二种 字面型

字面型更偏向表达按钮整体,常见于各类移动端的按钮当中,整体的表达也更适合移动端这类屏幕尺寸较少的设备当中,更符合卡片化设计的思路,反而不太适合桌面端的设计。

1.4 文字按钮/链接(Text Button/Link)

文字按钮为页面中视觉层级较低的按钮形式,因而可以在页面中大面积的重复使用,文字按钮与链接(Link)基本一致,且没有太多区分,所以在设计上,文字按钮与链接基本相同。

文字按钮重复的出现,以表格页面当中的操作列表最为突出,因为表格当中常用的操作最好能够直接展示出来,因此表格中基本都采取文字按钮的形式。

1.5 图标按钮(Icon Button)

图标按钮为页面中控件占比最小的按钮,所以在页面中的使用也是最为高效的。因为没有了文字元素,会导致用户在图标的理解上出现偏差。为了解决这一问题,主要是通过用户在Hover时使用展示Tooltip提示按钮的含义,同时建议在图标按钮的使用上多为高频且易理解的图标。

我举一个简单例子,在桌面端产品中,帮助文档一定是非常重要的一个入口,当用户对页面中的功能有所疑惑时,可以通过此来帮助用户进行理解,通常需要在大多数页面当中展示帮助中心的入口,这时图标按钮就变得最为合适。

因此,我们可以得出图标按钮的应用场景通常为:当页面中需要高效的展示一个或几个图标时,同时图标按钮的展示最为频繁时,当同时满足以上两点,使用图标按钮就是一个更优的解决方案方案。

1.6 按钮菜单(Menu Button)

按钮菜单为页面中许多操作的集合,通常是将高频的操作以及一些低频的操作进行整合,组成的按钮菜单。这样既能够减少页面元素的冗杂,同时也能够满足业务对于功能的需求。

举个例子,在表格页面当中,B端设计师最常见到的按钮菜单就是新建,这类新建按钮其实是必不可少的,同时业务需要,还需要多个业务按钮进行展示,按钮菜单的出现,帮助用户进行按钮的整理,同时也满足业务需求。

1.7 按钮中加图标(Icon add Button)

这其实是主按钮的一种衍生,通过图标对主按钮的含义进行解释,从而帮助用户提高这个按钮的识别性。如果一个按钮你想比主按钮更加强调,那便可使用在按钮中添加图标,这样既能够强化图标的含义,同时也加深了用户对于按钮的印象。

在六个常见按钮形式中,我们根据重要的优先级,给常见按钮进行一个简单的排序:

图标按钮-按钮菜单-主按钮-次按钮-文字按钮-图标按钮

二、特殊按钮类型

2.1 危险按钮(Danger Button)

危险按钮在删除操作中最为常见,通常是为了警告用户,这个数据删除不可恢复,让用户谨慎考虑。在常见的删除操作中,都需要用户进行二次确认,避免用户误操作。

当然,在实际业务中,危险按钮不宜过多,如果业务当中无法避免,需要展示多个删除按钮时,推荐采取图标按钮进行展示或者Hover过后将其呼出。

2.2 幽灵按钮(Ghost Button)

幽灵按钮,看它的名字就能想到它的作用:像幽灵一般透明的存在。

它没有使用复杂的颜色、样式,而是用线条表示外部轮廓,证明它还是一个按钮。同时内部只用文字展示按钮名称。它只出现在按钮背景复杂的页面当中,比如:渐变色、纹理、动态图片背景的情况下,幽灵按钮能够完美融入到环境当中。

而现如今,传统意义上的幽灵按钮已经很少,毕竟在现如今的官网当中,幽灵按钮已经不再流行,更多的是出现在复杂页面的“实心按钮”,而在某种意义上讲,这类按钮才是幽灵按钮现在的状态。

幽灵按钮和次按钮有何不同?

在形式上,幽灵按钮和次按钮看起来没有并什么不同,因此会有很多疑惑,那我什么时候用幽灵按钮什么时候用次按钮呢?

首先幽灵按钮是属于特殊按钮体系中,因此它不会受整个按钮体系的束缚,比如我在一个设计系统中,分别定义了常规按钮的尺寸分别是24px、32px、40px,但是我在一个官网落地页当中需要有一个46px的按钮出现,次按钮就完全不合适。其次幽灵按钮边框粗细、字体大小都是和常规按钮体系不同,因此幽灵按钮就和次按钮有所不同。

第二个方面在次按钮的设计形式中,不仅仅只有描边按钮这一种形式,因此幽灵按钮与次按钮它们可能会有交集,但是属于两种不同类型的场景,因此也不能将它们混用。

2.3 悬浮按钮(Floating Action Button)

在Material Design 出现之初,悬浮按钮受到了很多人的追捧,它也是安卓设计的代名词。主要是用于页面当中最常用的操作,是整个APP中最核心的按钮,能够代表这个产品的核心功能,比如记账软件的添加账单记录,印象笔记的新增笔记(安卓)。

但沉浸式设计的出现,使得移动端寸土寸金的地方,需要固定一个按钮的情况就变得更加少见。

而在B端的设计过程中,逐渐衍生出了B端行业特有的方式。

悬浮按钮在B端场景中,主要是帮助用户进行辅助咨询的功能,例如在一些用户需要得到帮助的页面中,可以通过悬浮按钮,使用户的又疑问的页面进行快速提问,帮助用户能够进行快速的跳转,在飞书的应用列表中,其实用户刚开始理解应用列表其实存在一个理解成本,就可以通过悬浮按钮进行展示(后面有机会聊聊B端改版厌恶时也会提到)。

2.4 行为号召按钮(Call to Actiontion)

行为号召按钮简称:CTA按钮,主要目的是为了号召人们在某些特定的页面中使用此按钮来提高转化,比如立即购买、联系我们、立即订阅等等…

大多数时候,CTA按钮都是成对出现的。“是与否“ 、“登录与注册”、“确认与取消”等。因此,分析清楚CTA按钮后设计出视觉层级合理的页面称为非常重要的点。在设计中,一般会采取渐变色、主题色、主题色的互补色等等,它经常独立出现。

在B端软件的场景中,官网是CTA按钮出现最为重要的页面,一般在官网中,使用CTA按钮将用户引导从潜在客户向付费客户进行转变(点击过后一般是一个联系表单进行信息的填写),这也是在B端产品中非常重要的指标:潜客向付费客户转变。可以引导用户进入到下一个阶段,如果CTA按钮设计不好,人们对于官网只是浏览,不会有任何转化。

因此,在设计CTA按钮的形式与位置时,需要与产品、设计、运营等共同确定并讨论使用,大家站在不同的立场希望得到一个完美的方案,因为设计出清晰的结构层次将直接引导用户朝着理想的使用路径前进,从而极大提高转化率。

三、按钮细节

3.1 按钮文案

在我们日常设计中,常常会遇到一些棘手的文案问题:登陆、登录、确认、确定、发送、发布,在许许多多的工作场景中,犹豫究竟应该在按钮上使用哪种文案,这就需要我们通过具体的案例场景进行展示相应文案。

  • 登陆(Land):这是网络中错别字最为频繁的用此,很多人都会把登陆放到登录页面中,其实是错的。这个词里的“陆”字,就是陆地的意思,其基本含义只是登上陆地而已,拓展出来还会有“登陆到某一个市场”,但登陆网站的说法是绝对错误的。
  • 登录(Login):这是“登记记录、记载”的含义,我们正常输入账号密码就是为了去记录我们的一个信息,一般为官网登录页。
  • 确认(Confirm):这是带有一些不可逆我操作的提示,一般用于用户配置、选择项等。
  • 确定(Yes):这是询问用户是否进行某项不可逆操作,一般为一个单独的操作。
  • 发送(Send):这是尽快传递对方的聊天消息,一般为即时性的聊天发送。
  • 发布(Publish):这是用于用户填写的观点、意见、文章等反馈信息发布到一些正式场合,如论坛、社区等。

这些细节的文案就是帮助用户去理解页面中所传达的真正含义,多将文字放置到场景中,文案也更好的辅佐他们作出选择

3.2 按钮圆角

圆角在每一个软件中,随处可见。圆角大小所带来的不仅仅是视觉表现,还更多影响着用户的使用体验以及对于产品而言的整体的认知,如果在开始设计之前,没有对按钮圆角有具体的规划,很容易踩坑,如何设计好圆角,我们来进行系统分析

在下文中我们将按钮的圆角大小,分为以下三类:直角、圆角、半圆。

直角:

按钮四角的方向,具有很强烈的引导性,看上去也会更加刺眼,使得在页面当中注意力会减弱。同时,直角在按钮排列当中看上去更加统一,相同的东西在视觉上不太能引起我们的注意。

圆角:

相比与直角按钮,在使用圆角的按钮中,视觉上总是给人一种柔和亲近的感觉,当几个圆角按钮进行排列时,能够感受到圆角按钮更容易被点击。因此在使用的按钮中,建议添加圆角的细节元素。

为何直角的物体会给人更严肃的感受——每一个人都认为圆角会更好,但是并不是每一个人都能够解答为什么圆角更好。

在巴罗神经病学研究所对拐角的科学研究发现,“拐角的感知程度随着角度线性变化。锐角比钝角产生更强的幻觉显着性”。换句话说,拐角越尖,则出现越亮。拐角越多,越难看。

圆角还有另一种解释,是因为现实生活中有圆角的物体会更友好。从小,我们就知道尖角的物体会让人受伤,圆角的物体会更安全。这就是小孩在玩皮球与刀的时候,家长的反应完全不同。

小朋友玩刀会让家长十分的紧张,赶紧让孩子放下;而小朋友玩皮球时,家长则是非常的放心。这激起了神经科学所谓的尖锐边缘“回避反应”。因此,我们倾向于“避免锋利的边缘,因为在自然界中它们可能会构成危险”。

圆角是不是越大越好?

通常在移动端场景中,半圆按钮随处可见,移动端手指触摸操作上,对于圆角的影响小之又小;而到了桌面端的场景下,鼠标的使用,半圆按钮就会有所不妥。

如果相同面积中,按钮的圆角增大,相应的对于按钮的可操作区域就会随之减小,在同等尺寸下的按钮中,小圆角的按钮明显比大圆角的按钮更容易操作。

当然,麻烦事还不仅仅于操作区域,在结合实际业务,我们的按钮常常作为原子(原子设计理论)用来组成为下拉菜单进行联动,半圆按钮在下拉菜单的设计中,也会因为半圆的局限,使下拉菜单的设计会更加困难,同样在设计上,半圆角对于下拉菜单的适配也会相当突兀,因此在考虑这方面设计时,需要你多去思考之后的业务场景。

3.3 重要的Loading状态

按钮的状态中,Loading状态通常不会对用户进行直接展示,因为大多数情况下,Loading状态就发生在一秒钟以内,但是对于B端场景中有很多重要操作,在长时间等待中不展示loading状态,会导致用户在使用时犯下错误。

需要在合理的时间进行反馈

  • 当按钮响应时间小于1秒时,通常正常反馈即可。
  • 当按钮响应时间长于1秒时,我们通常会采取加载动画,减缓用户等待的焦虑感。

举个例子:比如一个确认提交的按钮,由于网络或者服务器等原因,需要长时间加载资源,而用户不知道我刚才按下的按钮是否有效,这时用户慌张,想要多按下几次这个按钮,系统就会不停提交数据,最后网络变好时,窗口就会一瞬间疯狂展示,导致用户体验下降。

为了防止这类事情的发生,需要在设计师考虑到按钮在加载一秒以后的状态,应当提示用户在网页已经收到请求,正在加载,同时在按钮状态中应该为不可操作状态。同时会给出加载转圈的动画,缓解用户的焦虑。

当你完成了第一步的设计后,想想在按钮的状态中,是否更能够帮助用户进行体验上的提升呢?

这也是在面试某大型互联网公司时,被问到过的一个问题~敲黑板。

对用户操作的适当反馈是用户界面设计的最基本准则。让用户了解当前状态、位置、是否成功、进度如何,减少不确定性;并引导他们在正确的方向上交互,而不是浪费精力在重复操作上。

在Loading的加载过程中,等待焦虑一直是用户想要了解到的,为了缓解类似情况,可以将等待的进度状态进行展示,使的用户在等待的过程中,能够清晰的清楚自己的按钮目前处于何种状态,我大概还需要等待多久,缓解用户在等待过程中的焦虑。

以上两个方式均是尼尔森十大原则的内容,能够在按钮Loading状态中,缓解用户在按钮加载过程中、重复提交、等待焦虑的问题,通过一些设计小细节,帮助产品提升用户体验。

四、按钮实际的使用场景

通过上文对于按钮的解释,大致明白按钮在设计中的作用,接下来我结合一个工作中的实际案例,来看看我们应该如何优化常见按钮在页面当中的设计。

项目背景:在桌面端,我们需要对整个导航栏进行设计改版,但其中涉及到对于导航的一个整体优化,主要是由于业务功能发生变化,我们需要在导航栏上增加快捷添加入口和通知中心,来满足导航的后续的功能需求,由于保密协议的原因,就不放自家产品。

第一步根据用户浏览模式确定按钮顺序

在桌面端中,浏览模式大致分为两类,F型浏览模式、Z型浏览模式(下方知识拓展会有讲到)。

首先,因为导航在整个页面的顶部,结合两种浏览模式在顶部时统一都为从左到右的浏览顺序。

因此确定整个导航按钮初步的按钮重要层级排序。

第二步交互路径优化

我们对于用户的按钮层级有着明显的划分,因为在整个导航右侧,又因为其的特殊性,我们把操作空间分为三部分:

左侧为操作路径最短的区域,因为桌面端的产品需要通过鼠标进行交互操作,而其中移动鼠标的长短直接决定用户是否愿意点击这个按钮,因此,靠左的按钮适合放置用户最常使用的操作。

中部为按钮内部区,可以放置一些低频,但是又必须出现在这个区域的按钮操作(比如:帮助中心、通知中心等等…)。

右侧为按钮最为重要层级最弱的区域,同时它也有一些特殊性。

一般在浏览器的右侧,为用户最容易定位的操作区域,因为靠近边缘导致在用户定位当中能够帮助用户进行快速选择。

回到页面中信息层级较高、同时需要精准定位的按钮就会将个人中心放置在最右侧,方便用户进行快速定位。

因此我们讲导航当中按钮重要层级进行简单排序,得到下图:

第三步信息整合优化按钮结构

通过亲密性原则,我们将除去左右两侧确定好的按钮之外的按钮进行简单分类,将有关联的按钮进行组合,形成较强的关联性~

第四步视觉调整

视觉调整作为最重要的一步,主要是为了在最后的按钮重要层级上,将一部分按钮突出、一部分按钮弱化,达到我们想要的整个层级效果。

通过团队内部讨论,将我们的新增按钮定位CTA按钮,因为它关联到我们每个使用系统的人都会用到,属于最高频的操作按钮,也因此将其突出。

F型浏览模式:

是新闻平台、博客等文字为主的网站布局所采取的阅读模式。

该阅读模式由尼尔森团队的眼动追踪研究结果从而进行普及,在这个研究中记录了超过200位用户浏览网页时,发现用户的主要阅读行为在许多网站和场景中表现得相当一致。这个阅读模式看起来有点像字母F,因此也被叫做F型浏览模式。

首先用户以水平方向进行阅读,通常是在阅读区域的上半部分。

接下来,他们在屏幕左侧垂直浏览,寻找段落开篇几句中感兴趣的内容。当他们找到感兴趣的内容时,他们在第二个水平方向上快速浏览,通常这块内容区比上一个内容区更短小、更简洁。这部分元素形成了F的下半部分。

最后,用户在垂直方向上浏览内容的左侧区域。

Z型浏览模式:

是扫描滚动的网站的典型模式。

正如你所期望的,“z”型模式的布局遵循字母Z的形状。“Z”型模式的设计跟踪了人眼扫描页面时的路线——从左到右,从上到下:

  • 首先,人们从左上角到右上角进行扫描,形成一条水平线
  • 接下来,向页面的左下侧扫描,链接成一条对角线
  • 最后,再次向右转,形成第二条水平线

当观众的视角以这种模式移动时,它形成一个虚构的“Z”字形。

五、按钮设计中与开发还原细节

在实际工作中,经常遇到自己设计的按钮与开发实际还原的按钮差距很大,一些没有经验的设计师会和开发说,你看我设计稿,每一个按钮都要按照设计稿的来,这样设计师与前端开发之间的矛盾就会越来越深。其实在按钮设计的细节中,开发怎么完美的还原,里面还有很多细节。

5.1 Padding思维

首先要想让开发完全还原你的设计稿,就必须了解开发实现的思维方式,针对它的思维方式进行相应的思考。

又由于Sketch与开发常使用的VScode之间逻辑上存在较大差异,导致设计师设计出来的很多设计稿开发根本无法实现,这也是B端设计师摆在你面前的第一个问题。

对你没看错,无法实现,我举一个例子:

这是一位同学问我的一个问题,他说我这个按钮为啥实现不了,前端不就是多几个代码去适配我的设计稿就可以了吗?

如果你也有很多疑问,那就接着看下去~

什么是Padding

在按钮当中,通俗的理解Padding就是文字与按钮之间的间距。

因为Sketch等软件在按钮的设计中,只有图形位置的概念,没有内间距Padding的概念,因此需要对按钮还原要明确的描述。

比如上图,前端同学在开发就会将Padding设置为24px,所以整个按钮长度便为24px*2+20px(文本宽度)=68px。

而为什么说上面的设计规范实现不了,因为对于前端而言,Padding通常都是统一且固定的,只会根据按钮文字长度进行相应的调整,比如我上面举的错误栗子,其实在前端同学面前这类设计是不能够被共用,而且对前端同学项目会变得越来越不能维护,所以按钮设计应该更规划。

5.2 Min-Width思维

但是如果真的需要去实现两个文字按钮长度和四个字的一样怎么办,需要去设定按钮的最小宽度。

按钮最小宽度的设定一般为4个字文字的长度,假设字体大小为16px,左右的Paddung为24px。

最小宽度为:88px,因此在按钮的标注中,需要展示最小间距为102px,方便前端进行CSS开发。

5.3 按钮的边框

在我们的sketch中,按钮边框有三种,内边框、居中边框、外边框,默认为居中,但是在前端的CSS代码中没有居中边框,没有居中边框,没有居中边框(重要的事情说三遍),默认为内边框,如果需要调整为外边框,最好能够标注。

按钮虽然作为一个最基础的元素,但是在整个设计体系中,它一直都扮演着一个十分重要的位置,在思维中,任何组件都可以通过上面按钮的思维,对每个组件进行拆解分析,无论是组件的状态、组件的类型,在实际工作中,都需要你去深入思考。关于我呢, 也因为踩了很多坑,因此想分享给大家。

参考链接

https://blog.prototypr.io/8-rules-for-perfect-button-design-185d1202ee9c

https://medium.com/@uxmovement/when-you-need-to-show-a-buttons-loading-state-41fc4d5e3c65

https://atlassian.design/guidelines/product/components/buttons

https://uxmovement.com/thinking/why-rounded-corners-are-easier-on-the-eyes/

相关阅读

B端设计:盘点筛选控件的基本知识

B端设计:导航菜单的设计5步法

作者:CE,2B行业的2B设计师~。公众号:CeDesign

本文由 @CE 原创发布于人人都是产品经理。未经许可,禁止转载

题图来自Unsplash,基于CC0协议。

文主要介绍了如何才能设计一个具有行为召唤能力的按钮,并从形状、色彩、按钮样式、添加图标、文字字体、位置等方面提出了建议。

“按钮”在UI设计中是一种常用的UI控件,一个成功的按钮控件可以有效的提高交互体验,引导用户,提升产品转化率。目前我们所熟知的按钮按功能类型主要分为六大类,分别是行为召唤按钮(CTA)、悬浮按钮、标签按钮、表格按钮、命令按钮及开关按钮。

其中,行为召唤按钮(Call To Action),简称CTA按钮,即用户在访问某页面后引导用户去点击并且跳转至下一个流程(如购买、联系、关注等行为)的一类按钮控件,其主要目的就是为了提高特定页面或屏幕的转化率,从而达到预期设定的结果。因此,CTA按钮也具有不同于其他种类按钮的特性。那么如何才能设计一个具有行为召唤能力的按钮呢?

尺寸

结合CTA按钮的先前目标是吸引用户的注意,提高转化,因此按钮尺寸越大,它被用户发现和点击的概率就越高,但也不宜过大,以免破坏布局的视觉组合和层次结构。最好的按钮和按钮文字应该遵循页面的比例进行设计,以便用户更好识别。

图1

另外,移动端的设计还需要考虑按钮与手指的适配度,为了保证按钮的最佳点击区域,设计师在绘制按钮时要保证其点击范围至少高度要在30px以上,例如主流的参考设计规范:苹果的HIQ中要求CTA按钮至少为 44×44 px,而微软则建议至少为34×26 px。具体的大小还是会根据实际情况而变。

图2

色彩

除了通过尺寸差异来营造视觉吸引力外,一个能够激励用户点击的按钮需要具备的另外一个关键元素就是颜色的选择与运用。好的色彩运用能够从情绪与视觉上干预用户,吸引其注意力。所以设计师在进行按钮设计时,需要考虑到整体的配色方案,使CTA按钮能够在众多UI控件中脱颖而出。

介于文章篇幅原因,简洁明了的来说,我们知道色彩经由色相、明度、纯度的变化与调和,可以产生丰富多样的层次变化,而能够吸引人目光的色彩特性我们可以称之为诱目性。

在能够使诱目性变高的色彩中,与无色彩对比,有色彩的比较好;与低纯度色对比,高纯度色的比较好;与低明度色比较,高明度色的较好。

所以为了让CTA按钮的颜色与整体页面形成和谐但又显著的对比,目前采用最多的设计原则是遵循高反差度原则,即通过合理运用色彩的色相、明度、纯度的对比方式,来凸显CTA按钮的重要性。

如下图(图3)案例所示,整体界面的色彩选择是白色,如果像左图中按钮依然选择纯度较低的蓝色,不仅会使整个页面显得“轻飘飘”,毫无着落点外,CTA按钮也无法凸显出来,而右图中的按钮不论是在色相还是纯度上都选择了与背景色反差较大的蓝色,直接诱导用户进行点击。

图3

按钮形状

我们常说艺术来源于模仿,设计来源于生活。这句话对于按钮外形的绘制非常适用,因为在现实生活中按钮的使用率也非常高,比如开关灯、开电脑、敲键盘等,所以设计师在设计过程中会吸取现实生活中按钮的形状,择优选择水平矩形或者圆角矩形来表示CTA按钮。

另一方面,一些研究建议圆角矩形更能加强信息的传递并且能够将人的视线集中在中心位置上。

当然我们有时也可以根据自己的创意去使用其他的形状,比如圆形,三角形或者自定义形,但是一定要确保统一性能够把控你的界面设计,以便用户能够识别出这个按钮元素。如下左图所示,在此处使用圆形按钮不仅打破了页面设计的统一性,而且相较于右图示中的圆矩形按钮提示的作用也被削弱了不少。

­­

图4

按钮样式

突出的按钮样式,特别是矩形或圆矩形按钮,利用投影等样式有效还原生活中按钮的模样,提示用户按钮是可以进行点击的。这与扁平化的按钮样式对比在空间维度上增加了一度,在复杂或宽裕的空间中更能起到强调的作用。

图5

但在追寻简洁设计的界面中,有些设计师也会首选扁平化的设计风格,来保证界面的内容设计不被打扰。所以,在这类扁平化的按钮设计中需要注意的是颜色的筛选与运用,在即保证界面风格统一的情况下,也要保证用户能够顺利的找到按钮元素,保证页面转化的效果。

文本字体

文本内容的字体、大小、粗细等因素都会影响到CTA按钮的视觉权重。一般设计遵循高优先级按钮使用粗体文本,低优先级按钮使用细体文本的原则,以此来影响用户阅读它们时的优先级,因此CTA按钮文本尽量选择粗体文本,诱使用户进行点击,提高转化率。

图6

添加图标

添加图标同样也可以给CTA按钮增加用户额外的视觉重量,提高用户在浏览页面时优先关注的概率,增加CTA按钮的转化效果。如下图的对比,视觉上会更加直观的注意到带有图标的按钮。

图7

位置

CTA按钮的设计,可以说是整个页面转化的核心与灵魂,尤其决定了一些付费推广的转化成果。所以除去按钮本身的设计外,CTA按钮的位置摆放也尤为关键。

一般来说按钮的位置与页面内容的繁简程度以及用户行动成本存在着一定关联性,文本内容愈简单,用户无需了解更多信息或者用户的行动成本很低甚至没有,用户就越容易在页面浏览之前点击按钮,此时按钮可以放置在页面相对靠前的位置,相反如果用户需要为采取的行动付出一定代价时,比如给出联系方式、关注、付款等情况下,用户采取预期CTA行动的时间就会推后,一般会浏览完页面后再进行考量,此时按钮的位置建议放置在浏览页面的下端。

所以CTA的位置随内容的简单到复杂递降,即从页面上端到页面下端,如图所示:

图8

综上所述,关于如何设计一款真正具有行为召唤的按钮,实现具体的动态行为交互,设计师一方面需要了解CTA的重要性,另一方面更要着重注意影响其表现的所有细节。

以上关于CTA按钮设计的相关规则,希望可以给大家带来一些参考。

本文由 @IQS开发者社区 原创发布于人人都是产品经理。未经许可,禁止转载。

题图来自 Unsplash,基于CC0协议

者 | 无名之辈FTER

责编 | 夕颜

出品 | 程序人生(ID:coder_life)

本文翻译自Rasa官方文档,并融合了自己的理解和项目实战,同时对文档中涉及到的技术点进行了一定程度的扩展,目的是为了更好的理解Rasa工作机制。与本文配套的项目GitHub地址:ChitChatAssistant https://github.com/jiangdongguo/ChitChatAssistant,欢迎star和issues,我们共同讨论、学习!

对话管理

1.1 多轮对话

多轮对话是相对于单轮对话而言的,单轮对话侧重于一问一答,即直接根据用户的问题给出精准的答案。问答更接近一个信息检索的过程,虽然也可能涉及简单的上下文处理,但通常是通过指代消解和 query 补全来完成的,而多轮对话侧重于需要维护一个用户目标状态的表示和一个决策过程来完成任务,具体来说就是用户带着明确的目的而来,希望得到满足特定限制条件的信息或服务,例如:订餐,订票,寻找音乐、电影或某种商品等。因为用户的需求可以比较复杂,可能需要分多轮进行陈述,用户也可能在对话过程中不断修改或完善自己的需求。此外,当用户的陈述的需求不够具体或明确的时候,机器也可以通过询问、澄清或确认来帮助用户找到满意的结果。

因此,任务驱动的多轮对话不是一个简单的自然语言理解加信息检索的过程,而是一个决策过程,需要机器在对话过程中不断根据当前的状态决策下一步应该采取的最优动作(如:提供结果,询问特定限制条件,澄清或确认需求,等等)从而最有效的辅助用户完成信息或服务获取的任务。

注:任务驱动的多轮对话系统通常为封闭域(domain)(闲聊系统为开放域),而特定限制条件对应于槽(Slot),也就是说,用户在满足特定限制条件时就是一次槽值填充的过程,如果用户能够在一次会话中,满足全部的限制条件,那么就不必进行多轮对话,即可直接使用户得到满意的信息或服务。

1.2 对话管理

对话管理,即Dialog Management(DM),它控制着人机对话的过程,是人机对话系统的重要组成部分。DM会根据NLU模块输出的语义表示执行对话状态的更新和追踪,并根据一定策略选择相应的候选动作。简单来说,就是DM会根据对话历史信息,决定此刻对用户的反应,比如在任务驱动的多轮对话系统中,用户带着明确的目的如订餐、订票等,用户需求比较复杂,有很多限制条件,可能需要分多轮进行陈述,一方面,用户在对话过程中可以不断修改或完善自己的需求,另一方面,当用户的陈述的需求不够具体或明确的时候,机器也可以通过询问、澄清或确认来帮助用户找到满意的结果。如下图所示,DM 的输入就是用户输入的语义表达(或者说是用户行为,是 NLU 的输出)和当前对话状态,输出就是下一步的系统行为和更新的对话状态。这是一个循环往复不断流转直至完成任务的过程。

从本质上来说,**任务驱动的对话管理实际就是一个决策过程,系统在对话过程中不断根据当前状态决定下一步应该采取的最优动作(如:提供结果,询问特定限制条件,澄清或确认需求等),从而最有效的辅助用户完成信息或服务获取的任务。**对话管理的任务大致有:

  • 对话状态维护(dialog state tracking, DST)

对话状态是指记录了哪些槽位已经被填充、下一步该做什么、填充什么槽位,还是进行何种操作。用数学形式表达为,t+1 时刻的对话状态S(t+1),依赖于之前时刻 t 的状态St,和之前时刻 t 的系统行为At,以及当前时刻 t+1 对应的用户行为O(t+1)。可以写成S(t+1)←St+At+O(t+1)。

  • 生成系统决策(dialog policy)

根据 DST 中的对话状态(DS),产生系统行为(dialog act),决定下一步做什么 dialog act 可以表示观测到的用户输入(用户输入 -> DA,就是 NLU 的过程),以及系统的反馈行为(DA -> 系统反馈,就是 NLG 的过程)。

  • 作为接口与后端/任务模型进行交互

Rasa Core

Rasa Core是Rasa框架提供的对话管理模块,它类似于聊天机器人的大脑,主要的任务是维护更新对话状态和动作选择,然后对用户的输入作出响应。所谓对话状态是一种机器能够处理的对聊天数据的表征,对话状态中包含所有可能会影响下一步决策的信息,如自然语言理解模块的输出、用户的特征等;所谓动作选择,是指基于当前的对话状态,选择接下来合适的动作,例如向用户追问需补充的信息、执行用户要求的动作等。举一个具体的例子,用户说“帮我妈妈预定一束花”,此时对话状态包括自然语言理解模块的输出、用户的位置、历史行为等特征。在这个状态下,系统接下来的动作可能是:

  • 向用户询问可接受的价格,如“请问预期价位是多少?”;

  • 向用户确认可接受的价格,如“像上次一样买价值200的花可以吗?”

  • 直接为用户预订

2.1 Stories

Rasa的故事是一种训练数据的形式,用来训练Rasa的对话管理模型。故事是用户和人工智能助手之间的对话的表示,转换为特定的格式,其中用户输入表示为相应的意图(和必要的实体),而助手的响应表示为相应的操作名称。Rasa核心对话系统的一个训练示例称为一个故事。这是一个故事数据格式的指南。两段对话样本示例:

<!-- ##表示story的描述,没有实际作用 -->## greet + location/price + cuisine + num people* greet - utter_greet* inform{"location": "rome", "price": "cheap"} - action_on_it - action_ask_cuisine* inform{"cuisine": "spanish"} - action_ask_numpeople * inform{"people": "six"} - action_ack_dosearch
<!-- Form Action-->## happy path * request_weather - weather_form - form{"name": "weather_form"} - form{"name": }

Story格式大致包含三个部分:

  • 1. 用户输入(User Messages)

使用*开头的语句表示用户的输入消息,我们无需使用包含某个具体内容的输入,而是使用NLU管道输出的intent和entities来表示可能的输入。需要注意的是,如果用户的输入可能包含entities,建议将其包括在内,将有助于policies预测下一步action。这部分大致包含三种形式,示例如下:

(1)* greet 表示用户输入没有entity情况;

(2)* inform{"people": "six"} 表示用户输入包含entity情况,响应这类intent为普通action;

(3)* request_weather 表示用户输入Message对应的intent为form action情况;

  • 2. 动作(Actions)

使用-开头的语句表示要执行动作(Action),可分为utterance actions和custom actions,其中,前者在domain.yaml中定义以utter_为前缀,比如名为greet的意图,它的回复应为utter_greet;后者为自定义动作,具体逻辑由我们自己实现,虽然在定义action名称的时候没有限制,但是还是建议以action_为前缀,比如名为inform的意图fetch_profile的意图,它的response可为action_fetch_profile。

  • 3. 事件(Events)

Events也是使用-开头,主要包含槽值设置(SlotSet)和激活/注销表单(Form),它是是Story的一部分,并且必须显示的写出来。Slot Events和Form Events的作用如下:

(1)Slot Events

Slot Events的作用当我们在自定义Action中设置了某个槽值,那么我们就需要在Story中Action执行之后显著的将这个SlotSet事件标注出来,格式为- slot{"slot_name": "value"}。比如,我们在action_fetch_profile中设置了Slot名为account_type的值,代码如下:

from rasa_sdk.actions import Actionfrom rasa_sdk.events import SlotSetimport requests

class FetchProfileAction(Action): def name(self): return "fetch_profile"

def run(self, dispatcher, tracker, domain): url = "http://myprofileurl.com" data = requests.get(url).json return [SlotSet("account_type", data["account_type"])]

那么,就需要在Story中执行action_fetch_profile之后,添加- slot{"account_type" : "premium"}。虽然,这么做看起来有点多余,但是Rasa规定这么做必须的,目的是提高训练时准确度。

## fetch_profile* fetch_profile - action_fetch_profile - slot{"account_type" : "premium"} - utter_welcome_premium

当然,如果您的自定义Action中将槽值重置为None,则对应的事件为-slot{"slot_name": }。

(2)Form Events

在Story中主要存在三种形式的表单事件(Form Events),它们可表述为:

  • Form Action事件

Form Action即表单动作事件,是自定义Action的一种,用于一个表单操作。示例如下:

- restaurant_form
  • Form activation事件

form activation即激活表单事件,当form action事件执行后,会立马执行该事件。示例如下:

- form{"name": "restaurant_form"}
  • Form deactivation事件

form deactivation即注销表单事件,作用与form activation相反。示例如下:

- form{"name": }

总之,我们在构建Story时,可以说是多种多样的,因为设计的故事情节是多种多样的,这就意味着上述三种内容的组合也是非常灵活的。另外,在设计Story时Rasa还提供了Checkpoints 和OR statements两种功能,来提升构建Story的灵活度,但是需要注意的是,东西虽好,但是不要太贪了,过多的使用不仅增加了复杂度,同时也会拖慢训练的速度。其中,Checkpoints用于模块化和简化训练数据,示例如下:

## first story* greet - action_ask_user_question> check_asked_question

## user affirms question> check_asked_question* affirm - action_handle_affirmation> check_handled_affirmation

## user denies question> check_asked_question* deny - action_handle_denial> check_handled_denial

## user leaves> check_handled_denial> check_handled_affirmation* goodbye - utter_goodbye

在上面的例子中,可以使用> check_asked_question表示first story,这样在其他story中,如果有相同的first story部分,可以直接用> check_asked_question代替。而OR Statements主要用于实现某一个action可同时响应多个意图的情况,比如下面的例子:

## story* affirm OR thankyou - action_handle_affirmation

2.2 Domain

Domain,译为**“领域”**,它描述了对话机器人应知道的所有信息,类似于“人的大脑”,存储了意图intents、实体entities、插槽slots以及动作actions等信息,其中,intents、entities在NLU训练样本中定义,slots对应于entities类型,只是表现形式不同。domain.yml文件组成结构如下:

具体介绍如下:

1. intents

intents: - affirm - deny - greet - request_weather - request_number - inform - inform_business - stop - chitchat

intents,即意图,是指我们输入一段文本,希望Bot能够明白这段文本是什么意思。在Rasa框架中,意图的定义是在NLU样本中实现的,并且在每个意图下面我们需要枚举尽可多的样本用于训练,以达到Bot能够准确识别出我们输入的一句话到底想要干什么。

2. session_config

session_config: carry_over_slots_to_new_session: true session_expiration_time: 60

session_config,即会话配置,这部分的作用为配置一次会话(conversation session)是否有超时限制。上例演示的是,每次会话的超时时间为60s,如果用户开始一段会话后,在60s内没有输入任何信息,那么这次会话将被结束,然后Bot又会开启一次新的会话,并将上一次会话的Slot值拷贝过来。当然,我们希望舍弃上一次会话Slot的值,可以将carry_over_slots_to_new_session设置为false。另外,当session_expiration_time被设置为0时,Bot永远不会结束当前会话并一直等待用户输入(注:执行action_session_start仍然可以开始一次新的会话,在设置为0的情况下)。

3. slots

slots: date_time: type: unfeaturized auto_fill: false address: type: unfeaturized auto_fill: false

Slots,即插槽,它就像对话机器人的内存,它通过键值对的形式可用来收集存储用户输入的信息(实体)或者查询数据库的数据等。关于Slots的设计与使用,详情请见本文2.6小节。

4. entities

entities: - date_time - address

entities,即实体,类似于输入文本中的关键字,需要在NLU样本中进行标注,然后Bot进行实体识别,并将其填充到Slot槽中,便于后续进行相关的业务操作。

5. actions

actions: - utter_answer_affirm # utter_开头的均为utter actions - utter_answer_deny - utter_answer_greet - utter_answer_goodbye - utter_answer_thanks - utter_answer_whoareyou - utter_answer_whattodo - utter_ask_date_time - utter_ask_address - utter_ask_number - utter_ask_business - utter_ask_type - action_default_fallback # default actions

当Rasa NLU识别到用户输入Message的意图后,Rasa Core对话管理模块就会对其作出回应,而完成这个回应的模块就是action。Rasa Core支持三种action,即default actions、utter actions以及 custom actions。关于如何实现Actions和处理业务逻辑,我们在一篇文章中详谈,这里仅作简单了解。

6. forms

forms: - weather_form

forms,即表单,该部分列举了在NLU样本中定义了哪些Form Actions。关于Form Actions的相关知识,请移步至本文的2.7小节。

7. responses

responses: utter_answer_greet: - text: "您好!请问我可以帮到您吗?" - text: "您好!很高兴为您服务。请说出您要查询的功能?"
utter_ask_date_time: - text: "请问您要查询哪一天的天气?"

utter_ask_address: - text: "请问您要查下哪里的天气?"
utter_default: - text: "没听懂,请换种说法吧~"

responses部分就是描述UtterActions具体的回复内容,并且每个UtterAction下可以定义多条信息,当用户发起一个意图,比如 “你好!”,就触发utter_answer_greet操作,Rasa Core会从该action的模板中自动选择其中的一条信息作为结果反馈给用户。

2.3 Responses

Responses的作用就是自动响应用户输入的信息,因此我们需要管理这些响应(Responses)。Rasa框架提供了三种方式来管理Responses,它们是:

  • 在domain.yaml文件中存储Responses;

  • 在训练数据中存储Responses;

  • 自定义一个NLG服务来生成Responses。

由于第一种我们在本文2.2(7)小节有过介绍,而创建NLG服务是这样的:

nlg: url: http://localhost:5055/nlg # url of the nlg endpoint # you can also specify additional parameters, if you need them: # headers: # my-custom-header: value # token: "my_authentication_token" # will be passed as a get parameter # basic_auth: # username: user # password: pass# example of redis external tracker store configtracker_store: type: redis url: localhost port: 6379 db: 0 password: password record_exp: 30000# example of mongoDB external tracker store config#tracker_store: #type: mongod #url: mongodb://localhost:27017 #db: rasa #user: username #password: password

2.4 Actions

当Rasa NLU识别到用户输入Message的意图后,Rasa Core对话管理模块就会对其作出回应,而完成这个回应的模块就是action。Rasa Core支持三种action,即default actions、utter actions以及 custom actions。关于如何实现Actions和处理业务逻辑,我们在一篇文章中详谈,这里仅作简单了解。

1. default actions

DefaultAction是Rasa Core默认的一组actions,我们无需定义它们,直接可以story和domain中使用。包括以下三种action:

  • action_listen:监听action,Rasa Core在会话过程中通常会自动调用该action;

  • action_restart:重置状态,比初始化Slots(插槽)的值等;

  • action_default_fallback:当Rasa Core得到的置信度低于设置的阈值时,默认执行该action;

2. utter actions

UtterAction是以utter_为开头,仅仅用于向用户发送一条消息作为反馈的一类actions。定义一个UtterAction很简单,只需要在domain.yml文件中的actions:字段定义以utter_为开头的action即可,而具体回复内容将被定义在templates:部分,这个我们下面有专门讲解。定义utter actions示例如下:

actions: - utter_answer_greet - utter_answer_goodbye - utter_answer_thanks - utter_introduce_self - utter_introduce_selfcando - utter_introduce_selffrom

3. custom actions

CustomAction,即自定义action,允许开发者执行任何操作并反馈给用户,比如简单的返回一串字符串,或者控制家电、检查银行账户余额等等。它与DefaultAction不同,自定义action需要我们在domain.yml文件中的actions部分先进行定义,然后在指定的webserver中实现它,其中,这个webserver的url地址在endpoint.yml文件中指定,并且这个webserver可以通过任何语言实现,当然这里首先推荐python来做,毕竟Rasa Core为我们封装好了一个rasa-core-sdk专门用来处理自定义action。关于action web的搭建和action的具体实现,我们在后面详细讲解,这里我们看下在在Rasa Core项目中需要做什么。假如我们在天气资讯的人机对话系统需提供查询天气和空气质量两个业务,那么我们就需要在domain.yml文件中定义查询天气和空气质量的action,即:

actions: ...  - action_search_weather

另外,FormAction也是自定义actions,但是需要在domainl.yaml文件的forms字段声明。

forms: - weather_form

2.5 Policies

Policies是Rasa Core中的策略模块,对应类rasa_core.policies.Policy,它的作用就是使用合适的策略(Policy)来预测一次对话后要执行的行为(Actions)。预测的原理是衡量命中的哪些Policies哪个置信度高,由置信度高的Policy选择合适的Action执行。假如出现不同的Policy拥有相同的置信度,那么就由它们的优先级决定,即选择优先级高的Policy。Rasa对提供的Policies进行了优先级排序,具体如下表:

它们的描述与作用如下:

  • Memoization Policy

MemoizationPolicy只记住(memorizes)训练数据中的对话。如果训练数据中存在这样的对话,那么它将以置信度为1.0预测下一个动作,否则将预测为None,此时置信度为0.0。下面演示了如何在策略配置文件config.yml文件中,配置MemoizationPlicy策略,其中,max_history(超参数)决定了模型查看多少个对话历史以决定下一个执行的action。

 policies: - name: "MemoizationPolicy" max_history: 5

注:max_history值越大训练得到的模型就越大并且训练时间会变长,关于该值到底该设置多少,我们可以举这么个例子,比如有这么一个Intent:out_of_scope来描述用户输入的消息off-topic(离题),当用户连续三次触发out_of_scope意图,这时候我们就需要主动告知用户需要向其提供帮助,如果要Rasa Core能够学习这种模型,max_history应该至少为3。story.md中表现如下:

* out_of_scope - utter_default* out_of_scope - utter_default* out_of_scope - utter_help_message
  • Keras Policy

KerasPolicy策略是Keras框架中实现的神经网络来预测选择执行下一个action,它默认的框架使用LSTM(Long Short-Term Memory,长短期记忆网络)算法,但是我们也可以重写KerasPolicy.model_architecture函数来实现自己的框架(architecture)。KerasPolicy的模型很简单,只是单一的LSTM+Dense+softmax,这就需要我们不断地完善自己的story来把各种情况下的story进行补充。下面演示了如何在策略配置文件config.yml文件中,配置KerasPolicy策略,其中,epochs表示训练的次数,max_history同上。

policies: - name: KerasPolicy epochs: 100 max_history: 5
  • Embedding Policy

基于机器学习的对话管理能够学习复杂的行为以完成任务,但是将其功能扩展到新领域并不简单,尤其是不同策略处理不合作用户行为的能力,以及在学习新任务(如预订酒店)时,如何将完成一项任务(如餐厅预订)重新应用于该任务时的情况。EmbeddingPolicy,即循环嵌入式对话策略(Recurrent Embedding Dialogue Policy,REDP),它通过将actions和对话状态嵌入到相同的向量空间(vector space)能够获得较好的效果,REDP包含一个基于改进的Neural Turing Machine的记忆组件和注意机制,在该任务上显著优于基线LSTM分类器。

EmbeddingPolicy效果上优于KerasPolicy,但是它有个问题是耗时,因为它没有使用GPU、没有充分利用CPU资源。KerasPolicy和EmbeddingPolicy比较示意图如下:

配置EmbeddingPolicy参数:

policies: - name: EmbeddingPolicy epochs: 100 featurizer: - name: FullDialogueTrackerFeaturizer state_featurizer: - name: LabelTokenizerSingleStateFeaturizer

注:新版的Rasa将EmbeddingPolicy重命名为TEDPolicy,但是我在config.yml配置文件中将其替换后,提示无法找到TEDPolicy异常,具体原因不明,暂还未涉及源码分析。

  • Form Policy

FormPolicy是MemoizationPolicy的扩展,用于处理(form)表单的填充事项。当一个FormAction被调用时,FormPolicy将持续预测表单动作,直到表单中的所有槽都被填满,然后再执行对应的FormAction。如果在Bot系统中使用了FormActions,就需要在config.yml配置文件中进行配置。

policies: - name: FormPolicy
  • Mapping Policy

MappingPolicy可用于直接将意图映射到要执行的action,从而实现被映射的action总会被执行,其中,这种映射是通过triggers属性实现的。举个栗子(domain.yml文件中):

intents: - greet: {triggers: utter_goodbye}

其中,greet是意图;utter_goodbye是action。一个意图最多只能映射到一个action,我们的机器人一旦收到映射意图的消息,它将执行对应的action。然后,继续监听下一条message。需要注意的是,对于上述映射,我们还需要要在story.md文件中添加如下样本,否则,任何机器学习策略都可能被预测的action_greet在dialouge历史中突然出现而混淆。

  • Fallback Policy

如果意图识别的置信度低于nlu_threshold,或者没有任何对话策略预测的action置信度高于core_threshold,FallbackPolicy将执行fallback action。通俗来说,就是我们的对话机器人意图识别和action预测的置信度没有满足对应的阈值,该策略将使机器人执行指定的默认action。configs.yml配置如下:

policies: - name: "FallbackPolicy" # 意图理解置信度阈值 nlu_threshold: 0.3 # action预测置信度阈值 core_threshold: 0.3 # fallback action fallback_action_name: 'action_default_fallback'

其中,action_default_fallback是Rasa Core中的一个默认操作,它将向用户发送utter_default模板消息,因此我们需要确保在domain.yml文件中指定此模板。当然,我们也可以在fallback_action_name字段自定义默认回复的action,比如my_fallback_cation,就可以这么改:

policies: - name: "FallbackPolicy" nlu_threshold: 0.4 core_threshold: 0.3 fallback_action_name: "my_fallback_action"

2.6 Slots

Slots,槽值,相当于机器人的内存(memory),它们以键值对的形式存在,用于存储用户输入时消息时比较重要的信息,而这些信息将为Action的执行提供关键数据。Slots的定义位于domain.yaml文件中,它们通常与Entities相对应,即Entities有哪些,Slots就有哪些,并且Slots存储的值就是NLU模型提取的Entities的值。

2.6.1 Slots Type

1. Text类型

示例:

# domain.yamlslots: cuisine: type: text

2. Boolean类型

示例:

slots: is_authenticated: type: bool

3. categorical类型

示例:

slots: risk_level: type: categorical values: - low - medium - high

4. Float类型

示例:

slots: temperature: type: float min_value: -100.0 max_value: 100.0

5. List类型

示例:

slots: shopping_items: type: list

6. Unfeaturized 类型

示例:

slots: internal_user_id: type: unfeaturized

2.6.2 Slots Set

Slots值填充有多种方式,它们的操作方式如下:

1. Slots Initial

# domain.yamlslots: name: type: text initial_value: "human"

在domain.yaml文件中声明slots时,可以通过initial_value字段为当前slot提供一个初始值,也就是说,当会话开始时,被设定初始值的slot已经被填充好。当然,这个操作不是必须的。

2. Slots Set from NLU

# stories.md# story_01* greet{"name": "Ali"} - slot{"name": "Ali"} - utter_greet

假如在stories.md文件添加一个包含-slot{}的story,这就意味着当NLU模型提取到一个名为name的实体且这个实体有在domain.yaml中定义,那么NLU模型提取到的实体值会被自动填充到name槽中。实际上,对于Rasa来说,就算你不添加-slot{}字段,这个实体值也会被提取并自动填充到name槽中。当然,如果你希望禁止这种自动填充行为,改为添加-slot{}字段填充,可以在domain.yaml定义slot时,设置auto_fill的值为False,即:

# domain.yamlslots: name: type: text auto_fill: False

3. Slots Set By Clicking Buttons

# domain.yamlutter_ask_color:- text: "what color would you like?" buttons: - title: "blue"  payload: '/choose{"color": "blue"}' # 格式 '/intent{"entity":"value",...}'  - title: "red" payload: '/choose{"color": "red"}' 

在点击Button时填充Slots的值,是指当我们的Bot(Rasa Core)在回复用户时,可以在回复的消息中附加Button信息,这种Button类似于快捷键,用户获取到之后,可以直接将其发送给Rasa Core,它会直接进行解析以识别intent和提取entity,并将entity的值填充到slot中。比如你让用户通过点击一个按钮来选择一种颜色,那么可以在domain.yaml中utter_ask_color的回复中添加buttons:/choose{"color": "blue"}和/choose{"color": "red"}。注:通常每个button由title和payload字段组成。

4. Slots Set by Actions

from rasa_sdk.actions import Actionfrom rasa_sdk.events import SlotSetimport requests

class FetchProfileAction(Action): def name(self): return "fetch_profile"

def run(self, dispatcher, tracker, domain): url = "http://myprofileurl.com" data = requests.get(url).json return [SlotSet("account_type", data["account_type"])]

该示例演示了如何在Custom Action中通过返回事件来填充Slots的值,即调用SlotSet事件函数并将该事件return。需要注意的是,为了达到这个目的,我们在编写Story时必须包含该Slot,即使用-slot{}实现,只有这样Rasa Core就会从提供的信息中进行学习,并决定执行正确的action。Story.md示例如下:

# story_01* greet - action_fetch_profile - slot{"account_type" : "premium"} - utter_welcome_premium

# story_02* greet - action_fetch_profile - slot{"account_type" : "basic"} - utter_welcome_basic

其中,account_type在domain.yaml中定义如下:

slots: account_type: type: categorical values: - premium - basic

2.6.3 Slots Get

目前主要有两种获取Slots值方式:

1. Get Slot in responses

responses: utter_greet: - text: "Hey, {name}. How are you?"

在domain.yaml的responses部分,可以通过{slotname}的形式获取槽值。

2. Get Slot in Custom Action

from rasa_sdk.actions import Actionfrom rasa_sdk.events import SlotSetimport requests

class FetchProfileAction(Action): def name(self): return "fetch_profile"

def run(self, dispatcher, tracker, domain): # 获取slot account_type的值 account_type = tracker.get_slot('account_type') return

Tracker,可理解为跟踪器,作用是以会话会话的形式维护助手和用户之间的对话状态。通过Tracker,能够轻松获取整个对话信息,其中就包括Slot的值。

2.7 Form

在Rasa Core中,当我们执行一个action需要同时填充多个slot时,可以使用FormAction来实现,因为FormAction会遍历监管的所有slot,当发现相关的slot未被填充时,就会向用户主动发起询问,直到所有slot被填充完毕,才会执行接下来的业务逻辑。使用步骤如下:

(1)构造story

在story中,不仅需要考虑用户按照我们的设计准确的提供有效信息,而且还要考虑用户在中间过程改变要执行的意图情况或称输入无效信息,因为对于FormAction来说,如果无法获得预期的信息就会报错,这里我们分别称这两种情况为happy path、unhappy path。示例如下:

## happy path* request_weather - weather_form - form{"name": "weather_form"} 激活form - form{"name": } 使form无效## unhappy path* request_weather - weather_form - form{"name": "weather_form"}* stop - utter_ask_continue* deny - action_deactivate_form - form{"name": }

注:* request_restaurant为意图;- restaurant_form为form action;- form{"name": "restaurant_form"}为激活form;- form{"name": }为注销form;- action_deactivate_form为默认的action,它的作用是用户可能在表单操作过程中改变主意,决定不继续最初的请求,我们使用这个default action来禁止(取消)表单,同时重置要请求的所有slots。

构建stroy最好使用官方提供的Interactive Learning,防止漏掉信息,详细见本文2.8小节。

(2)添加form字段到Domain

在doamin文件下新增forms:部分,并将所有用到的form名称添加到该字段下:

intents: - request_weather
forms: - weather_form

(3)配置FormPolicy

在工程的配置文件configs.yml中,新增FormPolicy策略:

policies: - name: EmbeddingPolicy epochs: 100 max_history: 5 - name: FallbackPolicy fallback_action_name: 'action_default_fallback' - name: MemoizationPolicy max_history: 5 - name: FormPolicy

(4)Form Action实现

class WeatherForm(FormAction):

def name(self) -> Text: """Unique identifier of the form"""

return "weather_form"

@staticmethod def required_slots(tracker: Tracker) -> List[Text]: """A list of required slots that the form has to fill"""

return ["date_time", "address"]

def submit( self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any], ) -> List[Dict]: """Define what the form has to do after all required slots are filled""" address = tracker.get_slot('address') date_time = tracker.get_slot('date_time')

return

当form action第一被调用时,form就会被激活并进入FormPolicy策略模式。每次执行form action,required_slots会被调用,当发现某个还未被填充时,会主动去调用形式为uter_ask_{slotname}的模板(注:定义在domain.yml的templates字段中);当所有slot被填充完毕,submit方法就会被调用,此时本次form操作完毕被取消激活。

2.8 Interactive Learning

虽然我们可以容易的人工构建story样本数据,但是往往会出现一些考虑不全,甚至出错等问题,基于此,Rasa Core框架为我们提供了一种交互式学习(Interactive Learning)来获得所需的样本数据。在互动学习模式中,当你与机器人交谈时,你会向它提供反馈,这是一种强大的方法来探索您的机器人可以做什么,也是修复它所犯错误的最简单的方法。基于机器学习的对话的一个优点是,当你的机器人还不知道如何做某事时,你可以直接教它。

(1)开启Action Server

python -m rasa run actions --port 5055 --actions actions --debug

(2)开启Interactive Learning

python -m rasa interactive -m models/20200313-101055.tar.gz --endpoints configs/endpoints.yml --config configs/config.yml # 或者(没有已训练模型情况)# rasa会先训练好模型,再开启交互式学习会话python -m rasa interactive --data /data --domain configs/domain.yml --endpoints configs/endpoints.yml --config configs/config.yml 

分别执行(1)、(2)命令后,我们可以预设一个交互场景根据终端的提示操作即可。如果一个交互场景所有流程执行完毕,按Ctrl+C结束并选择Start Fresh进入下一个场景即可。当然Rasa还提供了可视化界面,以帮助你了解每个Story样本构建的过程,网址:http://localhost:5005/visualization.html。

执行流程大致如下:

Bot loaded. Visualisation at http://localhost:5006/visualization.html .Type a message and press enter (press 'Ctr-c' to exit).? Your input -> 查询身份证439912199008071234? Is the intent 'request_idcard' correct for '查询身份证[439912199008071234](id_number)' and are all entities labeled correctly? Yes------Chat History

# Bot You ─────────────────────────────────────────────────────────────────── 1 action_listen─────────────────────────────────────────────────────────────────── 2 查询身份证[439912199008071234](id_number) intent: request_idcard 1.00



Current slots: address: None, business: None, date-time: None, id_number: None, requested_slot: None

------? The bot wants to run 'number_form', correct? YesChat History

# Bot You ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 1 action_listen────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 2 查询身份证[439912199008071234](id_number) intent: request_idcard 1.00────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 3 number_form 1.00 您要查询的身份证号码439912199008071234所属人为张三,湖南长沙人,现在就职于地球村物业。 form{"name": "number_form"} slot{"id_number": "439912199008071234"} form{"name": } slot{"requested_slot": }



Current slots: address: None, business: None, date-time: None, id_number: 439912199008071234, requested_slot: None

------? The bot wants to run 'action_listen', correct? Yes

生成的一个Story示例如下:

## interactive_story_10# unhappy path:chitchat stop but continue path* greet - utter_answer_greet* request_number{"type": "身份证号码"} - number_form - form{"name": "number_form"} - slot{"type": "身份证号码"} - slot{"number": } - slot{"business": } - slot{"requested_slot": "number"}* chitchat - utter_chitchat - number_form - slot{"requested_slot": "number"}* stop - utter_ask_continue* affirm - number_form - slot{"requested_slot": "number"}* form: request_number{"number": "440123199087233467"} - form: number_form - slot{"number": "440123199087233467"} - slot{"type": "身份证号码"} - form{"name": } - slot{"requested_slot": }* thanks - utter_noworries

改进ChitChatAssistant项目

3.1 config.yml

# zh_jieba_mitie_embeddings_config.yml

language: "zh"

pipeline:- name: "MitieNLP" model: "data/total_word_feature_extractor_zh.dat"- name: "JiebaTokenizer" dictionary_path: "data/dict"- name: "MitieEntityExtractor"- name: "EntitySynonymMapper"- name: "RegexFeaturizer"- name: "MitieFeaturizer"- name: "EmbeddingIntentClassifier"

policies: - name: FallbackPolicy nlu_threshold: 0.5 ambiguity_threshold: 0.1 core_threshold: 0.5 fallback_action_name: 'action_default_fallback' - name: MemoizationPolicy max_history: 5 - name: FormPolicy - name: MappingPolicy - name: EmbeddingPolicy epochs: 500

考虑到目前项目的样本较少,这里使用MITIE+EmbeddingPolicy组合,虽然训练时慢了点,但是能够保证实体提取的准确性,同时又能够提高意图识别的命中率。

3.2 weather_stories.md

## happy path* request_weather - weather_form - form{"name": "weather_form"} - form{"name": }
## happy path* greet - utter_answer_greet* request_weather - weather_form - form{"name": "weather_form"} - form{"name": }* thanks - utter_noworries

## unhappy path* greet - utter_answer_greet* request_weather - weather_form - form{"name": "weather_form"}* chitchat - utter_chitchat - weather_form - form{"name": }* thanks - utter_noworries

## very unhappy path* greet - utter_answer_greet* request_weather - weather_form - form{"name": "weather_form"}* chitchat - utter_chitchat - weather_form* chitchat - utter_chitchat - weather_form* chitchat - utter_chitchat - weather_form - form{"name": }* thanks - utter_noworries

## stop but continue path* greet - utter_answer_greet* request_weather - weather_form - form{"name": "weather_form"}* stop - utter_ask_continue* affirm - weather_form - form{"name": }* thanks - utter_noworries

## stop and really stop path* greet - utter_answer_greet* request_weather - weather_form - form{"name": "weather_form"}* stop - utter_ask_continue* deny - action_deactivate_form - form{"name": }

## chitchat stop but continue path* request_weather - weather_form - form{"name": "weather_form"}* chitchat - utter_chitchat - weather_form* stop - utter_ask_continue* affirm - weather_form - form{"name": }* thanks - utter_noworries

## stop but continue and chitchat path* greet - utter_answer_greet* request_weather - weather_form - form{"name": "weather_form"}* stop - utter_ask_continue* affirm - weather_form* chitchat - utter_chitchat - weather_form - form{"name": }* thanks - utter_noworries

## chitchat stop but continue and chitchat path* greet - utter_answer_greet* request_weather - weather_form - form{"name": "weather_form"}* chitchat - utter_chitchat - weather_form* stop - utter_ask_continue* affirm - weather_form* chitchat - utter_chitchat - weather_form - form{"name": }* thanks - utter_noworries

## chitchat, stop and really stop path* greet - utter_answer_greet* request_weather - weather_form - form{"name": "weather_form"}* chitchat - utter_chitchat - weather_form* stop - utter_ask_continue* deny - action_deactivate_form - form{"name": }
## interactive_story_1## 天气 + 时间 + 地点 + 地点* request_weather - weather_form - form{"name": "weather_form"} - slot{"requested_slot": "date_time"}* form: inform{"date_time": "明天"} - form: weather_form - slot{"date_time": "明天"} - slot{"requested_slot": "address"}* form: inform{"address": "广州"} - form: weather_form - slot{"address": "广州"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "后天"} OR request_weather{"date_time": "后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "广州"} - slot{"date_time": "后天"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

## interactive_story_1## 天气 + 时间 + 地点 + 时间* request_weather - weather_form - form{"name": "weather_form"} - slot{"requested_slot": "date_time"}* form: inform{"date_time": "明天"} - form: weather_form - slot{"date_time": "明天"} - slot{"requested_slot": "address"}* form: inform{"address": "广州"} - form: weather_form - slot{"address": "广州"} - form{"name": } - slot{"requested_slot": }* inform{"address": "上海"} OR request_weather{"address": "深圳"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "广州"} - slot{"address": "上海"} - form{"name": } - slot{"requested_slot": }* affirm - utter_answer_affirm

## interactive_story_2## 天气/时间/地点 + 地点* request_weather{"date_time": "明天", "address": "上海"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "上海"} - form{"name": } - slot{"requested_slot": }* inform{"address": "广州"} OR request_weather{"address": "广州"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "上海"} - slot{"address": "广州"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

## interactive_story_3## 天气/时间/地点 + 时间* request_weather{"address": "深圳", "date_time": "后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "深圳"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "大后天"} OR request_weather{"date_time": "大后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "深圳"} - slot{"date_time": "大后天"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

## interactive_story_2## 天气/时间/地点 + 地点 + 时间* request_weather{"date_time": "明天", "address": "上海"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "上海"} - form{"name": } - slot{"requested_slot": }* inform{"address": "北京"} OR request_weather{"address": "北京"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "上海"} - slot{"address": "北京"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "后天"} OR request_weather{"date_time": "后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "北京"} - slot{"date_time": "后天"} - form{"name": } - slot{"requested_slot": }* affirm - utter_answer_affirm

## interactive_story_3## 天气/时间/地点 + 地点 + 地点* request_weather{"date_time": "后天", "address": "北京"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "北京"} - form{"name": } - slot{"requested_slot": }* inform{"address": "深圳"} OR request_weather{"address": "深圳"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "北京"} - slot{"address": "深圳"} - form{"name": } - slot{"requested_slot": }* inform{"address": "南京"} OR request_weather{"address": "南京"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "深圳"} - slot{"address": "南京"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

## interactive_story_4## 天气/时间/地点 + 时间 + 地点* request_weather{"date_time": "明天", "address": "长沙"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "长沙"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "后天"} OR request_weather{"date_time": "后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "长沙"} - slot{"date_time": "后天"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "大后天"} OR request_weather{"date_time": "大后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "长沙"} - slot{"date_time": "大后天"} - form{"name": } - slot{"requested_slot": }* affirm - utter_answer_affirm

## interactive_story_5## 天气/时间/地点 + 时间 + 时间* request_weather{"date_time": "后天", "address": "深圳"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "深圳"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "明天"} OR request_weather{"date_time": "明天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "深圳"} - slot{"date_time": "明天"} - form{"name": } - slot{"requested_slot": }* inform{"address": "广州"} OR request_weather{"address": "广州"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "深圳"} - slot{"address": "广州"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

## interactive_story_4## 天气/时间 + 地点 + 时间* request_weather{"date_time": "明天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"requested_slot": "address"}* form: inform{"address": "广州"} - form: weather_form - slot{"address": "广州"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "后天"} OR request_weather{"date_time": "后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "广州"} - slot{"date_time": "后天"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

## interactive_story_5## 天气/地点 + 时间 + 时间* request_weather{"address": "广州"} - weather_form - form{"name": "weather_form"} - slot{"address": "广州"} - slot{"requested_slot": "date_time"}* form: inform{"date_time": "后天"} - form: weather_form - slot{"date_time": "后天"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "明天"} OR request_weather{"date_time": "明天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "广州"} - slot{"date_time": "明天"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks
## interactive_story_1## 天气/时间/地点 + chit + chit(restart)+询问天气* request_weather{"date_time": "今天", "address": "广州"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "今天"} - slot{"address": "广州"} - form{"name": } - slot{"requested_slot": }* chitchat - utter_chitchat* chitchat - utter_chitchat - action_restart* request_weather - weather_form - form{"name": "weather_form"} - slot{"requested_slot": "date_time"}* form: inform{"date_time": "今天"} - form: weather_form - slot{"date_time": "今天"} - slot{"requested_slot": "address"}* form: inform{"address": "广州"} - form: weather_form - slot{"address": "广州"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

在构建Story样本时,主要是使用Interactive Learning工具实现,以确保枚举尽可能多的unhappy story,同时又能够防止在构建样本时出现信息遗漏的情况。此外,本版本中除了查询天气这个案例,还新增了其他案例,并列举了如何使用同义词、自定义字典以及正则表达式的使用方法,详细见最新版项目。

GitHub地址:ChitChatAssistant https://github.com/jiangdongguo/ChitChatAssistant,欢迎star和issues,我们共同讨论、学习!

原文链接:

https://blog.csdn.net/AndrExpert/article/details/105434136

☞没有监控和日志咋整?老程序员来支招

☞朱广权李佳琦直播掉线,1.2亿人在线等

☞RPC的超时设置,一不小心就是线上事故!

☞拿下Gartner容器产品第一,阿里云打赢云原生关键一战!

☞深聊Solidity的测试场景、方法和实践,太详细了,必须收藏!

☞万字干货:一步步教你如何在容器上构建持续部署!

☞据说,这是当代极客们的【技术风向标】...

今日福利:评论区留言入选,可获得价值299元的「2020 AI开发者万人大会」在线直播门票一张。 快来动动手指,写下你想说的话吧。


上一篇:「HTML4」HTML入门
下一篇:HTML5 Audio(音频)