400 多行代碼!超詳細 Rasa 中文聊天機器人開發指南

400 多行代碼!超詳細 Rasa 中文聊天機器人開發指南 | 原力計劃

作者 | 無名之輩FTER

出品 | 程序人生(ID:coder_life)

本文翻譯自Rasa官方文檔,並融合了自己的理解和項目實戰,同時對文檔中涉及到的技術點進行了一定程度的擴展,目的是為了更好的理解Rasa工作機制。與本文配套的項目GitHub地址:ChitChatAssistant https://github.com/jiangdongguo/ChitChatAssistant,歡迎star和issues,我們共同討論、學習!

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

對話管理

1.1 多輪對話

多輪對話是相對於單輪對話而言的,單輪對話側重於一問一答,即直接根據用戶的問題給出精準的答案。問答更接近一個信息檢索的過程,雖然也可能涉及簡單的上下文處理,但通常是通過指代消解和 query 補全來完成的,而多輪對話側重於需要維護一個用戶目標狀態的表示和一個決策過程來完成任務,具體來說就是用戶帶著明確的目的而來,希望得到滿足特定限制條件的信息或服務,例如:訂餐,訂票,尋找音樂、電影或某種商品等。因為用戶的需求可以比較複雜,可能需要分多輪進行陳述,用戶也可能在對話過程中不斷修改或完善自己的需求。此外,當用戶的陳述的需求不夠具體或明確的時候,機器也可以通過詢問、澄清或確認來幫助用戶找到滿意的結果。

因此,任務驅動的多輪對話不是一個簡單的自然語言理解加信息檢索的過程,而是一個決策過程,

需要機器在對話過程中不斷根據當前的狀態決策下一步應該採取的最優動作(如:提供結果,詢問特定限制條件,澄清或確認需求,等等)從而最有效的輔助用戶完成信息或服務獲取的任務。

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

注:任務驅動的多輪對話系統通常為封閉域(domain)(閒聊系統為開放域),而特定限制條件對應於槽(Slot),也就是說,用戶在滿足特定限制條件時就是一次槽值填充的過程,如果用戶能夠在一次會話中,滿足全部的限制條件,那麼就不必進行多輪對話,即可直接使用戶得到滿意的信息或服務。

1.2 對話管理

對話管理,即Dialog Management(DM),它控制著人機對話的過程,是人機對話系統的重要組成部分。DM會根據NLU模塊輸出的語義表示執行對話狀態的更新和追蹤,並根據一定策略選擇相應的候選動作。簡單來說,就是DM會根據對話歷史信息,決定此刻對用戶的反應,比如在任務驅動的多輪對話系統中,用戶帶著明確的目的如訂餐、訂票等,用戶需求比較複雜,有很多限制條件,可能需要分多輪進行陳述,一方面,用戶在對話過程中可以不斷修改或完善自己的需求,另一方面,當用戶的陳述的需求不夠具體或明確的時候,機器也可以通過詢問、澄清或確認來幫助用戶找到滿意的結果。如下圖所示,DM 的輸入就是用戶輸入的語義表達(或者說是用戶行為,是 NLU 的輸出)和當前對話狀態,輸出就是下一步的系統行為和更新的對話狀態。這是一個循環往復不斷流轉直至完成任務的過程。

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

從本質上來說,**任務驅動的對話管理實際就是一個決策過程,系統在對話過程中不斷根據當前狀態決定下一步應該採取的最優動作(如:提供結果,詢問特定限制條件,澄清或確認需求等),從而最有效的輔助用戶完成信息或服務獲取的任務。**對話管理的任務大致有:

  • 對話狀態維護(dialog state tracking, DST)

對話狀態是指記錄了哪些槽位已經被填充、下一步該做什麼、填充什麼槽位,還是進行何種操作。用數學形式表達為,t+1 時刻的對話狀態S(t+1),依賴於之前時刻 t 的狀態St,和之前時刻 t 的系統行為At,以及當前時刻 t+1 對應的用戶行為O(t+1)。可以寫成S(t+1)←St+At+O(t+1)。

  • 生成系統決策(dialog policy)

根據 DST 中的對話狀態(DS),產生系統行為(dialog act),決定下一步做什麼 dialog act 可以表示觀測到的用戶輸入(用戶輸入 -> DA,就是 NLU 的過程),以及系統的反饋行為(DA -> 系統反饋,就是 NLG 的過程)。

  • 作為接口與後端/任務模型進行交互

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

Rasa Core

Rasa Core是Rasa框架提供的對話管理模塊,它類似於聊天機器人的大腦,主要的任務是維護更新對話狀態和動作選擇,然後對用戶的輸入作出響應。所謂對話狀態是一種機器能夠處理的對聊天數據的表徵,對話狀態中包含所有可能會影響下一步決策的信息,如自然語言理解模塊的輸出、用戶的特徵等;所謂動作選擇,是指基於當前的對話狀態,選擇接下來合適的動作,例如向用戶追問需補充的信息、執行用戶要求的動作等。舉一個具體的例子,用戶說“幫我媽媽預定一束花”,此時對話狀態包括自然語言理解模塊的輸出、用戶的位置、歷史行為等特徵。在這個狀態下,系統接下來的動作可能是:

  • 向用戶詢問可接受的價格,如“請問預期價位是多少?”;

  • 向用戶確認可接受的價格,如“像上次一樣買價值200的花可以嗎?”

  • 直接為用戶預訂

2.1 Stories

Rasa的故事是一種訓練數據的形式,用來訓練Rasa的對話管理模型。故事是用戶和人工智能助手之間的對話的表示,轉換為特定的格式,其中用戶輸入表示為相應的意圖(和必要的實體),而助手的響應表示為相應的操作名稱。Rasa核心對話系統的一個訓練示例稱為一個故事。這是一個故事數據格式的指南。兩段對話樣本示例:

<code>/<code><code>## greet + location/price + cuisine + num people/<code><code>* greet/<code><code> - utter_greet/<code><code>* inform{"location": "rome", "price": "cheap"}/<code><code> - action_on_it/<code><code> - action_ask_cuisine/<code><code>* inform{"cuisine": "spanish"}/<code><code> - action_ask_numpeople /<code><code>* inform{"people": "six"}/<code><code> - action_ack_dosearch/<code>
<code>/<code><code>## happy path /<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - form{"name": }/<code>

Story格式大致包含三個部分:

  • 1. 用戶輸入(User Messages)

