🚀 原问地址:https://rasa.com/docs/rasa/reaching-out-to-user

有时候我们希望机器人在没有用户提示的情况下与用户联系,例如,如果希望机器人在用户打开聊天窗口时发出消息,或者希望在用户有一段时间没有发送消息时提示用户。

1. 首次接触

在大多数案例中,当用户打开机器人的聊天窗口时,我们希望机器人发送第一条消息。这样做可以让用户了解机器人能做什么和不能做什么,并让他们进行更成功的对话。某些消息或语音通道具备现有配置选项,可以在用户首次开始对话时向机器人发送有效负载,但我们也可以将此选项添加到自定义通道中。

将频道配置为发送有效负载后,需要制定机器人应该如何反应和问候用户。我们可以为此重新使用现有意图的行为,也可以为此制定新的意图和规则。以下是有关如何制定欢迎规则的示例。

1.1 更新配置

由于我们为此行为使用规则,所以需要将RulePolicy添加到配置文件中:

  1. policies:
  2. - name: RulePolicy

1.2 添加规则

要让机器人仅在对话开始时使用欢迎消息响应greet意图,需要添加以下规则:

  1. rules:
  2. - rule: welcome user
  3. # this rule only applies at the beginning of a conversation
  4. conversation_start: true
  5. step:
  6. - intent: greet
  7. - action: utter_welcome

1.3 添加响应

最后,将utter_welcome回复添加到领域中:

  1. responses:
  2. utter_welcome:
  3. - text: "Hi there! What can I help you with today?"

2. 外部事件

有时我们希望外部设备改变正在进行的对话过程,例如,如果我们在 Raspberry Pi(树莓派)上连接了一个传感器,我们可以使用它通过机器人在植物需要浇水的时候进行通知。

以下示例来自提醒机器人示例机器人,其中包括提醒和外部事件。

2.1 触发意图

要让来自外部设备的事件更改正在进行的对话过程,我们可以让设备发送请求到对话的trigger_intent端点,trigger_intent端点将用户意图(可能带有实体)注入到对话中。对于 Rasa,就好像我们输入了一条按照特定意图和实体分类的消息。然后,机器人将像往常一样预测并执行下一个动作。

例如,以下请求会将EXTERNAL_dry_plant意图和plant实体注入到 ID 为user123的对话中:

  1. $ curl -H "Content-Type: application/json" \
  2. -X POST \
  3. -d '{
  4. "name": "EXTERNAL_dry_plant",
  5. "entities": {"plant": "Orchid"}
  6. }' \
  7. "http://localhost:5005/conversations/user123/trigger_intent?output_channel=latest"

2.2 获取对话ID

在现实生过中,外部设备会从 API 或数据库中获取对话 ID。在给植物浇水示例中,我们可能有一个植物数据库、给植物浇水的用户以及用户的对话 ID。树莓派将直接从数据中获取对话 ID,要在本地用提醒机器人示例,我们还需要手动获取对话 ID。

2.3 添加NLU训练数据

在给植物浇水示例中,树莓派需要将带有EXTERNAL_dry_plant意图的消息发送到trigger_intent端点。此意图将保留给树莓派使用,因此不会有任何 NLU 训练样本。

  1. intents:
  2. - EXTERNAL_dry_plant

:::info 💡 注意
——————————
我们应该使用EXTERNAL_前缀命名来自其他设备的意图,因为这样在处理训练数据时,可以更轻松地查看哪些意图来自外部设设备。 :::

2.4 更新领域

要告诉机器人哪种植物需要浇水,我们可以定义一个实体,并将其与意图一起作为请求发送出去。为了能够直接在响应中使用实体值,需要为给plant插槽定义from_entity插槽映射:

  1. entities:
  2. - plant
  3. slots:
  4. plant:
  5. type: text
  6. influence_conversation: false
  7. mappings:
  8. - type: from_entity
  9. entity: plant

2.5 添加规则

我们需要一个规则来告诉机器人在收到树莓派消息时如何响应:

  1. rules:
  2. - rule: warn about dry plant
  3. steps:
  4. - intent: EXTERNAL_dry_plant
  5. - action: utter_warn_dry

2.6 添加响应

我们需要为utter_warn_dry定义响应文本:

  1. responses:
  2. utter_warn_dry:
  3. - text: "Your {plant} needs some water!"

响应将使用来自plant插槽值来警告需要浇水的特定植物。

2.7 尝试一下

要尝试给植物浇水的示例,我们需要启动 Rasa X 或 CallbackChannel。

:::info 🔔 当心
————————————
外部事件和提醒在请求-响应通道(例如test通道或者rasa shell)中不起作用,实现提醒或外部事件的机器人自定义连接器应该建立在CallbackInput通道而不是RestInput通道之上。 :::

使用会话 ID,在终端中执行以下 POST 请求来模拟外部事件:

  1. $ curl -H "Content-Type: application/json" \
  2. -X POST -d '{
  3. "name": "EXTERNAL_dry_plant",
  4. "entities": {"plant": "Orchid"}
  5. }' \
  6. "http://localhost:5005/conversations/user1234/trigger_intent?output_channel=latest"

3. 提醒器

我们可以使用提醒让机器人在设定时间后与用户进行联系,以下介绍的是提醒机器人示例,我们可以克隆项目并按照 README 中的说明尝试完整版。

3.1 调度提醒器

1)定义提醒器

为了调度提醒器,我们需要定义一个返回ReminderScheduled事件的自定义操作。例如,以下自定义操作会在五秒后进行提醒:

  1. import datetime
  2. from rasa_sdk.events import ReminderScheduled
  3. from rasa_sdk import Action
  4. class ActionSetReminder(Action):
  5. """Schedules a reminder, supplied with the last message's entities."""
  6. def name(self) -> Text:
  7. return "action_set_reminder"
  8. async def run(
  9. self,
  10. dispatcher: CollectingDispatcher,
  11. tracker: Tracker,
  12. domain: Dict[Text, Any],
  13. ) -> List[Dict[Text, Any]]:
  14. dispatcher.utter_message("I will remind you in 5 seconds.")
  15. date = datetime.datetime.now() + datetime.timedelta(seconds=5)
  16. entities = tracker.latest_message.get("entities")
  17. reminder = ReminderScheduled(
  18. "EXTERNAL_reminder",
  19. trigger_date_time=date,
  20. entities=entities,
  21. name="my_reminder",
  22. kill_on_user_message=False,
  23. )
  24. return [reminder]

ReminderScheduled事件的第一个参数是提醒的名称,上述示例中为EXTERNAL_reminder。提醒器名称稍后将用作触发对提醒器操作的意图。使用EXTERNAL_前缀命名的提醒器名称,可以更方便地查看训练数据中的情况。

我们可以看到最后一条消息的实体也传递给了提醒器,这允许对提醒器作出反应的动作能充分利用用户的调度信息。

例如,如果我们想让机器人提醒给朋友打电话,你可以给它发送一条消息,比如“提醒我给保罗打电话”。如果“保罗”被提取为PERSON实体,则对提醒器作出反应的动作就可以使用该实体,并回复“记得给保罗打电话”。

2)添加规则

为了调度提醒器,我们还需要添加一条规则:

  1. rules:
  2. - rule: Schedule a reminder
  3. steps:
  4. - intent: ask_remind_call
  5. entities:
  6. - PERSON
  7. - action: action_schedule_reminder

3)添加训练数据

我们应该添加 NLU 训练数据来调度提醒器:

  1. nlu:
  2. - intent: ask_remind_call
  3. examples: |
  4. - remind me to call John
  5. - later I have to call Alan
  6. - Please, remind me to call Vova
  7. - please remind me to call Tanja
  8. - I must not forget to call Juste

同时,我们还需要将其添加到领域中:

  1. intents:
  2. - ask_remind_call

4)更新管道

通过在配置文件 config.yml 的管道中添加SpacyNLPSpacyEntityExtractor,我们无需训练数据中注释任何名称,因为Spacy本身具备PERSON维度:

  1. pipeline:
  2. - name: SpacyNLP
  3. model: "en_core_web_md"
  4. - name: SpacyEntityExtractor
  5. dimensions: ["PERSON"]

3.2 对提醒作出反应

1)定义反应

在收到trigger_intent端点的 POST 请求后,机器人会联系用户。但是,提醒会在一定时间后使用我们在ReminderScheduled事件中定义的名称,自动将请求发送到正确的对话 ID。

要定义对提醒的反应,我们仅需要编写一个规则,告诉机器人在收到提醒意图时要采取什么动作。在呼叫提醒示例中,我们希望通过使用提醒器附带的实体,从而提醒呼叫特定的人,因此我们需要编写自定义操作来执行此操作:

  1. class ActionReactToReminder(Action):
  2. """Reminds the user to call someone."""
  3. def name(self) -> Text:
  4. return "action_react_to_reminder"
  5. async def run(
  6. self,
  7. dispatcher: CollectingDispatcher,
  8. tracker: Tracker,
  9. domain: Dict[Text, Any],
  10. ) -> List[Dict[Text, Any]]:
  11. name = next(tracker.get_slot("PERSON"), "someone")
  12. dispatcher.utter_message(f"Remember to call {name}!")
  13. return []

2)添加规则

要告诉机器人在触发提醒时要运行什么操作,需要添加相对应的规则:

  1. rules:
  2. - rule: Trigger `action_react_to_reminder` for `EXTERNAL_reminder`
  3. steps:
  4. - intent: EXTERNAL_reminder
  5. - action: action_react_to_reminder

3)添加训练数据

我们需要定义触发对提醒作出反应的意图,这并不需要我们添加任何示例,因为意图是为提醒保留的。

  1. intents:
  2. - intent: EXTERNAL_reminder

3.3 取消提醒

1)定义取消提醒的动作

要取消已经安排的提醒,我们需要一个返回ReminderCancelled事件的自定义操作。返回ReminderCancelled会取消当前安排的所有提醒。如果只是想取消某些提醒,我们可以指定一些参数来缩小计划提醒的范围:

  • ReminderCancelled(intent="EXTERNAL_greet")取消所有EXTERNAL_greet意图的提醒
  • ReminderCancelled(entities={})取消给定实体的所有提醒
  • ReminderCancelled("...")取消在创建过程中提供的具有给定名称的唯一提醒

对于来电提醒示例,我们可以定义取消所有提醒的自定义操作action_forget_reminders

  1. class ForgetReminders(Action):
  2. """Cancels all reminders."""
  3. def name(self) -> Text:
  4. return "action_forget_reminders"
  5. async def run(
  6. self,
  7. dispatcher,
  8. tracker: Tracker,
  9. domain: Dict[Text, Any]
  10. ) -> List[Dict[Text, Any]]:
  11. dispatcher.utter_message(f"Okay, I'll cancel all your reminders.")
  12. # Cancel all reminders
  13. return [ReminderCancelled()]

:::info 🔔 当心
————————————
每当我们关闭 Rasa 服务器时,所有提醒都会取消。 :::

2)添加规则

我们需要添加取消提醒的规则:

  1. rules:
  2. - rule: Cancel a reminder
  3. steps:
  4. - intent: ask_forget_reminders
  5. - action: action_forget_reminders

3)添加训练数据

我们需要定义一个触发取消提醒的意图:

  1. nlu:
  2. - intent: ask_forget_reminders
  3. examples: |
  4. - Forget about the reminder
  5. - do not remind me
  6. - cancel the reminder
  7. - cancel all reminders please

并且我们需要将其添加到领域文件中去:

  1. intents:
  2. - intent: ask_forget_reminders

3.4 尝试一下

要尝试提醒,我们需要启动 Rasa X 或 CallbackChannel。除此之外,我们还需要启动操作服务器来调度、响应和取消提醒。

然后,如果向机器人发送提醒(例如“提醒我给保罗打电话”),我们应该会在 5 秒后收到一条提醒,上面写着“记得给保罗打电话”。