RASA源碼解析與開(kāi)發(fā):一遵馆、nlu模塊

nlu模塊的主要功能是解析用戶輸入數(shù)據(jù),識(shí)別出用戶輸入的實(shí)體丰榴、意圖等關(guān)鍵信息货邓,同時(shí)也可以添加諸如情感分析等自定義模塊。

一四濒、輸入數(shù)據(jù)

nlu模塊接受Message類型的數(shù)據(jù)作為輸入换况,與core模塊流轉(zhuǎn)的Usermessage數(shù)據(jù)不同职辨,Message定義在rasa/nlu/training_data/message.py中,默認(rèn)有三個(gè)變量复隆,分別為text拨匆、time、data挽拂。其中惭每,text中存儲(chǔ)的是用戶輸入的問(wèn)題,time存儲(chǔ)的是時(shí)間亏栈,data存儲(chǔ)的是解析后的數(shù)據(jù)台腥。

Message中常用的方法是get和set方法,get是從data中取出想要的數(shù)據(jù)绒北,set是將解析后的intent等信息存入data中黎侈。

有的時(shí)候,我們可能要傳入更多的信息進(jìn)入nlu模塊闷游,例如峻汉,在我們的智能客服場(chǎng)景下,需要傳入scene_id(場(chǎng)景id)脐往,這個(gè)時(shí)候我們需要修改源碼休吠,以支持傳入scene__id,需要修改的py文件如下:

rasa/nlu/training_data/message.py

rasa/nlu/model.py

rasa/core/interpreter.py

rasa/core/processor.py

下面將一一解析每一塊代碼的作用以及需要修改的部分业簿。

1瘤礁、message.py

message.py中定義的class Message是nlu模塊傳入的數(shù)據(jù),它有set梅尤、get等方法柜思,為了傳入其他數(shù)據(jù)進(jìn)入nlu模塊,我們需要在初始化部分初始化要傳輸?shù)臄?shù)據(jù)巷燥。

2赡盘、model.py

model.py中定義了Interpreter類,主要用于解析數(shù)據(jù)缰揪,該類在rasa/core/interpreter.py中的RasaNLUInterpreter調(diào)用陨享,用于解析text,修改其中的初始parse_data邀跃,用于傳輸額外數(shù)據(jù)給nlu模塊霉咨。

3、interpreter.py

interpreter.py中定義了數(shù)據(jù)的解析方法拍屑,在測(cè)試時(shí)途戒,Rasa采用的是RasaNLUInterpreter。所以要修改class RasaNLUInterpreter中的parse 函數(shù)僵驰。

下面以RasaNLUInterpreter源碼為例喷斋,詳解interpreter模塊如何調(diào)用nlu模型并輸入到processor模塊中唁毒。

class RasaNLUInterpreter(NaturalLanguageInterpreter):

? ? def __init__(

? ? ? ? self,

? ? ? ? model_directory: Text,

? ? ? ? config_file: Optional[Text] = None,

? ? ? ? lazy_init: bool = False,

? ? ):

? ? ? ? self.model_directory = model_directory

? ? ? ? self.lazy_init = lazy_init

? ? ? ? self.config_file = config_file

? ? ? ? if not lazy_init:

? ? ? ? ? ? self._load_interpreter()

? ? ? ? else:

? ? ? ? ? ? self.interpreter = None

? ? async def parse(

? ? ? ? self,

? ? ? ? text: Text,

? ? ? ? message_id: Optional[Text] = None,

? ? ? ? tracker: DialogueStateTracker = None,

? ? ) -> Dict[Text, Any]:

? ? ? ? """Parse a text message.

? ? ? ? Return a default value if the parsing of the text failed."""

? ? ? ? if self.lazy_init and self.interpreter is None:

? ? ? ? ? ? self._load_interpreter()

? ? ? ? result = self.interpreter.parse(text)

? ? ? ? return result

? ? def _load_interpreter(self) -> None:

? ? ? ? from rasa.nlu.model import Interpreter

