IBM Cloud Docs
交谈构建技巧

交谈构建技巧

获取有关如何处理常见任务的提示。

添加节点

  • 添加用于描述节点用途的节点名。

    您现在知道节点的用途,但几个月后,很可能就不记得了。 节点名会显示在日志中,这可帮助您日后调试交谈。

  • 要收集完成任务所需的信息,请使用带插槽的节点而不是一堆单独的节点来从用户获取信息。 请参阅使用槽收集信息

  • 对于复杂的流程流,请告诉用户他们在流程开始时需要提供的任何信息。

  • 了解助手如何遍历对话树,以及文件夹、分支、“跳转至”和离题对路径的影响。 请参阅对话流

  • 不要在所有位置添加“跳转至”。 “跳转至”会增加对话流的复杂性,并增加日后调试对话的难度。

  • 要跳转至当前节点所在分支中的节点,请使用跳过用户输入,而不要使用跳转至

    此选项可防止您在除去或重新排序跳转至的子节点时必须编辑当前节点的设置。 请参阅定义后续操作

  • 允许从节点离题之前,请测试最常见的用户场景。 确保将可能离题到的节点配置为返回。 请参阅离题

添加响应

  • 使回答保持简短、有用。

  • 在响应中反映出用户的意向。

    这样做可让用户确信机器人理解他们的意图,如果理解有误,用户也有机会立即更正误解之处。

  • 如果答案取决于频繁更改的数据,请在响应中包含指向外部站点的链接。

  • 避免过度使用按钮。 鼓励用户从一组按钮中选择预定义的选项不太像真正的交谈,而且会降低您了解用户真正希望做什么的能力。 允许真实用户用自己的话进行询问时,可以使用输入来训练系统,并派生出更好的意向。

  • 避免在一个节点足够时使用一堆节点。 例如,根据用户提供的详细信息,将多个条件响应添加到单个节点以返回不同的响应。 请参阅条件响应

  • 认真对响应进行措辞。 您可以仅仅根据响应的措辞方式就改变人们对系统的反应方式。 更改一行文本可能会使您不必编写多行代码来实施复杂的程序化解决方案。

从用户输入中捕获信息的技巧

很难知道在对话节点中使用什么语法,才能准确捕获要在用户输入中查找的信息。 以下是可用于实现共同目标的一些方法。

  • 返回用户的输入:可以捕获用户所说的准确文本,并在响应中返回这些文本。 在响应中使用以下 SpEL 表达式以在响应中复述用户指定的文本:

    You said: <? input.text ?>.

    如果开启了自动更正,并且您希望在更正之前返回用户的原始输入,那么可以使用 <? input.original_text ?>。 但是,请确保使用响应条件首先检查 original_text 字段是否存在。

  • 确定用户输入中的字数:可以对 input.text 对象执行任何支持的 String 方法。 例如,可以使用以下 SpEL 表达式来了解用户话语中的字数:

    input.text.split(' ').size()

  • 处理多个意向: 用户输入表示两个单独任务的输入。I want to open a savings account and apply for a credit card. 对话如何识别并处理这两个任务呢? 请参阅 复合问题 博客条目。

  • 处理模棱两可的意向: 用户输入的输入足够模棱两可,以至于助手可以找到两个或更多具有可能解决该问题的意向的节点。 对话如何知道该执行哪个对话分支呢? 如果启用了消歧,那么可以向用户显示其选项,并要求用户选取正确的选项。 有关更多详细信息,请参阅消歧

  • 处理输入中的多个实体:如果要仅对实体类型的检测到的第一个实例值求值,那么可以使用语法 @entity == 'specific-value',而不使用 @entity:(specific-value) 格式。

    例如,使用 @appliance == 'air conditioner' 时,将仅对检测到的第一个 @appliance 实体的值求值。 但是,使用 @appliance:(air conditioner) 会扩展到 entity['appliance'].contains('air conditioner'),只要在用户输入中检测到至少一个值为“空调”的 @appliance 实体,即为匹配。

  • 从日志中隐藏数据: 通过将信息存储在上下文变量中并将该上下文变量嵌套在消息上下文的 $private 部分中,可以防止将信息存储在日志中。 例如,$private.my_info。 但是,将数据存储在专用对象中只是在日志中隐藏这些数据。 相关信息仍会存储在底层 JSON 对象中。 不要允许将这些信息公开给客户机应用程序。

  • 检查个人信息:如果要检查个人可标识信息 (PII) 并阻止用户向稍后某个过程提交该信息,那么可以添加一个以模式实体为条件的对话节点。 将该节点放在对话树的开头,以确保它首先检查输入。 例如,该实体可能会检查美国社会安全号码模式或电子邮件地址模式。 然后,它可以使用类似 Please do not submit personally identifiable information. Can you reenter your request? 的内容进行响应,您可以选择重置上下文以确保不保留用户提交的 PII 信息。

