使用动作#

引言#

您已经在 事件生成与匹配 部分了解了如何使用事件。现在我们将介绍动作,以便您更好地管理事件及其时间依赖性。

重要

动作定义

<ActionName>[(param=<value>[, param=<value>]…)]

示例

# Bot utterance action
UtteranceBotAction(script="Hello", intensity=1.0)

# Bot gesture action
GestureBotAction(gesture="Wave with one hand")

动作使用帕斯卡命名法(Pascal case),类似于事件,但以 Action 结尾。

与事件不同,动作具有依赖于底层事件的生命周期。所有动作都包含以下主要事件,其中我们使用 X 作为动作名称的占位符:

StartXAction(**action_arguments) # Start of an action with given action specific arguments
StopXAction(action_uid: str) # Stop of an action with the related uid
XActionStarted(action_uid: str) # Event to signal the action has started
XActionFinished(action_uid: str, is_success: bool, was_stopped: bool, **action_arguments) # Event to signal that the action has ended

在 Colang 中,动作就像 Python 中的对象,事件可以像动作的对象方法一样访问。

XAction(**action_arguments).Start()
XAction.Stop(action_uid: str)
XAction.Started(action_uid: str)
XAction.Finished(action_uid: str, is_success: bool, was_stopped: bool, **action_arguments)

以下是两种常见的、依赖于事件的动作生命周期:

XAction.Start() -> Action state: `Starting`
XAction.Started() -> Action state: `Running`
XAction.Finished() -> Action state: `Finished`
XAction.Start() -> Action state: `Starting`
XAction.Started() -> Action state: `Running`
XAction.Stop() -> Action state: `Stopping`
XAction.Finished() -> Action state: `Finished`

UMIM 参考文档中,您会找到许多预定义的动作,它们应该涵盖最常见的用例。让我们看看一个最突出的动作,名为 UtteranceBotAction。此动作代表了与用户沟通的主要渠道,例如通过语音或文本(取决于具体系统)。此动作与从机器人的角度可分为输出事件和输入事件的事件相关联:

输出事件

UtteranceBotAction(script: str).Start()
UtteranceBotAction.Stop(action_uid: str)
UtteranceBotAction.Change(action_uid: str, intensity: float)

输入事件

UtteranceBotAction.Started(action_uid: str, action_started_at: str, ...)
UtteranceBotActionScript.Updated(action_uid: str, interim_transcript: str, ...)
UtteranceBotAction.Finished(action_uid: str, final_transcript: str, ...)

注意

请注意,Colang 不仅限于从机器人视角工作,也可以用于模拟用户端。在这种情况下,输出和输入的分类将会互换。

虽然使用 sendmatch 关键字如何处理这些事件没有强制性规则,但在大多数情况下,我们会生成输出事件并匹配输入事件。例如,如果我们希望机器人在做出姿势之前完成说话,我们可以创建以下交互模式:

actions/action_events/main.co#
flow main
    match StartEvent()
    send UtteranceBotAction(script="Hello").Start() as $event_ref
    match UtteranceBotActionFinished(action_uid=$event_ref.action_uid)
    send GestureBotAction(gesture="Wave").Start()
> /StartEvent

Hello

Gesture: Wave

我们可以看到,通过使用 Start 动作事件引用的 action_uid 属性,我们可以匹配特定动作的 Finished 事件。这一点很重要,因为可能还有其他 UtteranceBotAction 在此之前完成。

Start 动作#

Colang 支持多种基于动作概念的特性,这些特性使得设计交互模式更加方便。我们首先介绍的特性是 start 语句。

重要

Start 语句定义

start <Action> [as $<action_ref_name>] [and|or <Action> [as $<action_ref_name>]…)]

示例

start UtteranceBotAction(script="Hello") as $bot_action_ref
actions/start_keyword/main.co#
flow main
    start UtteranceBotAction(script="Hello") as $ref_action
    match $ref_action.Finished()
    start GestureBotAction(gesture="Wave")
    match RestartEvent()
Hello

Gesture: Wave

关键字 start 创建一个动作对象,然后生成一个特定于动作的 Start 动作事件。可以使用 as $ref_name 存储此动作对象的引用。有了这个动作引用,我们现在可以方便地匹配到 Finished 事件,并且不再需要使用 action_uid 参数来标识特定动作。

现在让我们看看一个常见的用户动作 UtteranceUserAction 的示例。此动作是 UtteranceBotAction 的对应物,是用户表达自己的主要渠道,例如通过说话、写作或任何其他方式,具体取决于实际系统。用户动作通常不是由 Colang 启动的,而是由用户(系统)启动的。以下是最重要的动作事件和参数:

UtteranceUserActionStarted(action_uid: str)
UtteranceUserActionTranscriptUpdated(action_uid: str, interim_transcript: str)
UtteranceUserActionIntensityUpdated(action_uid: str, intensity: float)
UtteranceUserActionFinished(action_uid: str, final_transcript: str)

基于此,现在让我们构建一个小的对话模式:

actions/dialog_pattern/main.co#
flow main
    match UtteranceUserAction.Finished(final_transcript="Hi")
    start UtteranceBotAction(script="Hi there! How are you?") as $ref_action_1
    match $ref_action_1.Finished()
    match UtteranceUserAction.Finished(final_transcript="Good and you?")
    start UtteranceBotAction(script="Great! Thanks") as $ref_action_2
    start GestureBotAction(gesture="Thumbs up") as $ref_action_3
    match $ref_action_2.Finished() and $ref_action_3.Finished()
> Hi

Hi there! How are you?

> Good and you?

Great! Thanks

Gesture: Thumbs up

您可能已经注意到,这与我们在引言示例 introduction/interaction_sequence/main.co 中看到的示例非常相似。区别在于我们拥有更多的时间控制,并且只有在机器人完成话语后才会开始匹配用户输入。另请注意,交互模式只有在第二个机器人话语动作和机器人姿势动作都完成后才会完成。

Await 动作#

让我们引入 await 语句来进一步简化前面的示例:

actions/await_keyword/main.co#
flow main
    match UtteranceUserAction.Finished(final_transcript="Hi")
    await UtteranceBotAction(script="Hi there! How are you?")
    match UtteranceUserAction.Finished(final_transcript="Good and you?")
    # ...

await 是启动一个动作并等待该动作完成(即匹配 .Finished() 事件)的快捷表示法。

重要

Await 语句定义

await <Action> [as $<action_ref_name>] [and|or <Action> [as $<action_ref_name>]…)]

示例

await UtteranceBotAction(script="Hello") as $bot_action_ref

不幸的是,我们无法用这个来简化示例的第二部分……但真的不能吗?实际上可以!我们可以利用动作分组,使用 and 关键字进行简化,如下所示:

actions/action_grouping/main.co#
flow main
    match UtteranceUserAction.Finished(final_transcript="Hi")
    await UtteranceBotAction(script="Hi there! How are you?")
    match UtteranceUserAction.Finished(final_transcript="Good and you?")
    await UtteranceBotAction(script="Great! Thanks") and GestureBotAction(gesture="Thumbs up")

动作分组与事件分组相同,只是使用了 startawait 关键字代替了 sendmatch

重要

注意,以下内容

await Action1() or Action2()

将随机启动其中一个动作(不是全部!)并等待其完成

为了进一步简化,我们实际上可以完全省略所有的 await 关键字,因为它是默认的语句关键字。

actions/omit_wait_keyword/main.co#
flow main
    match UtteranceUserAction.Finished(final_transcript="Hi")
    UtteranceBotAction(script="Hi there! How are you?")
    match UtteranceUserAction.Finished(final_transcript="Good and you?")
    UtteranceBotAction(script="Great! Thanks") and GestureBotAction(gesture="Thumbs up")

重要

await 是默认的语句关键字,可以省略。

如果我们想启动两个动作,并且只等待其中任何一个完成,我们可以这样做:

actions/wait_for_first_action_only/main.co#
flow main
    match StartEvent()
    start UtteranceBotAction(script="Great! Thanks") as $ref_action_1
        and GestureBotAction(gesture="Thumbs up") as $ref_action_2
    match $ref_action_1.Finished() or $ref_action_2.Finished()

更多关于动作#

如果需要,我们也可以使用动作引用来停止动作,如下所示:

actions/stop_action/main.co#
flow main
    match StartEvent()
    start UtteranceBotAction(script="Great! Thanks") as $ref_action
    send $ref_action.Stop()

不幸的是,对于简单的聊天 CLI,我们不会看到 Stop 事件的任何效果,因为话语会立即完成。但在任何真实系统中,话语都需要一些时间来完成,如果需要,可以像这样停止。

另一个需要指出的细节是,通过动作引用访问并匹配动作事件与直接通过动作匹配之间的区别:

# Case 1) Wait for Finished event of the specific action
start UtteranceBotAction(script="hi") as $action_ref
match $action_ref.Finished()

# Case 2) Wait for the Finished event of any UtteranceBotAction
match UtteranceBotAction.Finished()

在第一种情况下,匹配的是特定的动作引用(相同的 action_uid 参数),不会匹配到任何其他 UtteranceBotActionFinished 事件。第二种情况更普遍,会匹配到任何 UtteranceBotAction 的任何 Finished 事件。

至此,我们现在准备好在下一章 定义流程 中了解更多关于流程的概念。