使用 API 修改对话框
REST API 支持以编程方式修改对话。 您可以使用 /dialog_nodes API 来创建,删除或修改对话节点。
对话是相互连接的节点的树,它必须符合某些规则才能有效。 您对对话节点进行的任何更改都可能对其他节点或对话结构产生级联影响。 在使用 /dialog_nodes API 修改对话之前,请确保您了解更改如何影响对话的其余部分。 您可以生成当前对话框的备份副本。有关更多信息,请参阅 备份和复原数据。
有效的对话始终满足以下条件:
-
每个对话节点都具有唯一标识(
dialog_node属性)。 -
子节点知道其父节点(
parent属性)。 但是,父节点并不知道其子节点。 -
节点知道其前一个直接同代(如果有)(
previous_sibling属性)。 共享父代的所有同代构成一个链接列表,每个节点都指向前一个节点。 -
只有父代的一个子代可以是第一个同代 (表示其
previous_sibling为空)。 -
节点指向的前一个同代不能是其他父代的子代。
-
两个节点不能指向相同的前一个同代。
-
一个节点可以指定下一个要运行的另一个节点 (
next_step属性)。 -
节点不能是其自己的父代或其自己的同代。
-
节点必须具有包含以下其中一个值的 type 属性。 如果未指定 type 属性,那么 type 为
standard。event_handler:为框架节点或单个槽节点定义的处理程序。
在工具中,可以通过单击带槽的节点中的管理处理程序链接来定义框架节点处理程序。 (工具用户界面不会公开槽级别事件处理程序,但您可以通过 API 进行定义。)
frame:具有slot类型的一个或多个子节点的节点。 必须填充必需的所有子槽节点后,服务才能退出框架节点。
框架节点类型在工具中表示为带槽的节点。 包含插槽的节点表示为类型为
frame的节点。它是每个槽的父节点,表示为类型为slot的子节点。response_condition:条件响应。
在工具中,可以向节点添加一个或多个条件响应。 定义的每个条件响应在底层 JSON 中都会表示为
response_condition类型的单个节点。slot:frame类型的节点的子节点。
此节点类型在工具中表示为添加到单个节点的多个插槽之一。 该单个节点在 JSON 中表示为
frame类型的父节点。standard:典型的对话节点。 这是缺省类型。
-
对于具有相同父节点的
slot类型的节点,同代顺序 (由previous_sibling属性指定) 反映处理槽的顺序。 -
类型为
slot的节点必须具有类型为frame的父节点。 -
类型为
frame的节点必须具有至少一个类型为slot的子节点。 -
类型为
response_condition的节点必须具有类型为standard或frame的父节点。 -
类型为
response_condition和event_handler的节点不能具有子代。 -
类型为
event_handler的节点还必须具有event_name属性,此属性包含以下其中一个值,用于识别节点事件的类型:filled: 定义在用户提供的值满足槽的 检查 字段中指定的条件时要执行的操作,并填充该槽。 仅当为槽定义了“已找到”条件时,才会提供此名称的处理程序。focus: 定义要显示的问题,提示用户提供槽所需的信息。 仅当槽是必需的时,才会提供具有此名称的处理程序。generic:定义要监视的条件,此条件可以处理用户在填充槽或带槽的节点时可能会提出的无关问题。input:更新消息上下文以包含一个上下文变量,其变量值将从用户处收集以填充至槽。 对于框架节点中的每个槽,都必须提供具有此名称的处理程序。nomatch:定义在用户对槽提示的响应不包含有效值时要执行的操作。 仅当为槽定义了“找不到”条件时,才会提供具有此名称的处理程序。
下图说明了您在工具用户界面中定义针对每个指定事件触发的代码的位置。
事件处理程序 -
event_name 为
event_handler且类型为generic的节点可以具有类型为slot或frame的父代。 -
event_name 为
event_handler、focus、input或filled且类型为nomatch的节点必须具有类型为slot的父代。 -
如果多个具有相同 event_name 的 event_handler 与同一父节点相关联,那么同代的顺序是事件处理程序的运行顺序。
-
对于具有同一父槽节点的
event_handler节点,无论节点定义的位置如何,执行顺序都是相同的。 事件按以下顺序通过 event_name 触发:- focus
- input
- filled
- generic*
- nomatch
- 如果为此槽或父帧定义了具有 event_name
generic的event_handler,那么会在填充的和 nomatch event_handler 节点之间运行。
以下示例显示各种修改会如何导致级联更改。
创建节点
请考虑以下简单对话树:
可以使用以下主体向 /dialog_nodes 发起 POST 请求来创建新节点:
{
"dialog_node": "node_8"
}
现在,该对话类似于:
由于已创建 node_8 但未指定 parent 或 previous_sibling 的值,因此现在该节点是对话中的第一个节点。 除了创建 node_8 外,服务还修改了 node_1,以使其 previous_sibling 属性指向新节点。
可以通过指定父代和前一个同代,在对话中的其他地方创建节点:
{
"dialog_node": "node_9",
"parent": "node_2",
"previous_sibling": "node_5"
}
为 parent 和 previous_node 指定的值必须有效:
- 这两个值都必须引用现有节点。
- 指定的父代必须与前一个同代的父代相同(或者如果前一个同代没有父代,那么为
null)。 - 父代不能是类型为
response_condition或event_handler的节点。
生成的对话类似于:
除了创建 node_9 外,该服务还自动更新了 previous_siblingnode_6* 的 * 属性,以使其指向新节点。
将节点移至其他父代
通过将 POST /dialog_nodes/node_5 方法与以下主体配合使用,将 node_5 移至其他父代:
{
"parent": "node_1"
}
为 parent 指定的值必须有效:
- 必须引用现有节点。
- 它不得引用已修改的节点 (节点不能是其自己的父节点)。
- 它不得引用已修改的节点的后代。
- 不能引用类型为
response_condition或event_handler的节点。
这将生成以下已更改的结构:
这里发生了几件事:
- node_5 移至其新父代时,node_7 将随其一起移动(因为
parentnode_7** 的 ** 值未更改)。 移动节点时,该节点的所有后代都始终与该节点在一起。 - 由于我们没有为
previous_siblingnode_5** 指定 ** 值,因此现在该节点是 node_1 下的第一个同代。 previous_siblingnode_4** 的 ** 属性已更新为node_5。- node_9 的
previous_sibling属性已更新为null,因为它现在是 node_2 下的第一个同代。
对同代重新排序
现在,通过将 POST /dialog_nodes/node_5 方法与以下主体配合使用,将 node_5 设置为第二个同代,而不是第一个同代:
{
"previous_sibling": "node_4"
}
修改 previous_sibling 时,新值必须有效:
- 必须引用现有节点
- 它不得引用已修改的节点 (节点不能是其自己的同代)
- 必须引用同一父代的子代(所有同代必须具有相同的父代)
结构更改如下所示:
Node_7 保留其父代。 此外,还会修改 node_4,使其 previous_sibling 为 null,因为它现在是第一个同代。
删除节点
使用 DELETE /dialog_nodes/node_1 方法删除 node_1。
结果是:
Node_1,node_4,node_5 和 node_7 已全部删除。 删除节点时,该节点的所有后代都会一起删除。 因此,如果删除根节点,那么将删除对话树的整个分支。 对已删除节点的其他任何引用(例如,next_step 引用)都会更改为 null。
此外,node_2 将更新为指向 node_8 作为其新的前一个同代。
重命名节点
使用具有以下主体的 POST /dialog_nodes/node_2 方法来重命名 node_2:
{
"dialog_node": "node_X"
}
对话框的结构未更改,但已修改多个节点以反映已更改的名称:
parentnode_9** 和 node_6 的 ** 属性previous_siblingnode_3** 的 ** 属性
此外,对已修改节点的其他任何引用(例如,next_step 引用)也会更改。