一般代理设计最佳做法

本指南介绍了设计所有类型的代理时的一般最佳做法。

您还应该查看专门介绍如何设计语音代理的语音代理设计指南,以及介绍如何使用 Dialogflow 服务的最佳做法指南。

一般建议

以迭代方式构建代理

如果代理很大或很复杂,请先构建一个仅处理顶级请求的对话。建立起基本的结构后,再迭代对话路径,确保涵盖最终用户可能会采用的所有路由。

随着代理的不断完善,请考虑将测试用例功能用于测试驱动开发。

预建代理

Dialogflow 提供了代理模板来帮助您入门。预建代理,涵盖金融服务、电信和旅游等常见使用场景。这些代理附带意图和实体,以涵盖最常见的用户查询。只要添加特定于您的企业的路由和 fulfillment,即可快速构建一个正常运行的代理。

集成和连接您的服务

您可以通过多种方式与 Dialogflow 代理集成。本部分提供了选择集成方式的最佳做法。

集成

Dialogflow 集成服务为您的代理提供了现成可用的界面。如果您使用集成,则无需直接调用 Dialogflow API,因为集成会为您处理。这些集成可以提供文本代理,您可以将其嵌入您的网站、与其他消息传递平台连接或提供电话界面。

Dialogflow API

如果没有现成可用的集成,或者您想要自定义系统界面,则可以直接使用 Dialogflow API。 如果使用此方法,您需要为您的代理实现界面,或使用现有界面。

网络钩子

除非您的代理可以完全使用静态数据定义,否则您需要使用网络钩子来连接您的服务,并提供可以处理动态场景的代理。无论您是使用集成还是 Dialogflow API,这都适用。

代理资源

可以通过多种方法来使用 Dialogflow 代理资源获得所需的结果。本部分提供了有关如何为适当场景选择合适的资源的建议。

流和页面

页面为代理提供了结构。您可以将页面视为状态机中的节点,并流向相关页面组。您可以使用状态处理程序来控制节点之间的转换,系统在匹配意图、满足条件或调用事件时会调用状态处理程序。

简单的代理可能适用于单个流,但复杂的代理通常更适合使用多个流。 每个流都应代表代理的一个简要主题,其中与流关联的每个页面都有助于处理该主题。此外,每个流都可以有自己的一些设置,并且可由部分团队成员拥有,这有助于在设计大型代理时划分工作。

在设计复杂的大型代理时,您需要考虑“每个代理的流数”和“每个流的页数”限制。这些限制有助于让代理保持高效运行。

如果代理设计的每个代理的流过多,请将相关主题合并到单个流中。例如,您可以将以下主题合并到一个“获取平衡”流程中:

  • 获取支票余额
  • 获取储蓄余额
  • 获取按揭贷款余额
  • 获取余额

如果在代理设计中,每个流的页面过多,请合并相关的页面,并在每个页面中使用多个路由

如果您仍会遇到流程和页面限制方面的问题,可能是因为代理本身内置了太多业务逻辑。请考虑将此逻辑移至网络钩子。

下面列出了代理资源的对话控制粒度(按递增粒度顺序):

  1. 代理(一个代理处理所有对话)
  2. 流(一个流可处理一个或多个相关对话主题)
  3. 页面(一个页面可处理一个或多个相关对话)
  4. 路由(一个路由处理用户意图或条件检查)

意图参数与表单参数

您的系统从最终用户中获取结构化数据的主要方法是使用参数。 您可以对意图意图参数)或页面表单参数)使用参数。

某些页面的主要目的是从最终用户处收集特定信息。例如,页面可以设计为收集最终用户的联系信息。在这种情况下,您应始终使用表单参数来收集此信息。

在某些情况下,您可能希望在从一个页面转换到另一个页面时捕获最终用户信息。 例如,如果最终用户在对话开始时请求特定产品,您可能需要在转换到相应订单页面时捕获所需的产品。在这种情况下,请使用意图参数作为意图路由的一部分。

在有些情况下,最好同时使用意图参数和表单参数。例如,如果最终用户在对话开始时请求小尺寸衬衫,您需要在转换到衬衫订单页面时捕获所需的尺寸参数(小)。 衬衫订单页面可能会要求您提供其他信息,例如所需的颜色。衬衫订单页面应具有尺寸和颜色的表单参数。在本示例中,已提供并传播尺寸参数,因此代理将只请求颜色。但其他对话可能会遵循不同的路径,其中最终用户在衬衫订单页面处于活跃状态时未提供所需的尺寸。通过以这两种方式定义此参数,您的代理可以更加灵活地提取信息。

