利用生成式回退来扩大 intent 覆盖范围并妥善处理错误

1. 概览

上次更新日期:2023 年 8 月 7 日

构建内容

在此 Codelab 中,您将在 Dialogflow CX 中构建、部署和配置一个简单的虚拟客服,为水肺潜水者提供团体预订和私人包机服务。虚拟客服将使用生成式 AI 和 Google 最新的生成式大语言模型 (LLM) 来生成虚拟客服的回应。

学习内容

  • 如何启用相关 API
  • Dialogflow 如何根据 intent 参数自动预填充页面表单参数值
  • 如何在 Dialogflow 中配置事件处理程序
  • 如何为数据流和参数填充期间使用的不匹配事件处理脚本启用生成式回退
  • 如何配置自己的文本提示来处理基本对话和代理特定的对话场景
  • 如何编写良好的意图和参数说明,以便为所需参数(除用户定义的重复提示之外)生成重新提示处理程序
  • 如何测试代理并模拟可触发生成式回退的客户问题

所需条件

  • Google Cloud 项目
  • 浏览器,例如 Chrome

2. 准备工作

您需要先启用 Dialogflow API,然后才能开始在 Dialogflow CX 中使用生成式回退功能。

使用 Cloud 控制台启用 Dialogflow API

  1. 在浏览器中打开 Google Cloud 控制台
  2. 在 Google Cloud 控制台中,前往 API 库,浏览可启用的 API 和服务。
  3. 使用“API 库”页面顶部的搜索栏,搜索 Dialogflow API,然后点击生成的服务。
  4. 点击启用按钮,在您的 Google Cloud 项目中启用 Dialogflow API。

使用 gcloud CLI(替代方案)

或者,您也可以使用以下 gcloud 命令启用该 API:

gcloud services enable dialogflow.googleapis.com

如果该 API 已成功启用,您应该会看到类似于以下内容的消息:

Operation "operations/..." finished successfully.

获取代码

您无需从头开始创建虚拟客服,我们会为您提供一个代理,您需要从 Dialogflow CX 控制台中恢复该代理,然后再进行改进。

如需下载源代码,请执行以下操作:

  1. 打开新的浏览器标签页,转到代理代码库,然后通过命令行克隆该代码库。
  2. 初始代理已导出为 JSON 软件包。解压缩该文件,检查代理设置,查看流定义 Liveaboards.json,最后浏览流页面、意图和实体。

3. 创建新代理

打开 Dialogflow 控制台

您将使用 Dialogflow CX 控制台和您的 Google Cloud 项目来执行此 Codelab 中的其余步骤。

  1. 在浏览器中,导航到 Dialogflow CX 控制台
  2. 选择您要使用的 Google Cloud 项目,或创建要使用的新项目。
  3. 您应该会在 Dialogflow CX 控制台中看到代理列表。

如果您是第一次使用 Dialogflow CX,请参阅 Dialogflow CX 文档,详细了解如何根据需要配置项目和设置。

创建新的 Dialogflow CX 代理

  1. 要恢复从 GitHub 代码库下载的代理,您需要创建一个新的代理。在 Dialogflow CX 控制台中,点击页面右上角的创建新代理 (Create new agent)。

创建全新的代理

  1. 选择构建您自己的代理 (Build your own) 代理选项。

选择相应选项

  1. 使用下面的代理设置填写表单,然后点击创建以创建代理。
  • 作为显示名称,请选择:Divebooker
  • 根据位置选择:us-central1
  • 选择您的首选时区
  • 选择en - English作为默认语言
  1. Dialogflow 会自动为您打开该代理。我们还没有大功告成!

恢复 Divebooker 代理

  1. 返回代理列表页面,找到您刚刚创建的代理。点击 78d2781c655810e7 选项,然后点击恢复按钮。
  2. 选择 Upload 选项,然后拖放或选择您之前从 GitHub 代码库下载的 ZIP 文件。
  3. 点击恢复按钮以导入我们提供的代理

点击“恢复”按钮以导入我们提供的代理

太棒了!您已完成潜水预订虚拟客服的构建工作,可以随时为客户提供帮助。在下一部分中,您将对它进行测试,看看它在回答用户问题和协助处理预订请求方面表现如何。

4. 测试代理