条件用法提示

  • 检查具有特殊字符的值:如果要检查实体或上下文变量是否包含某个值,并且该值包含特殊字符(如撇号 (')),那么必须使用圆括号将要检查的值括起。 例如,要检查实体或上下文变量是否包含名称 O'Reilly,必须使用圆括号将该名称括起。

    @person:(O'Reilly)$person:(O'Reilly)

    助手会将这些简写引用转换为以下完整的 SpEL 表达式:

    entities['person']?.contains('O''Reilly')context['person'] == 'O''Reilly'

    对于名称中的单个撇号,SpEL 会使用另一个撇号对其转义。

  • 检查多个值:如果要检查多个值,可以创建一个条件,其中使用 OR 运算符 (||) 在条件中列出多个值。 例如,要定义一个条件,以在上下文变量 $state 包含 Massachusetts、Maine 或 New Hampshire 的缩写时求值为 true,那么可以使用以下表达式:

    $state:MA || $state:ME || $state:NH

  • 检查数字值: 比较数字时,首先确保要检查的实体或变量具有值。 如果实体或变量没有数字值,那么在数字比较中将其视为具有空值 (0)。

    例如,您要检查用户输入中包含的美元值是否小于 100。 如果使用条件 @price < 100,并且 @price 实体为空,那么会将该条件求值为 true,因为 0 小于 100,即使从未设置价格也是如此。 要防止此类型的不准确结果,请使用诸如 @price AND @price < 100 之类的条件。 如果 @price 没有值,那么此条件会正确返回 false。

  • 检查具有特定意向名称模式的意向:可以使用用于查找与模式相匹配的意向的条件。 例如,要查找意向名称以“User_”开头的任何检测到的意向,可以在条件中使用类似以下内容的语法:

    intents[0].intent.startsWith("User_")

    但是,如果使用此语法,那么会考虑所有检测到的意向,甚至是置信度低于 0.2 的意向。 此外,请检查是否未返回根据置信度分数被视为不相关的意向。 为此,请按如下所示更改该条件:

    !irrelevant && intents[0].intent.startsWith("User_")

  • 模糊匹配如何影响实体识别: 如果使用实体作为条件,并且已启用模糊匹配,那么仅当匹配的置信度大于 30% 时,@entity_name 才会求值为 true。 即,仅当 @entity_name.confidence > .3 时。

在输入中存储和识别实体模式组

要将模式实体的值存储在上下文变量中,请将 .literal 附加到实体名称。 使用此语法可确保将用户输入中与指定模式匹配的精确范围的文本存储在变量中。

追加 .literal
Variable(变量)
email <? @email.literal ?>

要在定义了组的模式实体中存储来自单个组的文本,请指定要存储的组的数组编号。 例如,假定为 @phone_number 实体定义的实体模式如下所示。 (请记住,圆括号表示模式组):

\b((958)|(555))-(\d{3})-(\d{4})\b

要仅存储用户输入中指定的电话号码中的区号,可以使用以下语法:

仅限区域代码
Variable(变量)
area_code <? @phone_number.groups[1] ?>

这些组由用于定义组模式的正则表达式定界。 例如,如果与实体 @phone_number 中定义的模式匹配的用户输入为: 958-234-3456,那么将创建以下组:

组详细信息
组号 正则表达式引擎值 对话值 说明
组[0] 958-234-3456 958-234-3456 第一个组始终是完整的匹配字符串。
组[1] ((958)l(555)) 958 与第一个已定义组的正则表达式相匹配的字符串,即 ((958)l(555))
组[2] (958) 958 与包含在 OR 表达式 ((958)l(555)) 中作为第一个操作数的组相匹配
组[3] (555) null 与包含在 OR 表达式 ((958)l(555)) 中作为第二个操作数的组不匹配
组[4] (\d{3}) 234 与为组定义的正则表达式相匹配的字符串。
组[5] (\d{4}) 3456 与为组定义的正则表达式相匹配的字符串。

为了帮助解码用于捕获您感兴趣的输入部分的组号,您可以一次抽取有关所有组的信息。 使用以下语法创建上下文变量,该上下文变量返回所有分组模式实体匹配项的数组:

匹配组的数组
Variable(变量)
array_of_matched_groups <? @phone_number.groups ?>

使用“试用”窗格来输入某些测试电话号码值。 对于输入 958-123-2345,此表达式会将 $array_of_matched_groups 设置为 ["958-123-2345","958","958",null,"123","2345"]

然后,可以对数组中的每个值从 0 开始进行计数,以获取其组号。

数组元素
数组元素值 数组元素编号
"958-123-2345" 0
"958" 1
"958" 2
空值 3
"123" 4
"2345" 5

从结果中,您可以确定要捕获电话号码的最后四位数字,例如,您需要组 #5。

要返回创建用于表示分组模式实体的 JSONArray 结构,请使用以下语法:

匹配组的 JSON
Variable(变量)
json_matched_groups <? @phone_number.groups_json ?>

此表达式将 $json_matched_groups 设置为以下 JSON 数组:

[
  {"group": "group_0","location": [0, 12]},
  {"group": "group_1","location": [0, 3]},
  {"group": "group_2","location": [0, 3]},
  {"group": "group_3"},
  {"group": "group_4","location": [4, 7]},
  {"group": "group_5","location": [8, 12]}
]

location 是实体的属性,使用从零开始的字符偏移量来指示检测到的实体值在输入文本中的开始和结束位置。

如果您期望在输入中提供两个电话号码,那么可以检查两个电话号码。 例如,如果提供了两个电话号码,请使用以下语法来捕获第二个号码的区号。

匹配组的 JSON
Variable(变量)
second_areacode <? entities['phone_number'][1].groups[1] ?>

如果输入是 I want to change my phone number from 958-234-3456 to 555-456-5678,那么 $second_areacode 等于 555