Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 亚洲一区二区影院,在线性爱视频,中文字幕不卡一区二区三区

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          HTML表單基本元素的應(yīng)用之提交按鈕和重置按鈕

          交按鈕

          提交按鈕用來(lái)將輸入的信息提交到服務(wù)器。代碼格式如下:

          <input type="submit" name="..." value="...">

          其中type="submit"定義提交按鈕;name屬性定義提交按鈕的名稱;value屬性定義按鈕的顯示文字。通過(guò)提交按鈕,可以將表單的信息提交給表單里action所指向的文件。

          (1)編寫代碼如下圖所示,在<body>標(biāo)簽中加入以下代碼。

          (2)在瀏覽器中打開文件,預(yù)覽效果圖如下所示,輸入內(nèi)容按【提交】按鈕,即可將表單中的數(shù)據(jù)發(fā)送到指定的文件。

          重置按鈕

          復(fù)位按鈕用來(lái)重置表單中輸入的信息。代碼格式如下:

          <input type="reset" name="..." value="..." >

          其中type="reset"定義復(fù)位按鈕;name屬性定義復(fù)位按鈕的名稱;value屬性定義按鈕的顯示文字。

          (1)編寫代碼如下圖所示,在<body>標(biāo)簽中加入以下代碼。

          (2)在瀏覽器中打開文件,預(yù)覽效果圖如下所示,輸入內(nèi)容后單機(jī)【重置】按鈕,即可將表單中的數(shù)據(jù)清空。

          者 | 無(wú)名之輩FTER

          責(zé)編 | 夕顏

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

          本文翻譯自Rasa官方文檔,并融合了自己的理解和項(xiàng)目實(shí)戰(zhàn),同時(shí)對(duì)文檔中涉及到的技術(shù)點(diǎn)進(jìn)行了一定程度的擴(kuò)展,目的是為了更好的理解Rasa工作機(jī)制。與本文配套的項(xiàng)目GitHub地址:ChitChatAssistant https://github.com/jiangdongguo/ChitChatAssistant,歡迎star和issues,我們共同討論、學(xué)習(xí)!

          對(duì)話管理

          1.1 多輪對(duì)話

          多輪對(duì)話是相對(duì)于單輪對(duì)話而言的,單輪對(duì)話側(cè)重于一問(wèn)一答,即直接根據(jù)用戶的問(wèn)題給出精準(zhǔn)的答案。問(wèn)答更接近一個(gè)信息檢索的過(guò)程,雖然也可能涉及簡(jiǎn)單的上下文處理,但通常是通過(guò)指代消解和 query 補(bǔ)全來(lái)完成的,而多輪對(duì)話側(cè)重于需要維護(hù)一個(gè)用戶目標(biāo)狀態(tài)的表示和一個(gè)決策過(guò)程來(lái)完成任務(wù),具體來(lái)說(shuō)就是用戶帶著明確的目的而來(lái),希望得到滿足特定限制條件的信息或服務(wù),例如:訂餐,訂票,尋找音樂(lè)、電影或某種商品等。因?yàn)橛脩舻男枨罂梢员容^復(fù)雜,可能需要分多輪進(jìn)行陳述,用戶也可能在對(duì)話過(guò)程中不斷修改或完善自己的需求。此外,當(dāng)用戶的陳述的需求不夠具體或明確的時(shí)候,機(jī)器也可以通過(guò)詢問(wèn)、澄清或確認(rèn)來(lái)幫助用戶找到滿意的結(jié)果。

          因此,任務(wù)驅(qū)動(dòng)的多輪對(duì)話不是一個(gè)簡(jiǎn)單的自然語(yǔ)言理解加信息檢索的過(guò)程,而是一個(gè)決策過(guò)程,需要機(jī)器在對(duì)話過(guò)程中不斷根據(jù)當(dāng)前的狀態(tài)決策下一步應(yīng)該采取的最優(yōu)動(dòng)作(如:提供結(jié)果,詢問(wèn)特定限制條件,澄清或確認(rèn)需求,等等)從而最有效的輔助用戶完成信息或服務(wù)獲取的任務(wù)。

          注:任務(wù)驅(qū)動(dòng)的多輪對(duì)話系統(tǒng)通常為封閉域(domain)(閑聊系統(tǒng)為開放域),而特定限制條件對(duì)應(yīng)于槽(Slot),也就是說(shuō),用戶在滿足特定限制條件時(shí)就是一次槽值填充的過(guò)程,如果用戶能夠在一次會(huì)話中,滿足全部的限制條件,那么就不必進(jìn)行多輪對(duì)話,即可直接使用戶得到滿意的信息或服務(wù)。

          1.2 對(duì)話管理

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

          從本質(zhì)上來(lái)說(shuō),**任務(wù)驅(qū)動(dòng)的對(duì)話管理實(shí)際就是一個(gè)決策過(guò)程,系統(tǒng)在對(duì)話過(guò)程中不斷根據(jù)當(dāng)前狀態(tài)決定下一步應(yīng)該采取的最優(yōu)動(dòng)作(如:提供結(jié)果,詢問(wèn)特定限制條件,澄清或確認(rèn)需求等),從而最有效的輔助用戶完成信息或服務(wù)獲取的任務(wù)。**對(duì)話管理的任務(wù)大致有:

          • 對(duì)話狀態(tài)維護(hù)(dialog state tracking, DST)

          對(duì)話狀態(tài)是指記錄了哪些槽位已經(jīng)被填充、下一步該做什么、填充什么槽位,還是進(jìn)行何種操作。用數(shù)學(xué)形式表達(dá)為,t+1 時(shí)刻的對(duì)話狀態(tài)S(t+1),依賴于之前時(shí)刻 t 的狀態(tài)St,和之前時(shí)刻 t 的系統(tǒng)行為At,以及當(dāng)前時(shí)刻 t+1 對(duì)應(yīng)的用戶行為O(t+1)??梢詫懗蒘(t+1)←St+At+O(t+1)。

          • 生成系統(tǒng)決策(dialog policy)

          根據(jù) DST 中的對(duì)話狀態(tài)(DS),產(chǎn)生系統(tǒng)行為(dialog act),決定下一步做什么 dialog act 可以表示觀測(cè)到的用戶輸入(用戶輸入 -> DA,就是 NLU 的過(guò)程),以及系統(tǒng)的反饋行為(DA -> 系統(tǒng)反饋,就是 NLG 的過(guò)程)。

          • 作為接口與后端/任務(wù)模型進(jìn)行交互

          Rasa Core

          Rasa Core是Rasa框架提供的對(duì)話管理模塊,它類似于聊天機(jī)器人的大腦,主要的任務(wù)是維護(hù)更新對(duì)話狀態(tài)和動(dòng)作選擇,然后對(duì)用戶的輸入作出響應(yīng)。所謂對(duì)話狀態(tài)是一種機(jī)器能夠處理的對(duì)聊天數(shù)據(jù)的表征,對(duì)話狀態(tài)中包含所有可能會(huì)影響下一步?jīng)Q策的信息,如自然語(yǔ)言理解模塊的輸出、用戶的特征等;所謂動(dòng)作選擇,是指基于當(dāng)前的對(duì)話狀態(tài),選擇接下來(lái)合適的動(dòng)作,例如向用戶追問(wèn)需補(bǔ)充的信息、執(zhí)行用戶要求的動(dòng)作等。舉一個(gè)具體的例子,用戶說(shuō)“幫我媽媽預(yù)定一束花”,此時(shí)對(duì)話狀態(tài)包括自然語(yǔ)言理解模塊的輸出、用戶的位置、歷史行為等特征。在這個(gè)狀態(tài)下,系統(tǒng)接下來(lái)的動(dòng)作可能是:

          • 向用戶詢問(wèn)可接受的價(jià)格,如“請(qǐng)問(wèn)預(yù)期價(jià)位是多少?”;

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

          • 直接為用戶預(yù)訂

          2.1 Stories

          Rasa的故事是一種訓(xùn)練數(shù)據(jù)的形式,用來(lái)訓(xùn)練Rasa的對(duì)話管理模型。故事是用戶和人工智能助手之間的對(duì)話的表示,轉(zhuǎn)換為特定的格式,其中用戶輸入表示為相應(yīng)的意圖(和必要的實(shí)體),而助手的響應(yīng)表示為相應(yīng)的操作名稱。Rasa核心對(duì)話系統(tǒng)的一個(gè)訓(xùn)練示例稱為一個(gè)故事。這是一個(gè)故事數(shù)據(jù)格式的指南。兩段對(duì)話樣本示例:

          <!-- ##表示story的描述,沒(méi)有實(shí)際作用 -->## greet + location/price + cuisine + num people* greet - utter_greet* inform{"location": "rome", "price": "cheap"} - action_on_it - action_ask_cuisine* inform{"cuisine": "spanish"} - action_ask_numpeople * inform{"people": "six"} - action_ack_dosearch
          <!-- Form Action-->## happy path * request_weather - weather_form - form{"name": "weather_form"} - form{"name": }

          Story格式大致包含三個(gè)部分:

          • 1. 用戶輸入(User Messages)

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

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

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

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

          • 2. 動(dòng)作(Actions)

          使用-開頭的語(yǔ)句表示要執(zhí)行動(dòng)作(Action),可分為utterance actions和custom actions,其中,前者在domain.yaml中定義以u(píng)tter_為前綴,比如名為greet的意圖,它的回復(fù)應(yīng)為utter_greet;后者為自定義動(dòng)作,具體邏輯由我們自己實(shí)現(xiàn),雖然在定義action名稱的時(shí)候沒(méi)有限制,但是還是建議以action_為前綴,比如名為inform的意圖fetch_profile的意圖,它的response可為action_fetch_profile。

          • 3. 事件(Events)

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

          (1)Slot Events

          Slot Events的作用當(dāng)我們?cè)谧远xAction中設(shè)置了某個(gè)槽值,那么我們就需要在Story中Action執(zhí)行之后顯著的將這個(gè)SlotSet事件標(biāo)注出來(lái),格式為- slot{"slot_name": "value"}。比如,我們?cè)赼ction_fetch_profile中設(shè)置了Slot名為account_type的值,代碼如下:

          from rasa_sdk.actions import Actionfrom rasa_sdk.events import SlotSetimport requests

          class FetchProfileAction(Action): def name(self): return "fetch_profile"

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

          那么,就需要在Story中執(zhí)行action_fetch_profile之后,添加- slot{"account_type" : "premium"}。雖然,這么做看起來(lái)有點(diǎn)多余,但是Rasa規(guī)定這么做必須的,目的是提高訓(xùn)練時(shí)準(zhǔn)確度。

          ## fetch_profile* fetch_profile - action_fetch_profile - slot{"account_type" : "premium"} - utter_welcome_premium

          當(dāng)然,如果您的自定義Action中將槽值重置為None,則對(duì)應(yīng)的事件為-slot{"slot_name": }。

          (2)Form Events

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

          • Form Action事件

          Form Action即表單動(dòng)作事件,是自定義Action的一種,用于一個(gè)表單操作。示例如下:

          - restaurant_form
          • Form activation事件

          form activation即激活表單事件,當(dāng)form action事件執(zhí)行后,會(huì)立馬執(zhí)行該事件。示例如下:

          - form{"name": "restaurant_form"}
          • Form deactivation事件

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

          - form{"name": }

          總之,我們?cè)跇?gòu)建Story時(shí),可以說(shuō)是多種多樣的,因?yàn)樵O(shè)計(jì)的故事情節(jié)是多種多樣的,這就意味著上述三種內(nèi)容的組合也是非常靈活的。另外,在設(shè)計(jì)Story時(shí)Rasa還提供了Checkpoints 和OR statements兩種功能,來(lái)提升構(gòu)建Story的靈活度,但是需要注意的是,東西雖好,但是不要太貪了,過(guò)多的使用不僅增加了復(fù)雜度,同時(shí)也會(huì)拖慢訓(xùn)練的速度。其中,Checkpoints用于模塊化和簡(jiǎn)化訓(xùn)練數(shù)據(jù),示例如下:

          ## first story* greet - action_ask_user_question> check_asked_question

          ## user affirms question> check_asked_question* affirm - action_handle_affirmation> check_handled_affirmation

          ## user denies question> check_asked_question* deny - action_handle_denial> check_handled_denial

          ## user leaves> check_handled_denial> check_handled_affirmation* goodbye - utter_goodbye

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

          ## story* affirm OR thankyou - action_handle_affirmation

          2.2 Domain

          Domain,譯為**“領(lǐng)域”**,它描述了對(duì)話機(jī)器人應(yīng)知道的所有信息,類似于“人的大腦”,存儲(chǔ)了意圖intents、實(shí)體entities、插槽slots以及動(dòng)作actions等信息,其中,intents、entities在NLU訓(xùn)練樣本中定義,slots對(duì)應(yīng)于entities類型,只是表現(xiàn)形式不同。domain.yml文件組成結(jié)構(gòu)如下:

          具體介紹如下:

          1. intents

          intents: - affirm - deny - greet - request_weather - request_number - inform - inform_business - stop - chitchat

          intents,即意圖,是指我們輸入一段文本,希望Bot能夠明白這段文本是什么意思。在Rasa框架中,意圖的定義是在NLU樣本中實(shí)現(xiàn)的,并且在每個(gè)意圖下面我們需要枚舉盡可多的樣本用于訓(xùn)練,以達(dá)到Bot能夠準(zhǔn)確識(shí)別出我們輸入的一句話到底想要干什么。

          2. session_config

          session_config: carry_over_slots_to_new_session: true session_expiration_time: 60

          session_config,即會(huì)話配置,這部分的作用為配置一次會(huì)話(conversation session)是否有超時(shí)限制。上例演示的是,每次會(huì)話的超時(shí)時(shí)間為60s,如果用戶開始一段會(huì)話后,在60s內(nèi)沒(méi)有輸入任何信息,那么這次會(huì)話將被結(jié)束,然后Bot又會(huì)開啟一次新的會(huì)話,并將上一次會(huì)話的Slot值拷貝過(guò)來(lái)。當(dāng)然,我們希望舍棄上一次會(huì)話Slot的值,可以將carry_over_slots_to_new_session設(shè)置為false。另外,當(dāng)session_expiration_time被設(shè)置為0時(shí),Bot永遠(yuǎn)不會(huì)結(jié)束當(dāng)前會(huì)話并一直等待用戶輸入(注:執(zhí)行action_session_start仍然可以開始一次新的會(huì)話,在設(shè)置為0的情況下)。

          3. slots

          slots: date_time: type: unfeaturized auto_fill: false address: type: unfeaturized auto_fill: false

          Slots,即插槽,它就像對(duì)話機(jī)器人的內(nèi)存,它通過(guò)鍵值對(duì)的形式可用來(lái)收集存儲(chǔ)用戶輸入的信息(實(shí)體)或者查詢數(shù)據(jù)庫(kù)的數(shù)據(jù)等。關(guān)于Slots的設(shè)計(jì)與使用,詳情請(qǐng)見(jiàn)本文2.6小節(jié)。

          4. entities

          entities: - date_time - address

          entities,即實(shí)體,類似于輸入文本中的關(guān)鍵字,需要在NLU樣本中進(jìn)行標(biāo)注,然后Bot進(jìn)行實(shí)體識(shí)別,并將其填充到Slot槽中,便于后續(xù)進(jìn)行相關(guān)的業(yè)務(wù)操作。

          5. actions

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

          當(dāng)Rasa NLU識(shí)別到用戶輸入Message的意圖后,Rasa Core對(duì)話管理模塊就會(huì)對(duì)其作出回應(yīng),而完成這個(gè)回應(yīng)的模塊就是action。Rasa Core支持三種action,即default actions、utter actions以及 custom actions。關(guān)于如何實(shí)現(xiàn)Actions和處理業(yè)務(wù)邏輯,我們?cè)谝黄恼轮性斦劊@里僅作簡(jiǎn)單了解。

          6. forms

          forms: - weather_form

          forms,即表單,該部分列舉了在NLU樣本中定義了哪些Form Actions。關(guān)于Form Actions的相關(guān)知識(shí),請(qǐng)移步至本文的2.7小節(jié)。

          7. responses

          responses: utter_answer_greet: - text: "您好!請(qǐng)問(wèn)我可以幫到您嗎?" - text: "您好!很高興為您服務(wù)。請(qǐng)說(shuō)出您要查詢的功能?"
          utter_ask_date_time: - text: "請(qǐng)問(wèn)您要查詢哪一天的天氣?"

          utter_ask_address: - text: "請(qǐng)問(wèn)您要查下哪里的天氣?"
          utter_default: - text: "沒(méi)聽懂,請(qǐng)換種說(shuō)法吧~"

          responses部分就是描述UtterActions具體的回復(fù)內(nèi)容,并且每個(gè)UtterAction下可以定義多條信息,當(dāng)用戶發(fā)起一個(gè)意圖,比如 “你好!”,就觸發(fā)utter_answer_greet操作,Rasa Core會(huì)從該action的模板中自動(dòng)選擇其中的一條信息作為結(jié)果反饋給用戶。

          2.3 Responses

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

          • 在domain.yaml文件中存儲(chǔ)Responses;

          • 在訓(xùn)練數(shù)據(jù)中存儲(chǔ)Responses;

          • 自定義一個(gè)NLG服務(wù)來(lái)生成Responses。

          由于第一種我們?cè)诒疚?.2(7)小節(jié)有過(guò)介紹,而創(chuàng)建NLG服務(wù)是這樣的:

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

          2.4 Actions

          當(dāng)Rasa NLU識(shí)別到用戶輸入Message的意圖后,Rasa Core對(duì)話管理模塊就會(huì)對(duì)其作出回應(yīng),而完成這個(gè)回應(yīng)的模塊就是action。Rasa Core支持三種action,即default actions、utter actions以及 custom actions。關(guān)于如何實(shí)現(xiàn)Actions和處理業(yè)務(wù)邏輯,我們?cè)谝黄恼轮性斦?,這里僅作簡(jiǎn)單了解。

          1. default actions

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

          • action_listen:監(jiān)聽action,Rasa Core在會(huì)話過(guò)程中通常會(huì)自動(dòng)調(diào)用該action;

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

          • action_default_fallback:當(dāng)Rasa Core得到的置信度低于設(shè)置的閾值時(shí),默認(rèn)執(zhí)行該action;

          2. utter actions

          UtterAction是以u(píng)tter_為開頭,僅僅用于向用戶發(fā)送一條消息作為反饋的一類actions。定義一個(gè)UtterAction很簡(jiǎn)單,只需要在domain.yml文件中的actions:字段定義以u(píng)tter_為開頭的action即可,而具體回復(fù)內(nèi)容將被定義在templates:部分,這個(gè)我們下面有專門講解。定義utter actions示例如下:

          actions: - utter_answer_greet - utter_answer_goodbye - utter_answer_thanks - utter_introduce_self - utter_introduce_selfcando - utter_introduce_selffrom

          3. custom actions

          CustomAction,即自定義action,允許開發(fā)者執(zhí)行任何操作并反饋給用戶,比如簡(jiǎn)單的返回一串字符串,或者控制家電、檢查銀行賬戶余額等等。它與DefaultAction不同,自定義action需要我們?cè)赿omain.yml文件中的actions部分先進(jìn)行定義,然后在指定的webserver中實(shí)現(xiàn)它,其中,這個(gè)webserver的url地址在endpoint.yml文件中指定,并且這個(gè)webserver可以通過(guò)任何語(yǔ)言實(shí)現(xiàn),當(dāng)然這里首先推薦python來(lái)做,畢竟Rasa Core為我們封裝好了一個(gè)rasa-core-sdk專門用來(lái)處理自定義action。關(guān)于action web的搭建和action的具體實(shí)現(xiàn),我們?cè)诤竺嬖敿?xì)講解,這里我們看下在在Rasa Core項(xiàng)目中需要做什么。假如我們?cè)谔鞖赓Y訊的人機(jī)對(duì)話系統(tǒng)需提供查詢天氣和空氣質(zhì)量?jī)蓚€(gè)業(yè)務(wù),那么我們就需要在domain.yml文件中定義查詢天氣和空氣質(zhì)量的action,即:

          actions: ...  - action_search_weather

          另外,F(xiàn)ormAction也是自定義actions,但是需要在domainl.yaml文件的forms字段聲明。

          forms: - weather_form

          2.5 Policies

          Policies是Rasa Core中的策略模塊,對(duì)應(yīng)類rasa_core.policies.Policy,它的作用就是使用合適的策略(Policy)來(lái)預(yù)測(cè)一次對(duì)話后要執(zhí)行的行為(Actions)。預(yù)測(cè)的原理是衡量命中的哪些Policies哪個(gè)置信度高,由置信度高的Policy選擇合適的Action執(zhí)行。假如出現(xiàn)不同的Policy擁有相同的置信度,那么就由它們的優(yōu)先級(jí)決定,即選擇優(yōu)先級(jí)高的Policy。Rasa對(duì)提供的Policies進(jìn)行了優(yōu)先級(jí)排序,具體如下表:

          它們的描述與作用如下:

          • Memoization Policy

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

           policies: - name: "MemoizationPolicy" max_history: 5

          注:max_history值越大訓(xùn)練得到的模型就越大并且訓(xùn)練時(shí)間會(huì)變長(zhǎng),關(guān)于該值到底該設(shè)置多少,我們可以舉這么個(gè)例子,比如有這么一個(gè)Intent:out_of_scope來(lái)描述用戶輸入的消息off-topic(離題),當(dāng)用戶連續(xù)三次觸發(fā)out_of_scope意圖,這時(shí)候我們就需要主動(dòng)告知用戶需要向其提供幫助,如果要Rasa Core能夠?qū)W習(xí)這種模型,max_history應(yīng)該至少為3。story.md中表現(xiàn)如下:

          * out_of_scope - utter_default* out_of_scope - utter_default* out_of_scope - utter_help_message
          • Keras Policy

          KerasPolicy策略是Keras框架中實(shí)現(xiàn)的神經(jīng)網(wǎng)絡(luò)來(lái)預(yù)測(cè)選擇執(zhí)行下一個(gè)action,它默認(rèn)的框架使用LSTM(Long Short-Term Memory,長(zhǎng)短期記憶網(wǎng)絡(luò))算法,但是我們也可以重寫KerasPolicy.model_architecture函數(shù)來(lái)實(shí)現(xiàn)自己的框架(architecture)。KerasPolicy的模型很簡(jiǎn)單,只是單一的LSTM+Dense+softmax,這就需要我們不斷地完善自己的story來(lái)把各種情況下的story進(jìn)行補(bǔ)充。下面演示了如何在策略配置文件config.yml文件中,配置KerasPolicy策略,其中,epochs表示訓(xùn)練的次數(shù),max_history同上。

          policies: - name: KerasPolicy epochs: 100 max_history: 5
          • Embedding Policy

          基于機(jī)器學(xué)習(xí)的對(duì)話管理能夠?qū)W習(xí)復(fù)雜的行為以完成任務(wù),但是將其功能擴(kuò)展到新領(lǐng)域并不簡(jiǎn)單,尤其是不同策略處理不合作用戶行為的能力,以及在學(xué)習(xí)新任務(wù)(如預(yù)訂酒店)時(shí),如何將完成一項(xiàng)任務(wù)(如餐廳預(yù)訂)重新應(yīng)用于該任務(wù)時(shí)的情況。EmbeddingPolicy,即循環(huán)嵌入式對(duì)話策略(Recurrent Embedding Dialogue Policy,REDP),它通過(guò)將actions和對(duì)話狀態(tài)嵌入到相同的向量空間(vector space)能夠獲得較好的效果,REDP包含一個(gè)基于改進(jìn)的Neural Turing Machine的記憶組件和注意機(jī)制,在該任務(wù)上顯著優(yōu)于基線LSTM分類器。

          EmbeddingPolicy效果上優(yōu)于KerasPolicy,但是它有個(gè)問(wèn)題是耗時(shí),因?yàn)樗鼪](méi)有使用GPU、沒(méi)有充分利用CPU資源。KerasPolicy和EmbeddingPolicy比較示意圖如下:

          配置EmbeddingPolicy參數(shù):

          policies: - name: EmbeddingPolicy epochs: 100 featurizer: - name: FullDialogueTrackerFeaturizer state_featurizer: - name: LabelTokenizerSingleStateFeaturizer

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

          • Form Policy

          FormPolicy是MemoizationPolicy的擴(kuò)展,用于處理(form)表單的填充事項(xiàng)。當(dāng)一個(gè)FormAction被調(diào)用時(shí),F(xiàn)ormPolicy將持續(xù)預(yù)測(cè)表單動(dòng)作,直到表單中的所有槽都被填滿,然后再執(zhí)行對(duì)應(yīng)的FormAction。如果在Bot系統(tǒng)中使用了FormActions,就需要在config.yml配置文件中進(jìn)行配置。

          policies: - name: FormPolicy
          • Mapping Policy

          MappingPolicy可用于直接將意圖映射到要執(zhí)行的action,從而實(shí)現(xiàn)被映射的action總會(huì)被執(zhí)行,其中,這種映射是通過(guò)triggers屬性實(shí)現(xiàn)的。舉個(gè)栗子(domain.yml文件中):

          intents: - greet: {triggers: utter_goodbye}

          其中,greet是意圖;utter_goodbye是action。一個(gè)意圖最多只能映射到一個(gè)action,我們的機(jī)器人一旦收到映射意圖的消息,它將執(zhí)行對(duì)應(yīng)的action。然后,繼續(xù)監(jiān)聽下一條message。需要注意的是,對(duì)于上述映射,我們還需要要在story.md文件中添加如下樣本,否則,任何機(jī)器學(xué)習(xí)策略都可能被預(yù)測(cè)的action_greet在dialouge歷史中突然出現(xiàn)而混淆。

          • Fallback Policy

          如果意圖識(shí)別的置信度低于nlu_threshold,或者沒(méi)有任何對(duì)話策略預(yù)測(cè)的action置信度高于core_threshold,F(xiàn)allbackPolicy將執(zhí)行fallback action。通俗來(lái)說(shuō),就是我們的對(duì)話機(jī)器人意圖識(shí)別和action預(yù)測(cè)的置信度沒(méi)有滿足對(duì)應(yīng)的閾值,該策略將使機(jī)器人執(zhí)行指定的默認(rèn)action。configs.yml配置如下:

          policies: - name: "FallbackPolicy" # 意圖理解置信度閾值 nlu_threshold: 0.3 # action預(yù)測(cè)置信度閾值 core_threshold: 0.3 # fallback action fallback_action_name: 'action_default_fallback'

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

          policies: - name: "FallbackPolicy" nlu_threshold: 0.4 core_threshold: 0.3 fallback_action_name: "my_fallback_action"

          2.6 Slots

          Slots,槽值,相當(dāng)于機(jī)器人的內(nèi)存(memory),它們以鍵值對(duì)的形式存在,用于存儲(chǔ)用戶輸入時(shí)消息時(shí)比較重要的信息,而這些信息將為Action的執(zhí)行提供關(guān)鍵數(shù)據(jù)。Slots的定義位于domain.yaml文件中,它們通常與Entities相對(duì)應(yīng),即Entities有哪些,Slots就有哪些,并且Slots存儲(chǔ)的值就是NLU模型提取的Entities的值。

          2.6.1 Slots Type

          1. Text類型

          示例:

          # domain.yamlslots: cuisine: type: text

          2. Boolean類型

          示例:

          slots: is_authenticated: type: bool

          3. categorical類型

          示例:

          slots: risk_level: type: categorical values: - low - medium - high

          4. Float類型

          示例:

          slots: temperature: type: float min_value: -100.0 max_value: 100.0

          5. List類型

          示例:

          slots: shopping_items: type: list

          6. Unfeaturized 類型

          示例:

          slots: internal_user_id: type: unfeaturized

          2.6.2 Slots Set

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

          1. Slots Initial

          # domain.yamlslots: name: type: text initial_value: "human"

          在domain.yaml文件中聲明slots時(shí),可以通過(guò)initial_value字段為當(dāng)前slot提供一個(gè)初始值,也就是說(shuō),當(dāng)會(huì)話開始時(shí),被設(shè)定初始值的slot已經(jīng)被填充好。當(dāng)然,這個(gè)操作不是必須的。

          2. Slots Set from NLU

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

          假如在stories.md文件添加一個(gè)包含-slot{}的story,這就意味著當(dāng)NLU模型提取到一個(gè)名為name的實(shí)體且這個(gè)實(shí)體有在domain.yaml中定義,那么NLU模型提取到的實(shí)體值會(huì)被自動(dòng)填充到name槽中。實(shí)際上,對(duì)于Rasa來(lái)說(shuō),就算你不添加-slot{}字段,這個(gè)實(shí)體值也會(huì)被提取并自動(dòng)填充到name槽中。當(dāng)然,如果你希望禁止這種自動(dòng)填充行為,改為添加-slot{}字段填充,可以在domain.yaml定義slot時(shí),設(shè)置auto_fill的值為False,即:

          # domain.yamlslots: name: type: text auto_fill: False

          3. Slots Set By Clicking Buttons

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

          在點(diǎn)擊Button時(shí)填充Slots的值,是指當(dāng)我們的Bot(Rasa Core)在回復(fù)用戶時(shí),可以在回復(fù)的消息中附加Button信息,這種Button類似于快捷鍵,用戶獲取到之后,可以直接將其發(fā)送給Rasa Core,它會(huì)直接進(jìn)行解析以識(shí)別intent和提取entity,并將entity的值填充到slot中。比如你讓用戶通過(guò)點(diǎn)擊一個(gè)按鈕來(lái)選擇一種顏色,那么可以在domain.yaml中utter_ask_color的回復(fù)中添加buttons:/choose{"color": "blue"}和/choose{"color": "red"}。注:通常每個(gè)button由title和payload字段組成。

          4. Slots Set by Actions

          from rasa_sdk.actions import Actionfrom rasa_sdk.events import SlotSetimport requests

          class FetchProfileAction(Action): def name(self): return "fetch_profile"

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

          該示例演示了如何在Custom Action中通過(guò)返回事件來(lái)填充Slots的值,即調(diào)用SlotSet事件函數(shù)并將該事件return。需要注意的是,為了達(dá)到這個(gè)目的,我們?cè)诰帉慡tory時(shí)必須包含該Slot,即使用-slot{}實(shí)現(xiàn),只有這樣Rasa Core就會(huì)從提供的信息中進(jìn)行學(xué)習(xí),并決定執(zhí)行正確的action。Story.md示例如下:

          # story_01* greet - action_fetch_profile - slot{"account_type" : "premium"} - utter_welcome_premium

          # story_02* greet - action_fetch_profile - slot{"account_type" : "basic"} - utter_welcome_basic

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

          slots: account_type: type: categorical values: - premium - basic

          2.6.3 Slots Get

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

          1. Get Slot in responses

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

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

          2. Get Slot in Custom Action

          from rasa_sdk.actions import Actionfrom rasa_sdk.events import SlotSetimport requests

          class FetchProfileAction(Action): def name(self): return "fetch_profile"

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

          Tracker,可理解為跟蹤器,作用是以會(huì)話會(huì)話的形式維護(hù)助手和用戶之間的對(duì)話狀態(tài)。通過(guò)Tracker,能夠輕松獲取整個(gè)對(duì)話信息,其中就包括Slot的值。

          2.7 Form

          在Rasa Core中,當(dāng)我們執(zhí)行一個(gè)action需要同時(shí)填充多個(gè)slot時(shí),可以使用FormAction來(lái)實(shí)現(xiàn),因?yàn)镕ormAction會(huì)遍歷監(jiān)管的所有slot,當(dāng)發(fā)現(xiàn)相關(guān)的slot未被填充時(shí),就會(huì)向用戶主動(dòng)發(fā)起詢問(wèn),直到所有slot被填充完畢,才會(huì)執(zhí)行接下來(lái)的業(yè)務(wù)邏輯。使用步驟如下:

          (1)構(gòu)造story

          在story中,不僅需要考慮用戶按照我們的設(shè)計(jì)準(zhǔn)確的提供有效信息,而且還要考慮用戶在中間過(guò)程改變要執(zhí)行的意圖情況或稱輸入無(wú)效信息,因?yàn)閷?duì)于FormAction來(lái)說(shuō),如果無(wú)法獲得預(yù)期的信息就會(huì)報(bào)錯(cuò),這里我們分別稱這兩種情況為happy path、unhappy path。示例如下:

          ## happy path* request_weather - weather_form - form{"name": "weather_form"} 激活form - form{"name": } 使form無(wú)效## unhappy path* request_weather - weather_form - form{"name": "weather_form"}* stop - utter_ask_continue* deny - action_deactivate_form - form{"name": }

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

          構(gòu)建stroy最好使用官方提供的Interactive Learning,防止漏掉信息,詳細(xì)見(jiàn)本文2.8小節(jié)。

          (2)添加form字段到Domain

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

          intents: - request_weather
          forms: - weather_form

          (3)配置FormPolicy

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

          policies: - name: EmbeddingPolicy epochs: 100 max_history: 5 - name: FallbackPolicy fallback_action_name: 'action_default_fallback' - name: MemoizationPolicy max_history: 5 - name: FormPolicy

          (4)Form Action實(shí)現(xiàn)

          class WeatherForm(FormAction):

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

          return "weather_form"

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

          return ["date_time", "address"]

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

          return

          當(dāng)form action第一被調(diào)用時(shí),form就會(huì)被激活并進(jìn)入FormPolicy策略模式。每次執(zhí)行form action,required_slots會(huì)被調(diào)用,當(dāng)發(fā)現(xiàn)某個(gè)還未被填充時(shí),會(huì)主動(dòng)去調(diào)用形式為uter_ask_{slotname}的模板(注:定義在domain.yml的templates字段中);當(dāng)所有slot被填充完畢,submit方法就會(huì)被調(diào)用,此時(shí)本次form操作完畢被取消激活。

          2.8 Interactive Learning

          雖然我們可以容易的人工構(gòu)建story樣本數(shù)據(jù),但是往往會(huì)出現(xiàn)一些考慮不全,甚至出錯(cuò)等問(wèn)題,基于此,Rasa Core框架為我們提供了一種交互式學(xué)習(xí)(Interactive Learning)來(lái)獲得所需的樣本數(shù)據(jù)。在互動(dòng)學(xué)習(xí)模式中,當(dāng)你與機(jī)器人交談時(shí),你會(huì)向它提供反饋,這是一種強(qiáng)大的方法來(lái)探索您的機(jī)器人可以做什么,也是修復(fù)它所犯錯(cuò)誤的最簡(jiǎn)單的方法?;跈C(jī)器學(xué)習(xí)的對(duì)話的一個(gè)優(yōu)點(diǎn)是,當(dāng)你的機(jī)器人還不知道如何做某事時(shí),你可以直接教它。

          (1)開啟Action Server

          python -m rasa run actions --port 5055 --actions actions --debug

          (2)開啟Interactive Learning

          python -m rasa interactive -m models/20200313-101055.tar.gz --endpoints configs/endpoints.yml --config configs/config.yml # 或者(沒(méi)有已訓(xùn)練模型情況)# rasa會(huì)先訓(xùn)練好模型,再開啟交互式學(xué)習(xí)會(huì)話python -m rasa interactive --data /data --domain configs/domain.yml --endpoints configs/endpoints.yml --config configs/config.yml 

          分別執(zhí)行(1)、(2)命令后,我們可以預(yù)設(shè)一個(gè)交互場(chǎng)景根據(jù)終端的提示操作即可。如果一個(gè)交互場(chǎng)景所有流程執(zhí)行完畢,按Ctrl+C結(jié)束并選擇Start Fresh進(jìn)入下一個(gè)場(chǎng)景即可。當(dāng)然Rasa還提供了可視化界面,以幫助你了解每個(gè)Story樣本構(gòu)建的過(guò)程,網(wǎng)址:http://localhost:5005/visualization.html。

          執(zhí)行流程大致如下:

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

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



          Current slots: address: None, business: None, date-time: None, id_number: None, requested_slot: None

          ------? The bot wants to run 'number_form', correct? YesChat History

          # Bot You ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 1 action_listen────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 2 查詢身份證[439912199008071234](id_number) intent: request_idcard 1.00────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 3 number_form 1.00 您要查詢的身份證號(hào)碼439912199008071234所屬人為張三,湖南長(zhǎng)沙人,現(xiàn)在就職于地球村物業(yè)。 form{"name": "number_form"} slot{"id_number": "439912199008071234"} form{"name": } slot{"requested_slot": }



          Current slots: address: None, business: None, date-time: None, id_number: 439912199008071234, requested_slot: None

          ------? The bot wants to run 'action_listen', correct? Yes

          生成的一個(gè)Story示例如下:

          ## interactive_story_10# unhappy path:chitchat stop but continue path* greet - utter_answer_greet* request_number{"type": "身份證號(hào)碼"} - number_form - form{"name": "number_form"} - slot{"type": "身份證號(hào)碼"} - slot{"number": } - slot{"business": } - slot{"requested_slot": "number"}* chitchat - utter_chitchat - number_form - slot{"requested_slot": "number"}* stop - utter_ask_continue* affirm - number_form - slot{"requested_slot": "number"}* form: request_number{"number": "440123199087233467"} - form: number_form - slot{"number": "440123199087233467"} - slot{"type": "身份證號(hào)碼"} - form{"name": } - slot{"requested_slot": }* thanks - utter_noworries

          改進(jìn)ChitChatAssistant項(xiàng)目

          3.1 config.yml

          # zh_jieba_mitie_embeddings_config.yml

          language: "zh"

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

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

          考慮到目前項(xiàng)目的樣本較少,這里使用MITIE+EmbeddingPolicy組合,雖然訓(xùn)練時(shí)慢了點(diǎn),但是能夠保證實(shí)體提取的準(zhǔn)確性,同時(shí)又能夠提高意圖識(shí)別的命中率。

          3.2 weather_stories.md

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

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

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

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

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

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

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

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

          ## chitchat, stop and really stop path* greet - utter_answer_greet* request_weather - weather_form - form{"name": "weather_form"}* chitchat - utter_chitchat - weather_form* stop - utter_ask_continue* deny - action_deactivate_form - form{"name": }
          ## interactive_story_1## 天氣 + 時(shí)間 + 地點(diǎn) + 地點(diǎn)* request_weather - weather_form - form{"name": "weather_form"} - slot{"requested_slot": "date_time"}* form: inform{"date_time": "明天"} - form: weather_form - slot{"date_time": "明天"} - slot{"requested_slot": "address"}* form: inform{"address": "廣州"} - form: weather_form - slot{"address": "廣州"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "后天"} OR request_weather{"date_time": "后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "廣州"} - slot{"date_time": "后天"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

          ## interactive_story_1## 天氣 + 時(shí)間 + 地點(diǎn) + 時(shí)間* request_weather - weather_form - form{"name": "weather_form"} - slot{"requested_slot": "date_time"}* form: inform{"date_time": "明天"} - form: weather_form - slot{"date_time": "明天"} - slot{"requested_slot": "address"}* form: inform{"address": "廣州"} - form: weather_form - slot{"address": "廣州"} - form{"name": } - slot{"requested_slot": }* inform{"address": "上海"} OR request_weather{"address": "深圳"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "廣州"} - slot{"address": "上海"} - form{"name": } - slot{"requested_slot": }* affirm - utter_answer_affirm

          ## interactive_story_2## 天氣/時(shí)間/地點(diǎn) + 地點(diǎn)* request_weather{"date_time": "明天", "address": "上海"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "上海"} - form{"name": } - slot{"requested_slot": }* inform{"address": "廣州"} OR request_weather{"address": "廣州"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "上海"} - slot{"address": "廣州"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

          ## interactive_story_3## 天氣/時(shí)間/地點(diǎn) + 時(shí)間* request_weather{"address": "深圳", "date_time": "后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "深圳"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "大后天"} OR request_weather{"date_time": "大后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "深圳"} - slot{"date_time": "大后天"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

          ## interactive_story_2## 天氣/時(shí)間/地點(diǎn) + 地點(diǎn) + 時(shí)間* request_weather{"date_time": "明天", "address": "上海"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "上海"} - form{"name": } - slot{"requested_slot": }* inform{"address": "北京"} OR request_weather{"address": "北京"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "上海"} - slot{"address": "北京"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "后天"} OR request_weather{"date_time": "后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "北京"} - slot{"date_time": "后天"} - form{"name": } - slot{"requested_slot": }* affirm - utter_answer_affirm

          ## interactive_story_3## 天氣/時(shí)間/地點(diǎn) + 地點(diǎn) + 地點(diǎn)* request_weather{"date_time": "后天", "address": "北京"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "北京"} - form{"name": } - slot{"requested_slot": }* inform{"address": "深圳"} OR request_weather{"address": "深圳"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "北京"} - slot{"address": "深圳"} - form{"name": } - slot{"requested_slot": }* inform{"address": "南京"} OR request_weather{"address": "南京"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "深圳"} - slot{"address": "南京"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

          ## interactive_story_4## 天氣/時(shí)間/地點(diǎn) + 時(shí)間 + 地點(diǎn)* request_weather{"date_time": "明天", "address": "長(zhǎng)沙"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "長(zhǎng)沙"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "后天"} OR request_weather{"date_time": "后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "長(zhǎng)沙"} - slot{"date_time": "后天"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "大后天"} OR request_weather{"date_time": "大后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "長(zhǎng)沙"} - slot{"date_time": "大后天"} - form{"name": } - slot{"requested_slot": }* affirm - utter_answer_affirm

          ## interactive_story_5## 天氣/時(shí)間/地點(diǎn) + 時(shí)間 + 時(shí)間* request_weather{"date_time": "后天", "address": "深圳"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "深圳"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "明天"} OR request_weather{"date_time": "明天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "深圳"} - slot{"date_time": "明天"} - form{"name": } - slot{"requested_slot": }* inform{"address": "廣州"} OR request_weather{"address": "廣州"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "深圳"} - slot{"address": "廣州"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

          ## interactive_story_4## 天氣/時(shí)間 + 地點(diǎn) + 時(shí)間* request_weather{"date_time": "明天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"requested_slot": "address"}* form: inform{"address": "廣州"} - form: weather_form - slot{"address": "廣州"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "后天"} OR request_weather{"date_time": "后天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "明天"} - slot{"address": "廣州"} - slot{"date_time": "后天"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

          ## interactive_story_5## 天氣/地點(diǎn) + 時(shí)間 + 時(shí)間* request_weather{"address": "廣州"} - weather_form - form{"name": "weather_form"} - slot{"address": "廣州"} - slot{"requested_slot": "date_time"}* form: inform{"date_time": "后天"} - form: weather_form - slot{"date_time": "后天"} - form{"name": } - slot{"requested_slot": }* inform{"date_time": "明天"} OR request_weather{"date_time": "明天"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "后天"} - slot{"address": "廣州"} - slot{"date_time": "明天"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks
          ## interactive_story_1## 天氣/時(shí)間/地點(diǎn) + chit + chit(restart)+詢問(wèn)天氣* request_weather{"date_time": "今天", "address": "廣州"} - weather_form - form{"name": "weather_form"} - slot{"date_time": "今天"} - slot{"address": "廣州"} - form{"name": } - slot{"requested_slot": }* chitchat - utter_chitchat* chitchat - utter_chitchat - action_restart* request_weather - weather_form - form{"name": "weather_form"} - slot{"requested_slot": "date_time"}* form: inform{"date_time": "今天"} - form: weather_form - slot{"date_time": "今天"} - slot{"requested_slot": "address"}* form: inform{"address": "廣州"} - form: weather_form - slot{"address": "廣州"} - form{"name": } - slot{"requested_slot": }* thanks - utter_answer_thanks

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

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

          原文鏈接:

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

          ?沒(méi)有監(jiān)控和日志咋整?老程序員來(lái)支招

          ?朱廣權(quán)李佳琦直播掉線,1.2億人在線等

          ?RPC的超時(shí)設(shè)置,一不小心就是線上事故!

          ?拿下Gartner容器產(chǎn)品第一,阿里云打贏云原生關(guān)鍵一戰(zhàn)!

          ?深聊Solidity的測(cè)試場(chǎng)景、方法和實(shí)踐,太詳細(xì)了,必須收藏!

          ?萬(wàn)字干貨:一步步教你如何在容器上構(gòu)建持續(xù)部署!

          ?據(jù)說(shuō),這是當(dāng)代極客們的【技術(shù)風(fēng)向標(biāo)】...

          今日福利:評(píng)論區(qū)留言入選,可獲得價(jià)值299元的「2020 AI開發(fā)者萬(wàn)人大會(huì)」在線直播門票一張。 快來(lái)動(dòng)動(dòng)手指,寫下你想說(shuō)的話吧。

          什么是DOM

          文檔對(duì)象模型 (DOM) 是HTML和XML文檔的編程接口。它提供了對(duì)文檔的結(jié)構(gòu)化的表述,并定義了一種方式可以使從程序中對(duì)該結(jié)構(gòu)進(jìn)行訪問(wèn),從而改變文檔的結(jié)構(gòu),樣式和內(nèi)容。文檔對(duì)象模型 (DOM) 是對(duì)HTML文件的另一種展示,通俗地說(shuō),一個(gè)HTML 文件,我們可以用編輯器以代碼的形式展示它,也可以用瀏覽器以頁(yè)面的形式展示它,同一份文件通過(guò)不同的展示方式,就有了不一樣的表現(xiàn)形式。而DOM 將文檔解析為一個(gè)由節(jié)點(diǎn)和對(duì)象(包含屬性和方法的對(duì)象)組成的結(jié)構(gòu)集合。簡(jiǎn)言之,它會(huì)將web頁(yè)面和腳本或程序語(yǔ)言連接起來(lái),我們可以使用腳本或者程序語(yǔ)言通過(guò)DOM 來(lái)改變或者控制web頁(yè)面。

          2 如何訪問(wèn)DOM

          我們可以通過(guò)JavaScript 來(lái)調(diào)用document和window元素的API來(lái)操作文檔或者獲取文檔的信息。

          3 Node

          Node 是一個(gè)接口,有許多接口都從Node 繼承方法和屬性:Document, Element, CharacterData (which Text, Comment, and CDATASection inherit), ProcessingInstruction, DocumentFragment, DocumentType, Notation, Entity, EntityReference。Node 有一個(gè)nodeType的屬性表示Node 的類型,是一個(gè)整數(shù),不同的值代表不同的節(jié)點(diǎn)類型。具體如下表所示:

          節(jié)點(diǎn)類型常量

          已棄用的節(jié)點(diǎn)類型常量

          假設(shè)我們要判斷一個(gè)Node 是不是一個(gè)元素,通過(guò)查表可知元素的nodeType屬性值為1,代碼可以這么寫:

          復(fù)制代碼if(X.nodeType === 1){
            console.log('X 是一個(gè)元素');
          }
          

          在Node 類型中,比較常用的就是element,text,comment,document,document_fragment這幾種類型。

          3.1 Element

          Element提供了對(duì)元素標(biāo)簽名,子節(jié)點(diǎn)和特性的訪問(wèn),我們常用HTML元素比如div,span,a等標(biāo)簽就是element中的一種。Element有下面幾條特性:(1)nodeType為1(2)nodeName為元素標(biāo)簽名,tagName也是返回標(biāo)簽名(3)nodeValue為null(4)parentNode可能是Document或Element(5)子節(jié)點(diǎn)可能是Element,Text,Comment,Processing_Instruction,CDATASection或EntityReference

          3.2 Text

          Text表示文本節(jié)點(diǎn),它包含的是純文本內(nèi)容,不能包含html代碼,但可以包含轉(zhuǎn)義后的html代碼。Text有下面的特性:(1)nodeType為3(2)nodeName為#text(3)nodeValue為文本內(nèi)容(4)parentNode是一個(gè)Element(5)沒(méi)有子節(jié)點(diǎn)

          3.3 Comment

          Comment表示HTML文檔中的注釋,它有下面的幾種特征:(1)nodeType為8(2)nodeName為#comment(3)nodeValue為注釋的內(nèi)容(4)parentNode可能是Document或Element(5)沒(méi)有子節(jié)點(diǎn)

          3.4 Document

          Document表示文檔,在瀏覽器中,document對(duì)象是HTMLDocument的一個(gè)實(shí)例,表示整個(gè)頁(yè)面,它同時(shí)也是window對(duì)象的一個(gè)屬性。Document有下面的特性:(1)nodeType為9(2)nodeName為#document(3)nodeValue為null(4)parentNode為null(5)子節(jié)點(diǎn)可能是一個(gè)DocumentType或Element

          3.5 DocumentFragment

          DocumentFragment是所有節(jié)點(diǎn)中唯一一個(gè)沒(méi)有對(duì)應(yīng)標(biāo)記的類型,它表示一種輕量級(jí)的文檔,可能當(dāng)作一個(gè)臨時(shí)的倉(cāng)庫(kù)用來(lái)保存可能會(huì)添加到文檔中的節(jié)點(diǎn)。DocumentFragment有下面的特性:(1)nodeType為11(2)nodeName為#document-fragment(3)nodeValue為null(4)parentNode為null

          4 節(jié)點(diǎn)創(chuàng)建型API

          用如其名,這類API是用來(lái)創(chuàng)建節(jié)點(diǎn)的

          4.1 createElement

          createElement通過(guò)傳入指定的一個(gè)標(biāo)簽名來(lái)創(chuàng)建一個(gè)元素,如果傳入的標(biāo)簽名是一個(gè)未知的,則會(huì)創(chuàng)建一個(gè)自定義的標(biāo)簽,注意:IE8以下瀏覽器不支持自定義標(biāo)簽。

          語(yǔ)法

          復(fù)制代碼  let element = document.createElement(tagName);
          

          使用createElement要注意:通過(guò)createElement創(chuàng)建的元素并不屬于HTML文檔,它只是創(chuàng)建出來(lái),并未添加到HTML文檔中,要調(diào)用appendChild或insertBefore等方法將其添加到HTML文檔樹中。

          例子:

          復(fù)制代碼  let elem = document.createElement("div");
            elem.id = 'test';
            elem.style = 'color: red';
            elem.innerHTML = '我是新創(chuàng)建的節(jié)點(diǎn)';
            document.body.appendChild(elem);
          

          運(yùn)行結(jié)果為:


          4.2 createTextNode

          createTextNode用來(lái)創(chuàng)建一個(gè)文本節(jié)點(diǎn)

          語(yǔ)法

          復(fù)制代碼  var text = document.createTextNode(data);
          

          createTextNode接收一個(gè)參數(shù),這個(gè)參數(shù)就是文本節(jié)點(diǎn)中的文本,和createElement一樣,創(chuàng)建后的文本節(jié)點(diǎn)也只是獨(dú)立的一個(gè)節(jié)點(diǎn),同樣需要appendChild將其添加到HTML文檔樹中

          例子:

          復(fù)制代碼  var node = document.createTextNode("我是文本節(jié)點(diǎn)");
            document.body.appendChild(node);
          

          運(yùn)行結(jié)果為:


          4.3 cloneNode

          cloneNode返回調(diào)用該方法的節(jié)點(diǎn)的一個(gè)副本

          語(yǔ)法

          復(fù)制代碼  var dupNode = node.cloneNode(deep);
          

          node 將要被克隆的節(jié)點(diǎn)dupNode 克隆生成的副本節(jié)點(diǎn)deep(可選)是否采用深度克隆,如果為true,則該節(jié)點(diǎn)的所有后代節(jié)點(diǎn)也都會(huì)被克隆,如果為false,則只克隆該節(jié)點(diǎn)本身.

          這里有幾點(diǎn)要注意:(1)和createElement一樣,cloneNode創(chuàng)建的節(jié)點(diǎn)只是游離有HTML文檔外的節(jié)點(diǎn),要調(diào)用appendChild方法才能添加到文檔樹中(2)如果復(fù)制的元素有id,則其副本同樣會(huì)包含該id,由于id具有唯一性,所以在復(fù)制節(jié)點(diǎn)后必須要修改其id(3)調(diào)用接收的deep參數(shù)最好傳入,如果不傳入該參數(shù),不同瀏覽器對(duì)其默認(rèn)值的處理可能不同

          注意如果被復(fù)制的節(jié)點(diǎn)綁定了事件,則副本也會(huì)跟著綁定該事件嗎?這里要分情況討論:(1)如果是通過(guò)addEventListener或者比如onclick進(jìn)行綁定事件,則副本節(jié)點(diǎn)不會(huì)綁定該事件(2)如果是內(nèi)聯(lián)方式綁定比如:<div onclick="showParent()"></div>,這樣的話,副本節(jié)點(diǎn)同樣會(huì)觸發(fā)事件。

          例子:

          復(fù)制代碼<body>
            <div id="parent">
              我是父元素的文本
              <br/>
              <span>
                  我是子元素
              </span>
            </div>
            <button id="btnCopy">復(fù)制</button>
          </body>
          <script>
            var parent = document.getElementById("parent");
            document.getElementById("btnCopy").onclick = function(){
            	var parent2 = parent.cloneNode(true);
            	parent2.id = "parent2";
            	document.body.appendChild(parent2);
            }
          </script>
          

          運(yùn)行結(jié)果為:


          4.4 createDocumentFragment

          DocumentFragments 是DOM節(jié)點(diǎn)。它們不是主DOM樹的一部分。通常的用例是創(chuàng)建文檔片段,將元素附加到文檔片段,然后將文檔片段附加到DOM樹。在DOM樹中,文檔片段被其所有的子元素所代替。因?yàn)槲臋n片段存在于內(nèi)存中,并不在DOM樹中,所以將子元素插入到文檔片段時(shí)不會(huì)引起頁(yè)面回流(reflow)(對(duì)元素位置和幾何上的計(jì)算)。因此,使用文檔片段document fragments 通常會(huì)起到優(yōu)化性能的作用。

          語(yǔ)法

          復(fù)制代碼  let fragment = document.createDocumentFragment();
          

          例子:

          復(fù)制代碼<body>
            <ul id="ul"></ul>
          </body>
          <script>
            (function()
            {
              var start = Date.now();
              var str = '', li;
              var ul = document.getElementById('ul');
              var fragment = document.createDocumentFragment();
              for(var i=0; i<1000; i++)
              {
                  li = document.createElement('li');
                  li.textContent = '第'+(i+1)+'個(gè)子節(jié)點(diǎn)';
                  fragment.appendChild(li);
              }
              ul.appendChild(fragment);
            })();
          </script>
          

          運(yùn)行結(jié)果為:


          4.5 節(jié)點(diǎn)創(chuàng)建型API總結(jié)

          節(jié)點(diǎn)創(chuàng)建型API主要包括createElement,createTextNode,cloneNode和createDocumentFragment四個(gè)方法,需要注意下面幾點(diǎn):(1)它們創(chuàng)建的節(jié)點(diǎn)只是一個(gè)孤立的節(jié)點(diǎn),要通過(guò)appendChild添加到文檔中(2)cloneNode要注意如果被復(fù)制的節(jié)點(diǎn)是否包含子節(jié)點(diǎn)以及事件綁定等問(wèn)題(3)使用createDocumentFragment來(lái)解決添加大量節(jié)點(diǎn)時(shí)的性能問(wèn)題

          5 頁(yè)面修改型API

          前面我們提到節(jié)點(diǎn)創(chuàng)建型API,它們只是創(chuàng)建節(jié)點(diǎn),并沒(méi)有真正修改到頁(yè)面內(nèi)容,而是要調(diào)用·appendChild·來(lái)將其添加到文檔樹中。我在這里將這類會(huì)修改到頁(yè)面內(nèi)容歸為一類。修改頁(yè)面內(nèi)容的api主要包括:appendChild,insertBefore,removeChild,replaceChild。

          5.1 appendChild

          appendChild我們?cè)谇懊嬉呀?jīng)用到多次,就是將指定的節(jié)點(diǎn)添加到調(diào)用該方法的節(jié)點(diǎn)的子元素的末尾。

          語(yǔ)法

          復(fù)制代碼  parent.appendChild(child);
          

          child節(jié)點(diǎn)將會(huì)作為parent節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn)。appendChild這個(gè)方法很簡(jiǎn)單,但是還有有一點(diǎn)需要注意:如果被添加的節(jié)點(diǎn)是一個(gè)頁(yè)面中存在的節(jié)點(diǎn),則執(zhí)行后這個(gè)節(jié)點(diǎn)將會(huì)添加到指定位置,其原本所在的位置將移除該節(jié)點(diǎn),也就是說(shuō)不會(huì)同時(shí)存在兩個(gè)該節(jié)點(diǎn)在頁(yè)面上,相當(dāng)于把這個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)地方。如果child綁定了事件,被移動(dòng)時(shí),它依然綁定著該事件。

          例子:

          復(fù)制代碼<body>
            <div id="child">
              要被添加的節(jié)點(diǎn)
            </div>
            <br/>
            <br/>
            <br/>
            <div id="parent">
              要移動(dòng)的位置
            </div>		
            <input id="btnMove" type="button" value="移動(dòng)節(jié)點(diǎn)" />
          </body>
          <script>
            document.getElementById("btnMove").onclick = function(){
          	  var child = document.getElementById("child");
           	  document.getElementById("parent").appendChild(child);
            } 
          </script>
          

          運(yùn)行結(jié)果:


          5.2 insertBefore

          insertBefore用來(lái)添加一個(gè)節(jié)點(diǎn)到一個(gè)參照節(jié)點(diǎn)之前

          語(yǔ)法

          復(fù)制代碼  parentNode.insertBefore(newNode,refNode);
          

          parentNode表示新節(jié)點(diǎn)被添加后的父節(jié)點(diǎn)newNode表示要添加的節(jié)點(diǎn)refNode表示參照節(jié)點(diǎn),新節(jié)點(diǎn)會(huì)添加到這個(gè)節(jié)點(diǎn)之前

          例子:

          復(fù)制代碼<body>
            <div id="parent">
              父節(jié)點(diǎn)
              <div id="child">				
                  子元素
              </div>
            </div>
            <input type="button" id="insertNode" value="插入節(jié)點(diǎn)" />
          </body>
          <script>
            var parent = document.getElementById("parent");
            var child = document.getElementById("child");
            document.getElementById("insertNode").onclick = function(){
            	var newNode = document.createElement("div");
            	newNode.textContent = "新節(jié)點(diǎn)"
            	parent.insertBefore(newNode,child);
            }
          </script>
          

          運(yùn)行結(jié)果:


          關(guān)于第二個(gè)參數(shù)參照節(jié)點(diǎn)還有幾個(gè)注意的地方:(1)refNode是必傳的,如果不傳該參數(shù)會(huì)報(bào)錯(cuò)(2)如果refNode是undefined或null,則insertBefore會(huì)將節(jié)點(diǎn)添加到子元素的末尾

          5.3 removeChild

          刪除指定的子節(jié)點(diǎn)并返回

          語(yǔ)法

          復(fù)制代碼  var deletedChild = parent.removeChild(node);
          

          deletedChild指向被刪除節(jié)點(diǎn)的引用,它等于node,被刪除的節(jié)點(diǎn)仍然存在于內(nèi)存中,可以對(duì)其進(jìn)行下一步操作。注意:如果被刪除的節(jié)點(diǎn)不是其子節(jié)點(diǎn),則程序?qū)?huì)報(bào)錯(cuò)。我們可以通過(guò)下面的方式來(lái)確??梢詣h除:

          復(fù)制代碼if(node.parentNode){
              node.parentNode.removeChild(node);
          }
          

          運(yùn)行結(jié)果:

          通過(guò)節(jié)點(diǎn)自己獲取節(jié)點(diǎn)的父節(jié)點(diǎn),然后將自身刪除


          5.4 replaceChild

          replaceChild用于使用一個(gè)節(jié)點(diǎn)替換另一個(gè)節(jié)點(diǎn)

          語(yǔ)法

          復(fù)制代碼  parent.replaceChild(newChild,oldChild);
          

          newChild是替換的節(jié)點(diǎn),可以是新的節(jié)點(diǎn),也可以是頁(yè)面上的節(jié)點(diǎn),如果是頁(yè)面上的節(jié)點(diǎn),則其將被轉(zhuǎn)移到新的位置oldChild是被替換的節(jié)點(diǎn)

          例子:

          復(fù)制代碼<body>
            <div id="parent">
              父節(jié)點(diǎn)
              <div id="child">				
                  子元素
              </div>
            </div>
            <input type="button" id="insertNode" value="替換節(jié)點(diǎn)" />
          </body>
          <script>
            var parent = document.getElementById("parent");
            var child = document.getElementById("child");
            document.getElementById("insertNode").onclick = function(){
            	var newNode = document.createElement("div");
            	newNode.textContent = "新節(jié)點(diǎn)"
            	parent.replaceChild(newNode,child)
            }
          

          運(yùn)行結(jié)果:


          5.5 頁(yè)面修改型API總結(jié)

          頁(yè)面修改型API主要是這四個(gè)接口,要注意幾個(gè)特點(diǎn):(1)不管是新增還是替換節(jié)點(diǎn),如果新增或替換的節(jié)點(diǎn)是原本存在頁(yè)面上的,則其原來(lái)位置的節(jié)點(diǎn)將被移除,也就是說(shuō)同一個(gè)節(jié)點(diǎn)不能存在于頁(yè)面的多個(gè)位置(2)節(jié)點(diǎn)本身綁定的事件會(huì)不會(huì)消失,會(huì)一直保留著。

          6 節(jié)點(diǎn)查詢型API

          6.1 document.getElementById

          這個(gè)接口很簡(jiǎn)單,根據(jù)元素id返回元素,返回值是Element類型,如果不存在該元素,則返回null

          語(yǔ)法

          復(fù)制代碼  var element = document.getElementById(id);
          

          使用這個(gè)接口有幾點(diǎn)要注意:(1)元素的Id是大小寫敏感的,一定要寫對(duì)元素的id(2)HTML文檔中可能存在多個(gè)id相同的元素,則返回第一個(gè)元素(3)只從文檔中進(jìn)行搜索元素,如果創(chuàng)建了一個(gè)元素并指定id,但并沒(méi)有添加到文檔中,則這個(gè)元素是不會(huì)被查找到的

          例子:

          復(fù)制代碼<body>
            <p id="para1">Some text here</p>
            <button onclick="changeColor('blue');">blue</button>
            <button onclick="changeColor('red');">red</button>
          </body>
          <script>
            function changeColor(newColor) {
              var elem = document.getElementById("para1");
              elem.style.color = newColor;
            }
          </script>
          

          運(yùn)行結(jié)果:


          6.2 document.getElementsByTagName

          返回一個(gè)包括所有給定標(biāo)簽名稱的元素的HTML集合HTMLCollection。 整個(gè)文件結(jié)構(gòu)都會(huì)被搜索,包括根節(jié)點(diǎn)。返回的 HTML集合是動(dòng)態(tài)的, 意味著它可以自動(dòng)更新自己來(lái)保持和 DOM 樹的同步而不用再次調(diào)用document.getElementsByTagName()

          語(yǔ)法

          復(fù)制代碼  var elements = document.getElementsByTagName(name);
          

          (1)如果要對(duì)HTMLCollection集合進(jìn)行循環(huán)操作,最好將其長(zhǎng)度緩存起來(lái),因?yàn)槊看窝h(huán)都會(huì)去計(jì)算長(zhǎng)度,暫時(shí)緩存起來(lái)可以提高效率(2)如果沒(méi)有存在指定的標(biāo)簽,該接口返回的不是null,而是一個(gè)空的HTMLCollection(3)name是一個(gè)代表元素的名稱的字符串。特殊字符 "*" 代表了所有元素。

          例子:

          復(fù)制代碼<body>
            <div>div1</div>
            <div>div2</div>	
            <input type="button" value="顯示數(shù)量" id="btnShowCount"/>
            <input type="button" value="新增div" id="btnAddDiv"/>	
          </body>
          <script>
            var divList = document.getElementsByTagName("div");
            document.getElementById("btnAddDiv").onclick = function(){
            	var div = document.createElement("div");
            	div.textContent ="div" + (divList.length+1);
            	document.body.appendChild(div);
            }
            document.getElementById("btnShowCount").onclick = function(){
              alert(divList.length);
            }
          </script>
          

          這段代碼中有兩個(gè)按鈕,一個(gè)按鈕是顯示HTMLCollection元素的個(gè)數(shù),另一個(gè)按鈕可以新增一個(gè)div標(biāo)簽到文檔中。前面提到HTMLCollcetion元素是即時(shí)的表示該集合是隨時(shí)變化的,也就是是文檔中有幾個(gè)div,它會(huì)隨時(shí)進(jìn)行變化,當(dāng)我們新增一個(gè)div后,再訪問(wèn)HTMLCollection時(shí),就會(huì)包含這個(gè)新增的div。

          運(yùn)行結(jié)果:


          6.3 document.getElementsByName

          getElementsByName主要是通過(guò)指定的name屬性來(lái)獲取元素,它返回一個(gè)即時(shí)的NodeList對(duì)象

          語(yǔ)法

          復(fù)制代碼  var elements = document.getElementsByName(name) 
          

          使用這個(gè)接口主要要注意幾點(diǎn):(1)返回對(duì)象是一個(gè)即時(shí)的NodeList,它是隨時(shí)變化的(2)在HTML元素中,并不是所有元素都有name屬性,比如div是沒(méi)有name屬性的,但是如果強(qiáng)制設(shè)置div的name屬性,它也是可以被查找到的(3)在IE中,如果id設(shè)置成某個(gè)值,然后傳入getElementsByName的參數(shù)值和id值一樣,則這個(gè)元素是會(huì)被找到的,所以最好不好設(shè)置同樣的值給id和name

          例子:

          復(fù)制代碼<script type="text/javascript">
            function getElements()
             {
             var x=document.getElementsByName("myInput");
             alert(x.length);
             }
          </script>
          <body>
            <input name="myInput" type="text" size="20" /><br />
            <input name="myInput" type="text" size="20" /><br />
            <input name="myInput" type="text" size="20" /><br />
            <br />
            <input type="button" onclick="getElements()" value="How many elements named 'myInput'?" />
          </body>
          

          運(yùn)行結(jié)果:


          6.4 document.getElementsByClassName

          這個(gè)API是根據(jù)元素的class返回一個(gè)即時(shí)的HTMLCollection

          語(yǔ)法

          復(fù)制代碼  var elements = document.getElementsByClassName(names); // or:
            var elements = rootElement.getElementsByClassName(names);
          
          • elements是一個(gè)實(shí)時(shí)集合,包含了找到的所有元素
          • names是一個(gè)字符串,表示要匹配的類名列表;類名通過(guò)空格分隔
          • getElementsByClassName可以在任何元素上調(diào)用,不僅僅是document。調(diào)用這個(gè)方法的元素將作為本次查找的根元素

          這個(gè)接口有下面幾點(diǎn)要注意:(1)返回結(jié)果是一個(gè)即時(shí)的HTMLCollection,會(huì)隨時(shí)根據(jù)文檔結(jié)構(gòu)變化(2)IE9以下瀏覽器不支持(3)如果要獲取2個(gè)以上classname,可傳入多個(gè)classname,每個(gè)用空格相隔,例如

          復(fù)制代碼  var elements = document.getElementsByClassName("test1 test2");
          

          例子:

          • 獲取所有class為 'test' 的元素
          復(fù)制代碼  var elements = document.getElementsByClassName('test');
          
          • 獲取所有class同時(shí)包括 'red' 和 'test' 的元素
          復(fù)制代碼  var elements = document.getElementsByClassName('red test');
          
          • 在id為'main'的元素的子節(jié)點(diǎn)中,獲取所有class為'test'的元素
          復(fù)制代碼  var elements = document.getElementById('main').getElementsByClassName('test');
          
          • 我們還可以對(duì)任意的HTMLCollection 使用Array.prototype的方法,調(diào)用時(shí)傳遞HTMLCollection 作為方法的參數(shù)。這里我們將查找到所有class為'test'的div元素:
          復(fù)制代碼  var testElements = document.getElementsByClassName('test');
            var testDivs = Array.prototype.filter.call(testElements, function(testElement){
              return testElement.nodeName === 'DIV';;
            });
          

          6.5 document.querySelector和document.querySelectorAll

          這兩個(gè)API很相似,通過(guò)css選擇器來(lái)查找元素,注意選擇器要符合CSS選擇器的規(guī)則

          • 6.5.1 document.querySelector

          document.querySelector返回第一個(gè)匹配的元素,如果沒(méi)有匹配的元素,則返回null

          語(yǔ)法

          復(fù)制代碼  var element = document.querySelector(selectors);
          

          注意,由于返回的是第一個(gè)匹配的元素,這個(gè)api使用的深度優(yōu)先搜索來(lái)獲取元素。

          例子:

          復(fù)制代碼<body>
            <div>
              <div>
                <span class="test">第三級(jí)的span</span>	
              </div>
            </div>
            <div class="test">			
              同級(jí)的第二個(gè)div
            </div>
            <input type="button" id="btnGet" value="獲取test元素" />
          </body>
          <script>
            document.getElementById("btnGet").addEventListener("click",function(){
              var element = document.querySelector(".test");
              alert(element.textContent);
            })
          </script>
          

          兩個(gè)class都包含“test”的元素,一個(gè)在文檔樹的前面,但是它在第三級(jí),另一個(gè)在文檔樹的后面,但它在第一級(jí),通過(guò)querySelector獲取元素時(shí),它通過(guò)深度優(yōu)先搜索,拿到文檔樹前面的第三級(jí)的元素。運(yùn)行結(jié)果:


          • 6.5.2 document.querySelectorAll返回的是所有匹配的元素,而且可以匹配多個(gè)選擇符

          語(yǔ)法

          復(fù)制代碼  var elementList = document.querySelectorAll(selectors);
          
          • elementList是一個(gè)靜態(tài)的NodeList類型的對(duì)象
          • selectors是一個(gè)由逗號(hào)連接的包含一個(gè)或多個(gè)CSS選擇器的字符串
          • 如果selectors參數(shù)中包含CSS偽元素,則返回一個(gè)空的elementList

          例子:

          復(fù)制代碼  var matches = document.querySelectorAll("div.note, div.alert");
          

          返回一個(gè)文檔中所有的class為"note"或者"alert"的div元素

          復(fù)制代碼<body>
            <div class="test">
              class為test
            </div>
            <div id="test">
              id為test
            </div>
            <input id="btnShow" type="button" value="顯示內(nèi)容" />
          </body>
          <script>
            document.getElementById("btnShow").addEventListener("click",function(){
          	var elements = document.querySelectorAll("#test,.test");	
          	for(var i = 0,length = elements.length;i<length;i++){
          		alert(elements[i].textContent);
          	}	
            })
          </script>
          

          這段代碼通過(guò)querySelectorAll,使用id選擇器和class選擇器選擇了兩個(gè)元素,并依次輸出其內(nèi)容。要注意兩點(diǎn):(1)querySelectorAll也是通過(guò)深度優(yōu)先搜索,搜索的元素順序和選擇器的順序無(wú)關(guān)(2)返回的是一個(gè)非即時(shí)的NodeList,也就是說(shuō)結(jié)果不會(huì)隨著文檔樹的變化而變化兼容性問(wèn)題:querySelector和querySelectorAll在ie8以下的瀏覽器不支持。

          運(yùn)行結(jié)果:


          7 節(jié)點(diǎn)關(guān)系型API

          在html文檔中的每個(gè)節(jié)點(diǎn)之間的關(guān)系都可以看成是家譜關(guān)系,包含父子關(guān)系,兄弟關(guān)系等等

          7.1 父關(guān)系型API

          7.1.1 parentNode

          每個(gè)節(jié)點(diǎn)都有一個(gè)parentNode屬性,它表示元素的父節(jié)點(diǎn)。Element的父節(jié)點(diǎn)可能是Element,Document或DocumentFragment

          7.1.2 parentElement

          返回元素的父元素節(jié)點(diǎn),與parentNode的區(qū)別在于,其父節(jié)點(diǎn)必須是一個(gè)Element,如果不是,則返回null

          7.2 子關(guān)系型APPI

          7.2.1 childNodes

          返回一個(gè)即時(shí)的NodeList,表示元素的子節(jié)點(diǎn)列表,子節(jié)點(diǎn)可能會(huì)包含文本節(jié)點(diǎn),注釋節(jié)點(diǎn)等

          7.2.2 children:

          一個(gè)即時(shí)的HTMLCollection,子節(jié)點(diǎn)都是Element,IE9以下瀏覽器不支持children屬性為只讀屬性,對(duì)象類型為HTMLCollection,你可以使用elementNodeReference.children[1].nodeName來(lái)獲取某個(gè)子元素的標(biāo)簽名稱

          7.2.3 firstChild

          只讀屬性返回樹中節(jié)點(diǎn)的第一個(gè)子節(jié)點(diǎn),如果節(jié)點(diǎn)是無(wú)子節(jié)點(diǎn),則返回 null

          7.2.4 lastChild

          返回當(dāng)前節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn)。如果父節(jié)點(diǎn)為一個(gè)元素節(jié)點(diǎn),則子節(jié)點(diǎn)通常為一個(gè)元素節(jié)點(diǎn),或一個(gè)文本節(jié)點(diǎn),或一個(gè)注釋節(jié)點(diǎn)。如果沒(méi)有子節(jié)點(diǎn),則返回null

          7.2.5 hasChildNodes

          返回一個(gè)布爾值,表明當(dāng)前節(jié)點(diǎn)是否包含有子節(jié)點(diǎn).

          7.3 兄弟關(guān)系型API

          7.3.1 previousSibling

          返回當(dāng)前節(jié)點(diǎn)的前一個(gè)兄弟節(jié)點(diǎn),沒(méi)有則返回nullGecko內(nèi)核的瀏覽器會(huì)在源代碼中標(biāo)簽內(nèi)部有空白符的地方插入一個(gè)文本結(jié)點(diǎn)到文檔中.因此,使用諸如Node.firstChild和Node.previousSibling之類的方法可能會(huì)引用到一個(gè)空白符文本節(jié)點(diǎn), 而不是使用者所預(yù)期得到的節(jié)點(diǎn)

          7.3.2 previousElementSibling

          previousElementSibling返回當(dāng)前元素在其父元素的子元素節(jié)點(diǎn)中的前一個(gè)元素節(jié)點(diǎn),如果該元素已經(jīng)是第一個(gè)元素節(jié)點(diǎn),則返回null,該屬性是只讀的。注意IE9以下瀏覽器不支持

          7.3.3 nextSibling

          Node.nextSibling是一個(gè)只讀屬性,返回其父節(jié)點(diǎn)的childNodes列表中緊跟在其后面的節(jié)點(diǎn),如果指定的節(jié)點(diǎn)為最后一個(gè)節(jié)點(diǎn),則返回nullGecko內(nèi)核的瀏覽器會(huì)在源代碼中標(biāo)簽內(nèi)部有空白符的地方插入一個(gè)文本結(jié)點(diǎn)到文檔中.因此,使用諸如Node.firstChild和Node.previousSibling之類的方法可能會(huì)引用到一個(gè)空白符文本節(jié)點(diǎn), 而不是使用者所預(yù)期得到的節(jié)點(diǎn)

          7.3.4 nextElementSibling

          nextElementSibling返回當(dāng)前元素在其父元素的子元素節(jié)點(diǎn)中的后一個(gè)元素節(jié)點(diǎn),如果該元素已經(jīng)是最后一個(gè)元素節(jié)點(diǎn),則返回null,該屬性是只讀的。注意IE9以下瀏覽器不支持

          8 元素屬性型API

          8.1 setAttribute

          設(shè)置指定元素上的一個(gè)屬性值。如果屬性已經(jīng)存在,則更新該值; 否則將添加一個(gè)新的屬性用指定的名稱和值

          語(yǔ)法

          復(fù)制代碼  element.setAttribute(name, value);
          

          其中name是特性名,value是特性值。如果元素不包含該特性,則會(huì)創(chuàng)建該特性并賦值。

          例子:

          復(fù)制代碼<body>
            <div id="div1">ABC</div>
          </body>
          <script>  
            let div1 = document.getElementById("div1"); 
            div1.setAttribute("align", "center");
          </script>
          

          運(yùn)行結(jié)果:


          如果元素本身包含指定的特性名為屬性,則可以世界訪問(wèn)屬性進(jìn)行賦值,比如下面兩條代碼是等價(jià)的:

          復(fù)制代碼  element.setAttribute("id","test");
            element.id = "test";
          

          8.2 getAttribute

          getAttribute()返回元素上一個(gè)指定的屬性值。如果指定的屬性不存在,則返回null或""(空字符串)

          語(yǔ)法

          復(fù)制代碼  let attribute = element.getAttribute(attributeName);  
          

          attribute是一個(gè)包含attributeName屬性值的字符串。attributeName是你想要獲取的屬性值的屬性名稱

          例子:

          復(fù)制代碼<body>
            <div id="div1">ABC</div>
          </body>
          <script>  
            let div1 = document.getElementById("div1");
            let align = div1.getAttribute("align");
            alert(align);
          </script>  
          

          運(yùn)行結(jié)果:


          8.3 removeAttribute

          removeAttribute()從指定的元素中刪除一個(gè)屬性

          語(yǔ)法

          復(fù)制代碼  element.removeAttribute(attrName)
          

          attrName是一個(gè)字符串,將要從元素中刪除的屬性名

          例子:

          復(fù)制代碼<body>
            <div id="div1" style="color:red" width="200px">ABC
             </div>
          </body>
          <script>  
            let div = document.getElementById("div1")
            div.removeAttribute("style");
          </script>
          

          在運(yùn)行之前div有個(gè)style="color:red"的屬性,在運(yùn)行之后這個(gè)屬性就被刪除了

          運(yùn)行結(jié)果:


          9 元素樣式型API

          9.1 window.getComputedStyle

          Window.getComputedStyle()方法給出應(yīng)用活動(dòng)樣式表后的元素的所有CSS屬性的值,并解析這些值可能包含的任何基本計(jì)算假設(shè)某個(gè)元素并未設(shè)置高度而是通過(guò)其內(nèi)容將其高度撐開,這時(shí)候要獲取它的高度就要用到getComputedStyle

          語(yǔ)法

          復(fù)制代碼  var style = window.getComputedStyle(element[, pseudoElt]);
          

          element是要獲取的元素,pseudoElt指定一個(gè)偽元素進(jìn)行匹配。返回的style是一個(gè)CSSStyleDeclaration對(duì)象。通過(guò)style可以訪問(wèn)到元素計(jì)算后的樣式

          9.2 getBoundingClientRect

          getBoundingClientRect用來(lái)返回元素的大小以及相對(duì)于瀏覽器可視窗口的位置

          語(yǔ)法

          復(fù)制代碼  var clientRect = element.getBoundingClientRect();
          

          clientRect是一個(gè)DOMRect對(duì)象,包含left,top,right,bottom,它是相對(duì)于可視窗口的距離,滾動(dòng)位置發(fā)生改變時(shí),它們的值是會(huì)發(fā)生變化的。除了IE9以下瀏覽器,還包含元素的height和width等數(shù)據(jù)

          9.3 直接修改元素的樣式

          例子:

          復(fù)制代碼  elem.style.color = 'red';
            elem.style.setProperty('font-size', '16px');
            elem.style.removeProperty('color');
          

          9.4 動(dòng)態(tài)添加樣式規(guī)則

          例子:

          復(fù)制代碼  var style = document.createElement('style');
            style.innerHTML = 'body{color:red} #top:hover{background-color: red;color: white;}';
            document.head.appendChild(style););
          

          10 總結(jié)

          JavaScript中的API太多了,將這些API記住并熟練使用對(duì)JavaScript的學(xué)習(xí)是有很大的幫助

          作者:yyzclyang

          鏈接:https://juejin.cn/post/6844903604445249543


          主站蜘蛛池模板: 久久国产高清一区二区三区| 国产精品无码一区二区三区不卡 | 国产精品一区二区av不卡| 真实国产乱子伦精品一区二区三区 | 免费视频精品一区二区| 黑人大战亚洲人精品一区| 日韩在线观看一区二区三区| 色婷婷一区二区三区四区成人网 | 色偷偷av一区二区三区| 国产精品女同一区二区久久| 日本韩国一区二区三区| 日韩免费一区二区三区在线| 天堂资源中文最新版在线一区| 亚洲日韩精品一区二区三区| 丝袜人妻一区二区三区网站 | 久久蜜桃精品一区二区三区| 99精品一区二区三区| 欧洲精品无码一区二区三区在线播放 | 天美传媒一区二区三区| 老熟女五十路乱子交尾中出一区| 免费观看一区二区三区| 久久青草国产精品一区| 水蜜桃av无码一区二区| 少妇特黄A一区二区三区| 日韩人妻一区二区三区免费| 91在线视频一区| 蜜臀Av午夜一区二区三区| 97久久精品一区二区三区| 亚洲一区精品中文字幕| 日本视频一区在线观看免费| 国产精品视频一区二区三区| 无码av中文一区二区三区桃花岛 | 国产精品毛片a∨一区二区三区| 日韩有码一区二区| 中文字幕在线观看一区二区三区| 中文字幕亚洲一区二区三区| 无码国产精品一区二区免费式芒果| 亚洲码一区二区三区| 亚洲中文字幕久久久一区| 精品国产一区二区麻豆| 日韩精品无码久久一区二区三|