? ? ? ? self.interpreter = Interpreter.load(self.model_directory)

在初始化的時(shí)候,通過(guò)self._load_interpreter()來(lái)引入rasa.nlu.model中的Interpreter模塊星爪,Interpreter有一個(gè)load方法浆西,load傳入已經(jīng)訓(xùn)練好的模型文件路徑,加載出nlu模型供interpreter調(diào)用顽腾。

如果想新建自己的interpreter類近零,需要注意兩點(diǎn):

如何傳入已經(jīng)訓(xùn)練好的nlu模型文件路徑和在rasa中引入自己的interpreter模塊,這部分可以在endpoints.yml中引入抄肖,詳見(jiàn)

額外數(shù)據(jù)的引入久信,由于parse方法中傳入了tracker,歷史信息都可以在tracker中獲取漓摩。

4裙士、processor.py

processor.py中將信息傳入interpreter.py中,這部分在_parse_message中管毙。

在processor中腿椎,def?parse_message調(diào)用interpreter方法,然后在外層封裝為def _handle_message_with_tracker如下所示:

async def _handle_message_with_tracker(

? ? ? ? self, message: UserMessage, tracker: DialogueStateTracker

? ? ) -> None:

? ? ? ? if message.parse_data:

? ? ? ? ? ? parse_data = message.parse_data

? ? ? ? else:

? ? ? ? ? ? parse_data = await self._parse_message(message, tracker)

? ? ? ? # don't ever directly mutate the tracker

? ? ? ? # - instead pass its events to log

? ? ? ? tracker.update(

? ? ? ? ? ? UserUttered(

? ? ? ? ? ? ? ? message.text,

? ? ? ? ? ? ? ? parse_data["intent"],

? ? ? ? ? ? ? ? parse_data["entities"],

? ? ? ? ? ? ? ? parse_data,

? ? ? ? ? ? ? ? input_channel=message.input_channel,

? ? ? ? ? ? ? ? message_id=message.message_id,

? ? ? ? ? ? ? ? metadata=message.metadata,

? ? ? ? ? ? ),

? ? ? ? ? ? self.domain,

? ? ? ? )

? ? ? ? if parse_data["entities"]:

? ? ? ? ? ? self._log_slots(tracker)

? ? ? ? logger.debug(

? ? ? ? ? ? f"Logged UserUtterance - tracker now has {len(tracker.events)} events."

? ? ? ? )

從def _handle_message_with_tracker可以看到夭咬,首先檢查message.parse_data是否為空(channel.py里面給parse_data傳值)啃炸,如果為空,調(diào)用interpreter去識(shí)別意圖等皱埠,最后將結(jié)果寫(xiě)入parse_data中肮帐。

二咖驮、自定義nlu組件

rasa的nlu模塊除了自身提供的實(shí)體抽取和意圖識(shí)別模型外边器,還可以添加自定義模型,如自定義意圖識(shí)別模塊或者情感分析等模塊托修。下面將詳細(xì)介紹自定義模塊的添加忘巧。

1、數(shù)據(jù)結(jié)構(gòu)

傳入rasa_nlu的數(shù)據(jù)存儲(chǔ)格式為Message對(duì)象睦刃,Message對(duì)象定義在rasa/nlu/training_data/message.py中砚嘴。其中message.text中就是輸入的文本。

2涩拙、處理流程

下圖是文本傳入rasa_nlu后的標(biāo)準(zhǔn)流程际长。

2.1 tokenizer

rasa提供了5種分詞(Tokenizers)如下:

TokenizerRequiresDescription

WhitespaceTokenizer/為每個(gè)以空格分隔的字符序列創(chuàng)建token。

MitieTokenizer需要先配置MitieNLP使用Mitie進(jìn)行分詞兴泥,用MITIE tokenizer創(chuàng)建tokens工育,從而服務(wù)于 MITIEFeaturizer。