Dialogflow 提供了一个内置的模拟器,用于与代理聊天并发现错误。对于每个回合,您都可以验证触发的意图、代理响应、活动页面和会话参数的正确值。

我们将测试一些场景,并针对每个场景查看代理给出特定回答的原因。我们先从第 1 项开始。

未解析的 intent

  1. 在 Dialogflow 控制台和代理中,点击测试代理以打开模拟器。

点击“Test Agent”以打开模拟器

  1. 输入对代理的问候语,例如 Hello 并询问 what is a liveaboard?。此问题与任何 intent 都不匹配,系统会显示类似“Sorry, I don't see how to help”(抱歉,我不确定如何提供帮助)的通用提示。您可以通过在模拟器上检查原始响应来检查是否调用了 sys.no-match-default 内置事件

向经纪人问好,问问什么是直播家

向下滚动到 JSON 响应的末尾。请注意,在搜索匹配 intent 时,Dialogflow 会发现这是一个 NO_MATCH,并引发非匹配事件。

检查 Dialogflow 是否引发了 sys.no-match-default 事件

  1. 切换到 Build 标签页,然后打开 Liveaboards 流程的 Start Page

切换到“构建”标签页,然后打开 Liveaboards 流程的初始页。

默认情况下,每个流都有针对无匹配和无输入内置事件的事件处理脚本。创建流时会自动创建这些事件处理脚本,且无法删除这些事件处理脚本。

  1. 点击 sys.no-match-default 事件处理脚本,然后向下滚动到代理响应部分。Dialogflow 提供了备用响应列表,但您也可以定义不同类型的响应消息,以便为最终用户提供不仅仅是文本响应。

查看预定义的代理响应

现在,让我们朝着快乐的道路出发吧!

成功之路

在第二个案例中,假设您是一名潜水员,希望为明年 7 月的 12 人团体预订前往加拉帕戈斯群岛的潜水游轮。

  1. 在“Simulator”面板中,点击 Reset 图标以发起与代理的新对话。

重置即可发起新对话

请考虑改用竖屏视图以改善用户体验

  1. 告诉代理您要预订加拉帕戈斯群岛的包机,并提供您的旅行详细信息。您不需要使用完全相同的提示,试试看吧!

测试理想路径

  1. 打开初始页,然后点击 head.send.group.request 路由。向下滚动到 Transition 部分,告知 Dialogflow 当此意图匹配时要转换的页面。

过渡到“收集更多信息”页面

  1. 关闭路线定义并展开收集更多信息页面。请注意条目执行方式和参数列表。

“收集更多信息”页面

对于 Dialogflow CX 中的每个页面,您可以定义一个表单,这是应从最终用户处针对该页面收集的参数列表。请注意,代理并未请求旅行目的地,因为我们将它作为初始输入的一部分传递,而 destination 也是一个 intent 参数。当页面最初变为活跃状态时,在其活跃期内,任何与 intent 参数同名的表单参数都会自动设置为会话参数值,并跳过相应的提示。

  1. 切换到管理标签页,然后点击意图 (Intents) 部分下的 head.send group request 意图。查看为此意图提供的训练短语以及训练短语的注释部分。

查看为此意图提供的训练短语以及训练短语的注释部分。

  1. 考虑训练短语“我需要为 15 名潜水员组织一场哥斯达黎加旅行”。“哥斯达黎加”带有 destination 和“15”注释与 number-of-guests 搭配使用。当您为训练短语的某些部分添加注释时,Dialogflow 会识别出这些部分只是最终用户将在运行时提供的实际值的示例。这就是为什么你一开始输入“你们是否提供加拉帕戈斯群岛的包机服务?”Dialogflow 从“加拉帕戈斯群岛”中提取了目的地参数。

接下来,我们将了解在被要求填写表单参数时,如果没有为代理提供有效的输入,会发生什么情况。

输入无效

  1. 在“Simulator”面板中,点击 Reset 图标以发起与代理的新对话。
  2. 表达预订团体的意图,这次不要告诉客服人员您想去哪里,以及当您被要求提供目的地时,请回复一个不是哥斯达黎加、加拉帕戈斯或墨西哥的随机值。

输入的目标平台无效

  1. 管理标签页上,点击资源部分下的实体类型。请注意两个标签页:在“System”标签页下,您可以找到代理当前使用的系统实体。自定义标签页提供为匹配特定于此代理的数据而创建的自定义实体的列表。