使用*開頭的語句表示用戶的輸入消息,我們無需使用包含某個具體內容的輸入,而是使用NLU管道輸出的intent和entities來表示可能的輸入。需要注意的是,如果用戶的輸入可能包含entities,建議將其包括在內,將有助於policies預測下一步action。這部分大致包含三種形式,示例如下:

(1)* greet 表示用戶輸入沒有entity情況;

(2)* inform{"people": "six"} 表示用戶輸入包含entity情況,響應這類intent為普通action;

(3)* request_weather 表示用戶輸入Message對應的intent為form action情況;

  • 2. 動作(Actions)

使用-開頭的語句表示要執行動作(Action),可分為utterance actions和custom actions,其中,前者在domain.yaml中定義以utter_為前綴,比如名為greet的意圖,它的回覆應為utter_greet;後者為自定義動作,具體邏輯由我們自己實現,雖然在定義action名稱的時候沒有限制,但是還是建議以action_為前綴,比如名為inform的意圖fetch_profile的意圖,它的response可為action_fetch_profile。

  • 3. 事件(Events)

Events也是使用-開頭,主要包含槽值設置(SlotSet)和激活/註銷表單(Form),它是是Story的一部分,並且必須顯示的寫出來。Slot Events和Form Events的作用如下:

(1)Slot Events

Slot Events的作用當我們在自定義Action中設置了某個槽值,那麼我們就需要在Story中Action執行之後顯著的將這個SlotSet事件標註出來,格式為- slot{"slot_name": "value"}。比如,我們在action_fetch_profile中設置了Slot名為account_type的值,代碼如下:

<code>from rasa_sdk.actions import Action/<code><code>from rasa_sdk.events import SlotSet/<code><code>import requests/<code>

<code>class FetchProfileAction(Action):/<code><code> def name(self):/<code><code> return "fetch_profile"/<code>

<code> def run(self, dispatcher, tracker, domain):/<code><code> url = "http://myprofileurl.com"/<code><code> data = requests.get(url).json/<code><code> return [SlotSet("account_type", data["account_type"])]/<code>

那麼,就需要在Story中執行action_fetch_profile之後,添加- slot{"account_type" : "premium"}。雖然,這麼做看起來有點多餘,但是Rasa規定這麼做必須的,目的是提高訓練時準確度。

<code>## fetch_profile/<code><code>* fetch_profile/<code><code> - action_fetch_profile/<code><code> - slot{"account_type" : "premium"}/<code><code> - utter_welcome_premium/<code>

當然,如果您的自定義Action中將槽值重置為None,則對應的事件為-slot{"slot_name": }。

(2)Form Events

在Story中主要存在三種形式的表單事件(Form Events),它們可表述為:

  • Form Action事件

Form Action即表單動作事件,是自定義Action的一種,用於一個表單操作。示例如下:

<code>- restaurant_form/<code>
  • Form activation事件

form activation即激活表單事件,當form action事件執行後,會立馬執行該事件。示例如下:

<code>- form{"name": "restaurant_form"}/<code>
  • Form deactivation事件

form deactivation即註銷表單事件,作用與form activation相反。示例如下:

<code>- form{"name": }/<code>

總之,我們在構建Story時,可以說是多種多樣的,因為設計的故事情節是多種多樣的,這就意味著上述三種內容的組合也是非常靈活的。另外,在設計Story時Rasa還提供了Checkpoints 和OR statements兩種功能,來提升構建Story的靈活度,但是需要注意的是,東西雖好,但是不要太貪了,過多的使用不僅增加了複雜度,同時也會拖慢訓練的速度。其中,Checkpoints用於模塊化和簡化訓練數據,示例如下:

<code>## first story/<code><code>* greet/<code><code> - action_ask_user_question/<code><code>> check_asked_question/<code>

<code>## user affirms question/<code><code>> check_asked_question/<code><code>* affirm/<code><code> - action_handle_affirmation/<code><code>> check_handled_affirmation/<code>

<code>## user denies question/<code><code>> check_asked_question/<code><code>* deny/<code><code> - action_handle_denial/<code><code>> check_handled_denial/<code>

<code>## user leaves/<code><code>> check_handled_denial/<code><code>> check_handled_affirmation/<code><code>* goodbye/<code><code> - utter_goodbye/<code>

在上面的例子中,可以使用> check_asked_question表示first story,這樣在其他story中,如果有相同的first story部分,可以直接用> check_asked_question代替。而OR Statements主要用於實現某一個action可同時響應多個意圖的情況,比如下面的例子:

<code>## story/<code><code>* affirm OR thankyou/<code><code> - action_handle_affirmation/<code>

2.2 Domain

Domain,譯為**“領域”**,它描述了對話機器人應知道的所有信息,類似於“人的大腦”,存儲了意圖intents、實體entities、插槽slots以及動作actions等信息,其中,intents、entities在NLU訓練樣本中定義,slots對應於entities類型,只是表現形式不同。domain.yml文件組成結構如下:

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

具體介紹如下:

1. intents

<code>intents:/<code><code> - affirm/<code><code> - deny/<code><code> - greet/<code><code> - request_weather/<code><code> - request_number/<code><code> - inform/<code><code> - inform_business/<code><code> - stop/<code><code> - chitchat/<code>

intents,即意圖,是指我們輸入一段文本,希望Bot能夠明白這段文本是什麼意思。在Rasa框架中,意圖的定義是在NLU樣本中實現的,並且在每個意圖下面我們需要枚舉儘可多的樣本用於訓練,以達到Bot能夠準確識別出我們輸入的一句話到底想要幹什麼。

2. session_config

<code>session_config:/<code><code> carry_over_slots_to_new_session: true/<code><code> session_expiration_time: 60/<code>

session_config,即會話配置,這部分的作用為配置一次會話(conversation session)是否有超時限制。上例演示的是,每次會話的超時時間為60s,如果用戶開始一段會話後,在60s內沒有輸入任何信息,那麼這次會話將被結束,然後Bot又會開啟一次新的會話,並將上一次會話的Slot值拷貝過來。當然,我們希望捨棄上一次會話Slot的值,可以將carry_over_slots_to_new_session設置為false。另外,當session_expiration_time被設置為0時,Bot永遠不會結束當前會話並一直等待用戶輸入(注:執行action_session_start仍然可以開始一次新的會話,在設置為0的情況下)。

3. slots