SpacyTokenizer需要先配置SpacyNLP使用Spacy進(jìn)行分詞搓彻,用Spacytokenizer創(chuàng)建tokens如绸,從而服務(wù)于SpacyFeaturizer嘱朽。

ConveRTTokenizer/使用ConveRt進(jìn)行分詞,用ConveRT Tokenizer創(chuàng)建tokens怔接,從而服務(wù)于ConveRTFeaturizer搪泳。

JiebaTokenizer/使用Jieba作為 Tokenizer,對(duì)中文進(jìn)行分詞扼脐,用戶的自定義字典文件可以通過(guò)特定的文件目錄路徑 dictionary_path自動(dòng)加載岸军。

分詞后的結(jié)果會(huì)回傳進(jìn)Message對(duì)象中,key值為"tokens"瓦侮,詳見(jiàn)rasa/nlu/tokenizers/tokenizer.py的process方法凛膏。

2.2 featurizer

rasa提供了2類featurizer方法— dense_featurizer和sparse_featurizer。

dense_featurizer:

FeaturizerRequiresDescription

MitieFeaturizer需要先配置MitieNLP使用MITIE featurizer為意圖分類創(chuàng)建特征脏榆。

SpacyFeaturizer需要先配置SpacyNLP使用spacy featurizer為意圖分類創(chuàng)建特征猖毫。

ConveRTFeaturizer需要配置ConveRTTokenizer使用ConveRT模型創(chuàng)建用戶消息和響應(yīng),由于ConveRT模型僅在英語(yǔ)語(yǔ)料上訓(xùn)練须喂,因此只有當(dāng)訓(xùn)練數(shù)據(jù)是英語(yǔ)語(yǔ)言時(shí)才能使用這個(gè)featurizer吁断。

sparse_featurizer:

FeaturizerRequiresDescription

RegexFeaturizer/為實(shí)體提取和意圖分類創(chuàng)建特征。在訓(xùn)練期間坞生,regex intent featurizer 以訓(xùn)練數(shù)據(jù)的格式創(chuàng)建一系列正則表達(dá)式列表仔役。對(duì)于每個(gè)正則,都將設(shè)置一個(gè)特征是己,標(biāo)記是否在輸入中找到該表達(dá)式又兵,然后將其輸入到intent classifier / entity extractor 中以簡(jiǎn)化分類(假設(shè)分類器在訓(xùn)練階段已經(jīng)學(xué)習(xí)了該特征集合,該特征集合表示一定的意圖)

CountVectorsFeaturizer/創(chuàng)建用戶信息和標(biāo)簽(意圖和響應(yīng))的詞袋表征卒废,用作意圖分類器的輸入沛厨,輸入的意圖特征以詞袋表征

特征化后的結(jié)果會(huì)回傳進(jìn)Message對(duì)象中,key值為"text_dense_features"/"text_sparse_features"摔认。

2.3 intent classifier

ClassifierRequiresDescription

MitieIntentClassifiertokenizer 和 featurizer該分類器使用MITIE進(jìn)行意圖分類逆皮。底層分類器使用的是具有稀疏線性核的多類線性支持向量機(jī)

SklearnIntentClassifierfeaturizer該sklearn意圖分類器訓(xùn)練一個(gè)線性支持向量機(jī),該支持向量機(jī)通過(guò)網(wǎng)格搜索得到優(yōu)化参袱。除了其他分類器电谣,它還提供沒(méi)有“獲勝”的標(biāo)簽的排名。spacy意圖分類器需要在管道中的先加入一個(gè)featurizer抹蚀。該featurizer創(chuàng)建用于分類的特征剿牺。

EmbeddingIntentClassifierfeaturizer嵌入式意圖分類器將用戶輸入和意圖標(biāo)簽嵌入到同一空間中。Supervised embeddings通過(guò)最大化它們之間的相似性來(lái)訓(xùn)練环壤。該算法基于StarSpace的晒来。但是,在這個(gè)實(shí)現(xiàn)中镐捧,損失函數(shù)略有不同潜索,添加了額外的隱藏層和dropout臭增。該算法還提供了未“獲勝”標(biāo)簽的相似度排序。在embedding intent classifier之前竹习,需要在管道中加入一個(gè)featurizer誊抛。該featurizer創(chuàng)建用以embeddings的特征。建議使用CountVectorsFeaturizer整陌,它可選的預(yù)處理有SpacyNLP和SpacyTokenizer拗窃。

KeywordIntentClassifier/該分類器通過(guò)搜索關(guān)鍵字的消息來(lái)工作。默認(rèn)情況下泌辫,匹配是大小寫(xiě)敏感的随夸,只精確匹配地搜索用戶消息中關(guān)鍵字。

classifier會(huì)從Message對(duì)象中獲取tokenizer 和 featurizer作為分類模型的輸入進(jìn)行意圖識(shí)別震放。

3宾毒、自定義方法

由2中的處理流程可以看出,rasa提供的意圖識(shí)別重點(diǎn)是featurizer殿遂,rasa本身提供了5種featurizer方法诈铛,除了ConveRT模型之外均可用與中文場(chǎng)景,其中MitieFeaturizer和SpacyFeaturizer需要特定的Tokenizer和對(duì)應(yīng)的詞向量墨礁。

我們也可以自定義自己的中文模型幢竹,先通過(guò)jieba分詞(可以引入用戶字典),然后用自己訓(xùn)練的詞向量模型來(lái)featurize用戶輸入的text恩静,最后再引入自定義的分類模型識(shí)別意圖焕毫。

以KeywordIntentClassifier意圖識(shí)別模型為例,解釋如何自定義意圖識(shí)別模型:

class KeywordIntentClassifier(Component):

? ? """Intent classifier using simple keyword matching.

? ? The classifier takes a list of keywords and associated intents as an input.

? ? A input sentence is checked for the keywords and the intent is returned.

? ? """

? ? provides = [INTENT_ATTRIBUTE]

? ? defaults = {"case_sensitive": True}

? ? def __init__(

? ? ? ? self,

? ? ? ? component_config: Optional[Dict[Text, Any]] = None,

? ? ? ? intent_keyword_map: Optional[Dict] = None,

? ? ):

? ? ? ? super(KeywordIntentClassifier, self).__init__(component_config)

? ? ? ? self.case_sensitive = self.component_config.get("case_sensitive")

? ? ? ? self.intent_keyword_map = intent_keyword_map or {}

? ? def train(

? ? ? ? self,

? ? ? ? training_data: "TrainingData",

? ? ? ? cfg: Optional["RasaNLUModelConfig"] = None,

? ? ? ? **kwargs: Any,

? ? ) -> None:

? ? ? ? duplicate_examples = set()

? ? ? ? for ex in training_data.training_examples:

? ? ? ? ? ? if (

? ? ? ? ? ? ? ? ex.text in self.intent_keyword_map.keys()

? ? ? ? ? ? ? ? and ex.get(INTENT_ATTRIBUTE) != self.intent_keyword_map[ex.text]

? ? ? ? ? ? ):

? ? ? ? ? ? ? ? duplicate_examples.add(ex.text)

? ? ? ? ? ? ? ? raise_warning(

? ? ? ? ? ? ? ? ? ? f"Keyword '{ex.text}' is a keyword to trigger intent "

? ? ? ? ? ? ? ? ? ? f"'{self.intent_keyword_map[ex.text]}' and also "

? ? ? ? ? ? ? ? ? ? f"intent '{ex.get(INTENT_ATTRIBUTE)}', it will be removed "

? ? ? ? ? ? ? ? ? ? f"from the list of keywords for both of them. "

? ? ? ? ? ? ? ? ? ? f"Remove (one of) the duplicates from the training data.",

? ? ? ? ? ? ? ? ? ? docs=DOCS_URL_COMPONENTS + "#keyword-intent-classifier",

? ? ? ? ? ? ? ? )

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? self.intent_keyword_map[ex.text] = ex.get(INTENT_ATTRIBUTE)