目标自定义实体

  1. 点击 destination 实体,了解该实体匹配的值。“欧洲”不是其中一个条目,且也不是同义词。
  2. 在流程图上,展开包含表单参数的收集更多信息页面。点击目的地参数。
  3. 在参数面板上,向下滚动到“重新提示事件处理脚本”部分,然后点击默认非匹配事件处理脚本

此参数级事件处理脚本专门用于处理表单填充期间无效的最终用户输入。因为“欧洲”是意外输入,系统调用了 sys.no-match-default 事件,并调用了为此事件定义的相应重新提示处理程序。代理说部分列出了两条备选的重新提示消息。

当最终用户进入无效目的地时,备用的静态重新提示消息。

太棒了!这些测试用例代表了代理应妥善处理的常见场景。很多时候用户会提出聊天机器人无法回答的问题,或提出聊天机器人无法满足的请求。设计时要考虑到长尾客户,这就非常复杂,也就是说,要避开大多数用户都会遵循的惯用路径。想一想对话中可能出现的所有问题,以及用户可能会采取的所有意外或不支持的路径。

自动语音识别 (ASR) 技术的进步意味着我们几乎总能准确了解用户所说的内容。但是,确定用户所指仍然是一项挑战。话语通常无法单独理解;只有在上下文中才能解读。在此 Codelab 的下一部分中,我们将探索 Google 最新的生成式大语言模型 (LLM) 如何帮助对话回到正轨并推进对话。

5. 启用生成式回退

什么是生成式后备功能?

生成式回退功能是一项 Dialogflow CX 功能,它使用 Google 的大语言模型 (LLM) 来生成虚拟客服响应。

如何提供帮助?

在关键用例之间,有一些比较常见的用户请求,例如在用户不理解的情况下重复代理所说的内容、在用户要求时持有行以及总结对话。在我们进行的第一项测试中,客服人员未能回答“什么是 Liveaboard?”这个问题。因为我们没有为其创建意图,而是设计了流程来处理与水肺潜水和游艇相关的那些一般问题。

即使具有稳健的 intent,仍然有出错的余地。用户可能会因为保持沉默(“无输入”错误)或说一些意外的内容(“无匹配”错误)而离开脚本。虽然阻止错误比在错误发生后处理好,但也不是完全可以避免的错误。“抱歉,我不确定怎么回答”之类的宽泛问题或类似的解决方案,其最低可行性通常还不够好。错误提示的灵感来源于合作原则,根据该原则,高效沟通依赖于对话参与者之间暗流合作的假设。

在下一部分中,我们将介绍如何配置生成式后备功能,以扩大意图的覆盖范围并简化错误处理,从而提供更出色的客户体验。

为整个流的无匹配事件启用生成式回退

您可以为流程、页面或参数填充期间使用的不匹配事件处理脚本启用生成式回退。为非匹配事件启用生成式回退后,每当该事件触发时,Dialogflow 都会尝试生成生成的回复并向用户回复。如果回复不成功,系统会改为发出常规规定的客服人员回复。

您可以在代理中针对无匹配事件处理脚本启用生成式回退,这种处理可用于流程、页面或参数执行方式。

我们将开始为整个 Liveaboards 流程的 no-match-default 活动启用生成式回退功能。

  1. 展开流的初始页
  2. 点击事件处理脚本下的 sys.no-match-default
  3. 勾选代理响应下的启用生成式回退,然后点击保存

选中“代理响应”下的“启用生成式回退”

保存即可在 Liveaboards 初始页上启用生成式回退

针对特定非匹配事件启用生成式回退

现在,我们想要启用生成式回退,以便在代理询问乘客人数时处理无效输入:

  1. 打开包含表单参数的收集更多信息页面。点击 number-of-guests 参数。
  2. 前往目标“No-match”事件处理脚本(向下滚动到“Reprompt eventHandlers”部分,然后点击 No-match default 事件处理脚本)

导航至目标无匹配事件处理脚本(向下滚动到“重新提示事件处理脚本”部分,然后点击无匹配默认事件处理脚本)

  1. 选中代理响应下的启用生成式回退

对参数 number-of-guest 启用生成回退

  1. 最后,点击保存
  2. 现在,重复执行针对 destinationemail-address 启用生成式回退的具体步骤。