<code>slots:/<code><code> date_time:/<code><code> type: unfeaturized/<code><code> auto_fill: false/<code><code> address:/<code><code> type: unfeaturized/<code><code> auto_fill: false/<code>

Slots,即插槽,它就像對話機器人的內存,它通過鍵值對的形式可用來收集存儲用戶輸入的信息(實體)或者查詢數據庫的數據等。關於Slots的設計與使用,詳情請見本文2.6小節。

4. entities

<code>entities:/<code><code> - date_time/<code><code> - address/<code>

entities,即實體,類似於輸入文本中的關鍵字,需要在NLU樣本中進行標註,然後Bot進行實體識別,並將其填充到Slot槽中,便於後續進行相關的業務操作。

5. actions

<code>actions:/<code><code> - utter_answer_affirm # utter_開頭的均為utter actions/<code><code> - utter_answer_deny/<code><code> - utter_answer_greet/<code><code> - utter_answer_goodbye/<code><code> - utter_answer_thanks/<code><code> - utter_answer_whoareyou/<code><code> - utter_answer_whattodo/<code><code> - utter_ask_date_time/<code><code> - utter_ask_address/<code><code> - utter_ask_number/<code><code> - utter_ask_business/<code><code> - utter_ask_type/<code><code> - action_default_fallback # default actions/<code>

當Rasa NLU識別到用戶輸入Message的意圖後,Rasa Core對話管理模塊就會對其作出回應,而完成這個回應的模塊就是action。Rasa Core支持三種action,即default actions、utter actions以及 custom actions。關於如何實現Actions和處理業務邏輯,我們在一篇文章中詳談,這裡僅作簡單瞭解。

6. forms

<code>forms:/<code><code> - weather_form/<code>

forms,即表單,該部分列舉了在NLU樣本中定義了哪些Form Actions。關於Form Actions的相關知識,請移步至本文的2.7小節。

7. responses

<code>responses:/<code><code> utter_answer_greet:/<code><code> - text: "您好!請問我可以幫到您嗎?"/<code><code> - text: "您好!很高興為您服務。請說出您要查詢的功能?"/<code>
<code> utter_ask_date_time:/<code><code> - text: "請問您要查詢哪一天的天氣?"/<code>

<code> utter_ask_address:/<code><code> - text: "請問您要查下哪裡的天氣?"/<code>
<code> utter_default:/<code><code> - text: "沒聽懂,請換種說法吧~"/<code>

responses部分就是描述UtterActions具體的回覆內容,並且每個UtterAction下可以定義多條信息,當用戶發起一個意圖,比如 “你好!”,就觸發utter_answer_greet操作,Rasa Core會從該action的模板中自動選擇其中的一條信息作為結果反饋給用戶。

2.3 Responses

Responses的作用就是自動響應用戶輸入的信息,因此我們需要管理這些響應(Responses)。Rasa框架提供了三種方式來管理Responses,它們是:

  • 在domain.yaml文件中存儲Responses;

  • 在訓練數據中存儲Responses;

  • 自定義一個NLG服務來生成Responses。

由於第一種我們在本文2.2(7)小節有過介紹,而創建NLG服務是這樣的:

<code>nlg:/<code><code> url: http://localhost:5055/nlg # url of the nlg endpoint/<code><code> # you can also specify additional parameters, if you need them:/<code><code> # headers:/<code><code> # my-custom-header: value/<code><code> # token: "my_authentication_token" # will be passed as a get parameter/<code><code> # basic_auth:/<code><code> # username: user/<code><code> # password: pass/<code><code># example of redis external tracker store config/<code><code>tracker_store:/<code><code> type: redis/<code><code> url: localhost/<code><code> port: 6379/<code><code> db: 0/<code><code> password: password/<code><code> record_exp: 30000/<code><code># example of mongoDB external tracker store config/<code><code>#tracker_store:/<code><code> #type: mongod/<code><code> #url: mongodb://localhost:27017/<code><code> #db: rasa/<code><code> #user: username/<code><code> #password: password/<code>

2.4 Actions

當Rasa NLU識別到用戶輸入Message的意圖後,Rasa Core對話管理模塊就會對其作出回應,而完成這個回應的模塊就是action。Rasa Core支持三種action,即default actions、utter actions以及 custom actions。關於如何實現Actions和處理業務邏輯,我們在一篇文章中詳談,這裡僅作簡單瞭解。

1. default actions

DefaultAction是Rasa Core默認的一組actions,我們無需定義它們,直接可以story和domain中使用。包括以下三種action:

  • action_listen:監聽action,Rasa Core在會話過程中通常會自動調用該action;

  • action_restart:重置狀態,比初始化Slots(插槽)的值等;

  • action_default_fallback:當Rasa Core得到的置信度低於設置的閾值時,默認執行該action;

2. utter actions

UtterAction是以utter_為開頭,僅僅用於向用戶發送一條消息作為反饋的一類actions。定義一個UtterAction很簡單,只需要在domain.yml文件中的actions:字段定義以utter_為開頭的action即可,而具體回覆內容將被定義在templates:部分,這個我們下面有專門講解。定義utter actions示例如下:

<code>actions:/<code><code> - utter_answer_greet/<code><code> - utter_answer_goodbye/<code><code> - utter_answer_thanks/<code><code> - utter_introduce_self/<code><code> - utter_introduce_selfcando/<code><code> - utter_introduce_selffrom/<code>

3. custom actions

CustomAction,即自定義action,允許開發者執行任何操作並反饋給用戶,比如簡單的返回一串字符串,或者控制家電、檢查銀行賬戶餘額等等。它與DefaultAction不同,自定義action需要我們在domain.yml文件中的actions部分先進行定義,然後在指定的webserver中實現它,其中,這個webserver的url地址在endpoint.yml文件中指定,並且這個webserver可以通過任何語言實現,當然這裡首先推薦python來做,畢竟Rasa Core為我們封裝好了一個rasa-core-sdk專門用來處理自定義action。關於action web的搭建和action的具體實現,我們在後面詳細講解,這裡我們看下在在Rasa Core項目中需要做什麼。假如我們在天氣資訊的人機對話系統需提供查詢天氣和空氣質量兩個業務,那麼我們就需要在domain.yml文件中定義查詢天氣和空氣質量的action,即:

<code>actions:/<code><code> ... /<code><code> - action_search_weather/<code>

另外,FormAction也是自定義actions,但是需要在domainl.yaml文件的forms字段聲明。