? ? ? ? for keyword in duplicate_examples:

? ? ? ? ? ? self.intent_keyword_map.pop(keyword)

? ? ? ? ? ? logger.debug(

? ? ? ? ? ? ? ? f"Removed '{keyword}' from the list of keywords because it was "

? ? ? ? ? ? ? ? "a keyword for more than one intent."

? ? ? ? ? ? )

? ? ? ? self._validate_keyword_map()

? ? def _validate_keyword_map(self) -> None:

? ? ? ? re_flag = 0 if self.case_sensitive else re.IGNORECASE

? ? ? ? ambiguous_mappings = []

? ? ? ? for keyword1, intent1 in self.intent_keyword_map.items():

? ? ? ? ? ? for keyword2, intent2 in self.intent_keyword_map.items():

? ? ? ? ? ? ? ? if (

? ? ? ? ? ? ? ? ? ? re.search(r"\b" + keyword1 + r"\b", keyword2, flags=re_flag)

? ? ? ? ? ? ? ? ? ? and intent1 != intent2

? ? ? ? ? ? ? ? ):

? ? ? ? ? ? ? ? ? ? ambiguous_mappings.append((intent1, keyword1))

? ? ? ? ? ? ? ? ? ? raise_warning(

? ? ? ? ? ? ? ? ? ? ? ? f"Keyword '{keyword1}' is a keyword of intent '{intent1}', "

? ? ? ? ? ? ? ? ? ? ? ? f"but also a substring of '{keyword2}', which is a "

? ? ? ? ? ? ? ? ? ? ? ? f"keyword of intent '{intent2}."

? ? ? ? ? ? ? ? ? ? ? ? f" '{keyword1}' will be removed from the list of keywords.\n"

? ? ? ? ? ? ? ? ? ? ? ? f"Remove (one of) the conflicting keywords from the"

? ? ? ? ? ? ? ? ? ? ? ? f" training data.",

? ? ? ? ? ? ? ? ? ? ? ? docs=DOCS_URL_COMPONENTS + "#keyword-intent-classifier",

? ? ? ? ? ? ? ? ? ? )

? ? ? ? for intent, keyword in ambiguous_mappings:

? ? ? ? ? ? self.intent_keyword_map.pop(keyword)

? ? ? ? ? ? logger.debug(

? ? ? ? ? ? ? ? f"Removed keyword '{keyword}' from intent '{intent}' because it matched a "

? ? ? ? ? ? ? ? "keyword of another intent."

? ? ? ? ? ? )

? ? def process(self, message: Message, **kwargs: Any) -> None:

? ? ? ? intent_name = self._map_keyword_to_intent(message.text)

? ? ? ? confidence = 0.0 if intent_name is None else 1.0

? ? ? ? intent = {"name": intent_name, "confidence": confidence}

? ? ? ? if message.get(INTENT_ATTRIBUTE) is None or intent is not None:

? ? ? ? ? ? message.set(INTENT_ATTRIBUTE, intent, add_to_output=True)

? ? def _map_keyword_to_intent(self, text: Text) -> Optional[Text]:

? ? ? ? re_flag = 0 if self.case_sensitive else re.IGNORECASE

? ? ? ? for keyword, intent in self.intent_keyword_map.items():

? ? ? ? ? ? if re.search(r"\b" + keyword + r"\b", text, flags=re_flag):

? ? ? ? ? ? ? ? logger.debug(

? ? ? ? ? ? ? ? ? ? f"KeywordClassifier matched keyword '{keyword}' to"

? ? ? ? ? ? ? ? ? ? f" intent '{intent}'."

? ? ? ? ? ? ? ? )

? ? ? ? ? ? ? ? return intent

? ? ? ? logger.debug("KeywordClassifier did not find any keywords in the message.")

? ? ? ? return None

? ? def persist(self, file_name: Text, model_dir: Text) -> Dict[Text, Any]:

? ? ? ? """Persist this model into the passed directory.

? ? ? ? Return the metadata necessary to load the model again.

? ? ? ? """

? ? ? ? file_name = file_name + ".json"

? ? ? ? keyword_file = os.path.join(model_dir, file_name)

? ? ? ? utils.write_json_to_file(keyword_file, self.intent_keyword_map)

? ? ? ? return {"file": file_name}

? ? @classmethod

? ? def load(

? ? ? ? cls,

? ? ? ? meta: Dict[Text, Any],

? ? ? ? model_dir: Optional[Text] = None,

? ? ? ? model_metadata: "Metadata" = None,

? ? ? ? cached_component: Optional["KeywordIntentClassifier"] = None,

? ? ? ? **kwargs: Any,

? ? ) -> "KeywordIntentClassifier":

? ? ? ? if model_dir and meta.get("file"):

? ? ? ? ? ? file_name = meta.get("file")

? ? ? ? ? ? keyword_file = os.path.join(model_dir, file_name)

? ? ? ? ? ? if os.path.exists(keyword_file):

? ? ? ? ? ? ? ? intent_keyword_map = utils.read_json_file(keyword_file)

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? raise_warning(

? ? ? ? ? ? ? ? ? ? f"Failed to load key word file for `IntentKeywordClassifier`, "

? ? ? ? ? ? ? ? ? ? f"maybe {keyword_file} does not exist?",

? ? ? ? ? ? ? ? )

? ? ? ? ? ? ? ? intent_keyword_map = None

? ? ? ? ? ? return cls(meta, intent_keyword_map)

? ? ? ? else:

? ? ? ? ? ? raise Exception(

? ? ? ? ? ? ? ? f"Failed to load keyword intent classifier model. "

? ? ? ? ? ? ? ? f"Path {os.path.abspath(meta.get('file'))} doesn't exist."

? ? ? ? ? ? )

從上面的代碼可以看到驶乾,整個(gè)KeywordIntentClassifier由4部分組成邑飒,train、process轻掩、persist和load幸乒。其中懦底,train是模型訓(xùn)練部分唇牧,process是模型推理部分,persist是模型持久化部分聚唐,load是加載訓(xùn)練好的模型丐重。一開(kāi)始的provide定義的是返回值提供什么字段,default是模型的一些自定義參數(shù)杆查,比如模型的epochs和batch_size等扮惦,這里定義的是這些參數(shù)的默認(rèn)值,在config.yml中自定義的pipeline可以傳入其他值亲桦,如下所示:


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末崖蜜,一起剝皮案震驚了整個(gè)濱河市浊仆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌豫领,老刑警劉巖抡柿,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異等恐,居然都是意外死亡洲劣,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)课蔬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)囱稽,“玉大人,你說(shuō)我怎么就攤上這事二跋≌骄” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵扎即,是天一觀的道長(zhǎng)样傍。 經(jīng)常有香客問(wèn)我,道長(zhǎng)铺遂,這世上最難降的妖魔是什么衫哥? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮襟锐,結(jié)果婚禮上撤逢,老公的妹妹穿的比我還像新娘。我一直安慰自己粮坞,他們只是感情好蚊荣,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著莫杈,像睡著了一般互例。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上筝闹,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天媳叨,我揣著相機(jī)與錄音,去河邊找鬼关顷。 笑死糊秆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的议双。 我是一名探鬼主播痘番,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了汞舱?” 一聲冷哼從身側(cè)響起伍纫,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎昂芜,沒(méi)想到半個(gè)月后翻斟,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡说铃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年访惜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腻扇。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡债热,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出幼苛,到底是詐尸還是另有隱情窒篱,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布舶沿,位于F島的核電站墙杯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏括荡。R本人自食惡果不足惜高镐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望畸冲。 院中可真熱鬧嫉髓,春花似錦、人聲如沸邑闲。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)苫耸。三九已至州邢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間褪子,已是汗流浹背量淌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留褐筛,地道東北人类少。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像渔扎,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子信轿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容