太棒了!您已启用生成式回退功能,以处理意外 intent 和无效参数值。接下来,我们将介绍如何通过文本提示来配置生成式回退功能,以指示 LLM 如何回答。

6. 配置生成式回退

生成式回退功能将请求传递给大语言模型,以生成生成的响应。请求采用文本提示的形式,文本提示混合使用自然语言以及有关代理和对话当前状态的信息。您可以通过多种方式配置此功能:

  1. 选择要用于生成回答的特定(已定义)提示。
  2. 定义自定义提示。

选择已定义的提示

  1. 在 Dialogflow CX 控制台中,点击代理设置 (Agent Settings)

前往“代理设置”

  1. 前往ML标签页,然后进入ML 子标签页。

生成式 AI 子标签页

该功能在开箱后就提供了两个模板提示:默认模板(不可见)和示例模板(可指导您编写自己的提示)。

  1. 选择示例模板,然后点击下拉菜单右侧的修改按钮对其进行检查。

点击模板下拉菜单右侧的“修改”按钮可检查该模板。

借助预定义提示,虚拟客服可以处理基本的对话场景。例如:

  • 向用户致以问候和告别。
  • 重复客服人员所说的话,以防用户无法理解。
  • 在用户要求时,保持竖线。
  • 总结对话。

我们来尝试为 Divebooker 代理定义一个特定的文本提示!

7. 定义您自己的提示

  1. 复制下面的提示并将其粘贴到文本提示区域中
You are a friendly agent that likes helping traveling divers.
You are under development and you can only help
$flow-description

At the moment you can't help customers with land-based diving and courses. You cannot recommend local dive shops and diving resorts.

Currently you can $route-descriptions

The conversation between the human and you so far was:
${conversation USER:"Human:" AGENT:"AI"}

Then the human asked:
$last-user-utterance

You say:
  1. 选择另存为新模板,将新提示存储为新模板(选择新的模板名称),然后选择面板右下角的保存

创建专用于该代理的自定义文本提示,并将其另存为新模板

  1. 如需实际将新创建的提示设为活动提示,您还需要保存设置。

保存新设置

在自行撰写文本提示时,请确保其清晰、简洁且规范。撰写 LLM 提示的方式会极大地影响 LLM 所做回答的质量。LLM 会按照指令进行训练,因此你的提示越像是精确的指示,就越有可能得到更好的结果。根据获得的结果撰写提示,然后不断改进。

如需撰写有效的提示,请遵循以下最佳实践:

  1. 简明扼要地描述您希望 LLM 执行的任务。不多不少。简明扼要。
  2. 此外,提示应该具体、定义明确,避免使用模糊或模棱两可的语言。
  3. 将复杂的任务分解成更容易管理的更小部分。通过将任务分解为较小的步骤,可帮助模型一次只关注一项任务,并降低出错或混淆的可能性。
  4. 为了提高回答质量,请在提示中添加示例。LLM 会在上下文中通过有关如何响应的示例进行学习。

创建提示时,除了以自然语言描述应生成何种上下文之外,还可以使用以下占位符:

  • $conversation 代理与用户之间的对话,不包括用户最后一句话。您可以调整回合前缀(例如:“人类”“AI”或“您”或“代理”)
  • $last-user-utterance 用户最后一条话语。
  • $flow-description:活跃流的流说明。
  • $route-descriptions活跃 intent 的 intent 说明。

现在,我们已经有了初始文本提示,下一项任务是确保数据流和意图都有良好的描述。

8. 添加流程和 intent 说明

添加流程说明

  1. 如需为 Liveaboards 流程添加说明,请将鼠标悬停在 Flows(流程)部分中的流程上,访问流程设置。

将鼠标悬停在“流”(Flows) 部分中的流上,访问流设置。

  1. 点击选项 78d2781c655810e7 按钮。
  2. 选择流设置,然后添加以下说明(或类似说明):search, find and book liveaboards

为 Liveaboards 流程添加说明

  1. 点击保存

添加 intent 说明

  1. 现在,我们来为 head.send.group.request intent 添加一段简明的说明。切换到管理标签页,选择资源部分下的意图,然后选择 head.send.group.request intent。
  2. 添加以下说明:assist users with group or full charter reservations. Initially collect travel details including departure period, destination, number of guests (min 4 max 15 people), contact details. The destination must be one of the following in the Pacific: Costa Rica, Mexico, Galapagos Islands