<code>forms:/<code><code> - weather_form/<code>

2.5 Policies

Policies是Rasa Core中的策略模塊,對應類rasa_core.policies.Policy,它的作用就是使用合適的策略(Policy)來預測一次對話後要執行的行為(Actions)。預測的原理是衡量命中的哪些Policies哪個置信度高,由置信度高的Policy選擇合適的Action執行。假如出現不同的Policy擁有相同的置信度,那麼就由它們的優先級決定,即選擇優先級高的Policy。Rasa對提供的Policies進行了優先級排序,具體如下表:

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

它們的描述與作用如下:

  • Memoization Policy

MemoizationPolicy只記住(memorizes)訓練數據中的對話。如果訓練數據中存在這樣的對話,那麼它將以置信度為1.0預測下一個動作,否則將預測為None,此時置信度為0.0。下面演示瞭如何在策略配置文件config.yml文件中,配置MemoizationPlicy策略,其中,max_history(超參數)決定了模型查看多少個對話歷史以決定下一個執行的action。

<code> policies:/<code><code> - name: "MemoizationPolicy"/<code><code> max_history: 5/<code>

注:max_history值越大訓練得到的模型就越大並且訓練時間會變長,關於該值到底該設置多少,我們可以舉這麼個例子,比如有這麼一個Intent:out_of_scope來描述用戶輸入的消息off-topic(離題),當用戶連續三次觸發out_of_scope意圖,這時候我們就需要主動告知用戶需要向其提供幫助,如果要Rasa Core能夠學習這種模型,max_history應該至少為3。story.md中表現如下:

<code>* out_of_scope/<code><code> - utter_default/<code><code>* out_of_scope/<code><code> - utter_default/<code><code>* out_of_scope/<code><code> - utter_help_message/<code>
  • Keras Policy

KerasPolicy策略是Keras框架中實現的神經網絡來預測選擇執行下一個action,它默認的框架使用LSTM(Long Short-Term Memory,長短期記憶網絡)算法,但是我們也可以重寫KerasPolicy.model_architecture函數來實現自己的框架(architecture)。KerasPolicy的模型很簡單,只是單一的LSTM+Dense+softmax,這就需要我們不斷地完善自己的story來把各種情況下的story進行補充。下面演示瞭如何在策略配置文件config.yml文件中,配置KerasPolicy策略,其中,epochs表示訓練的次數,max_history同上。

<code>policies:/<code><code> - name: KerasPolicy/<code><code> epochs: 100/<code><code> max_history: 5/<code>
  • Embedding Policy

基於機器學習的對話管理能夠學習複雜的行為以完成任務,但是將其功能擴展到新領域並不簡單,尤其是不同策略處理不合作用戶行為的能力,以及在學習新任務(如預訂酒店)時,如何將完成一項任務(如餐廳預訂)重新應用於該任務時的情況。EmbeddingPolicy,即循環嵌入式對話策略(Recurrent Embedding Dialogue Policy,REDP),它通過將actions和對話狀態嵌入到相同的向量空間(vector space)能夠獲得較好的效果,REDP包含一個基於改進的Neural Turing Machine的記憶組件和注意機制,在該任務上顯著優於基線LSTM分類器。

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

EmbeddingPolicy效果上優於KerasPolicy,但是它有個問題是耗時,因為它沒有使用GPU、沒有充分利用CPU資源。KerasPolicy和EmbeddingPolicy比較示意圖如下:

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

配置EmbeddingPolicy參數:

<code>policies:/<code><code> - name: EmbeddingPolicy/<code><code> epochs: 100/<code><code> featurizer:/<code><code> - name: FullDialogueTrackerFeaturizer/<code><code> state_featurizer:/<code><code> - name: LabelTokenizerSingleStateFeaturizer/<code>

注:新版的Rasa將EmbeddingPolicy重命名為TEDPolicy,但是我在config.yml配置文件中將其替換後,提示無法找到TEDPolicy異常,具體原因不明,暫還未涉及源碼分析。

  • Form Policy

FormPolicy是MemoizationPolicy的擴展,用於處理(form)表單的填充事項。當一個FormAction被調用時,FormPolicy將持續預測表單動作,直到表單中的所有槽都被填滿,然後再執行對應的FormAction。如果在Bot系統中使用了FormActions,就需要在config.yml配置文件中進行配置。

<code>policies:/<code><code> - name: FormPolicy/<code>
  • Mapping Policy

MappingPolicy可用於直接將意圖映射到要執行的action,從而實現被映射的action總會被執行,其中,這種映射是通過triggers屬性實現的。舉個栗子(domain.yml文件中):

<code>intents:/<code><code> - greet: {triggers: utter_goodbye}/<code>

其中,greet是意圖;utter_goodbye是action。一個意圖最多隻能映射到一個action,我們的機器人一旦收到映射意圖的消息,它將執行對應的action。然後,繼續監聽下一條message。需要注意的是,對於上述映射,我們還需要要在story.md文件中添加如下樣本,否則,任何機器學習策略都可能被預測的action_greet在dialouge歷史中突然出現而混淆。

  • Fallback Policy

如果意圖識別的置信度低於nlu_threshold,或者沒有任何對話策略預測的action置信度高於core_threshold,FallbackPolicy將執行fallback action。通俗來說,就是我們的對話機器人意圖識別和action預測的置信度沒有滿足對應的閾值,該策略將使機器人執行指定的默認action。configs.yml配置如下:

<code>policies:/<code><code> - name: "FallbackPolicy"/<code><code> # 意圖理解置信度閾值/<code><code> nlu_threshold: 0.3/<code><code> # action預測置信度閾值/<code><code> core_threshold: 0.3/<code><code> # fallback action/<code><code> fallback_action_name: 'action_default_fallback'/<code>

其中,action_default_fallback是Rasa Core中的一個默認操作,它將向用戶發送utter_default模板消息,因此我們需要確保在domain.yml文件中指定此模板。當然,我們也可以在fallback_action_name字段自定義默認回覆的action,比如my_fallback_cation,就可以這麼改:

<code>policies:/<code><code> - name: "FallbackPolicy"/<code><code> nlu_threshold: 0.4/<code><code> core_threshold: 0.3/<code><code> fallback_action_name: "my_fallback_action"/<code>

2.6 Slots

