rasa form的中斷形式 自然機器語言學習 人工智慧

python包包俠發表於2021-09-02

Forms形式

最常見的對話模式之一是從使用者那裡收集一些資訊以便做某事(預訂餐廳、呼叫 API、搜尋資料庫等)。這也稱為**槽填充**。

用法#

要在 Rasa Open Source 中使用表單,您需要確保將 規則策略新增到您的策略配置中。例如:

policies:
- name: RulePolicy

  

定義表單#

通過將表單新增到域中forms部分來定義表單表單的名稱也是您可以在 故事規則中用於處理表單執行的操作的名稱。您還需要為表單應填寫的每個插槽定義插槽對映您可以為要填充的每個插槽指定一個或多個插槽對映。

下面的示例形式restaurant_form將填充槽 cuisine從所提取的實體cuisine和槽num_people從實體number

1 forms:
2   restaurant_form:
3     required_slots:
4         cuisine:
5           - type: from_entity
6             entity: cuisine
7         num_people:
8           - type: from_entity
9             entity: number

一旦第一次呼叫表單操作,表單就會被啟用並提示使用者輸入下一個所需的槽值。它通過查詢呼叫 響應utter_ask_<form_name>_<slot_name>或未utter_ask_<slot_name>找到前者來實現此目的。確保在您的域檔案中為每個必需的槽定義這些響應。

啟用表格#

要啟用表單,您需要新增故事規則,其中描述了助手應何時執行該表單。在特定意圖觸發表單的情況下,您可以例如使用以下規則:

1 rules:
2 - rule: Activate form
3   steps:
4   - intent: request_restaurant
5   - action: restaurant_form
6   - active_loop: restaurant_form

active_loop: restaurant_form步驟表示應在restaurant_form執行後啟用該表單 

停用表格#

填滿所有必需的空位後,表單將自動停用。您可以使用規則或故事來描述助手在表單結尾處的行為。如果您不新增適用的故事或規則,則助手將在表單完成後自動收聽下一條使用者訊息。以下示例utter_all_slots_filled在表單your_form填滿所有必需的插槽後立即 執行話語

 1 rules:
 2 - rule: Submit form
 3   condition:
 4   # Condition that form is active.
 5   - active_loop: restaurant_form
 6   steps:
 7   # Form is deactivated
 8   - action: restaurant_form
 9   - active_loop: null
10   - slot_was_set:
11     - requested_slot: null
12   # The actions we want to run when the form is submitted.
13   - action: utter_submit
14   - action: utter_slots_values

使用者可能希望儘早退出表單。有關如何為這種情況編寫故事或規則的資訊,請參閱 編寫不愉快形式路徑的故事/規則

插槽對映#

Rasa 開源帶有四個預定義的對映,用於根據最新的使用者訊息填充表單的插槽。如果您需要自定義函式來提取所需資訊,請參閱 自定義插槽對映

來自_實體#

from_entity對映填充基於提取的實體槽。它將尋找一個被稱為entity_name填充 slot的實體slot_name如果intent_nameNone,則無論意圖名稱如何,都將填充插槽。否則,僅當使用者的意圖為 時才會填充該插槽intent_name

如果role_name和/或group_name提供,實體的角色/組標籤也需要匹配給定的值。如果訊息的意圖是 ,則槽對映將不適用excluded_intent請注意,您還可以為引數intent定義意圖列表not_intent

 1 forms:
 2   your_form:
 3     required_slots:
 4         slot_name:
 5         - type: from_entity
 6           entity: entity_name
 7           role: role_name
 8           group: group name
 9           intent: intent_name
10           not_intent: excluded_intent

 

from_entity對映中,當提取的實體唯一地對映到插槽時,即使表單沒有請求該插槽,該插槽也會被填充。如果對映不是唯一的,則提取的實體將被忽略。

forms:
  your_form:
    required_slots:
        departure_city:
          - type: from_entity
            entity: city
            role: from
          - type: from_entity
            entity: city
        arrival_city:
          - type: from_entity
            entity: city
            role: to
          - type: from_entity
            entity: city
        arrival_date:
          - type: from_entity
            entity: date

在上面的例子中,實體date唯一地設定槽arrival_date,一個實體city與角色from唯一地設定狹槽departure_city和一個實體city與角色to唯一地設定狹槽arrival_city,因此它們可被用於擬合即使未要求這些時隙對應的狹槽。但是,city沒有角色的實體可以同時填充departure_cityarrival_city 插槽,具體取決於請求的是哪個,因此如果cityarrival_date請求插槽時提取了實體,則表單將忽略它。

from_text #

from_text對映將使用下一個使用者說話的文字,以填補插槽 slot_name如果intent_nameNone,則無論意圖名稱如何,都將填充插槽。否則,僅當使用者的意圖為 時才會填充該插槽intent_name

如果訊息的意圖是 ,則槽對映將不適用excluded_intent請注意,您可以為引數intent定義意圖列表not_intent

1 forms:
2   your_form:
3     required_slots:
4         slot_name:
5         - type: from_text
6           intent: intent_name
7           not_intent: excluded_intent

 

from_intent #

from_intent對映將填充槽slot_name用值my_value如果使用者意圖是intent_nameNone如果訊息的意圖是 ,則槽對映將不適用excluded_intent請注意,您還可以為引數intent定義意圖列表not_intent

from_intent形式的初始啟用期間插槽對映將不適用。要根據啟用表單的意圖填充插槽,請使用from_trigger_intent 對映。

1 forms:
2   your_form:
3     required_slots:
4         slot_name:
5         - type: from_intent
6           value: my_value
7           intent: intent_name
8           not_intent: excluded_intent

from_trigger_intent #

from_trigger_intent對映將填充槽slot_name用值my_value 如果窗體通過用意圖使用者訊息啟用intent_name如果訊息的意圖是 ,則槽對映將不適用 excluded_intent請注意,您還可以為引數intent定義意圖列表not_intent

1 forms:
2   your_form:
3     required_slots:
4         slot_name:
5         - type: from_trigger_intent
6           value: my_value
7           intent: intent_name
8           not_intent: excluded_intent

 

為不愉快的表單路徑寫故事/規則#

您的使用者不會總是回覆您詢問他們的資訊。通常,使用者會提出問題、閒聊、改變主意或以其他方式偏離快樂的道路。

當表單處於活動狀態時,如果使用者的輸入未填充請求的插槽,則表單操作的執行將被拒絕,即表單將自動引發ActionExecutionRejection這些是表單將引發的特定場景ActionExecutionRejection

  • 請求了一個插槽,但使用者沒有用他們的最後一條訊息填充該插槽,並且您沒有定義用於驗證插槽或 提取插槽的自定義操作 
  • 請求了一個插槽,但您用於驗證插槽或 提取插槽的自定義操作 未返回任何SlotSet事件。

要故意拒絕表單執行,您還可以返回一個ActionExecutionRejected事件作為自定義驗證或槽對映的一部分。

要處理可能導致表單執行被拒絕的情況,您可以編寫包含預期中斷的規則或故事。例如,如果您希望您的使用者與您的機器人聊天,您可以新增一個規則來處理這個問題:

 1 rules:
 2 - rule: Example of an unhappy path
 3   condition:
 4   # Condition that form is active.
 5   - active_loop: restaurant_form
 6   steps:
 7   # This unhappy path handles the case of an intent `chitchat`.
 8   - intent: chitchat
 9   - action: utter_chitchat
10   # Return to form after handling the `chitchat` intent
11   - action: restaurant_form
12   - active_loop: restaurant_form

在某些情況下,使用者可能會在表單操作的中間改變主意,並決定不繼續他們的初始請求。在這種情況下,助手應該停止請求請求的插槽。

您可以使用預設操作優雅地處理此類情況,該操作action_deactivate_loop將停用表單並重置請求的插槽。此類對話的示例故事如下所示:

 1 stories:
 2 - story: User interrupts the form and doesn't want to continue
 3   steps:
 4   - intent: request_restaurant
 5   - action: restaurant_form
 6   - active_loop: restaurant_form
 7   - intent: stop
 8   - action: utter_ask_continue
 9   - intent: stop
10   - action: action_deactivate_loop
11   - active_loop: null

 

這是強烈建議你建立一個使用這些規則或故事, 互動學習如果您手寫這些規則/故事,您可能會錯過重要的事情。

高階用法#

使用自定義操作可以完全自定義表單

驗證表單輸入#

從使用者輸入中提取槽值後,您可以驗證提取的槽。預設情況下,Rasa Open Source 僅驗證在請求插槽後是否填充了任何插槽。

您可以實施自定義操作 validate_<form_name> 來驗證任何提取的插槽。確保將此操作新增到actions 域部分:

actions:
- validate_restaurant_form

執行表單時,它將執行您的自定義操作。

此自定義操作可以擴充套件FormValidationAction類以簡化驗證提取槽的過程。在這種情況下,您需要編寫validate_<slot_name>為每個提取的插槽命名函式

以下示例顯示了自定義操作的實現,該操作驗證指定的插槽cuisine是否有效。

 1 from typing import Text, List, Any, Dict
 2 
 3 from rasa_sdk import Tracker, FormValidationAction
 4 from rasa_sdk.executor import CollectingDispatcher
 5 from rasa_sdk.types import DomainDict
 6 
 7 
 8 class ValidateRestaurantForm(FormValidationAction):
 9     def name(self) -> Text:
10         return "validate_restaurant_form"
11 
12     @staticmethod
13     def cuisine_db() -> List[Text]:
14         """Database of supported cuisines"""
15 
16         return ["caribbean", "chinese", "french"]
17 
18     def validate_cuisine(
19         self,
20         slot_value: Any,
21         dispatcher: CollectingDispatcher,
22         tracker: Tracker,
23         domain: DomainDict,
24     ) -> Dict[Text, Any]:
25         """Validate cuisine value."""
26 
27         if slot_value.lower() in self.cuisine_db():
28             # validation succeeded, set the value of the "cuisine" slot to value
29             return {"cuisine": slot_value}
30         else:
31             # validation failed, set this slot to None so that the
32             # user will be asked for the slot again
33             return {"cuisine": None}

您還可以擴充套件Action類並檢索提取的插槽tracker.slots_to_validate 以完全自定義驗證過程。

自定義插槽對映#

如果沒有預定義的插槽對映適合您的用例,您可以使用 自定義操作 validate_<form_name>來編寫您自己的提取程式碼。Rasa Open Source 會在表單執行時觸發這個動作。

如果您使用的是 Rasa SDK,我們建議您擴充套件提供的 FormValidationAction使用FormValidationAction時,提取海關槽位需要三個步驟:

  1. extract_<slot_name>為每個應該以自定義方式對映的插槽定義一個方法
  2. 確保在域檔案中為表單只列出那些使用預定義對映的插槽 
  3. 覆蓋required_slots以將具有自定義對映的所有插槽新增到表單應請求的插槽列表中。

下面的示例展示了一個表單的實現outdoor_seating,除了使用預定義對映的插槽之外,它還以自定義方式 提取插槽 該方法根據關鍵字是否出現在最後一條使用者訊息中來extract_outdoor_seating設定槽outdoor_seatingoutdoor

 1 from typing import Dict, Text, List, Optional, Any
 2 
 3 from rasa_sdk import Tracker
 4 from rasa_sdk.executor import CollectingDispatcher
 5 from rasa_sdk.forms import FormValidationAction
 6 
 7 
 8 class ValidateRestaurantForm(FormValidationAction):
 9     def name(self) -> Text:
10         return "validate_restaurant_form"
11 
12     async def required_slots(
13         self,
14         slots_mapped_in_domain: List[Text],
15         dispatcher: "CollectingDispatcher",
16         tracker: "Tracker",
17         domain: "DomainDict",
18     ) -> Optional[List[Text]]:
19         required_slots = slots_mapped_in_domain + ["outdoor_seating"]
20         return required_slots
21 
22     async def extract_outdoor_seating(
23         self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict
24     ) -> Dict[Text, Any]:
25         text_of_last_user_message = tracker.latest_message.get("text")
26         sit_outside = "outdoor" in text_of_last_user_message
27 
28         return {"outdoor_seating": sit_outside}

預設情況下,FormValidationAction 將自動將 設定為未填充requested_slot的第一個插槽required_slots

動態表單行為#

預設情況下,Rasa Open Source 會從域檔案中為您的表單列出的插槽中請求下一個空插槽。如果您使用 自定義插槽對映FormValidationAction,它將要求該required_slots方法返回的第一個空插槽如果required_slots填寫了所有插槽,則該表格將被停用。

如果需要,您可以動態更新表單所需的插槽。例如,當您需要基於前一個槽的填充方式的更多詳細資訊時,或者您想更改請求槽的順序時,這很有用。

如果您使用的是 Rasa SDK,我們建議您使用FormValidationAction和 覆蓋required_slots來適應您的動態行為。您應該extract_<slot name>為每個不使用預定義對映的插槽實現一個方法,如自定義插槽對映 中所述下面的示例將詢問使用者是否想坐在陰涼處或陽光下,以防他們說他們想坐在外面。

 1 from typing import Text, List, Optional
 2 
 3 from rasa_sdk.forms import FormValidationAction
 4 
 5 class ValidateRestaurantForm(FormValidationAction):
 6     def name(self) -> Text:
 7         return "validate_restaurant_form"
 8 
 9     async def required_slots(
10         self,
11         slots_mapped_in_domain: List[Text],
12         dispatcher: "CollectingDispatcher",
13         tracker: "Tracker",
14         domain: "DomainDict",
15     ) -> Optional[List[Text]]:
16         additional_slots = ["outdoor_seating"]
17         if tracker.slots.get("outdoor_seating") is True:
18             # If the user wants to sit outside, ask
19             # if they want to sit in the shade or in the sun.
20             additional_slots.append("shade_or_sun")
21 
22         return additional_slots + slots_mapped_in_domain

request_slot 插槽#

該插槽requested_slot將作為型別為 的插槽自動新增到域中text的值requested_slot將在對話期間被忽略。如果你想改變這個行為,你需要將 加入requested_slot 到你的域檔案中作為一個分類槽, influence_conversation設定為true如果您想以不同的方式處理不愉快的路徑,您可能想要這樣做,具體取決於使用者當前詢問的插槽。例如,如果您的使用者用另一個問題來回答機器人的一個問題,比如您為什麼需要知道這一點? 對此explain意圖的反應取決於我們在故事中的位置。在餐廳案例中,您的故事將如下所示:

 1 stories:
 2 - story: explain cuisine slot
 3   steps:
 4   - intent: request_restaurant
 5   - action: restaurant_form
 6   - active_loop: restaurant
 7   - slot_was_set:
 8     - requested_slot: cuisine
 9   - intent: explain
10   - action: utter_explain_cuisine
11   - action: restaurant_form
12   - active_loop: null
13 
14 - story: explain num_people slot
15   steps:
16   - intent: request_restaurant
17   - action: restaurant_form
18   - active_loop: restaurant
19   - slot_was_set:
20     - requested_slot: cuisine
21   - slot_was_set:
22     - requested_slot: num_people
23   - intent: explain
24   - action: utter_explain_num_people
25   - action: restaurant_form
26   - active_loop: null

同樣,強烈建議您使用 互動式學習來構建這些故事。

使用自定義操作請求下一個插槽#

一旦表單確定使用者接下來必須填充哪個位置,它就會執行操作utter_ask_<form_name>_<slot_name>utter_ask_<slot_name> 要求使用者提供必要的資訊。如果常規話語還不夠,您還可以使用自定義操作action_ask_<form_name>_<slot_name>或 action_ask_<slot_name>要求下一個槽。

 1 from typing import Dict, Text, List
 2 
 3 from rasa_sdk import Tracker
 4 from rasa_sdk.events import EventType
 5 from rasa_sdk.executor import CollectingDispatcher
 6 from rasa_sdk import Action
 7 
 8 
 9 class AskForSlotAction(Action):
10     def name(self) -> Text:
11         return "action_ask_cuisine"
12 
13     def run(
14         self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict
15     ) -> List[EventType]:
16         dispatcher.utter_message(text="What cuisine?")
17         return []

 

相關文章