路由和路由组

如果要转换到其他页面,请将响应消息加入队列,或者在匹配意图或满足条件时调用网络钩子,请使用路由

如果您发现自己在多个页面上使用同一组路由,请使用路由组。这样可以避免代理设计中出现不必要的重复。

意图重复使用

如果您发现自己定义了具有相似训练短语的多个意图,请考虑跨多个页面重复使用意图。理想情况下,您应该定义一些在多个页面中使用的通用意图,以及一些仅在单个页面中使用的特定意图。这样可以避免代理设计中出现不必要的重复。

例如,确认意图通常最好定义为可重复使用的意图。 confirmation.yes 意图可以具有训练短语,例如:

  • 确定
  • 是的,我有
  • 当然
  • 肯定
  • 可以

confirmation.no 意图可以具有训练短语,例如:

  • 不是
  • 不行
  • 不可能
  • 我不是
  • 当然不会
  • 不用了

这些可重复使用的确认意图可用于代理的许多场景。

在某些情况下,您还应该考虑创建专用确认意图。例如,在确认订单时,您可能需要使用带有以下训练短语的专用 order.confirmation.yes 意图:

  • 订单对我来说没问题
  • 我接受此订单

此外,包含训练短语的专用 order.confirmation.no 意图,例如:

  • 我不想接受此订单
  • 我不接受此订单

当订单确认页面处于活跃状态时,这四个意图的意图路由都应在范围内。这可确保正确处理最终用户的任何通用或特定确认。

默认负意图

您应该使用最终用户可能会说出但不应与代理中的任何意图匹配的短语来填充默认负意图

Fulfillment

您有多种选项来使用 fulfillment 响应最终用户。在对话回合期间,代理可以向响应队列附加多条消息,并在对话结束时将串联后的队列发送到最终用户。本部分介绍用于创建消息的每个选项。

  • 页面条目 fulfillment:当页面最初处于活跃状态时,系统会调用此 fulfillment。如果您需要一条描述页面用途的消息,并且该消息仅在页面处于活跃状态时才说一次,那么此函数将非常有用。例如:
    • 关于您的收支账户,您想要了解什么?
    • 您想要购买哪种类型的产品?
    • 我需要收集您想要订购的衬衫的一些信息。
  • 路由:当调用意图路由或具有 fulfillment 的条件路由时,系统会调用此 fulfillment。如果您希望消息响应最终用户关于意图匹配的消息、满足条件(可能是表单填充完成条件)或转换,这将非常有用。 例如:
    • 可以,您的国际方案包含日本。(意图匹配)
    • 确定要购买 300 件衬衫吗?(满足比较条件
    • 好的,预约是明天早上 7 点。 (表单填充完成)
    • 现在,我们来讨论一下 Aardvarks。(转换)
  • 事件处理程序:调用事件时,系统会调用此 fulfillment。当您需要响应事件的消息时,此函数非常有用。例如:
    • 您正在考虑购买的股票的价值增加了 10%。(自定义事件)
    • 您能再说一下吗?(无匹配事件
  • 表单的初始提示:当代理执行表单填充时,系统会调用此 fulfillment。这些消息应该向最终用户询问特定问题。每个表单参数都有自己的初始提示 fulfillment。例如:
    • 您想要哪种尺寸的衬衫?
    • 您想要哪种颜色的衬衫?
  • 表单的重新提示处理程序:当代理执行表单填充时,此 fulfillment 会被调用,并且它不理解最终用户对当前参数的选择。仅当您希望重新提示消息与初始提示消息不同时,才需要此 fulfillment。如果不存在重新提示处理程序,则代理将仅使用初始提示作为重新提示消息。例如:
    • 我不明白。能否为衬衫提供有效的颜色?

命名

本部分提供有关为代理资源命名的建议。

意图命名

如果您的代理有许多意图,则应考虑使用可帮助您有序组织这些意图的命名方案。常见的做法是使用标点符号细分意图名称,使含义的具体性从左到右逐渐增强。此外,意图名称应反映最终用户在一轮对话中的意图。

有很多合适的命名架构,以下是一个示例:

  • phone-service.order.cancel
  • phone-service.order.create
  • phone-service.order.change
  • tv-service.order.cancel
  • tv-service.order.create
  • tv-service.order.change
  • account.balance.get
  • account.balance.pay
  • account.address.get
  • account.address.update

转换

状态处理程序中定义的转换通过更改活跃页面来控制对话。本部分提供了有关组织代理转换的建议。

随附转换

在定义触发转换的路由时,请考虑可能存在补充或反向路由。

例如:

  • 如果您有用于 confirmation.yes 的意图路由,请考虑为 confirmation.no 定义另一个路由。
  • 如果您使用布尔 = 运算符定义条件路由,请考虑定义另一个使用 != 的路由。

处理最终用户输入

本部分提供意图和训练短语的指南,因此您的代理可以采用最佳方式处理和处理最终用户输入。

定义至少 20 个训练短语

每个意图应该至少有 20 个训练短语。否则,NLU 模型可能没有足够的信息来适当地匹配您的意图。这是最低程度的要求。理想情况下,您应该定义更大的值,尤其是对于大型代理的头意图,最好大约 50 个。

注意意图偏差

当一个或多个意图的训练短语明显多于其他意图时,由于数据不平衡,导致 NLU 模型偏向于较大的意图。当训练短语的数量存在一个数量级或更多的差异时,就会发生这种意图偏差。

在某些情况下,这是预期的行为,因为您可以定义应比其他意图更频繁地匹配的一些意图,因为它们与最终用户观察到的频率更高实时流量。

在其他情况下,此行为可能是不可取的,因为您不需要偏向于这些较大的意图。在这种情况下,请将这些较大意图的训练短语数减少到与其他意图相同的数量级。 例如:

意图 A 训练短语 意图 B 训练短语 意图 B 的偏差
20 50
20 200 边界
20 2000

实体使用和训练短语数量

对于 intent 中使用的所有实体类型:

  • 为实体类型的每个示例添加注解。
  • 对于每种实体类型,请提供至少五个包含带注释示例的训练短语。
  • 提供的训练短语数量至少是实体类型的三倍。 例如,如果您在一个意图中为注释使用了 10 种不同的实体类型,那么至少应该有 30 个训练短语。

训练短语应该自然

训练短语应该具有对话能力和自然性质;它们应该匹配用户所说内容。 尽可能将在生产环境中发生的最终用户输入用作训练数据,特别注意最常见的输入。

必要的训练短语种类

包含问题、命令、动词和常见名词的同义词的各种变体,以确保短语涵盖各种可能的请求。

最好包含一些较短的短语,如“支付我的账单”,以及较长的短语和句子,例如“我刚刚收到了邮件,说我需要支付对账单余额”。我们不建议简短或较长的短语比例,但应以发送到生产环境中代理的实际最终用户输入为基础。

定义长度、短语和句子结构不同的训练短语对于确保为代理提供良好的训练非常重要。无需为了多样化而添加多样性,但必须提供足够多的种类,以便 NLU 模型可以从各种最终用户输入中成功检测最终用户的意图。 如果您的多样性不足,则存在过拟合风险。换句话说,有可能模型与您提供的特定示例过于紧密,从而无法泛化到其他示例。

大小写多样性

在极少数情况下,您可能需要添加仅大小写不同的训练短语。这通常适用于您希望最终用户提供全大写文本输入的情况。

替代方法可以是:

不必要的训练短语种类

避免训练短语中出现细微的变化,因为它们为 NLU 模型提供重复信息。 例如,请勿添加仅包含以下内容的变体:

  • 大写罕见情况除外):例如“Order a ticket”和“order a ticket”。
  • 填充字词:例如,“好的,订票”和“订票”。
  • 标点符号:例如,“你能帮我一下吗?”或“你可以帮我!?”

注释一致性

选择用作注解的训练短语部分应包含匹配实体所需的全部文本,但不能超出这个范围。此外,请确保针对整个意图对训练短语的类似部分进行了注释。

例如,下表显示了使用 @sys.date 系统实体添加注释的好方法和错误方法:

良好 较差
9 月 7 日出发 9 月 7 日出发
将在 7 月 4 日离开 在 7 月 4 日离开

对系统实体使用语义上有意义的注解

为注解选择的训练短语部分的语义含义可能会受到训练短语中其余文本的影响。例如:

带注解的训练短语 带注释的文字的语义含义
我今年 7 岁 用户的年龄
合同有效期为 7 年 时长

Dialogflow 的机器学习模型在匹配系统实体时会考虑语义含义。 训练短语部分的语义含义必须与系统实体的预期语义含义一致。

例如,请勿使用 @sys.duration 系统实体注解上面的第一个“7 年”示例。“7 年”的语义含义与简单的时长不匹配。您应改为为注解选择“7”并使用 @sys.number 系统实体。

定义意图以处理不合规的表单填充答案

考虑定义意图以处理不合规的表单填充答案。 例如,您的代理可能会询问“您的旅行日期是什么?”,后跟最终用户的“我不知道”的答案。此答案不符合表单参数提示,但如果代理的意图路由范围与此答案匹配,则代理可以很好地处理这种情况。

避免使用 @sys.any

避免使用 @sys.any 系统实体类型。只有在您已完全用尽所有大道(包括构建自定义实体)时,才应使用它。这种实体类型非常广泛,可能会导致意外行为。

如果您确实使用此实体类型,请避免为此实体类型为单个训练短语的多个部分添加注释,因为这会导致歧义,因为代理行为将未定义。

@sys.any 与表单参数搭配使用不太安全,因为在提示表单参数时,代理需要特定信息。

注释应包含各种实体值

在定义带注释的训练短语时,应在短语中使用各种实体值示例。 您不应始终为注释使用相同的实体示例。以下示例展示了商品实体类型的良好注释和错误注释:

良好 较差
我想买件衬衫 我想买件衬衫
订购新帽子 订购新衬衫
手表添加到我的购物车 衬衫添加到我的购物车

自定义实体应包含多样性

自定义实体应涵盖广泛的示例。 NLU 模型为语法形式提供多样性,但您必须添加所有可能的项目。

避免积极匹配的实体

请勿定义几乎与任何内容都匹配的实体。 这样做会降低机器学习的性能和质量。 每个训练短语中几乎所有内容都将被评估为可能的匹配。

映射和列出实体应专注于不同的值

映射和列表实体类型应具有有限的范围,可捕获一种信息类型的不同值。实体应尽可能集中、简短、简洁。

如果您的实体值很复杂,那么可能意图训练短语更适合您的情况。例如,假设最终用户输入如下内容:

  • “如何使用 A 方案进行国际通话?”
  • “通过 B 方案使用国际数据漫游。”

请勿同时为操作和计划创建实体类型,如下所示:

操作实体类型 方案实体类型
“如何拨打国际电话” “方案 A”
“使用国际数据漫游” “方案 B”

相反,您应使用训练语句和意图匹配来捕获操作,并使用实体来捕获方案。

使用正则表达式实体捕获非字词标识符

捕获涉及非字词标识符的最终用户输入时,应使用正则表达式实体。例如,如需捕获“AA-256”或“AC-436”等产品 ID,请使用正则表达式,例如 “[AZ]{2}-\d{3}”。

避免嵌套复合实体

请勿在复合实体中使用多于一层嵌套。 每层嵌套都会显著降低质量。

避免使用类似意图

每个意图都应捕获最终用户的意图。 如果您使用类似的训练短语定义不同的意图,则匹配可能不可靠,因为 NLU 模型无法以足够的置信度确定要匹配意图。

如果两个训练短语代表同一意图,则它们应该属于同一意图。例如,“更改当前账单付款日期”和“更多付款时间”都应属于同一意图,因为它们都请求更改截止日期。不过,“我是否可以使用 A 方案进行国际通话?”和“我是否可以使用 A 方案进行国际数据漫游?”可能属于不同的意图,因为最终用户希望在每种情况下使用不同的内容。

避免类似的实体类型

您应该避免定义具有相同实体条目的多个实体类型,因为这可能会导致 NLU 模型产生歧义。

在生产环境中使用无匹配事件来改善意图

在生产环境中运行代理时,某些最终用户输入必然会产生无匹配事件。您可以通过以下三种方式之一改进代理:

  • 将最终用户输入作为训练短语添加到所需的意图。但是,这并非最佳选择。 如果您多次为该意图执行此操作,则可能会导致意图偏差
  • 清理所需意图的训练短语,以便全部准确反映该意图。 在某些情况下,训练短语不连续的意图可能会阻止意图匹配。
  • 如果不应与最终用户输入匹配的意图具有与最终用户输入相匹配的训练短语,请删除这些训练短语。

避免使用特殊字符

训练短语中的特殊字符({_#[ 等)会被忽略。例外情况是表达式处理正常的情况。

避免填充词

填充词是您可以忽略的字词,但仍可以理解文本。例如:

  • 可以吗
  • 怎么样

没有必要在训练短语中使用填充词,但无害,因为 NLU 模型会忽略这些词。但是,您不应定义仅因填充词而异的训练短语。

切勿定义由填充词组成的实体。

使用机器学习设置进行实验

机器学习设置可用于调整最终用户输入的处理方式。在大多数情况下,默认设置非常适合。但是,您可能需要微调设置以提高代理性能。

响应最终用户

本部分提供有关使用 fulfillment 响应最终用户的准则。

欢迎最终用户

新创建的代理具有为欢迎意图自动创建的意图路由。您应修改此路由,以包含欢迎最终用户的 fulfillment 消息。此消息应描述代理,并让最终用户了解其功能。

确认最终用户信息

通常,最好在响应中重复最终用户提供的信息。这样可以让最终用户知道代理理解其请求。

当意图匹配且发生转换时,让最终用户知道对话正在根据其请求进行。例如:

对话 说明
最终用户:我对我的收支账户有疑问:
代理:好的,你想知道收支账户的哪些信息?
最终用户输入生成了意图匹配,并按照路由执行,其中包括 fulfillment 消息和到检查账号问题的页面的转换。请注意,代理确认最终用户希望了解其支票账户。

表单填充完成后,重复最终用户提供的数据。例如:

对话 说明
最终用户:明天
代理:好的,您计划理发的时间是明天晚上 7 点。请问您还需要其他帮助吗?
最终用户提供日期表单参数,即活动页面上的最后一个表单参数。代理确认预定的理发时间和日期。

引导对话

代理应始终引导与最终用户的对话。这可通过将每个响应以如下问题结束来轻松完成:

  • 请问您还需要其他帮助吗?
  • 你希望知道小猎犬的哪些信息?
  • 要取消或提交此订单吗?
  • 您需要哪方面的帮助?
  • 您是单独旅行还是与他人一起旅行?

定义这些问题时,请注意避免提出多个问题,例如:

  • 你还在吗?你要询问哪项服务?
  • 你还想下单吗?你想添加什么东西吗?

最终用户可能仅回答其中一个问题,并且您的代理可能无法正确处理这种情况。

处理错误和意外的最终用户输入

本部分提供有关处理错误和意外的最终用户输入的建议。

为内置事件创建事件处理程序

您应该视情况为内置事件创建事件处理程序。处理这些事件类似于在软件编程中捕获异常。 根据具体情况,您可能需要使用特定于参数的事件处理程序、特定于页面的事件处理程序或特定于流的事件处理程序来处理事件。

处理 webhook 错误

当 webhook 服务发生故障时,代理必须能够妥善处理故障,这一点非常重要。您可以通过为特定于网络钩子的内置事件定义事件处理脚本来实现此目的。以下是处理 webhook 错误的推荐方法:

  • 请勿从触发 webhook 调用状态处理程序中提供转换目标,否则系统将无法调用 webhook 错误事件处理脚本。请改为在来自 webhook 服务的 webhook 响应中设置转换目标。
  • 选择一个可将错误计数器初始化为零的页面。此页面应在触发 webhook 调用的页面之前处于活跃状态。 此页面的条目执行方式应使用执行方式参数预设将错误计数器初始化为 0。例如:

    参数
    webhook-error-count 0
  • 创建一个用于处理 webhook 错误事件的 webhook 错误页面:

    • 条目执行方式应向最终用户确认失败,并使用执行方式参数预设来递增错误计数器会话参数。例如:

      参数
      webhook-error-count $sys.func.ADD($session.params.webhook-error-count, 1)
    • 定义一个条件路由,条件是错误计数小于允许的上限。(例如 $session.params.webhook-error-count <= 3)。此路由应具有执行方式,用于通知最终用户代理将重试。此路由的转换目标应设置为 PREVIOUS_PAGE,或者应设置为可再次尝试调用 webhook 的任何页面。

    • 定义一个条件路由,其中包含错误计数大于允许上限的条件(例如 $session.params.webhook-error-count > 3)。此路由应包含执行方式,用于通知最终用户代理将不再重试。此路由的转换目标应设置为不会触发 webhook 重试的页面。

  • webhook 事件处理脚本应该具有一个过渡目标,用于过渡到 webhook 错误页面。

工具

本部分提供了有关使用工具改进代理设计的建议。

使用验证工具

您应始终使用验证工具来检查代理。此工具会发现本指南中所述的一些问题。

使用测试用例功能

您应始终为代理定义测试用例。这些测试用例有助于防止代理演变以应对更多场景的回归。