Slots,槽值,相當於機器人的內存(memory),它們以鍵值對的形式存在,用於存儲用戶輸入時消息時比較重要的信息,而這些信息將為Action的執行提供關鍵數據。Slots的定義位於domain.yaml文件中,它們通常與Entities相對應,即Entities有哪些,Slots就有哪些,並且Slots存儲的值就是NLU模型提取的Entities的值。

2.6.1 Slots Type

1. Text類型

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

示例:

<code># domain.yaml/<code><code>slots:/<code><code> cuisine:/<code><code> type: text/<code>

2. Boolean類型

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

示例:

<code>slots:/<code><code> is_authenticated:/<code><code> type: bool/<code>

3. categorical類型

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

示例:

<code>slots:/<code><code> risk_level:/<code><code> type: categorical/<code><code> values:/<code><code> - low/<code><code> - medium/<code><code> - high/<code>

4. Float類型

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

示例:

<code>slots:/<code><code> temperature:/<code><code> type: float/<code><code> min_value: -100.0/<code><code> max_value: 100.0/<code>

5. List類型

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

示例:

<code>slots:/<code><code> shopping_items:/<code><code> type: list/<code>

6. Unfeaturized 類型

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

示例:

<code>slots:/<code><code> internal_user_id:/<code><code> type: unfeaturized/<code>

2.6.2 Slots Set

Slots值填充有多種方式,它們的操作方式如下:

1. Slots Initial

<code># domain.yaml/<code><code>slots:/<code><code> name:/<code><code> type: text/<code><code> initial_value: "human"/<code>

在domain.yaml文件中聲明slots時,可以通過initial_value字段為當前slot提供一個初始值,也就是說,當會話開始時,被設定初始值的slot已經被填充好。當然,這個操作不是必須的。

2. Slots Set from NLU

<code># stories.md/<code><code># story_01/<code><code>* greet{"name": "Ali"}/<code><code> - slot{"name": "Ali"}/<code><code> - utter_greet/<code>

假如在stories.md文件添加一個包含-slot{}的story,這就意味著當NLU模型提取到一個名為name的實體且這個實體有在domain.yaml中定義,那麼NLU模型提取到的實體值會被自動填充到name槽中。實際上,對於Rasa來說,就算你不添加-slot{}字段,這個實體值也會被提取並自動填充到name槽中。當然,如果你希望禁止這種自動填充行為,改為添加-slot{}字段填充,可以在domain.yaml定義slot時,設置auto_fill的值為False,即:

<code># domain.yaml/<code><code>slots:/<code><code> name:/<code><code> type: text/<code><code> auto_fill: False/<code>

3. Slots Set By Clicking Buttons

<code># domain.yaml/<code><code>utter_ask_color:/<code><code>- text: "what color would you like?"/<code><code> buttons:/<code><code> - title: "blue" /<code><code> payload: '/choose{"color": "blue"}' # 格式 '/intent{"entity":"value",...}' /<code><code> - title: "red"/<code><code> payload: '/choose{"color": "red"}' /<code>

在點擊Button時填充Slots的值,是指當我們的Bot(Rasa Core)在回覆用戶時,可以在回覆的消息中附加Button信息,這種Button類似於快捷鍵,用戶獲取到之後,可以直接將其發送給Rasa Core,它會直接進行解析以識別intent和提取entity,並將entity的值填充到slot中。比如你讓用戶通過點擊一個按鈕來選擇一種顏色,那麼可以在domain.yaml中utter_ask_color的回覆中添加buttons:/choose{"color": "blue"}和/choose{"color": "red"}。注:通常每個button由title和payload字段組成。

4. Slots Set by Actions

<code>from rasa_sdk.actions import Action/<code><code>from rasa_sdk.events import SlotSet/<code><code>import requests/<code>

<code>class FetchProfileAction(Action):/<code><code> def name(self):/<code><code> return "fetch_profile"/<code>

<code> def run(self, dispatcher, tracker, domain):/<code><code> url = "http://myprofileurl.com"/<code><code> data = requests.get(url).json/<code><code> return [SlotSet("account_type", data["account_type"])]/<code>

該示例演示瞭如何在Custom Action中通過返回事件來填充Slots的值,即調用SlotSet事件函數並將該事件return。需要注意的是,為了達到這個目的,我們在編寫Story時必須包含該Slot,即使用-slot{}實現,只有這樣Rasa Core就會從提供的信息中進行學習,並決定執行正確的action。Story.md示例如下:

<code># story_01/<code><code>* greet/<code><code> - action_fetch_profile/<code><code> - slot{"account_type" : "premium"}/<code><code> - utter_welcome_premium/<code>

<code># story_02/<code><code>* greet/<code><code> - action_fetch_profile/<code><code> - slot{"account_type" : "basic"}/<code><code> - utter_welcome_basic/<code>

其中,account_type在domain.yaml中定義如下:

<code>slots:/<code><code> account_type:/<code><code> type: categorical/<code><code> values:/<code><code> - premium/<code><code> - basic/<code>

2.6.3 Slots Get

目前主要有兩種獲取Slots值方式:

1. Get Slot in responses

<code>responses:/<code><code> utter_greet:/<code><code> - text: "Hey, {name}. How are you?"/<code>

在domain.yaml的responses部分,可以通過{slotname}的形式獲取槽值。

2. Get Slot in Custom Action

<code>from rasa_sdk.actions import Action/<code><code>from rasa_sdk.events import SlotSet/<code><code>import requests/<code>

<code>class FetchProfileAction(Action):/<code><code> def name(self):/<code><code> return "fetch_profile"/<code>

<code> def run(self, dispatcher, tracker, domain):/<code><code> # 獲取slot account_type的值/<code><code> account_type = tracker.get_slot('account_type')/<code><code> return /<code>

Tracker,可理解為跟蹤器,作用是以會話會話的形式維護助手和用戶之間的對話狀態。通過Tracker,能夠輕鬆獲取整個對話信息,其中就包括Slot的值。

2.7 Form

在Rasa Core中,當我們執行一個action需要同時填充多個slot時,可以使用FormAction來實現,因為FormAction會遍歷監管的所有slot,當發現相關的slot未被填充時,就會向用戶主動發起詢問,直到所有slot被填充完畢,才會執行接下來的業務邏輯。使用步驟如下:

(1)構造story

在story中,不僅需要考慮用戶按照我們的設計準確的提供有效信息,而且還要考慮用戶在中間過程改變要執行的意圖情況或稱輸入無效信息,因為對於FormAction來說,如果無法獲得預期的信息就會報錯,這裡我們分別稱這兩種情況為happy path、unhappy path。示例如下:

<code>## happy path/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"} 激活form/<code><code> - form{"name": } 使form無效/<code><code>## unhappy path/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code>* stop/<code><code> - utter_ask_continue/<code><code>* deny/<code><code> - action_deactivate_form/<code><code> - form{"name": }/<code>

注:* request_restaurant為意圖;- restaurant_form為form action;- form{"name": "restaurant_form"}為激活form;- form{"name": }為註銷form;- action_deactivate_form為默認的action,它的作用是用戶可能在表單操作過程中改變主意,決定不繼續最初的請求,我們使用這個default action來禁止(取消)表單,同時重置要請求的所有slots。

構建stroy最好使用官方提供的Interactive Learning,防止漏掉信息,詳細見本文2.8小節。

(2)添加form字段到Domain

在doamin文件下新增forms:部分,並將所有用到的form名稱添加到該字段下:

<code>intents:/<code><code> - request_weather/<code>
<code>forms:/<code><code> - weather_form/<code>

(3)配置FormPolicy

在工程的配置文件configs.yml中,新增FormPolicy策略:

<code>policies:/<code><code> - name: EmbeddingPolicy/<code><code> epochs: 100/<code><code> max_history: 5/<code><code> - name: FallbackPolicy/<code><code> fallback_action_name: 'action_default_fallback'/<code><code> - name: MemoizationPolicy/<code><code> max_history: 5/<code><code> - name: FormPolicy/<code>

(4)Form Action實現

<code>class WeatherForm(FormAction):/<code>

<code> def name(self) -> Text:/<code><code> """Unique identifier of the form"""/<code>

<code> return "weather_form"/<code>

<code> @staticmethod/<code><code> def required_slots(tracker: Tracker) -> List[Text]:/<code><code> """A list of required slots that the form has to fill"""/<code>

<code> return ["date_time", "address"]/<code>

<code> def submit(/<code><code> self,/<code><code> dispatcher: CollectingDispatcher,/<code><code> tracker: Tracker,/<code><code> domain: Dict[Text, Any],/<code><code> ) -> List[Dict]:/<code><code> """Define what the form has to do/<code><code> after all required slots are filled"""/<code><code> address = tracker.get_slot('address')/<code><code> date_time = tracker.get_slot('date_time')/<code>

<code> return /<code>

當form action第一被調用時,form就會被激活並進入FormPolicy策略模式。每次執行form action,required_slots會被調用,當發現某個還未被填充時,會主動去調用形式為uter_ask_{slotname}的模板(注:定義在domain.yml的templates字段中);當所有slot被填充完畢,submit方法就會被調用,此時本次form操作完畢被取消激活。

2.8 Interactive Learning

雖然我們可以容易的人工構建story樣本數據,但是往往會出現一些考慮不全,甚至出錯等問題,基於此,Rasa Core框架為我們提供了一種交互式學習(Interactive Learning)來獲得所需的樣本數據。在互動學習模式中,當你與機器人交談時,你會向它提供反饋,這是一種強大的方法來探索您的機器人可以做什麼,也是修復它所犯錯誤的最簡單的方法。基於機器學習的對話的一個優點是,當你的機器人還不知道如何做某事時,你可以直接教它。

(1)開啟Action Server

<code>python -m rasa run actions --port 5055 --actions actions --debug/<code>

(2)開啟Interactive Learning

<code>python -m rasa interactive -m models/20200313-101055.tar.gz --endpoints configs/endpoints.yml --config configs/config.yml /<code><code># 或者(沒有已訓練模型情況)/<code><code># rasa會先訓練好模型,再開啟交互式學習會話/<code><code>python -m rasa interactive --data /data --domain configs/domain.yml --endpoints configs/endpoints.yml --config configs/config.yml /<code>

分別執行(1)、(2)命令後,我們可以預設一個交互場景根據終端的提示操作即可。如果一個交互場景所有流程執行完畢,按Ctrl+C結束並選擇Start Fresh進入下一個場景即可。當然Rasa還提供了可視化界面,以幫助你瞭解每個Story樣本構建的過程,網址:http://localhost:5005/visualization.html。

執行流程大致如下:

<code>Bot loaded. Visualisation at http://localhost:5006/visualization.html ./<code><code>Type a message and press enter (press 'Ctr-c' to exit)./<code><code>? Your input -> 查詢身份證439912199008071234/<code><code>? Is the intent 'request_idcard' correct for '查詢身份證[439912199008071234](id_number)' and are all entities labeled correctly? Yes/<code><code>------/<code><code>Chat History/<code>

<code> # Bot You /<code><code>───────────────────────────────────────────────────────────────────/<code><code> 1 action_listen/<code><code>───────────────────────────────────────────────────────────────────/<code><code> 2 查詢身份證[439912199008071234](id_number)/<code><code> intent: request_idcard 1.00/<code>



<code>Current slots:/<code><code> address: None, business: None, date-time: None, id_number: None, requested_slot: None/<code>

<code>------/<code><code>? The bot wants to run 'number_form', correct? Yes/<code><code>Chat History/<code>

<code> # Bot You /<code><code>──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────/<code><code> 1 action_listen/<code><code>──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────/<code><code> 2 查詢身份證[439912199008071234](id_number)/<code><code> intent: request_idcard 1.00/<code><code>──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────/<code><code> 3 number_form 1.00/<code><code> 您要查詢的身份證號碼439912199008071234所屬人為張三,湖南長沙人,現在就職於地球村物業。/<code><code> form{"name": "number_form"}/<code><code> slot{"id_number": "439912199008071234"}/<code><code> form{"name": }/<code><code> slot{"requested_slot": }/<code>



<code>Current slots:/<code><code> address: None, business: None, date-time: None, id_number: 439912199008071234, requested_slot: None/<code>

<code>------/<code><code>? The bot wants to run 'action_listen', correct? Yes/<code>

生成的一個Story示例如下:

<code>## interactive_story_10/<code><code># unhappy path:chitchat stop but continue path/<code><code>* greet/<code><code> - utter_answer_greet/<code><code>* request_number{"type": "身份證號碼"}/<code><code> - number_form/<code><code> - form{"name": "number_form"}/<code><code> - slot{"type": "身份證號碼"}/<code><code> - slot{"number": }/<code><code> - slot{"business": }/<code><code> - slot{"requested_slot": "number"}/<code><code>* chitchat/<code><code> - utter_chitchat/<code><code> - number_form/<code><code> - slot{"requested_slot": "number"}/<code><code>* stop/<code><code> - utter_ask_continue/<code><code>* affirm/<code><code> - number_form/<code><code> - slot{"requested_slot": "number"}/<code><code>* form: request_number{"number": "440123199087233467"}/<code><code> - form: number_form/<code><code> - slot{"number": "440123199087233467"}/<code><code> - slot{"type": "身份證號碼"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* thanks/<code><code> - utter_noworries/<code>
400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

改進ChitChatAssistant項目

3.1 config.yml

<code># zh_jieba_mitie_embeddings_config.yml/<code>

<code>language: "zh"/<code>

<code>pipeline:/<code><code>- name: "MitieNLP"/<code><code> model: "data/total_word_feature_extractor_zh.dat"/<code><code>- name: "JiebaTokenizer"/<code><code> dictionary_path: "data/dict"/<code><code>- name: "MitieEntityExtractor"/<code><code>- name: "EntitySynonymMapper"/<code><code>- name: "RegexFeaturizer"/<code><code>- name: "MitieFeaturizer"/<code><code>- name: "EmbeddingIntentClassifier"/<code>

<code>policies:/<code><code> - name: FallbackPolicy/<code><code> nlu_threshold: 0.5/<code><code> ambiguity_threshold: 0.1/<code><code> core_threshold: 0.5/<code><code> fallback_action_name: 'action_default_fallback'/<code><code> - name: MemoizationPolicy/<code><code> max_history: 5/<code><code> - name: FormPolicy/<code><code> - name: MappingPolicy/<code><code> - name: EmbeddingPolicy/<code><code> epochs: 500/<code>

考慮到目前項目的樣本較少,這裡使用MITIE+EmbeddingPolicy組合,雖然訓練時慢了點,但是能夠保證實體提取的準確性,同時又能夠提高意圖識別的命中率。

3.2 weather_stories.md

<code>## happy path/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - form{"name": }/<code>
<code>## happy path/<code><code>* greet/<code><code> - utter_answer_greet/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - form{"name": }/<code><code>* thanks/<code><code> - utter_noworries/<code>

<code>## unhappy path/<code><code>* greet/<code><code> - utter_answer_greet/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code>* chitchat/<code><code> - utter_chitchat/<code><code> - weather_form/<code><code> - form{"name": }/<code><code>* thanks/<code><code> - utter_noworries/<code>

<code>## very unhappy path/<code><code>* greet/<code><code> - utter_answer_greet/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code>* chitchat/<code><code> - utter_chitchat/<code><code> - weather_form/<code><code>* chitchat/<code><code> - utter_chitchat/<code><code> - weather_form/<code><code>* chitchat/<code><code> - utter_chitchat/<code><code> - weather_form/<code><code> - form{"name": }/<code><code>* thanks/<code><code> - utter_noworries/<code>

<code>## stop but continue path/<code><code>* greet/<code><code> - utter_answer_greet/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code>* stop/<code><code> - utter_ask_continue/<code><code>* affirm/<code><code> - weather_form/<code><code> - form{"name": }/<code><code>* thanks/<code><code> - utter_noworries/<code>

<code>## stop and really stop path/<code><code>* greet/<code><code> - utter_answer_greet/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code>* stop/<code><code> - utter_ask_continue/<code><code>* deny/<code><code> - action_deactivate_form/<code><code> - form{"name": }/<code>

<code>## chitchat stop but continue path/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code>* chitchat/<code><code> - utter_chitchat/<code><code> - weather_form/<code><code>* stop/<code><code> - utter_ask_continue/<code><code>* affirm/<code><code> - weather_form/<code><code> - form{"name": }/<code><code>* thanks/<code><code> - utter_noworries/<code>

<code>## stop but continue and chitchat path/<code><code>* greet/<code><code> - utter_answer_greet/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code>* stop/<code><code> - utter_ask_continue/<code><code>* affirm/<code><code> - weather_form/<code><code>* chitchat/<code><code> - utter_chitchat/<code><code> - weather_form/<code><code> - form{"name": }/<code><code>* thanks/<code><code> - utter_noworries/<code>

<code>## chitchat stop but continue and chitchat path/<code><code>* greet/<code><code> - utter_answer_greet/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code>* chitchat/<code><code> - utter_chitchat/<code><code> - weather_form/<code><code>* stop/<code><code> - utter_ask_continue/<code><code>* affirm/<code><code> - weather_form/<code><code>* chitchat/<code><code> - utter_chitchat/<code><code> - weather_form/<code><code> - form{"name": }/<code><code>* thanks/<code><code> - utter_noworries/<code>

<code>## chitchat, stop and really stop path/<code><code>* greet/<code><code> - utter_answer_greet/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code>* chitchat/<code><code> - utter_chitchat/<code><code> - weather_form/<code><code>* stop/<code><code> - utter_ask_continue/<code><code>* deny/<code><code> - action_deactivate_form/<code><code> - form{"name": }/<code>
<code>## interactive_story_1/<code><code>## 天氣 + 時間 + 地點 + 地點/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"requested_slot": "date_time"}/<code><code>* form: inform{"date_time": "明天"}/<code><code> - form: weather_form/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"requested_slot": "address"}/<code><code>* form: inform{"address": "廣州"}/<code><code> - form: weather_form/<code><code> - slot{"address": "廣州"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"date_time": "後天"} OR request_weather{"date_time": "後天"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"address": "廣州"}/<code><code> - slot{"date_time": "後天"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* thanks/<code><code> - utter_answer_thanks/<code>

<code>## interactive_story_1/<code><code>## 天氣 + 時間 + 地點 + 時間/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"requested_slot": "date_time"}/<code><code>* form: inform{"date_time": "明天"}/<code><code> - form: weather_form/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"requested_slot": "address"}/<code><code>* form: inform{"address": "廣州"}/<code><code> - form: weather_form/<code><code> - slot{"address": "廣州"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"address": "上海"} OR request_weather{"address": "深圳"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"address": "廣州"}/<code><code> - slot{"address": "上海"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* affirm/<code><code> - utter_answer_affirm/<code>

<code>## interactive_story_2/<code><code>## 天氣/時間/地點 + 地點/<code><code>* request_weather{"date_time": "明天", "address": "上海"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"address": "上海"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"address": "廣州"} OR request_weather{"address": "廣州"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"address": "上海"}/<code><code> - slot{"address": "廣州"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* thanks/<code><code> - utter_answer_thanks/<code>