请注意,说明中包含重要信息,例如船只上允许的最少和最多乘客人数。请注意这一点!

  1. 点击保存

大功告成!你已针对无匹配事件处理脚本为流程和参数执行方式启用了生成式回退。您还定义了自己的文本提示,生成式回退功能将其传递给大语言模型以生成生成式回答。

在下一部分中,您将重新测试代理,看看它如何能够回答之前同样具有挑战性的问题。

9. 重新测试代理

现在,您已经在虚拟客服中配置并启用生成式回退机制,接下来可以提出类似的具有挑战性的问题,看看它如何处理响应。

点击测试代理,再次打开模拟器。

再次测试代理

再次询问代理船员和船员潜水。从现在开始,请注意每个对话框如何包含用户定义的消息,以及生成的响应(以红色框突出显示)。

重新测试客服人员,并再次询问什么是直播设备

你是否得到了内容充实的有用回答,而不是一般性的重复提示?太棒了!它在文本提示和流程说明中提供了您希望代理完成的任务的简洁明了描述后,现在可以更加智能,可以更智能地回答详细问题,而无需创建具体意图。客服人员可以给出更明智的回答,而不是无所谓的回答,这会让客户感到更满意。

不要羞于向经纪人提出质疑,询问它能否帮助您找到水肺潜水课程,因为您还不是一名认证潜水员。

 询问经纪人是否可以帮您找到水肺潜水课程

没错,目前我们还没有设计出协助水肺潜水课程的客服。客服人员是怎么知道的?在文本提示中,我们清楚地说明了客服人员可以和不能提供哪些帮助。“目前您无法帮助客户进行陆地潜水和运动课程。您不能推荐当地潜水商店和潜水度假村”

现在,重新测试您高兴的场景,让对话更充实。我们来看看体验有何变化。

重新测试令人满意的场景,并在对话中发挥创意

重新测试令人满意的场景,并在对话中发挥创意

当 Dialogflow 与意图匹配或尝试按照流程设计收集参数时,它会显示设计时定义的 fulfillment。当用户关闭脚本,要求提供旅行详情的摘要或主动询问是否提供电话号码时,生成式后备功能就派上用场了。

太棒了!您已经重新测试了令人满意的场景,希望您与客服人员进行了愉快而自然的对话,对话尽可能接近在线客服人员的体验。

很遗憾,对话可能会出错。我们来另做一个测试。这次,在被要求提供入住人数时,请说一个大于 15 的数字。

请提供超过 15 名房客

请提供超过 15 名房客

您需要注意以下几点:

  1. 为什么 20 不是有效数字?因为我们在意图说明中设置了允许的房客数量限制:“客服人员会收集出发时间、目的地、房客数量等信息***(最少 4 人,最多 15 人)*** * 详细联系信息*”,了解所有最新动态。LLM 返回的“很抱歉,我们只能协助预订最多 15 位房客的群组预订”这一生成式回答完全符合我们对房客数量的限制。为进一步做到这一点,guests 是一个自定义正则表达式实体,仅匹配范围 4 - 15 中包含的数字。
  2. 对话继续下去,因为最终用户仍热切希望获得 15 名潜水员的优惠。这种情况经常在自然对话中发生,我们经常会改变想法!请注意代理的合作方式,它会温和地引导用户返回到成功的路径。

对话设计涉及为对话的一半编写脚本,希望其能够足够可靠,让任何人都可以介入并执行另一半的对话。在进行长尾设计时,开发者需要关注用户在对话框的每个步骤中可能会说些什么,以定义路线、处理程序和参数。因此,我们为 Dialogflow CX 添加了生成式回退功能,让开发者能够专注于对话设计原则,而不是专注于实现细节,从而为用户提供强大的对话体验。

我们再进行一次测试,这次再用一个地点对机器人进行挑战,该地点不在可用目的地列表(例如马尔代夫)中。然后,我们将快速了解一下后台发生的情况。

选择一个不在可选目的地(例如马尔代夫)列表中的地点,再次挑战机器人

请注意,由于我们还为 destination 参数的 no-match 事件启用了生成式回退,因此会向大语言模型发送请求以生成生成的响应。常规的预设回复(在客服人员说的下方)会被忽略。

下面的文本框有助于您更好地了解这些占位符如何帮助确定向大语言模型发送的请求。

这是我们在 Dialogflow 中配置的自定义文本提示,其中占位符以粗体突出显示:

You are a friendly agent that likes helping traveling divers.
You are under development and you can only help
$flow-description

At the moment you can't help customers with land-based diving and courses. You cannot recommend local dive shops and diving resorts.

Currently you can $route-descriptions

The conversation between the human and you so far was:
${conversation USER:"Human:" AGENT:"AI"}

Then the human asked:
$last-user-utterance

You say:

在下面的文本框中,我添加了大语言模型收到的输入和包含生成的回答(将回答给用户的回答)的输出:

llm_input:
You are a friendly agent that likes helping traveling divers.
You are under development and you can only help search, find and book liveaboards.

At the moment you can't help customers with land-based diving and courses. You cannot recommend local dive shops and diving resorts.

Currently you can assist users who are looking for a group reservation or a full charter. Initially collect travel details including departure period, destination, number of guests (min 4 max 15 people), contact details. The destination must be one of the following in the Pacific: Costa Rica, Mexico, Galapagos Islands.

The conversation between the human and you so far was:
Human: Hi, my name's Alessia
AI Hi Alessia, what can I help you with today?
Human: Can you help me find a nice boat for myself and my family?
AI To assist you with that I need to collect the details of your travel and then we'll get back to you with an offer shortly.
Where would you like to go? We can organize a charter in Costa Rica, Galapagos Islands and several locations around Mexico

Then the human asked:
The kids want to go to the Maldives

llm_output:
You say:
I'm sorry Alessia, we can only help you with liveaboards in Costa Rica, Galapagos Islands and several locations around Mexico.

与之前进行的测试类似,发送回用户的响应由模型生成,并依赖于我们在 intent 说明中提供的信息:“目的地必须是太平洋地区的以下任一项:哥斯达黎加、墨西哥、加拉帕戈斯群岛

修改禁止的词组列表

您可以通过多种方式配置生成式后备功能:

  1. 选择要用于生成回答的特定(已定义)提示。
  2. 定义自定义提示。
  3. 更改禁止的词组列表。

到目前为止,我们已经介绍了前两种方法。我们来了解一下第三种。

  1. 代理设置中,前往机器学习 (ML) 标签页,然后进入生成式 AI 子标签页。
  2. 禁止的短语部分,将以下句子添加到列表中:
  3. Dangerous country
  4. Hateful place
  5. Medical assistance
  6. 点击保存
  7. 点击重置图标,然后重新测试上一个场景。请不要提供全球美丽的潜水胜地,而是输入禁止的短语之一。

测试某个禁止的词组

系统会根据禁止的短语列表对提示和生成的回答进行检查。禁止使用的词组是指禁止在生成式 AI 中使用的词组。如果输入内容包含禁止的短语或被认为不安全的短语,则生成将失败,系统会改为发出常规规定的回复(在代理中注明的相同 fulfillment)。

棒极了!我们已经介绍了一系列对话场景,在这些对话中,生成式回答可以起到很大的作用。请继续测试!

10. 恭喜

恭喜您完成此 Codelab!该放松一下了!

Cbo 放松

您已成功创建虚拟客服,并为流程和参数填充期间使用的 no-match 事件处理脚本启用了生成式回退。

生成式回退功能与良好的流程和意图描述相结合,可以提供代理的具体合作响应,而不是像“抱歉,我不确定怎么回答”之类的宽泛提示或“抱歉,您输入的选项无效”。大语言模型生成的错误提示可以温和地引导用户重新走向成功的路径,或者重塑他们对可行和不可能的预期。

您可以随意测试其他对话场景,并探索与 Dialogflow CX生成式 AI 相关的其他可用功能。

清理

您可以执行以下清理操作,以避免因此 Codelab 中使用的资源向您的 Google Cloud 账号收取费用:

  • 导航到 Dialogflow CX 控制台并删除您创建的所有代理。
  • 在 Google Cloud 控制台中,转到“API 和服务”页面,然后停用 Dialogflow API。

深入阅读

通过以下指南和资源,继续了解对话式 AI 和生成式 AI:

许可

此作品已获得 Creative Commons Attribution 2.0 通用许可授权。