<code>## interactive_story_3/<code><code>## 天氣/時間/地點 + 時間/<code><code>* request_weather{"address": "深圳", "date_time": "後天"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "後天"}/<code><code> - slot{"address": "深圳"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"date_time": "大後天"} OR request_weather{"date_time": "大後天"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "後天"}/<code><code> - slot{"address": "深圳"}/<code><code> - slot{"date_time": "大後天"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* thanks/<code><code> - utter_answer_thanks/<code>

<code>## interactive_story_2/<code><code>## 天氣/時間/地點 + 地點 + 時間/<code><code>* request_weather{"date_time": "明天", "address": "上海"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"address": "上海"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"address": "北京"} OR request_weather{"address": "北京"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"address": "上海"}/<code><code> - slot{"address": "北京"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"date_time": "後天"} OR request_weather{"date_time": "後天"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"address": "北京"}/<code><code> - slot{"date_time": "後天"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* affirm/<code><code> - utter_answer_affirm/<code>


<code>## interactive_story_3/<code><code>## 天氣/時間/地點 + 地點 + 地點/<code><code>* request_weather{"date_time": "後天", "address": "北京"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "後天"}/<code><code> - slot{"address": "北京"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"address": "深圳"} OR request_weather{"address": "深圳"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "後天"}/<code><code> - slot{"address": "北京"}/<code><code> - slot{"address": "深圳"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"address": "南京"} OR request_weather{"address": "南京"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "後天"}/<code><code> - slot{"address": "深圳"}/<code><code> - slot{"address": "南京"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* thanks/<code><code> - utter_answer_thanks/<code>

<code>## interactive_story_4/<code><code>## 天氣/時間/地點 + 時間 + 地點/<code><code>* request_weather{"date_time": "明天", "address": "長沙"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"address": "長沙"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"date_time": "後天"} OR request_weather{"date_time": "後天"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"address": "長沙"}/<code><code> - slot{"date_time": "後天"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"date_time": "大後天"} OR request_weather{"date_time": "大後天"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "後天"}/<code><code> - slot{"address": "長沙"}/<code><code> - slot{"date_time": "大後天"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* affirm/<code><code> - utter_answer_affirm/<code>

<code>## interactive_story_5/<code><code>## 天氣/時間/地點 + 時間 + 時間/<code><code>* request_weather{"date_time": "後天", "address": "深圳"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "後天"}/<code><code> - slot{"address": "深圳"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"date_time": "明天"} OR request_weather{"date_time": "明天"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "後天"}/<code><code> - slot{"address": "深圳"}/<code><code> - slot{"date_time": "明天"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"address": "廣州"} OR request_weather{"address": "廣州"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"address": "深圳"}/<code><code> - slot{"address": "廣州"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* thanks/<code><code> - utter_answer_thanks/<code>

<code>## interactive_story_4/<code><code>## 天氣/時間 + 地點 + 時間/<code><code>* request_weather{"date_time": "明天"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"requested_slot": "address"}/<code><code>* form: inform{"address": "廣州"}/<code><code> - form: weather_form/<code><code> - slot{"address": "廣州"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"date_time": "後天"} OR request_weather{"date_time": "後天"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "明天"}/<code><code> - slot{"address": "廣州"}/<code><code> - slot{"date_time": "後天"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* thanks/<code><code> - utter_answer_thanks/<code>

<code>## interactive_story_5/<code><code>## 天氣/地點 + 時間 + 時間/<code><code>* request_weather{"address": "廣州"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"address": "廣州"}/<code><code> - slot{"requested_slot": "date_time"}/<code><code>* form: inform{"date_time": "後天"}/<code><code> - form: weather_form/<code><code> - slot{"date_time": "後天"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* inform{"date_time": "明天"} OR request_weather{"date_time": "明天"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "後天"}/<code><code> - slot{"address": "廣州"}/<code><code> - slot{"date_time": "明天"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* thanks/<code><code> - utter_answer_thanks/<code>
<code>## interactive_story_1/<code><code>## 天氣/時間/地點 + chit + chit(restart)+詢問天氣/<code><code>* request_weather{"date_time": "今天", "address": "廣州"}/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"date_time": "今天"}/<code><code> - slot{"address": "廣州"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* chitchat/<code><code> - utter_chitchat/<code><code>* chitchat/<code><code> - utter_chitchat/<code><code> - action_restart/<code><code>* request_weather/<code><code> - weather_form/<code><code> - form{"name": "weather_form"}/<code><code> - slot{"requested_slot": "date_time"}/<code><code>* form: inform{"date_time": "今天"}/<code><code> - form: weather_form/<code><code> - slot{"date_time": "今天"}/<code><code> - slot{"requested_slot": "address"}/<code><code>* form: inform{"address": "廣州"}/<code><code> - form: weather_form/<code><code> - slot{"address": "廣州"}/<code><code> - form{"name": }/<code><code> - slot{"requested_slot": }/<code><code>* thanks/<code><code> - utter_answer_thanks/<code>

在構建Story樣本時,主要是使用Interactive Learning工具實現,以確保枚舉儘可能多的unhappy story,同時又能夠防止在構建樣本時出現信息遺漏的情況。此外,本版本中除了查詢天氣這個案例,還新增了其他案例,並列舉了如何使用同義詞、自定義字典以及正則表達式的使用方法,詳細見最新版項目。

GitHub地址:ChitChatAssistant https://github.com/jiangdongguo/ChitChatAssistant,歡迎star和issues,我們共同討論、學習!

原文鏈接:

https://blog.csdn.net/AndrExpert/article/details/105434136

400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

☞沒有監控和日誌咋整?老程序員來支招

☞朱廣權李佳琦直播掉線,1.2億人在線等

☞RPC的超時設置,一不小心就是線上事故!

☞拿下Gartner容器產品第一,阿里雲打贏雲原生關鍵一戰!

☞深聊Solidity的測試場景、方法和實踐,太詳細了,必須收藏!

☞萬字乾貨:一步步教你如何在容器上構建持續部署!

☞據說,這是當代極客們的【技術風向標】...

今日福利:評論區留言入選,可獲得價值299元的「2020 AI開發者萬人大會」在線直播門票一張。 快來動動手指,寫下你想說的話吧。


分享到:


相關文章: