fengdexin 2 долоо хоног өмнө
commit
b934567eeb

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+/actions/api/111.py

+ 0 - 0
actions/__init__.py


+ 743 - 0
actions/actions.py

@@ -0,0 +1,743 @@
+# This files contains your custom actions which can be used to run
+# custom Python code.
+#
+# See this guide on how to implement these action:
+# https://rasa.com/docs/rasa/custom-actions
+
+
+# This is a simple example for a custom action which utters "Hello World!"
+
+# from typing import Any, Text, Dict, List
+#
+# from rasa_sdk import Action, Tracker
+# from rasa_sdk.executor import CollectingDispatcher
+
+
+import logging
+import json
+from datetime import datetime
+from typing import Any, Dict, List, Text, Optional
+import requests
+from neo4j import GraphDatabase
+from rasa_sdk import Action, Tracker
+from rasa_sdk.types import DomainDict
+from rasa_sdk.forms import FormValidationAction
+from rasa_sdk.executor import CollectingDispatcher
+from datetime import datetime, date, timedelta
+import jieba
+import jellyfish
+from .api import question_parser
+from .api import answer_search
+from .api import app
+import subprocess
+import datetime
+
+from rasa_sdk.events import (
+    SlotSet,
+    UserUtteranceReverted,
+    ConversationPaused,
+    EventType,
+)
+
+USER_INTENT_OUT_OF_SCOPE = "out_of_scope"
+
+logger = logging.getLogger(__name__)
+
+class FindTheCorrespondingFault_des(Action):
+    def name(self) -> Text:
+        return "FindTheCorrespondingFault_des"
+
+    def run(
+            self,
+            dispatcher: CollectingDispatcher,
+            tracker: Tracker,
+            domain: Dict[Text, Any]
+    ) -> List[Dict[Text, Any]]:
+        user_message = tracker.latest_message.get('text')
+        print("text", user_message)
+
+        # 获取故障实体
+        fault_des = tracker.get_slot('fault_des')
+        # 获取意图
+        intentions = tracker.get_intent_of_latest_message()
+        print(f"fault_des: {fault_des}")
+        print(f"Intentions: {intentions}")
+
+
+        # 连接到 Neo4j 数据库
+        try:
+            driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "fdx3081475970"))
+        except Exception as e:
+            dispatcher.utter_message(text=f"无法连接到 Neo4j 数据库: {e}")
+            return []
+
+        # 定义查询
+        query = """
+        MATCH (n: 故障描述)
+        RETURN n.name AS name
+        """
+
+        # 执行查询
+        names = []
+        try:
+            with driver.session() as session:
+                result = session.run(query)
+                names = [record["name"] for record in result]
+        except Exception as e:
+            dispatcher.utter_message(text=f"查询 Neo4j 数据库时出错: {e}")
+            driver.close()
+            return []
+
+        # 设置相似度阈值
+        threshold = 0.5
+
+        # 存储相似度和名称
+        similarity_scores = [(name, jellyfish.jaro_winkler_similarity(name, user_message)) for name in names]
+
+        # 过滤出相似度大于阈值的名称
+        filtered_similarities = [(name, score) for name, score in similarity_scores if score > threshold]
+
+        # 找出相似度最高的名称
+        highest_similarity_name = None
+        if filtered_similarities:
+            highest_similarity_name, highest_similarity_score = max(filtered_similarities, key=lambda x: x[1])
+            print(f"最高相似度的名称: {highest_similarity_name}")
+
+        # 如果找到了相似度高的名称,则用它替换原始的 fault_name
+        if highest_similarity_name is not None:
+            fault_des = highest_similarity_name
+
+        # 关闭 Neo4j 驱动连接
+        driver.close()
+
+        # 准备数据结构
+        if type(fault_des) != list:
+            data = {'args': {fault_des: ['fault_des']}, 'question_types': [intentions]}
+        else:
+            data = {'args': {fault_des[0]: ['fault_des']}, 'question_types': [intentions]}
+
+        parser = question_parser.QuestionParser()
+        searcher = answer_search.AnswerSearcher()
+        print(f"Data: {data}")
+        sqls = parser.parser_main(data)  # 生成相关的查询语句
+        print(f"SQLs: {sqls}")
+        final_answer = searcher.search_main(sqls)  # 查询相关内容
+        entity_lists = searcher.get_entity(sqls)
+        entity_str = json.dumps(entity_lists)
+        print(f"Entity Lists: {entity_str}")
+        print(f"Final Answer: {final_answer}")
+
+        # 进行判断 输出结果
+        if not final_answer:
+            dispatcher.utter_message(template='utter_out')
+        else:
+            if highest_similarity_name is not None and highest_similarity_score is not None:
+                match_info = (
+                    f"当前实体的匹配数值为:{highest_similarity_score:.2f}\n"
+                    f"根据当前的实体匹配结果的输出为:\n"
+                )
+                answer = match_info + '\n'.join(final_answer)
+            else:
+                answer = '\n'.join(final_answer)
+
+            # 发送最终答案和实体信息
+            dispatcher.utter_message(text=answer)
+            dispatcher.utter_message(text=f'{entity_lists}')
+
+        return [SlotSet('fault_des', entity_str)]
+
+class FindTheCorrespondingFault_hmc(Action):
+    def name(self) -> Text:
+        return "FindTheCorrespondingFault_hmc"
+
+    def run(
+            self,
+            dispatcher: CollectingDispatcher,
+            tracker: Tracker,
+            domain: Dict[Text, Any]
+    ) -> List[Dict[Text, Any]]:
+        user_message = tracker.latest_message.get('text')
+        print("text", user_message)
+
+        # 获取故障实体
+        fault_hmc = tracker.get_slot('fault_hmc')
+        # 获取意图
+        intentions = tracker.get_intent_of_latest_message()
+        print(f"Fault HMC: {fault_hmc}")
+        print(f"Intentions: {intentions}")
+
+
+        # 连接到 Neo4j 数据库
+        try:
+            driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "fdx3081475970"))
+        except Exception as e:
+            dispatcher.utter_message(text=f"无法连接到 Neo4j 数据库: {e}")
+            return []
+
+        # 定义查询
+        query = """
+        MATCH (n: HMC)
+        RETURN n.name AS name
+        """
+
+        # 执行查询
+        names = []
+        try:
+            with driver.session() as session:
+                result = session.run(query)
+                names = [str(record["name"]) for record in result]
+        except Exception as e:
+            dispatcher.utter_message(text=f"查询 Neo4j 数据库时出错: {e}")
+            driver.close()
+            return []
+
+        # 设置相似度阈值
+        threshold = 0.5
+
+        # 存储相似度和名称
+        similarity_scores = [(name, jellyfish.jaro_winkler_similarity(str(name), user_message)) for name in names]
+
+        # 过滤出相似度大于阈值的名称
+        filtered_similarities = [(name, score) for name, score in similarity_scores if score > threshold]
+
+        # 找出相似度最高的名称
+        highest_similarity_name = None
+        if filtered_similarities:
+            highest_similarity_name, highest_similarity_score = max(filtered_similarities, key=lambda x: x[1])
+            print(f"最高相似度的名称: {highest_similarity_name}")
+
+        # 如果找到了相似度高的名称,则用它替换原始的 fault_name
+        if highest_similarity_name is not None:
+            fault_hmc = highest_similarity_name
+
+        # 关闭 Neo4j 驱动连接
+        driver.close()
+
+        # 准备数据结构
+        if type(fault_hmc) != list:
+            data = {'args': {fault_hmc: ['fault_hmc']}, 'question_types': [intentions]}
+        else:
+            data = {'args': {fault_hmc[0]: ['fault_hmc']}, 'question_types': [intentions]}
+
+        parser = question_parser.QuestionParser()
+        searcher = answer_search.AnswerSearcher()
+        print(f"Data: {data}")
+        sqls = parser.parser_main(data)  # 生成相关的查询语句
+        print(f"SQLs: {sqls}")
+        final_answer = searcher.search_main(sqls)  # 查询相关内容
+        entity_lists = searcher.get_entity(sqls)
+        entity_str = json.dumps(entity_lists)
+        print(f"Entity Lists: {entity_str}")
+        print(f"Final Answer: {final_answer}")
+
+        # 进行判断 输出结果
+        if not final_answer:
+            dispatcher.utter_message(template='utter_out')
+        else:
+            if highest_similarity_name is not None and highest_similarity_score is not None:
+                match_info = (
+                    f"当前实体的匹配数值为:{highest_similarity_score:.2f}\n"
+                    f"根据当前的实体匹配结果的输出为:\n"
+                )
+                answer = match_info + '\n'.join(final_answer)
+            else:
+                answer = '\n'.join(final_answer)
+
+            # 发送最终答案和实体信息
+            dispatcher.utter_message(text=answer)
+            dispatcher.utter_message(text=f'{entity_lists}')
+
+        return [SlotSet('fault_hmc', entity_str)]
+
+class FindTheCorrespondingFault_obj(Action):
+    def name(self) -> Text:
+        return "FindTheCorrespondingFault_obj"
+
+    def run(
+            self,
+            dispatcher: CollectingDispatcher,
+            tracker: Tracker,
+            domain: Dict[Text, Any]
+    ) -> List[Dict[Text, Any]]:
+        user_message = tracker.latest_message.get('text')
+        print("text", user_message)
+
+        # 获取故障实体
+        fault_obj = tracker.get_slot('fault_obj')
+        # 获取意图
+        intentions = tracker.get_intent_of_latest_message()
+        print(f"fault_obj: {fault_obj}")
+        print(f"Intentions: {intentions}")
+
+
+        # 连接到 Neo4j 数据库
+        try:
+            driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "fdx3081475970"))
+        except Exception as e:
+            dispatcher.utter_message(text=f"无法连接到 Neo4j 数据库: {e}")
+            return []
+
+        # 定义查询
+        query = """
+        MATCH (n: 成品)
+        RETURN n.name AS name
+        """
+
+        # 执行查询
+        names = []
+        try:
+            with driver.session() as session:
+                result = session.run(query)
+                names = [record["name"] for record in result]
+        except Exception as e:
+            dispatcher.utter_message(text=f"查询 Neo4j 数据库时出错: {e}")
+            driver.close()
+            return []
+
+        # 设置相似度阈值
+        threshold = 0.5
+
+        # 存储相似度和名称
+        similarity_scores = [(name, jellyfish.jaro_winkler_similarity(name, user_message)) for name in names]
+
+        # 过滤出相似度大于阈值的名称
+        filtered_similarities = [(name, score) for name, score in similarity_scores if score > threshold]
+
+        # 找出相似度最高的名称
+        highest_similarity_name = None
+        if filtered_similarities:
+            highest_similarity_name, highest_similarity_score = max(filtered_similarities, key=lambda x: x[1])
+            print(f"最高相似度的名称: {highest_similarity_name}")
+
+        # 如果找到了相似度高的名称,则用它替换原始的 fault_name
+        if highest_similarity_name is not None:
+            fault_obj = highest_similarity_name
+
+        # 关闭 Neo4j 驱动连接
+        driver.close()
+
+        # 准备数据结构
+        if type(fault_obj) != list:
+            data = {'args': {fault_obj: ['fault_obj']}, 'question_types': [intentions]}
+        else:
+            data = {'args': {fault_obj[0]: ['fault_obj']}, 'question_types': [intentions]}
+
+        parser = question_parser.QuestionParser()
+        searcher = answer_search.AnswerSearcher()
+        print(f"Data: {data}")
+        sqls = parser.parser_main(data)  # 生成相关的查询语句
+        print(f"SQLs: {sqls}")
+        final_answer = searcher.search_main(sqls)  # 查询相关内容
+        entity_lists = searcher.get_entity(sqls)
+        entity_str = json.dumps(entity_lists)
+        print(f"Entity Lists: {entity_str}")
+        print(f"Final Answer: {final_answer}")
+
+        # 进行判断 输出结果
+        if not final_answer:
+            dispatcher.utter_message(template='utter_out')
+        else:
+            if highest_similarity_name is not None and highest_similarity_score is not None:
+                match_info = (
+                    f"当前实体的匹配数值为:{highest_similarity_score:.2f}\n"
+                    f"根据当前的实体匹配结果的输出为:\n"
+                )
+                answer = match_info + '\n'.join(final_answer)
+            else:
+                answer = '\n'.join(final_answer)
+
+            # 发送最终答案和实体信息
+            dispatcher.utter_message(text=answer)
+            dispatcher.utter_message(text=f'{entity_lists}')
+
+        return [SlotSet('fault_obj', entity_str)]
+
+class FindTheCorrespondingFault_x_obj(Action):
+    def name(self) -> Text:
+        return "FindTheCorrespondingFault_x_obj"
+
+    def run(
+            self,
+            dispatcher: CollectingDispatcher,
+            tracker: Tracker,
+            domain: Dict[Text, Any]
+    ) -> List[Dict[Text, Any]]:
+        user_message = tracker.latest_message.get('text')
+        print("text", user_message)
+
+        # 获取故障实体
+        fault_x_obj = tracker.get_slot('fault_x_obj')
+        # 获取意图
+        intentions = tracker.get_intent_of_latest_message()
+        print(f"Fault x obj: {fault_x_obj}")
+        print(f"Intentions: {intentions}")
+
+
+        # 连接到 Neo4j 数据库
+        try:
+            driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "fdx3081475970"))
+        except Exception as e:
+            dispatcher.utter_message(text=f"无法连接到 Neo4j 数据库: {e}")
+            return []
+
+        # 定义查询
+        query = """
+        MATCH (n: 型号)
+        RETURN n.name AS name
+        """
+
+        # 执行查询
+        names = []
+        try:
+            with driver.session() as session:
+                result = session.run(query)
+                names = [record["name"] for record in result]
+        except Exception as e:
+            dispatcher.utter_message(text=f"查询 Neo4j 数据库时出错: {e}")
+            driver.close()
+            return []
+
+        # 设置相似度阈值
+        threshold = 0.5
+
+        # 存储相似度和名称
+        similarity_scores = [(name, jellyfish.jaro_winkler_similarity(name, user_message)) for name in names]
+
+        # 过滤出相似度大于阈值的名称
+        filtered_similarities = [(name, score) for name, score in similarity_scores if score > threshold]
+
+        # 找出相似度最高的名称
+        highest_similarity_name = None
+        if filtered_similarities:
+            highest_similarity_name, highest_similarity_score = max(filtered_similarities, key=lambda x: x[1])
+            print(f"最高相似度的名称: {highest_similarity_name}")
+
+        # 如果找到了相似度高的名称,则用它替换原始的 fault_name
+        if highest_similarity_name is not None:
+            fault_x_obj = highest_similarity_name
+
+        # 关闭 Neo4j 驱动连接
+        driver.close()
+
+        # 准备数据结构
+        if type(fault_x_obj) != list:
+            data = {'args': {fault_x_obj: ['fault_x_obj']}, 'question_types': [intentions]}
+        else:
+            data = {'args': {fault_x_obj[0]: ['fault_x_obj']}, 'question_types': [intentions]}
+
+        parser = question_parser.QuestionParser()
+        searcher = answer_search.AnswerSearcher()
+        print(f"Data: {data}")
+        sqls = parser.parser_main(data)  # 生成相关的查询语句
+        print(f"SQLs: {sqls}")
+        final_answer = searcher.search_main(sqls)  # 查询相关内容
+        entity_lists = searcher.get_entity(sqls)
+        entity_str = json.dumps(entity_lists)
+        print(f"Entity Lists: {entity_str}")
+        print(f"Final Answer: {final_answer}")
+
+        # 进行判断 输出结果
+        if not final_answer:
+            dispatcher.utter_message(template='utter_out')
+        else:
+            if highest_similarity_name is not None and highest_similarity_score is not None:
+                match_info = (
+                    f"当前实体的匹配数值为:{highest_similarity_score:.2f}\n"
+                    f"根据当前的实体匹配结果的输出为:\n"
+                )
+                answer = match_info + '\n'.join(final_answer)
+            else:
+                answer = '\n'.join(final_answer)
+
+            # 发送最终答案和实体信息
+            dispatcher.utter_message(text=answer)
+            dispatcher.utter_message(text=f'{entity_lists}')
+
+        return [SlotSet('fault_x_obj', entity_str)]
+
+class FindTheCorrespondingFault_s_sys(Action):
+    def name(self) -> Text:
+        return "FindTheCorrespondingFault_s_sys"
+
+    def run(
+            self,
+            dispatcher: CollectingDispatcher,
+            tracker: Tracker,
+            domain: Dict[Text, Any]
+    ) -> List[Dict[Text, Any]]:
+        user_message = tracker.latest_message.get('text')
+        print("text", user_message)
+
+        # 获取故障实体
+        s_sys = tracker.get_slot('s_sys')
+        # 获取意图
+        intentions = tracker.get_intent_of_latest_message()
+        print(f"s_sys: {s_sys}")
+        print(f"Intentions: {intentions}")
+
+        # 连接到 Neo4j 数据库
+        try:
+            driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "fdx3081475970"))
+        except Exception as e:
+            dispatcher.utter_message(text=f"无法连接到 Neo4j 数据库: {e}")
+            return []
+
+        # 定义查询
+        query = """
+        MATCH (n: 子系统)
+        RETURN n.name AS name
+        """
+
+        # 执行查询
+        names = []
+        try:
+            with driver.session() as session:
+                result = session.run(query)
+                names = [record["name"] for record in result]
+        except Exception as e:
+            dispatcher.utter_message(text=f"查询 Neo4j 数据库时出错: {e}")
+            driver.close()
+            return []
+
+        # 设置相似度阈值
+        threshold = 0.5
+
+        # 存储相似度和名称
+        similarity_scores = [(name, jellyfish.jaro_winkler_similarity(name, user_message)) for name in names]
+
+        # 过滤出相似度大于阈值的名称
+        filtered_similarities = [(name, score) for name, score in similarity_scores if score > threshold]
+
+        # 找出相似度最高的名称
+        highest_similarity_name = None
+        if filtered_similarities:
+            highest_similarity_name, highest_similarity_score = max(filtered_similarities, key=lambda x: x[1])
+            print(f"最高相似度的名称: {highest_similarity_name}")
+
+        # 如果找到了相似度高的名称,则用它替换原始的 fault_name
+        if highest_similarity_name is not None:
+            s_sys = highest_similarity_name
+
+        # 关闭 Neo4j 驱动连接
+        driver.close()
+
+        # 准备数据结构
+        if type(s_sys) != list:
+            data = {'args': {s_sys: ['s_sys']}, 'question_types': [intentions]}
+        else:
+            data = {'args': {s_sys[0]: ['s_sys']}, 'question_types': [intentions]}
+
+        parser = question_parser.QuestionParser()
+        searcher = answer_search.AnswerSearcher()
+        print(f"Data: {data}")
+        sqls = parser.parser_main(data)  # 生成相关的查询语句
+        print(f"SQLs: {sqls}")
+        final_answer = searcher.search_main(sqls)  # 查询相关内容
+        entity_lists = searcher.get_entity(sqls)
+        entity_str = json.dumps(entity_lists)
+        print(f"Entity Lists: {entity_str}")
+        print(f"Final Answer: {final_answer}")
+
+        # 进行判断 输出结果
+        if not final_answer:
+            dispatcher.utter_message(template='utter_out')
+        else:
+            if highest_similarity_name is not None and highest_similarity_score is not None:
+                match_info = (
+                    f"当前实体的匹配数值为:{highest_similarity_score:.2f}\n"
+                    f"根据当前的实体匹配结果的输出为:\n"
+                )
+                answer = match_info + '\n'.join(final_answer)
+            else:
+                answer = '\n'.join(final_answer)
+
+            # 发送最终答案和实体信息
+            dispatcher.utter_message(text=answer)
+            dispatcher.utter_message(text=f'{entity_lists}')
+
+        return [SlotSet('s_sys', entity_str)]
+
+class FindTheCorrespondingFault_system(Action):
+    def name(self) -> Text:
+        return "FindTheCorrespondingFault_system"
+
+    def run(
+            self,
+            dispatcher: CollectingDispatcher,
+            tracker: Tracker,
+            domain: Dict[Text, Any]
+    ) -> List[Dict[Text, Any]]:
+        user_message = tracker.latest_message.get('text')
+        print("text", user_message)
+
+        # 获取故障实体
+        system = tracker.get_slot('system')
+        # 获取意图
+        intentions = tracker.get_intent_of_latest_message()
+        print(f"system: {system}")
+        print(f"Intentions: {intentions}")
+
+        # 连接到 Neo4j 数据库
+        try:
+            driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "fdx3081475970"))
+        except Exception as e:
+            dispatcher.utter_message(text=f"无法连接到 Neo4j 数据库: {e}")
+            return []
+
+        # 定义查询
+        query = """
+        MATCH (n: 系统)
+        RETURN n.name AS name
+        """
+
+        # 执行查询
+        names = []
+        try:
+            with driver.session() as session:
+                result = session.run(query)
+                names = [record["name"] for record in result]
+        except Exception as e:
+            dispatcher.utter_message(text=f"查询 Neo4j 数据库时出错: {e}")
+            driver.close()
+            return []
+
+        # 设置相似度阈值
+        threshold = 0.5
+
+        # 存储相似度和名称
+        similarity_scores = [(name, jellyfish.jaro_winkler_similarity(name, user_message)) for name in names]
+
+        # 过滤出相似度大于阈值的名称
+        filtered_similarities = [(name, score) for name, score in similarity_scores if score > threshold]
+
+        # 找出相似度最高的名称
+        highest_similarity_name = None
+        if filtered_similarities:
+            highest_similarity_name, highest_similarity_score = max(filtered_similarities, key=lambda x: x[1])
+            print(f"最高相似度的名称: {highest_similarity_name}")
+
+        # 如果找到了相似度高的名称,则用它替换原始的 fault_name
+        if highest_similarity_name is not None:
+            system = highest_similarity_name
+
+        # 关闭 Neo4j 驱动连接
+        driver.close()
+
+        # 准备数据结构
+        if type(system) != list:
+            data = {'args': {system: ['system']}, 'question_types': [intentions]}
+        else:
+            data = {'args': {system[0]: ['system']}, 'question_types': [intentions]}
+
+        parser = question_parser.QuestionParser()
+        searcher = answer_search.AnswerSearcher()
+        print(f"Data: {data}")
+        sqls = parser.parser_main(data)  # 生成相关的查询语句
+        print(f"SQLs: {sqls}")
+        final_answer = searcher.search_main(sqls)  # 查询相关内容
+        # entity_lists = searcher.get_entity(sqls)
+        try:
+            entity_lists = searcher.get_entity(sqls)
+        except Exception as e:
+            dispatcher.utter_message(text=f"获取实体信息时出错: {e}")
+            return []
+        entity_str = json.dumps(entity_lists)
+        print(f"Entity Lists: {entity_str}")
+        print(f"Final Answer: {final_answer}")
+
+        # 进行判断 输出结果
+        if not final_answer:
+            dispatcher.utter_message(template='utter_out')
+        else:
+            if highest_similarity_name is not None and highest_similarity_score is not None:
+                match_info = (
+                    f"当前实体的匹配数值为:{highest_similarity_score:.2f}\n"
+                    f"根据当前的实体匹配结果的输出为:\n"
+                )
+                answer = match_info + '\n'.join(final_answer)
+            else:
+                answer = '\n'.join(final_answer)
+
+            # 发送最终答案和实体信息
+            dispatcher.utter_message(text=answer)
+            dispatcher.utter_message(text=f'{entity_lists}')
+
+        return [SlotSet('system', entity_str)]
+
+# class FindTheCorrespondingsmallTalk(Action):
+#     def name(self) -> Text:
+#         return "action_default_fallback"
+#
+#     def run(
+#             self,
+#             dispatcher: CollectingDispatcher,
+#             tracker: Tracker,
+#             domain: Dict[Text, Any],
+#     ) -> List[EventType]:
+#
+#         try:
+#             # 获取用户的最新消息
+#             user_message = tracker.latest_message
+#
+#             # 调用 app.talk() 处理用户输入
+#             answer = app.talk(user_message['text'])
+#
+#             # 如果 answer 是一个列表,逐条发送每一条消息
+#             if isinstance(answer, list):
+#                 for message in answer:
+#                     dispatcher.utter_message(text=message)
+#             else:
+#                 # 否则直接发送单条消息
+#                 dispatcher.utter_message(text=answer)
+#         except Exception as e:
+#             print(e)
+#             dispatcher.utter_message(template='utter_exception')
+#
+#         return []
+
+
+class FindTheCorrespondingsmallTalk(Action):
+    def name(self) -> Text:
+        return "action_default_fallback"
+
+    def run(
+            self,
+            dispatcher: CollectingDispatcher,
+            tracker: Tracker,
+            domain: Dict[Text, Any],
+    ) -> List[EventType]:
+
+        try:
+            # food = tracker.latest_message
+            # answer = app.talk(food['text'])
+            dispatcher.utter_message(template="utter_exception")
+        except Exception as e:
+            print(e)
+            dispatcher.utter_message(template="utter_exception")
+        return []
+
+        # try:
+        #     # 获取用户的最新消息
+        #     user_message = tracker.latest_message
+        #
+        #     # 调用 app.talk() 处理用户输入
+        #     answer = app.talk(user_message['text'])
+        #
+        #     # 如果 answer 是一个列表,则逐条发送每一条消息
+        #     if isinstance(answer, list):
+        #         for message in answer:
+        #             if isinstance(message, dict) and 'text' in message:
+        #                 dispatcher.utter_message(text=message['text'])
+        #             else:
+        #                 dispatcher.utter_message(text=str(message))  # 确保其他类型也能被处理
+        #     elif isinstance(answer, dict) and 'text' in answer:
+        #         # 如果是单个字典,直接发送
+        #         dispatcher.utter_message(text=answer['text'])
+        #     else:
+        #         # 否则直接发送单条消息(假设 answer 是字符串)
+        #         dispatcher.utter_message(text=str(answer))
+        # except Exception as e:
+        #     print(e)
+        #     dispatcher.utter_message(template='utter_exception')
+        #
+        # return []

+ 564 - 0
actions/api/answer_search.py

@@ -0,0 +1,564 @@
+from py2neo import Graph
+
+
+class AnswerSearcher:
+    def __init__(self):
+        self.g = Graph("bolt://localhost:7687", auth=("neo4j", "fdx3081475970"))
+        self.num_limit = 20
+
+    '''执行cypher查询,并返回相应结果'''
+    def search_main(self, sqls):
+        final_answers = []
+        for sql_ in sqls:
+            question_type = sql_['question_type']
+            queries = sql_['sql']
+            answers = []
+            for query in queries:
+                ress = self.g.run(query).data()
+                answers += ress
+            final_answer = self.answer_prettify(question_type, answers)
+            if final_answer:
+                final_answers.append(final_answer)
+        return final_answers
+
+    def get_entity(self, sqls):
+        entity_lists = []
+        for sql_ in sqls:
+            question_type = sql_['question_type']
+            queries = sql_['sql']
+            answers = []
+            for query in queries:
+                ress = self.g.run(query).data()
+                answers += ress
+            entity_list = self.get_entity_list(question_type, answers)
+            print("entity_list", entity_list)
+            if entity_list:
+                entity_lists.append(entity_list)
+        return entity_lists
+
+    def get_entity_list(self, question_type, answers):
+        entity_list = []
+        if not answers:
+            return ''
+
+        if question_type == 'query_des':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = str(i['m.name'])
+                obj = str(i['n.name'])
+                rel = str(i['r.name'])
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "故障描述"})
+                    seen_subjects.add(sub)
+                entity_list["data"].append({"name": obj, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'query_hmc':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = str(i['m.name'])
+                obj = str(i['n.name'])
+                rel = str(i['r.name'])
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "HMC"})
+                    seen_subjects.add(sub)
+                entity_list["data"].append({"name": obj, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'query_obj':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = str(i['m.name'])
+                obj = str(i['n.name'])
+                rel = str(i['r.name'])
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "成品"})
+                    seen_subjects.add(sub)
+                entity_list["data"].append({"name": obj, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'query_x_obj':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = str(i['m.name'])
+                obj = str(i['n.name'])
+                rel = str(i['r.name'])
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": obj, "category": "型号"})
+                    seen_subjects.add(obj)
+                entity_list["data"].append({"name": sub, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'query_s_sys':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = str(i['m.name'])
+                obj = str(i['n.name'])
+                rel = str(i['r.name'])
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "子系统"})
+                    seen_subjects.add(sub)
+                entity_list["data"].append({"name": obj, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'query_system':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = str(i['m.name'])
+                obj = str(i['n.name'])
+                rel = str(i['r.name'])
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "系统"})
+                    seen_subjects.add(sub)
+                entity_list["data"].append({"name": obj, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'fault_des':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = str(i['m.name'])
+                obj = str(i['n.name'])
+                rel = str(i['r.name'])
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "HMC"})
+                    seen_subjects.add(sub)
+                entity_list["data"].append({"name": obj, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'fault_excluds':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = str(i['m.name'])
+                obj = str(i['n.name'])
+                rel = str(i['r.name'])
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+
+                a = i['a.name'] if 'a.name' in i else None
+                b = i['b.name'] if 'b.name' in i else None
+                c = i['c.name'] if 'c.name' in i else None
+
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "HMC"})
+                    seen_subjects.add(sub)
+
+                if a and b and c:
+                    entity_list["data"].append({"name": c, "category": b})
+                    entity_list["links"].append({"source": a, "target": c, "value": b})
+                    entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+                else:
+                    entity_list["data"].append({"name": obj, "category": rel})
+                    entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                    entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'des_excluds':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = str(i['m.name'])
+                obj = str(i['n.name'])
+                rel = str(i['r.name'])
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+
+                a = i['a.name'] if 'a.name' in i else None
+                b = i['b.name'] if 'b.name' in i else None
+                c = i['c.name'] if 'c.name' in i else None
+
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "故障名称"})
+                    seen_subjects.add(sub)
+
+                if a and b and c:
+                    entity_list["data"].append({"name": c, "category": b})
+                    entity_list["links"].append({"source": a, "target": c, "value": b})
+                    entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+                else:
+                    entity_list["data"].append({"name": obj, "category": rel})
+                    entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                    entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'fault_obj':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = str(i['m.name'])
+                obj = str(i['n.name'])
+                rel = str(i['r.name'])
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "HMC"})
+                    seen_subjects.add(sub)
+                entity_list["data"].append({"name": obj, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'hmc_system':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = str(i['m.name'])
+                obj = str(i['n.name'])
+                rel = str(i['r.name'])
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "HMC"})
+                    seen_subjects.add(sub)
+                entity_list["data"].append({"name": obj, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'sys_s_sys':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = str(i['m.name'])
+                obj = str(i['n.name'])
+                rel = str(i['r.name'])
+                ossid = i['n.doc_id']
+                file_name = str(i['n.doc_name'])
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "系统"})
+                    seen_subjects.add(sub)
+                entity_list["data"].append({"name": obj, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'sys_fault_obj':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = i['m.name']
+                obj = i['n.name']
+                rel = i['r.name']
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "系统"})
+                    seen_subjects.add(sub)
+                entity_list["data"].append({"name": obj, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'fault_obj_sys':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = i['m.name']
+                obj = i['n.name']
+                rel = i['r.name']
+                ossid = i['m.doc_id']
+                file_name = i['m.doc_name']
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "成品"})
+                    seen_subjects.add(sub)
+                entity_list["data"].append({"name": obj, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        elif question_type == 'fault_obj_fault_x_obj':
+            entity_list = {
+                "data": [],
+                "links": [],
+                "docs": []
+            }
+            seen_subjects = set()
+            for i in answers:
+                sub = i['m.name']
+                obj = i['n.name']
+                rel = i['r.name']
+                ossid = i['n.doc_id']
+                file_name = i['n.doc_name']
+                filePage = i['n.filePage']
+                if sub not in seen_subjects:
+                    entity_list["data"].append({"name": sub, "category": "成品"})
+                    seen_subjects.add(sub)
+                entity_list["data"].append({"name": obj, "category": rel})
+                entity_list["links"].append({"source": sub, "target": obj, "value": rel})
+                entity_list['docs'].append({"ossId": ossid, "file_name": file_name, "filePage": filePage})
+
+        return entity_list
+
+    '''根据对应的qustion_type,调用相应的回复模板'''
+    def answer_prettify(self, question_type, answers):
+        final_answer = []
+        if not answers:
+            return ''
+
+        if question_type == 'query_des':
+            # 初始化集合用于存储去重后的尾实体及其关系
+            desc_with_rel_set = set()
+
+                # 遍历答案,同时获取头实体、关系和尾实体
+            for answer in answers:
+                subject = answer['m.name']
+                rel = answer['r.name']
+                desc = answer['n.name']
+
+                # 将关系和尾实体组合成字符串并加入集合
+                desc_with_rel_set.add(f"{rel}为{desc}")
+
+            # 将集合转换为列表,并按需限制数量
+            desc_with_rel = list(desc_with_rel_set)[:self.num_limit]
+
+            # 将头实体和与其有关联的尾实体及关系组合成最终的回答
+            final_answer = '故障描述{0}{1}。'.format(subject, '; '.join(desc_with_rel))
+
+        elif question_type == 'query_hmc':
+            # 初始化集合用于存储去重后的尾实体及其关系
+            desc_with_rel_set = set()
+
+            # 遍历答案,同时获取头实体、关系和尾实体
+            for answer in answers:
+                subject = answer['m.name']
+                rel = answer['r.name']
+                desc = answer['n.name']
+
+                # 将关系和尾实体组合成字符串并加入集合
+                desc_with_rel_set.add(f"{rel}为{desc}")
+
+            # 将集合转换为列表,并按需限制数量
+            desc_with_rel = list(desc_with_rel_set)[:self.num_limit]
+
+            # 将头实体和与其有关联的尾实体及关系组合成最终的回答
+            final_answer = 'HMC码{0}{1}。'.format(subject, '; '.join(desc_with_rel))
+
+        elif question_type == 'query_obj':
+            # 初始化集合用于存储去重后的尾实体及其关系
+            desc_with_rel_set = set()
+
+            # 遍历答案,同时获取头实体、关系和尾实体
+            for answer in answers:
+                subject = answer['m.name']
+                rel = answer['r.name']
+                desc = answer['n.name']
+
+                # 将关系和尾实体组合成字符串并加入集合
+                desc_with_rel_set.add(f"{rel}为{desc}")
+
+            # 将集合转换为列表,并按需限制数量
+            desc_with_rel = list(desc_with_rel_set)[:self.num_limit]
+
+            # 将头实体和与其有关联的尾实体及关系组合成最终的回答
+            final_answer = '成品{0}{1}。'.format(subject, '; '.join(desc_with_rel))
+
+        elif question_type == 'query_x_obj':
+            # 初始化集合用于存储去重后的尾实体及其关系
+            desc_with_rel_set = set()
+
+            # 遍历答案,同时获取头实体、关系和尾实体
+            for answer in answers:
+                subject = answer['m.name']
+                rel = answer['r.name']
+                desc = answer['n.name']
+
+                # 将关系和尾实体组合成字符串并加入集合
+                desc_with_rel_set.add(f"{rel}为{desc}")
+
+            # 将集合转换为列表,并按需限制数量
+            desc_with_rel = list(desc_with_rel_set)[:self.num_limit]
+
+            # 将头实体和与其有关联的尾实体及关系组合成最终的回答
+            final_answer = '型号{0}{1}。'.format(subject, '; '.join(desc_with_rel))
+
+        elif question_type == 'query_s_sys':
+            # 初始化集合用于存储去重后的尾实体及其关系
+            desc_with_rel_set = set()
+
+            # 遍历答案,同时获取头实体、关系和尾实体
+            for answer in answers:
+                subject = answer['m.name']
+                rel = answer['r.name']
+                desc = answer['n.name']
+
+                # 将关系和尾实体组合成字符串并加入集合
+                desc_with_rel_set.add(f"{rel}为{desc}")
+
+            # 将集合转换为列表,并按需限制数量
+            desc_with_rel = list(desc_with_rel_set)[:self.num_limit]
+
+            # 将头实体和与其有关联的尾实体及关系组合成最终的回答
+            final_answer = '子系统{0}{1}。'.format(subject, '; '.join(desc_with_rel))
+
+        elif question_type == 'query_system':
+            # 初始化集合用于存储去重后的尾实体及其关系
+            desc_with_rel_set = set()
+
+            # 遍历答案,同时获取头实体、关系和尾实体
+            for answer in answers:
+                subject = answer['m.name']
+                rel = answer['r.name']
+                desc = answer['n.name']
+
+                # 将关系和尾实体组合成字符串并加入集合
+                desc_with_rel_set.add(f"{rel}为{desc}")
+
+            # 将集合转换为列表,并按需限制数量
+            desc_with_rel = list(desc_with_rel_set)[:self.num_limit]
+
+            # 将头实体和与其有关联的尾实体及关系组合成最终的回答
+            final_answer = '系统{0}{1}。'.format(subject, '; '.join(desc_with_rel))
+
+        elif question_type == 'fault_des':
+            desc = [i['n.name'] for i in answers]
+            subject = answers[0]['m.name']
+            final_answer = '{0}的故障现象是: {1}。'.format(subject, ';'.join(list(set(desc))[:self.num_limit]))
+
+        elif question_type == 'fault_excluds':
+            desc = []
+            for i in answers:
+                if 'c.name' in i and i['c.name']:
+                    desc.append(i['c.name'])
+                elif 'n.name' in i:
+                    desc.append(i['n.name'])
+
+            subject = answers[0]['m.name']
+            final_answer = '发生{0}时应该{1}。'.format(subject, ';'.join(list(set(desc))[:self.num_limit]))
+
+        elif question_type == 'des_excluds':
+            desc = []
+            for i in answers:
+                if 'c.name' in i and i['c.name']:
+                    desc.append(i['c.name'])
+                elif 'n.name' in i:
+                    desc.append(i['n.name'])
+
+            subject = answers[0]['m.name']
+            final_answer = '发生{0}时应该{1}。'.format(subject, ';'.join(list(set(desc))[:self.num_limit]))
+
+        elif question_type == 'fault_obj':
+            desc = [i['n.name'] for i in answers]
+            subject = answers[0]['m.name']
+            final_answer = 'HMC码{0}对应的部件位:{1}。'.format(subject, ';'.join(list(set(desc))[:self.num_limit]))
+
+        elif question_type == 'hmc_system':
+            desc = [i['n.name'] for i in answers]
+            subject = answers[0]['m.name']
+            final_answer = 'HMC码{0}对应的系统为{1}。'.format(subject, ';'.join(list(set(desc))[:self.num_limit]))
+
+        elif question_type == "sys_s_sys":
+            desc = [i['n.name'] for i in answers]
+            subject = answers[0]['m.name']
+            final_answer = '{0}包含的子系统有:{1}。'.format(subject, ';'.join(list(set(desc))[:self.num_limit]))
+
+        elif question_type == "sys_fault_obj":
+            desc = [i['n.name'] for i in answers]
+            subject = answers[0]['m.name']
+            final_answer = '{0}包含的成品有:{1}。'.format(subject, ';'.join(list(set(desc))[:self.num_limit]))
+
+        elif question_type == "fault_obj_sys":
+            desc = [i['n.name'] for i in answers]
+            subject = answers[0]['m.name']
+            final_answer = '{0}属于{1}。'.format(subject, ';'.join(list(set(desc))[:self.num_limit]))
+
+        elif question_type == "fault_obj_fault_x_obj":
+            desc = [i['n.name'] for i in answers]
+            subject = answers[0]['m.name']
+            final_answer = '{0}的型号为:{1}。'.format(subject, ';'.join(list(set(desc))[:self.num_limit]))
+
+        return final_answer
+
+
+if __name__ == '__main__':
+    searcher = AnswerSearcher()

+ 98 - 0
actions/api/app.py

@@ -0,0 +1,98 @@
+'''
+详情见http://api.qingyunke.com/
+智能机器人API接口说明
+支持功能:天气、翻译、藏头诗、笑话、歌词、计算、域名信息/备案/收录查询、IP查询、手机号码归属、人工智能聊天
+接口地址:http://api.qingyunke.com/api.php?key=free&appid=0&msg=关键词
+        key 固定参数free
+        appid 设置为0,表示智能识别,可忽略此参数
+        msg 关键词,请参考下方参数示例,该参数可智能识别,该值请经过 urlencode 处理后再提交
+返回结果:{"result":0,"content":"内容"}
+     result 状态,0表示正常,其它数字表示错误
+     content 信息内容
+想要学习Python?Python学习交流群:1136201545满足你的需求,资料都已经上传群文件,可以自行下载!
+'''
+# import urllib.request
+# import time
+# import ssl
+# import json
+# import string
+# import jieba
+#
+#
+# def talk(keyword):
+#     target = r'http://api.qingyunke.com/api.php?key=free&appid=0&msg='
+#     while True:
+#         if keyword == "exit":
+#             return "不聊算了,拜拜"
+#         else:
+#             tmp = target + keyword
+#             url = urllib.parse.quote(tmp, safe=string.printable)
+#             page = urllib.request.urlopen(url)
+#
+#             html = page.read().decode("utf-8")
+#
+#             res = json.loads(html)
+#             list = jieba.cut(res['content'], cut_all=False)
+#
+#             jieba.add_word('菲菲')
+#             answers = ''
+#             for i in list:
+#                 if i == '菲菲':
+#                     i = 'Friday'
+#                     answers += i
+#                 else:
+#                     answers += i
+#             return answers.replace('{br}','\n').replace('"','"')
+
+
+import urllib.request
+import time
+import ssl
+import json
+import string
+import jieba
+
+import requests
+
+
+def post_request(url, data):
+    try:
+        # 发送POST请求
+        response = requests.post(url, json=data)
+
+        # 检查响应状态码
+        if response.status_code == 200:
+            result = response.text
+            return result
+        else:
+            print(f"请求失败,状态码:{response.status_code}")
+            print("响应内容:", response.text)
+            return None
+
+    except Exception as e:
+        print(f"请求过程中发生错误:{e}")
+        return None
+
+
+def talk(keyword):
+    # 要请求的URL
+    url = "http://127.0.0.1:11001/llm"
+
+    if keyword == "exit":
+        return "不聊算了,拜拜"
+
+    # 要发送的数据
+    data = {
+        "prompt": keyword
+    }
+
+    # 调用函数
+    response_data = post_request(url, data)
+    return response_data
+
+
+if __name__ == "__main__":
+    initial_keyword = input("请输入第一条消息:")
+
+    result = talk(initial_keyword)
+    print(result)

+ 31 - 0
actions/api/llm_flask_api.py

@@ -0,0 +1,31 @@
+import requests
+from flask import Flask, request, jsonify
+import numpy as np
+import pandas as pd
+import os
+#大模型url
+url = "http://localhost:11434/api/generate"
+
+
+app = Flask(__name__)
+
+@app.route('/llm', methods=['POST'])
+def llm():
+    try:
+        # mod = request.json.get('model')
+        mod = "qwen2.5:1.5b"
+        q = request.json.get('prompt')
+        payload = {"model": mod, "prompt": q,"stream":False}
+        response = requests.post(url, json=payload)
+        response_json = response.json()
+        response_text = response_json.get('response', '无结果')
+        return response_text
+
+
+    except Exception as e:
+        return jsonify({
+            'code': 500,
+            'msg': str(e)
+        })
+if __name__ == '__main__':
+    app.run(debug=True, port=11001)

+ 148 - 0
actions/api/question_parser.py

@@ -0,0 +1,148 @@
+class QuestionParser:
+
+    '''构建实体节点'''
+    def build_entitydict(self, args):
+        entity_dict = {}
+        for arg, types in args.items():
+            for type in types:
+                if type not in entity_dict:
+                    entity_dict[type] = [arg]
+                else:
+                    entity_dict[type].append(arg)
+
+        return entity_dict
+
+    '''解析主函数'''
+    def parser_main(self, res_classify):
+        args = res_classify['args']                          # 解析问句中的实体
+        entity_dict = self.build_entitydict(args)            # 创建一个实体的字典
+        question_types = res_classify['question_types']      # 创建意图的字典并将一个问句中的意图放入到res_classify中去
+        sqls = []
+        for question_type in question_types:
+            sql_ = {}
+            sql_['question_type'] = question_type
+            sql = []
+
+            if question_type == 'query_des': # 故障时间
+                sql = self.sql_transfer(question_type, entity_dict.get('fault_des'))
+
+            elif question_type == 'query_hmc': # 故障时间
+                sql = self.sql_transfer(question_type, entity_dict.get('fault_hmc'))
+
+            elif question_type == 'query_obj': # 故障时间
+                sql = self.sql_transfer(question_type, entity_dict.get('fault_obj'))
+
+            elif question_type == 'query_x_obj': # 故障时间
+                sql = self.sql_transfer(question_type, entity_dict.get('fault_x_obj'))
+
+            elif question_type == 'query_s_sys': # 故障时间
+                sql = self.sql_transfer(question_type, entity_dict.get('s_sys'))
+
+            elif question_type == 'query_system': # 故障时间
+                sql = self.sql_transfer(question_type, entity_dict.get('system'))
+
+            elif question_type == 'fault_des':   # 故障现象
+                sql = self.sql_transfer(question_type, entity_dict.get('fault_hmc'))
+
+            elif question_type == 'fault_excluds': # 故障时间
+                sql = self.sql_transfer(question_type, entity_dict.get('fault_hmc'))
+
+            elif question_type == 'des_excluds':  # 故障所属系统
+                sql = self.sql_transfer(question_type, entity_dict.get('fault_des'))
+
+            elif question_type == 'fault_obj':  # 专业
+                sql = self.sql_transfer(question_type, entity_dict.get('fault_hmc'))
+
+            elif question_type == 'hmc_system':  # 故障产品或部位
+                sql = self.sql_transfer(question_type, entity_dict.get('fault_hmc'))
+
+            elif question_type == 'sys_s_sys':  # 故障原因
+                sql = self.sql_transfer(question_type, entity_dict.get('system'))
+
+            elif question_type == 'sys_fault_obj':  # 排除方法
+                sql = self.sql_transfer(question_type, entity_dict.get('s_sys'))
+
+            elif question_type == 'fault_obj_sys':  # 排除方法
+                sql = self.sql_transfer(question_type, entity_dict.get('fault_obj'))
+
+            elif question_type == 'fault_obj_fault_x_obj':  # 排除方法
+                sql = self.sql_transfer(question_type, entity_dict.get('fault_obj'))
+
+
+            if sql:
+                sql_['sql'] = sql
+
+                sqls.append(sql_)
+
+        return sqls
+
+    '''针对不同的问题,分开进行处理'''
+    def sql_transfer(self, question_type, entities):
+        if not entities:
+            return []
+
+        # 查询语句
+        sql = []
+        # 查询故障的故障现象
+        if question_type == 'query_des':
+            sql = ["MATCH (m:故障描述)-[r]->(n) WHERE m.name = '{0}' return m.name, r.name, n.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        elif question_type == 'query_hmc':
+            sql = ["MATCH (m:HMC)-[r]->(n) WHERE m.name = {0} return m.name, r.name, n.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        elif question_type == 'query_obj':
+            sql = ["MATCH (m:成品)-[r]->(n) WHERE m.name = '{0}' return m.name, r.name, n.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        elif question_type == 'query_x_obj':
+            sql = ["MATCH (m)-[r]->(n:型号) WHERE n.name = '{0}' return m.name, r.name, n.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        elif question_type == 'query_s_sys':
+            sql = ["MATCH (m:子系统)-[r]->(n) WHERE m.name = '{0}' return m.name, r.name, n.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        elif question_type == 'query_system':
+            sql = ["MATCH (m:系统)-[r]->(n) WHERE m.name = '{0}' return m.name, r.name, n.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        elif question_type == 'fault_des':
+            sql = ["MATCH (m:HMC)-[r:故障描述]->(n:故障描述) WHERE m.name = {0} return m.name, r.name, n.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+            # MATCH (m:故障名称)-[r:原因]->(n:原因) where m.name = '转速指示器显示与规程规定不符' return m.name, r.name, n.name
+
+        # 查询故障的发生时间
+        elif question_type == 'fault_excluds':
+            sql = ["MATCH (m:HMC)-[r:维修策略]->(n:维修策略) WHERE m.name = {0} " \
+                   "OPTIONAL MATCH(a)-[b]->(c) WHERE a.type=n.name return m.name, n.name, r.name, a.name, b.name, c.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        # 查询故障的所属系统
+        elif question_type == 'des_excluds':
+            sql = ["MATCH (m:故障描述)-[r:维修策略]->(n:维修策略) WHERE m.name = '{0}' " \
+                   "OPTIONAL MATCH(a)-[b]->(c) WHERE a.type=n.name return m.name, n.name, r.name, a.name, b.name, c.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        elif question_type == 'fault_obj':
+            sql = ["MATCH (m:HMC)-[r:成品]->(n:成品) WHERE m.name = {0} return m.name, r.name, n.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        # 查询故障的产品部位
+        elif question_type == 'hmc_system':
+            sql = ["MATCH (m:HMC)-[r:系统]->(n:系统) WHERE m.name = {0} return m.name, r.name, n.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        # 查询故障的故障原因
+        elif question_type == 'sys_s_sys':
+            sql = ["MATCH (m:系统)-[r:子系统]->(n:子系统) WHERE m.name = '{0}' return m.name, r.name, n.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        # 查询故障的解决办法
+        elif question_type == 'sys_fault_obj':
+            # sql = ["MATCH (m:故障名称)-[r:排除方法]->(n:排除方法) WHERE m.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities]
+            sql = ["MATCH (m:子系统)-[r:成品]->(n:成品) WHERE m.name = '{0}' return m.name, r.name, n.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        elif question_type == 'fault_obj_sys':
+            # sql = ["MATCH (m:故障名称)-[r:排除方法]->(n:排除方法) WHERE m.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities]
+            sql = ["MATCH (n:子系统)-[r:成品]->(m:成品) WHERE m.name = '{0}' return m.name, r.name, n.name, m.doc_id, m.doc_name, m.filePage".format(i) for i in entities]
+
+        elif question_type == 'fault_obj_fault_x_obj':
+            sql = ["MATCH (m:成品)-[r:型号]->(n:型号) WHERE m.name = '{0}' return m.name, r.name, n.name, n.doc_id, n.doc_name, n.filePage".format(i) for i in entities]
+
+        return sql
+
+
+# ["MATCH (m:{type:'{0}'})-[r:{type:'{0}'}]->(n:{type:'{0}')  return m.name, r.name, n.name".format(i) for i in entities]
+
+if __name__ == '__main__':
+    handler = QuestionParser()

+ 20 - 0
app.log

@@ -0,0 +1,20 @@
+rasa_sdk.endpoint - INFO - Starting action endpoint server...
+sanic.root - DEBUG - CORS: Configuring CORS with resources: {'/*': {'origins': ['.*'], 'methods': 'DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT', 'allow_headers': ['.*'], 'expose_headers': None, 'supports_credentials': False, 'max_age': None, 'send_wildcard': False, 'automatic_options': True, 'vary_header': True, 'resources': {'/*': {'origins': '*'}}, 'intercept_exceptions': True, 'always_send': True}}
+rasa_sdk.executor - INFO - Registered function for 'FindTheCorrespondingFault_des'.
+rasa_sdk.executor - INFO - Registered function for 'FindTheCorrespondingFault_hmc'.
+rasa_sdk.executor - INFO - Registered function for 'FindTheCorrespondingFault_obj'.
+rasa_sdk.executor - INFO - Registered function for 'FindTheCorrespondingFault_x_obj'.
+rasa_sdk.executor - INFO - Registered function for 'FindTheCorrespondingFault_s_sys'.
+rasa_sdk.executor - INFO - Registered function for 'FindTheCorrespondingFault_system'.
+rasa_sdk.executor - INFO - Registered function for 'action_default_fallback'.
+rasa_sdk.endpoint - INFO - Action endpoint is up and running on http://0.0.0.0:5055
+rasa_sdk.utils - DEBUG - Using the default number of Sanic workers (1).
+sanic.root - DEBUG - 
+
+                 Sanic
+         Build Fast. Run Fast.
+
+
+sanic.root - INFO - Goin' Fast @ http://0.0.0.0:5055
+asyncio - DEBUG - Using proactor: IocpProactor
+sanic.root - INFO - Starting worker [20028]

+ 51 - 0
config.yml

@@ -0,0 +1,51 @@
+language: zh
+
+pipeline:
+- name: JiebaTokenizer
+- name: LanguageModelFeaturizer
+  model_name: bert
+  model_weights: bert-base-chinese
+- name: RegexFeaturizer
+#- name: CRFEntityExtractor
+- name: LexicalSyntacticFeaturizer
+- name: CountVectorsFeaturizer
+- name: CountVectorsFeaturizer
+  analyzer: char_wb
+  min_ngram: 1
+  max_ngram: 4
+- name: DIETClassifier
+  entity_recognition: true
+  intent_classification: true
+  epochs: 400
+  learning_rate: 0.0001
+  ranking_length: 5
+- name: EntitySynonymMapper
+- name: RegexEntityExtractor
+  use_word_boundaries: False #中文环境要用
+  use_lookup_tables: True
+  use_regexes: True
+- name: FallbackClassifier
+  threshold: 0.3
+  ambiguity_threshold: 0.1
+policies:
+- name: RulePolicy
+  core_fallback_threshold: 0.3
+  core_fallback_action_name: "action_default_fallback"
+  enable_fallback_prediction: True
+- max_history: 6
+  name: AugmentedMemoizationPolicy
+- name: TEDPolicy
+  max_history: 10
+  epochs: 40
+  batch_size: 64
+  embedding_dimension: 256
+  attention_dimension: 128
+  entity_recognition_model: "DIETClassifier"
+  entity_recognition_embeddings: "sentence_transformers_all-MiniLM-L6-v2"
+
+
+- name: "MemoizationPolicy"
+- name: "RulePolicy"
+
+response_timeout: 120  # 以秒为单位
+action_execution_timeout: 120  # 以秒为单位

+ 33 - 0
credentials.yml

@@ -0,0 +1,33 @@
+# This file contains the credentials for the voice & chat platforms
+# which your bot is using.
+# https://rasa.com/docs/rasa/messaging-and-voice-channels
+
+rest:
+#  # you don't need to provide anything here - this channel doesn't
+#  # require any credentials
+
+
+#facebook:
+#  verify: "<verify>"
+#  secret: "<your secret>"
+#  page-access-token: "<your page access token>"
+
+#slack:
+#  slack_token: "<your slack token>"
+#  slack_channel: "<the slack channel>"
+#  slack_signing_secret: "<your slack signing secret>"
+
+#socketio:
+#  user_message_evt: <event name for user message>
+#  bot_message_evt: <event name for bot messages>
+#  session_persistence: <true/false>
+
+#mattermost:
+#  url: "https://<mattermost instance>/api/v4"
+#  token: "<bot token>"
+#  webhook_url: "<callback URL>"
+
+# This entry is needed if you are using Rasa X. The entry represents credentials
+# for the Rasa X "channel", i.e. Talk to your bot and Share with guest testers.
+rasa:
+  url: "http://localhost:5002/api"

+ 242 - 0
data.json

@@ -0,0 +1,242 @@
+[
+    {
+        "HMC": 202310150001,
+        "故障描述": "启动后无显示",
+        "维修策略": "检查电源连接",
+        "系统": "计算机",
+        "子系统": "显示系统",
+        "成品": "台式机",
+        "型号": "X86-PRO",
+        "机型": "波音747",
+        "文档id": 1,
+        "文档名称": "故障分析手册"
+    },
+    {
+        "HMC": 202310150002,
+        "故障描述": "无法识别硬盘",
+        "维修策略": "更换SATA线或硬盘",
+        "系统": "计算机",
+        "子系统": "存储系统",
+        "成品": "笔记本",
+        "型号": "X75",
+        "机型": "波音747",
+        "文档id": 1,
+        "文档名称": "故障分析手册"
+    },
+    {
+        "HMC": 202310150003,
+        "故障描述": "打印机卡纸",
+        "维修策略": "清理纸张残留物",
+        "系统": "办公设备",
+        "子系统": "打印系统",
+        "成品": "打印机",
+        "型号": "LBP2900",
+        "机型": "波音747",
+        "文档id": 1,
+        "文档名称": "故障分析手册"
+    },
+    {
+        "HMC": 202310150004,
+        "故障描述": "音箱无声",
+        "维修策略": "检查音频线缆连接",
+        "系统": "家用电器",
+        "子系统": "音频系统",
+        "成品": "音箱",
+        "型号": "S800",
+        "机型": "波音747",
+        "文档id": 1,
+        "文档名称": "故障分析手册"
+    },
+    {
+        "HMC": 202310150005,
+        "故障描述": "路由器无法上网",
+        "维修策略": "重启路由器或恢复出厂设置",
+        "系统": "网络设备",
+        "子系统": "网络连接",
+        "成品": "路由器",
+        "型号": "R6220",
+        "机型": "波音747",
+        "文档id": 1,
+        "文档名称": "故障分析手册"
+    },
+    {
+        "HMC": 202310150006,
+        "故障描述": "服务器过热报警",
+        "维修策略": "检查散热风扇",
+        "系统": "服务器",
+        "子系统": "冷却系统",
+        "成品": "服务器",
+        "型号": "R430",
+        "机型": "波音747",
+        "文档id": 2,
+        "文档名称": "排故手册"
+    },
+    {
+        "HMC": 202310150007,
+        "故障描述": "手机屏幕破碎",
+        "维修策略": "更换手机屏幕",
+        "系统": "移动设备",
+        "子系统": "显示屏",
+        "成品": "手机",
+        "型号": "P30Pro",
+        "机型": "波音747",
+        "文档id": 2,
+        "文档名称": "排故手册"
+    },
+    {
+        "HMC": 202310150008,
+        "故障描述": "投影仪图像模糊",
+        "维修策略": "清洁镜头",
+        "系统": "办公设备",
+        "子系统": "光学系统",
+        "成品": "投影仪",
+        "型号": "X11",
+        "机型": "波音747",
+        "文档id": 2,
+        "文档名称": "排故手册"
+    },
+    {
+        "HMC": 202310150009,
+        "故障描述": "电冰箱不制冷",
+        "维修策略": "检查制冷剂是否泄漏",
+        "系统": "家用电器",
+        "子系统": "制冷系统",
+        "成品": "冰箱",
+        "型号": "BCD-216",
+        "机型": "波音747",
+        "文档id": 2,
+        "文档名称": "排故手册"
+    },
+    {
+        "HMC": 202310150010,
+        "故障描述": "电视遥控器失灵",
+        "维修策略": "更换电池或遥控器",
+        "系统": "家用电器",
+        "子系统": "控制系统",
+        "成品": "电视",
+        "型号": "55U7300",
+        "机型": "波音747",
+        "文档id": 2,
+        "文档名称": "排故手册"
+    },
+    {
+        "HMC": 202310150011,
+        "故障描述": "洗衣机漏水",
+        "维修策略": "检查水管接口",
+        "系统": "家用电器",
+        "子系统": "水路系统",
+        "成品": "洗衣机",
+        "型号": "XQG100",
+        "机型": "波音747",
+        "文档id": 2,
+        "文档名称": "排故手册"
+    },
+    {
+        "HMC": 202310150012,
+        "故障描述": "微波炉加热慢",
+        "维修策略": "检查磁控管",
+        "系统": "家用电器",
+        "子系统": "加热系统",
+        "成品": "微波炉",
+        "型号": "NN-SN706S",
+        "机型": "波音747",
+        "文档id": 2,
+        "文档名称": "排故手册"
+    },
+    {
+        "HMC": 202310150013,
+        "故障描述": "汽车启动困难",
+        "维修策略": "检查电池电量",
+        "系统": "汽车",
+        "子系统": "电气系统",
+        "成品": "轿车",
+        "型号": "A4L",
+        "机型": "波音747",
+        "文档id": 2,
+        "文档名称": "排故手册"
+    },
+    {
+        "HMC": 202310150014,
+        "故障描述": "空调外机噪音大",
+        "维修策略": "清洗或更换风扇叶片",
+        "系统": "家用电器",
+        "子系统": "冷却系统",
+        "成品": "空调",
+        "型号": "KFR-35GW",
+        "机型": "波音747",
+        "文档id": 3,
+        "文档名称": "分析手册"
+    },
+    {
+        "HMC": 202310150015,
+        "故障描述": "电动剃须刀无法充电",
+        "维修策略": "检查充电插口",
+        "系统": "个人护理",
+        "子系统": "电源系统",
+        "成品": "剃须刀",
+        "型号": "ES-LV9A",
+        "机型": "波音747",
+        "文档id": 3,
+        "文档名称": "分析手册"
+    },
+    {
+        "HMC": 202310150016,
+        "故障描述": "照相机拍照延迟",
+        "维修策略": "更新固件",
+        "系统": "数码产品",
+        "子系统": "图像处理系统",
+        "成品": "相机",
+        "型号": "D850",
+        "机型": "波音747",
+        "文档id": 3,
+        "文档名称": "分析手册"
+    },
+    {
+        "HMC": 202310150017,
+        "故障描述": "电脑频繁死机",
+        "维修策略": "进行系统清理",
+        "系统": "计算机",
+        "子系统": "操作系统",
+        "成品": "台式机",
+        "型号": "i7-9700K",
+        "机型": "波音747",
+        "文档id": 3,
+        "文档名称": "分析手册"
+    },
+    {
+        "HMC": 202310150018,
+        "故障描述": "平板电脑触摸屏反应迟钝",
+        "维修策略": "校准触摸屏",
+        "系统": "移动设备",
+        "子系统": "触摸控制系统",
+        "成品": "平板",
+        "型号": "iPad Pro",
+        "机型": "波音747",
+        "文档id": 3,
+        "文档名称": "分析手册"
+    },
+    {
+        "HMC": 202310150019,
+        "故障描述": "扫地机器人行走异常",
+        "维修策略": "检查轮子和传感器",
+        "系统": "智能家居",
+        "子系统": "导航系统",
+        "成品": "机器人",
+        "型号": "DS920",
+        "机型": "波音747",
+        "文档id": 3,
+        "文档名称": "分析手册"
+    },
+    {
+        "HMC": 202310150020,
+        "故障描述": "电动牙刷震动减弱",
+        "维修策略": "更换电池或电机",
+        "系统": "个人护理",
+        "子系统": "动力系统",
+        "成品": "牙刷",
+        "型号": "T3000",
+        "机型": "波音747",
+        "文档id": 3,
+        "文档名称": "分析手册"
+    }
+]

+ 25 - 0
data/lookup_tables/fault_des.yml

@@ -0,0 +1,25 @@
+version: "2.0"
+nlu:
+  - lookup: fault_des  # 故障时间
+    examples: |
+      - 启动后无显示
+      - 无法识别硬盘
+      - 打印机卡纸
+      - 音箱无声
+      - 路由器无法上网
+      - 服务器过热报警
+      - 手机屏幕破碎
+      - 投影仪图像模糊
+      - 电冰箱不制冷
+      - 电视遥控器失灵
+      - 洗衣机漏水
+      - 微波炉加热慢
+      - 汽车启动困难
+      - 空调外机噪音大
+      - 电动剃须刀无法充电
+      - 照相机拍照延迟
+      - 电脑频繁死机
+      - 平板电脑触摸屏反应迟钝
+      - 扫地机器人行走异常
+      - 电动牙刷震动减弱
+

+ 26 - 0
data/lookup_tables/fault_excluds.yml

@@ -0,0 +1,26 @@
+version: "2.0"
+nlu:
+  - lookup: fault_excluds  # 排除方法
+    examples: |
+      - 检查电源连接
+      - 更换SATA线或硬盘
+      - 清理纸张残留物
+      - 检查音频线缆连接
+      - 重启路由器或恢复出厂设置
+      - 检查散热风扇
+      - 更换手机屏幕
+      - 清洁镜头
+      - 检查制冷剂是否泄漏
+      - 更换电池或遥控器
+      - 检查水管接口
+      - 检查磁控管
+      - 检查电池电量
+      - 清洗或更换风扇叶片
+      - 检查充电插口
+      - 更新固件
+      - 进行系统清理
+      - 校准触摸屏
+      - 检查轮子和传感器
+      - 更换电池或电机
+
+

+ 24 - 0
data/lookup_tables/fault_hmc.yml

@@ -0,0 +1,24 @@
+version: "2.0"
+nlu:
+  - lookup: fault_hmc  #故障名称
+    examples: |
+      - 202310150001
+      - 202310150002
+      - 202310150003
+      - 202310150004
+      - 202310150005
+      - 202310150006
+      - 202310150007
+      - 202310150008
+      - 202310150009
+      - 202310150010
+      - 202310150011
+      - 202310150012
+      - 202310150013
+      - 202310150014
+      - 202310150015
+      - 202310150016
+      - 202310150017
+      - 202310150018
+      - 202310150019
+      - 202310150020

+ 45 - 0
data/lookup_tables/fault_obj.yml

@@ -0,0 +1,45 @@
+version: "2.0"
+nlu:
+  - lookup: fault_obj  # 故障专业
+    examples: |
+      - 台式机
+      - 笔记本
+      - 打印机
+      - 音箱
+      - 路由器
+      - 服务器
+      - 手机
+      - 投影仪
+      - 冰箱
+      - 电视
+      - 洗衣机
+      - 微波炉
+      - 轿车
+      - 空调
+      - 剃须刀
+      - 相机
+      - 平板
+      - 机器人
+      - 牙刷
+      - 车辆
+      - 离合器片
+      - 电线
+      - 部件
+      - 发动机气门
+      - 发动机
+      - 机油
+      - 机油滤清器
+      - 发动机舱
+      - 颠簸感
+      - 减震器
+      - 球头
+      - 橡胶件
+      - 悬挂系统
+      - 加速
+      - 氧传感器
+      - 燃油滤清器
+      - 节气门
+      - 火花塞
+      - 混合气
+      - 进气系统
+

+ 25 - 0
data/lookup_tables/fault_x_obj.yml

@@ -0,0 +1,25 @@
+version: "2.0"
+nlu:
+  - lookup: fault_x_obj   # 故障产品或部位
+    examples: |
+      - X86-PRO
+      - X75
+      - LBP2900
+      - S800
+      - R6220
+      - R430
+      - P30Pro
+      - X11
+      - BCD-216
+      - 55U7300
+      - XQG100
+      - NN-SN706S
+      - A4L
+      - KFR-35GW
+      - ES-LV9A
+      - D850
+      - i7-9700K
+      - iPad Pro
+      - DS920
+      - T3000
+

+ 23 - 0
data/lookup_tables/s_sys.yml

@@ -0,0 +1,23 @@
+version: "2.0"
+nlu:
+  - lookup: s_sys  # 故障系统
+    examples: |
+      - 显示系统
+      - 存储系统
+      - 打印系统
+      - 音频系统
+      - 网络连接
+      - 冷却系统
+      - 显示屏
+      - 光学系统
+      - 制冷系统
+      - 控制系统
+      - 水路系统
+      - 加热系统
+      - 电气系统
+      - 电源系统
+      - 图像处理系统
+      - 操作系统
+      - 触摸控制系统
+      - 导航系统
+      - 动力系统

+ 15 - 0
data/lookup_tables/system.yml

@@ -0,0 +1,15 @@
+version: "2.0"
+nlu:
+  - lookup: system  # 故障原因
+    examples: |
+      - 计算机
+      - 办公设备
+      - 家用电器
+      - 网络设备
+      - 服务器
+      - 移动设备
+      - 汽车
+      - 个人护理
+      - 数码产品
+      - 智能家居
+

+ 127 - 0
data/nlu.yml

@@ -0,0 +1,127 @@
+version: "2.0"
+
+nlu:
+- intent: greet
+  examples: |
+    - 你好
+    - hello
+    - 您好
+    - hi
+    - 喂
+
+- intent: goodbye
+  examples: |
+    - 拜拜
+    - 再见
+    - 拜
+    - 退出
+    - 结束
+
+- intent: query_des
+  examples: |
+    - [洗衣机漏水](fault_des)
+    - [服务器报警](fault_des)
+    - [路由器无法上网](fault_des)
+    - [手机屏幕破碎](fault_des)
+
+- intent: query_hmc
+  examples: |
+    - [202310150010](fault_hmc)
+    - [202310150011](fault_hmc)
+    - [202310150012](fault_hmc)
+    - [202310150013](fault_hmc)
+
+- intent: query_obj
+  examples: |
+    - [打印机](fault_obj)
+    - [冰箱](fault_obj)
+    - [剃须刀](fault_obj)
+    - [机器人](fault_obj)
+
+- intent: query_x_obj
+  examples: |
+    - [LBP2900](fault_x_obj)
+    - [P30Pro](fault_x_obj)
+    - [KFR-35GW](fault_x_obj)
+    - [i7-9700K](fault_x_obj)
+
+- intent: query_s_sys
+  examples: |
+    - [显示系统](s_sys)
+    - [网络连接](s_sys)
+    - [触摸控制系统](s_sys)
+    - [图像处理系统](s_sys)
+
+- intent: query_system
+  examples: |
+    - [办公设备](system)
+    - [移动设备](system)
+    - [计算机](system)
+    - [网络设备](system)
+
+- intent: fault_des   # 故障描述
+  examples: |
+    - HMC码[202310150010](fault_hmc)代表的故障是什么
+    - 请问HMC码[202310150010](fault_hmc)对应的故障描述是什么
+    - HMC码[202310150010](fault_hmc)的故障现象是什么
+
+- intent: fault_excluds    # hmc_维修策略
+  examples: |
+    - HMC码[202310150010](fault_hmc)的维修策略是什么
+    - HMC码[202310150010](fault_hmc)的排故方法是什么
+    - HMC码[202310150010](fault_hmc)的排故顺序是什么
+    - HMC码[202310150010](fault_hmc)的排故步骤是什么
+    - 对于HMC码[202310150010](fault_hmc),推荐的维修步骤有哪些
+    - HMC码[202310150010](fault_hmc)的维修方案是什么
+    - 修复HMC码[202310150010](fault_hmc)相关的步骤
+    - HMC码[202310150010](fault_hmc)现象怎么解决
+
+- intent: des_excluds    # 描述_策略
+  examples: |
+    - [音箱无声](fault_des)的维修策略是什么
+    - [音箱无声](fault_des)的排故方法是什么
+    - [音箱无声](fault_des)的排故顺序是什么
+    - [音箱无声](fault_des)的排故步骤是什么
+    - 对于[音箱无声](fault_des),推荐的维修步骤有哪些
+    - [音箱无声](fault_des)的维修方案是什么
+    - 修复[音箱无声](fault_des)相关的步骤
+    - [音箱无声](fault_des)的解决方法是什么
+
+- intent: fault_obj    # 所属系统
+  examples: |
+    - HMC码[202310150010](fault_hmc)对应的部件是什么
+    - HMC码[202310150010](fault_hmc)是哪个部件的故障
+    - HMC码[202310150010](fault_hmc)关联的具体部件是什么
+
+- intent: hmc_system     # 系统
+  examples: |
+    - HMC码[202310150010](fault_hmc)对应的系统是什么
+    - HMC码[202310150010](fault_hmc)是哪个系统出了问题
+    - HMC码[202310150010](fault_hmc)关联的是哪个系统
+
+- intent: sys_s_sys    # 系统-子系统
+  examples: |
+    - [计算机](system)包含有哪些子系统
+    - [网络设备](system)下有哪些子系统
+    - [计算机](system)的子系统
+
+- intent: sys_fault_obj    # 子系统-部件
+  examples: |
+    - [光学系统](s_sys)包含哪些部件
+    - 我想了解[光学系统](s_sys)的组成
+    - [光学系统](s_sys)有哪些关键部件
+    - [光学系统](s_sys)中主要的部件都有哪些
+
+- intent: fault_obj_sys     # 部件-子系统
+  examples: |
+    - [路由器](fault_obj)属于哪个子系统的
+    - [路由器](fault_obj)是哪个子系统中的一部分
+    - [路由器](fault_obj)属于哪个子系统
+
+- intent: fault_obj_fault_x_obj   # 部件-型号
+  examples: |
+    - [路由器](fault_obj)的型号是什么
+    - [路由器](fault_obj)是什么型号的
+    - [台式机]什么型号
+
+

+ 145 - 0
data/rules.yml

@@ -0,0 +1,145 @@
+version: "2.0"
+
+rules:
+
+- rule: Say goodbye anytime the user says goodbye
+  steps:
+  - intent: goodbye
+  - action: utter_goodbye
+
+- rule: say greet anytime the user says hello
+  steps:
+    - intent: greet
+    - action: utter_greet
+
+- rule: activate query des
+  steps:
+    - intent: query_des
+    - action: fault_des_form
+    - active_loop: fault_des_form
+
+- rule: activate query hmc
+  steps:
+    - intent: query_hmc
+    - action: fault_hmc_form
+    - active_loop: fault_hmc_form
+
+- rule: activate query obj
+  steps:
+    - intent: query_obj
+    - action: fault_obj_form
+    - active_loop: fault_obj_form
+
+- rule: activate query x_obj
+  steps:
+    - intent: query_x_obj
+    - action: fault_x_obj_form
+    - active_loop: fault_x_obj_form
+
+- rule: activate query s_sys
+  steps:
+    - intent: query_s_sys
+    - action: s_sys_form
+    - active_loop: s_sys_form
+
+- rule: activate query system
+  steps:
+    - intent: query_system
+    - action: system_form
+    - active_loop: system_form
+
+- rule: activate fault des
+  steps:
+    - intent: fault_des
+    - action: fault_hmc_form
+    - active_loop: fault_hmc_form
+
+- rule: activate fault excluds
+  steps:
+    - intent: fault_excluds
+    - action: fault_hmc_form
+    - active_loop: fault_hmc_form
+
+- rule: activate des excluds
+  steps:
+    - intent: des_excluds
+    - action: fault_des_form
+    - active_loop: fault_des_form
+
+- rule: activate fault obj
+  steps:
+    - intent: fault_obj
+    - action: fault_hmc_form
+    - active_loop: fault_hmc_form
+
+- rule: activate fault system
+  steps:
+    - intent: hmc_system
+    - action: fault_hmc_form
+    - active_loop: fault_hmc_form
+
+- rule: activate fault_s_sys
+  steps:
+    - intent: sys_s_sys
+    - action: system_form
+    - active_loop: system_form
+
+- rule: activate fault sys_fault_obj
+  steps:
+    - intent: sys_fault_obj
+    - action: s_sys_form
+    - active_loop: s_sys_form
+
+- rule: activate fault fault_obj_sys
+  steps:
+    - intent: fault_obj_sys
+    - action: fault_obj_form
+    - active_loop: fault_obj_form
+
+- rule: activate fault obj fault_x obj
+  steps:
+    - intent: fault_obj_fault_x_obj
+    - action: fault_obj_form
+    - active_loop: fault_obj_form
+
+- rule: deactivate contact des
+  condition:
+  - active_loop: fault_des_form
+  steps:
+    - active_loop: null
+    - action: FindTheCorrespondingFault_des
+
+- rule: deactivate contact hmc
+  condition:
+  - active_loop: fault_hmc_form
+  steps:
+    - active_loop: null
+    - action: FindTheCorrespondingFault_hmc
+
+- rule: deactivate contact obj
+  condition:
+  - active_loop: fault_obj_form
+  steps:
+    - active_loop: null
+    - action: FindTheCorrespondingFault_obj
+
+- rule: deactivate contact fault_x_obj
+  condition:
+  - active_loop: fault_x_obj_form
+  steps:
+    - active_loop: null
+    - action: FindTheCorrespondingFault_x_obj
+
+- rule: deactivate contact s_sys
+  condition:
+  - active_loop: s_sys_form
+  steps:
+    - active_loop: null
+    - action: FindTheCorrespondingFault_s_sys
+
+- rule: deactivate contact system
+  condition:
+  - active_loop: system_form
+  steps:
+    - active_loop: null
+    - action: FindTheCorrespondingFault_system

+ 104 - 0
data/stories.yml

@@ -0,0 +1,104 @@
+version: "2.0"
+
+stories:
+  - story: greet
+    steps:
+      - intent: greet
+      - action: utter_greet
+
+  - story: query des
+    steps:
+      - intent: query_des
+      - action: fault_des_form
+      - action: FindTheCorrespondingFault_des
+
+  - story: query hmc
+    steps:
+      - intent: query_hmc
+      - action: fault_hmc_form
+      - action: FindTheCorrespondingFault_hmc
+
+  - story: query obj
+    steps:
+      - intent: query_obj
+      - action: fault_obj_form
+      - action: FindTheCorrespondingFault_obj
+
+  - story: query x_obj
+    steps:
+      - intent: query_x_obj
+      - action: fault_x_obj_form
+      - action: FindTheCorrespondingFault_x_obj
+
+  - story: query s_sys
+    steps:
+      - intent: query_s_sys
+      - action: s_sys_form
+      - action: FindTheCorrespondingFault_s_sys
+
+  - story: query system
+    steps:
+      - intent: query_system
+      - action: system_form
+      - action: FindTheCorrespondingFault_system
+
+  - story: query fault_des
+    steps:
+      - intent: fault_des
+      - action: fault_hmc_form
+      - action: FindTheCorrespondingFault_hmc
+
+  - story: query fault_excluds
+    steps:
+      - intent: fault_excluds
+      - action: fault_hmc_form
+      - action: FindTheCorrespondingFault_hmc
+
+  - story: query des_excluds
+    steps:
+      - intent: des_excluds
+      - action: fault_des_form
+      - action: FindTheCorrespondingFault_des
+
+  - story: query fault_obj
+    steps:
+    - intent: fault_obj
+    - action: fault_hmc_form
+    - action: FindTheCorrespondingFault_hmc
+
+  - story: query hmc_system
+    steps:
+      - intent: hmc_system
+      - action: fault_hmc_form
+      - action: FindTheCorrespondingFault_hmc
+
+
+  - story: query sys_s_sys
+    steps:
+      - intent: sys_s_sys
+      - action: system_form
+      - action: FindTheCorrespondingFault_system
+
+  - story: query sys_fault_obj
+    steps:
+      - intent: sys_fault_obj
+      - action: s_sys_form
+      - action: FindTheCorrespondingFault_s_sys
+
+
+  - story: query fault_obj_sys
+    steps:
+      - intent: fault_obj_sys
+      - action: fault_obj_form
+      - action: FindTheCorrespondingFault_obj
+
+  - story: query fault_obj_fault_x_obj
+    steps:
+      - intent: fault_obj_fault_x_obj
+      - action: fault_obj_form
+      - action: FindTheCorrespondingFault_obj
+
+  - story: say goodbye
+    steps:
+      - intent: goodbye
+      - action: utter_goodbye

+ 156 - 0
domain.yml

@@ -0,0 +1,156 @@
+version: '2.0'
+config:
+  store_entities_as_slots: true
+session_config:
+  session_expiration_time: 120
+  carry_over_slots_to_new_session: true
+intents:
+- greet:
+    use_entities: true
+- goodbye:
+    use_entities: true
+- query_des:
+    use_entities: true
+- query_hmc:
+    use_entities: true
+- query_obj:
+    use_entities: true
+- query_x_obj:
+    use_entities: true
+- query_s_sys:
+    use_entities: true
+- query_system:
+    use_entities: true
+- fault_des:
+    use_entities: true
+- fault_excluds:
+    use_entities: true
+- des_excluds:
+    use_entities: true
+- fault_obj:
+    use_entities: true
+- hmc_system:
+    use_entities: true
+- sys_s_sys:
+    use_entities: true
+- sys_fault_obj:
+    use_entities: true
+- fault_obj_sys:
+    use_entities: true
+- fault_obj_fault_x_obj:
+    use_entities: true
+
+entities:
+- fault_des
+- fault_hmc
+- fault_obj
+- fault_x_obj
+- s_sys
+- system
+
+slots:
+  fault_des:
+    type: text
+    initial_value: null
+    auto_fill: true
+    influence_conversation: true
+  fault_hmc:
+    type: text
+    initial_value: null
+    auto_fill: true
+    influence_conversation: true
+  fault_obj:
+    type: text
+    initial_value: null
+    auto_fill: true
+    influence_conversation: true
+  fault_x_obj:
+    type: text
+    initial_value: null
+    auto_fill: true
+    influence_conversation: true
+  s_sys:
+    type: text
+    initial_value: null
+    auto_fill: true
+    influence_conversation: true
+  system:
+    type: text
+    initial_value: null
+    auto_fill: true
+    influence_conversation: true
+
+responses:
+  utter_greet:
+  - text: 您好,有什么可以帮助您的吗?
+  utter_goodbye:
+  - text: 再见,欢迎下次使用
+  utter_default:
+  - text: 可以换一个方式进行提问吗?
+  utter_rephrase:
+  - text: 抱歉,没能明白您的话,请重新输入。
+  utter_out:
+    - text: 对不起,暂时理解不了您的意思。
+  utter_moreinfoormation:
+  - text: 正在加载请稍后……
+  utter_exception:
+  - text: 遇到一些未知错误,请联系管理员
+actions:
+- FindTheCorrespondingFault_des
+- FindTheCorrespondingFault_hmc
+- FindTheCorrespondingFault_obj
+- FindTheCorrespondingFault_x_obj
+- FindTheCorrespondingFault_s_sys
+- FindTheCorrespondingFault_system
+- FindTheCorrespondingsmallTalk
+- utter_greet
+- utter_goodbye
+- utter_default
+- utter_rephrase
+- utter_moreinfoormation
+- utter_exception
+- action_default_fallback
+forms:
+  fault_des_form:
+    required_slots:
+      fault_des:
+      - entity: fault_des
+        type: from_entity
+      - intent: enter_data
+        type: from_text
+  fault_hmc_form:
+    required_slots:
+      fault_hmc:
+      - entity: fault_hmc
+        type: from_entity
+      - intent: enter_data
+        type: from_text
+  fault_obj_form:
+    required_slots:
+      fault_obj:
+      - entity: fault_obj
+        type: from_entity
+      - intent: enter_data
+        type: from_text
+  fault_x_obj_form:
+    required_slots:
+      fault_x_obj:
+      - entity: fault_x_obj
+        type: from_entity
+      - intent: enter_data
+        type: from_text
+  s_sys_form:
+    required_slots:
+      s_sys:
+      - entity: s_sys
+        type: from_entity
+      - intent: enter_data
+        type: from_text
+  system_form:
+    required_slots:
+      system:
+      - entity: system
+        type: from_entity
+      - intent: enter_data
+        type: from_text
+e2e_actions: []

+ 45 - 0
endpoints.yml

@@ -0,0 +1,45 @@
+# This file contains the different endpoints your bot can use.
+
+# Server where the models are pulled from.
+# https://rasa.com/docs/rasa/model-storage#fetching-models-from-a-server
+
+#models:
+#  url: http://my-server.com/models/default_core@latest
+#  wait_time_between_pulls:  10   # [optional](default: 100)
+
+# Server which runs your custom actions.
+# https://rasa.com/docs/rasa/custom-actions
+
+action_endpoint:
+  url: "http://localhost:5055/webhook"
+  timeout: 60
+
+# Tracker store which is used to store the conversations.
+# By default the conversations are stored in memory.
+# https://rasa.com/docs/rasa/tracker-stores
+
+#tracker_store:
+#    type: redis
+
+
+
+#    url: <host of the redis instance, e.g. localhost>
+#    port: <port of your redis instance, usually 6379>
+#    db: <number of your database within redis, e.g. 0>
+#    password: <password used for authentication>
+#    use_ssl: <whether or not the communication is encrypted, default false>
+
+#tracker_store:
+#    type: mongod
+#    url: <url to your mongo instance, e.g. mongodb://localhost:27017>
+#    db: <name of the db within your mongo instance, e.g. rasa>
+#    username: <username used for authentication>
+#    password: <password used for authentication>
+
+# Event broker which all conversation events should be streamed to.
+# https://rasa.com/docs/rasa/event-brokers
+
+#event_broker:
+#  type: "pika"
+#  url: "amqp://username:password@localhost"
+#  queue: "rasa_queue"

BIN
events.db


BIN
events.db-shm


+ 0 - 0
events.db-wal


+ 5 - 0
hook-py2neo.py

@@ -0,0 +1,5 @@
+# 和py2neo库相关的钩子文件
+from PyInstaller.utils.hooks import collect_submodules, collect_data_files
+
+hiddenimports = collect_submodules('py2neo')
+datas = collect_data_files('py2neo')

+ 63 - 0
match_entity_extractor.py

@@ -0,0 +1,63 @@
+import os
+from itertools import combinations
+from typing import Any, Text, Dict
+from rasa.nlu.extractors.extractor import EntityExtractor
+
+
+class MatchEntityExtractor(EntityExtractor):
+    """绝对匹配提取实体"""
+    provides = ["entities"]
+
+    defaults = {
+        "dictionary_path": None,
+        "take_long": None,
+        "take_short": None
+    }
+
+    def __init__(self, component_config=None):
+        print("init")
+        super(MatchEntityExtractor, self).__init__(component_config)
+        self.dictionary_path = self.component_config.get("dictionary_path")
+        self.take_long = self.component_config.get("take_long")
+        self.take_short = self.component_config.get("take_short")
+        if self.take_long and self.take_short:
+            raise ValueError("take_long and take_short can not be both True")
+        self.data = {}  # 用于绝对匹配的数据
+        for file_path in os.listdir(self.dictionary_path):
+            if file_path.endswith(".txt"):
+                file_path = os.path.join(self.dictionary_path, file_path)
+                file_name = os.path.basename(file_path)[:-4]
+                with open(file_path, mode="r", encoding="utf-8") as f:
+                    self.data[file_name] = f.read().splitlines()
+
+    def process(self, message, **kwargs):
+        """绝对匹配提取实体词"""
+        print("process")
+        entities = []
+        for entity, value in self.data.items():
+            for i in value:
+                start = message.text.find(i)
+                if start != -1:
+                    entities.append({
+                        "start": start,
+                        "end": start + len(i),
+                        "value": i,
+                        "entity": entity,
+                        "confidence": 1
+                    })
+        if self.take_long or self.take_short:
+            for i in list(combinations(entities, 2)):
+                v0, v1 = i[0]["value"], i[1]["value"]
+                if v0 in v1 or v1 in v0:
+                    (long, short) = (i[0], i[1]) if len(v0) > len(v1) else (i[1], i[0])
+                    if self.take_long == True and short in entities:
+                        entities.remove(short)
+                    if self.take_short == True and long in entities:
+                        entities.remove(long)
+        extracted = self.add_extractor_name(entities)
+        message.set("entities", extracted, add_to_output=True)
+
+    @classmethod
+    def load(cls, meta: Dict[Text, Any], model_dir=None, model_metadata=None, cached_component=None, **kwargs):
+        print("load")
+        return cls(meta)

+ 132 - 0
neo4j_jaro_winkler.py

@@ -0,0 +1,132 @@
+# from typing import Any, Optional, Text, Dict
+# from rasa.nlu.components import Component
+# from rasa.shared.nlu.training_data.message import Message
+# from rasa.shared.nlu.training_data.training_data import TrainingData
+# from neo4j import GraphDatabase
+# import jellyfish
+#
+# class Neo4jJaroWinkler(Component):
+#     """Custom component to query Neo4j and calculate Jaro-Winkler similarity."""
+#
+#     name = "Neo4jJaroWinkler"
+#
+#     defaults = {
+#         "uri": "bolt://localhost:7687",
+#         "auth": ("neo4j", "fdx3081475970"),
+#         "label": "故障名称",
+#         "property": "name",
+#         "threshold": 0.85
+#     }
+#
+#     def __init__(self, component_config: Optional[Dict[Text, Any]] = None) -> None:
+#         super().__init__(component_config)
+#         self.uri = component_config.get("uri")
+#         self.auth = component_config.get("auth")
+#         self.label = component_config.get("label")
+#         self.property = component_config.get("property")
+#         self.threshold = component_config.get("threshold")
+#         self.driver = GraphDatabase.driver(self.uri, auth=self.auth)
+#
+#     def train(self, training_data: TrainingData, config: Dict[Text, Any], **kwargs: Any) -> None:
+#         """Train the component."""
+#         pass
+#
+#     def process(self, message: Message, **kwargs: Any) -> None:
+#         """Process a message."""
+#         target_string = message.text
+#         query = f"""
+#         MATCH (n:{self.label})
+#         RETURN n.{self.property} AS name
+#         """
+#
+#         with self.driver.session() as session:
+#             result = session.run(query)
+#             names = [record["name"] for record in result]
+#
+#         similarity_scores = [(name, jellyfish.jaro_winkler_similarity(name, target_string)) for name in names]
+#         filtered_similarities = [(name, score) for name, score in similarity_scores if score > self.threshold]
+#
+#         if filtered_similarities:
+#             highest_similarity_name, highest_similarity_score = max(filtered_similarities, key=lambda x: x[1])
+#             message.set("highest_similarity_name", highest_similarity_name)
+#             message.set("highest_similarity_score", highest_similarity_score)
+#         else:
+#             message.set("highest_similarity_name", None)
+#             message.set("highest_similarity_score", None)
+#
+#     def persist(self, file_name: Text, model_dir: Text) -> Optional[Dict[Text, Any]]:
+#         """Persist the component."""
+#         pass
+#
+#     def __del__(self):
+#         """Close the Neo4j driver when the component is deleted."""
+#         self.driver.close()
+
+# neo4j_jaro_winkler.py
+
+from typing import Any, Optional, Text, Dict
+from rasa.nlu.components import Component
+from rasa.shared.nlu.training_data.message import Message
+from rasa.shared.nlu.training_data.training_data import TrainingData
+from neo4j import GraphDatabase
+import jellyfish
+
+class Neo4jJaroWinkler(Component):
+    """Custom component to query Neo4j and calculate Jaro-Winkler similarity."""
+
+    name = "Neo4jJaroWinkler"
+
+    defaults = {
+        "uri": "bolt://localhost:7687",
+        "auth": ("neo4j", "fdx3081475970"),
+        "label": "故障名称",
+        "property": "name",
+        "threshold": 0.85
+    }
+
+    def __init__(self, component_config: Optional[Dict[Text, Any]] = None) -> None:
+        super().__init__(component_config)
+        self.uri = component_config.get("uri")
+        self.auth = tuple(component_config.get("auth"))
+        self.label = component_config.get("label")
+        self.property = component_config.get("property")
+        self.threshold = component_config.get("threshold")
+        self.driver = GraphDatabase.driver(self.uri, auth=self.auth)
+
+    def train(self, training_data: TrainingData, config: Dict[Text, Any], **kwargs: Any) -> None:
+        """Train the component."""
+        pass
+
+    def process(self, message: Message, **kwargs: Any) -> None:
+        """Process a message."""
+        target_string = message.data.get("text")
+        if not target_string:
+            return
+
+        query = f"""
+        MATCH (n:{self.label})
+        RETURN n.{self.property} AS name
+        """
+
+        with self.driver.session() as session:
+            result = session.run(query)
+            names = [record["name"] for record in result]
+
+        similarity_scores = [(name, jellyfish.jaro_winkler_similarity(name, target_string)) for name in names]
+        filtered_similarities = [(name, score) for name, score in similarity_scores if score > self.threshold]
+
+        if filtered_similarities:
+            highest_similarity_name, highest_similarity_score = max(filtered_similarities, key=lambda x: x[1])
+            message.set("highest_similarity_name", highest_similarity_name)
+            message.set("highest_similarity_score", highest_similarity_score)
+        else:
+            message.set("highest_similarity_name", None)
+            message.set("highest_similarity_score", None)
+
+    def persist(self, file_name: Text, model_dir: Text) -> Optional[Dict[Text, Any]]:
+        """Persist the component."""
+        pass
+
+    def __del__(self):
+        """Close the Neo4j driver when the component is deleted."""
+        self.driver.close()

+ 304 - 0
process_new.py

@@ -0,0 +1,304 @@
+from time import time
+from py2neo import Graph, Node, Relationship, Subgraph
+import json
+import pandas as pd
+import re
+
+
+# 读取Excel文件
+def excel_to_json(excel_file, json_file):
+    # 加载Excel文件
+    data_frame = pd.read_excel(excel_file)
+
+    # 将DataFrame转换为JSON字符串
+    json_data = data_frame.to_json(orient='records', force_ascii=False)
+
+    # 写入到JSON文件
+    with open(json_file, 'w', encoding='utf-8') as f:
+        f.write(json_data)
+
+    print(f"转换完成,JSON文件已保存至:{json_file}")
+
+
+# 示例用法
+excel_file_path = r'C:\Users\Machenike\Desktop\xn_data.xlsx'  # Excel文件路径
+json_file_path = r'D:\hiddz\KG_QA\T_Neo\data.json'  # 输出的JSON文件路径
+
+
+excel_to_json(excel_file_path, json_file_path)
+
+
+class FaultGraph:
+    def __getinitargs__(self):
+        self.data_path = json_file_path
+
+    def read_nodes(self):
+        hmcs = []  # 故障名称
+        describs = []  # 故障日期
+        systems = []  # 故障现象
+        s_systems = []  # 所属系统
+        excludes = []  # 专业
+        objs = []  # 故障产品或部位
+        x_objects = []  # 故障原因
+        plans = []  # 排除方法
+        doc_id = []
+        doc_name = []
+
+        fault_infos = []
+        rels_des = []
+        rels_sys = []
+        sys_sys = []
+        rels_excludes = []
+        rels_objs = []
+        rels_obj_obj = []
+        sys_objs = []
+        plan_sys = []
+
+        count = 0
+        with open('data.json', "r", encoding='utf-8') as f:
+            data = json.load(f)
+            for i in data:
+                fault_dict = {}
+                count += 1
+                HMC = str(i['HMC'])
+                fault_dict['HMC'] = HMC
+                fault_dict['故障描述'] = ''
+                fault_dict['维修策略'] = ''
+                fault_dict['系统'] = ''
+                fault_dict['子系统'] = ''
+                fault_dict['成品'] = ''
+                fault_dict['型号'] = ''
+                fault_dict['机型'] = ''
+                fault_dict['文档id'] = ''
+                fault_dict['文档名称'] = ''
+
+                if 'HMC' in i:
+                    fault_dict['HMC'] = i['HMC']
+                    fault_dict['文档id'] = i['文档id']
+                    fault_dict['文档名称'] = i['文档名称']
+                    hmcs.append((i['HMC'], i['文档id'], i['文档名称']))
+
+                if '故障描述' in i:
+                    fault_dict['故障描述'] = i['故障描述']
+                    fault_dict['文档id'] = i['文档id']
+                    fault_dict['文档名称'] = i['文档名称']
+                    rels_des.append([HMC, i['故障描述']])
+                    describs.append((i['故障描述'], i['文档id'], i['文档名称']))
+
+                if '机型' in i:
+                    fault_dict['机型'] = i['机型']
+                    fault_dict['文档id'] = i['文档id']
+                    fault_dict['文档名称'] = i['文档名称']
+                    plans.append((i['机型'], i['文档id'], i['文档名称']))
+                    plan_sys.append([i['机型'], i['系统']])
+
+                if '系统' in i:
+                    fault_dict['系统'] = i['系统']
+                    fault_dict['文档id'] = i['文档id']
+                    fault_dict['文档名称'] = i['文档名称']
+                    systems.append((i['系统'], i['文档id'], i['文档名称']))
+                    rels_sys.append([HMC, i['系统']])
+
+                if '子系统' in i:
+                    fault_dict['子系统'] = i['子系统']
+                    fault_dict['文档id'] = i['文档id']
+                    fault_dict['文档名称'] = i['文档名称']
+                    if i['子系统'] != None:
+                        s_systems.append((i['子系统'], i['文档id'], i['文档名称']))
+                    else:
+                        continue
+                    sys_sys.append([i['系统'], i['子系统']])
+
+                if '维修策略' in i:
+                    fault_dict['维修策略'] = i['维修策略']
+                    fault_dict['文档id'] = i['文档id']
+                    fault_dict['文档名称'] = i['文档名称']
+                    excludes.append((i['维修策略'], i['文档id'], i['文档名称']))
+                    rels_excludes.append([HMC, i['维修策略']])
+                    rels_excludes.append([i['故障描述'], i['维修策略']])
+
+                if '成品' in i:
+                    fault_dict['成品'] = i['成品']
+                    fault_dict['文档id'] = i['文档id']
+                    fault_dict['文档名称'] = i['文档名称']
+                    objs.append((i['成品'], i['文档id'], i['文档名称']))
+                    rels_objs.append([HMC, i['成品']])
+                    sys_objs.append([i['子系统'], i['成品']])
+
+                if '型号' in i:
+                    fault_dict['型号'] = i['型号']
+                    fault_dict['文档id'] = i['文档id']
+                    fault_dict['文档名称'] = i['文档名称']
+                    x_objects.append((i['型号'], i['文档id'], i['文档名称']))
+                    rels_obj_obj.append([i['成品'], i['型号']])
+
+                if '文档id' in i:
+                    fault_dict['文档id'] = i['文档id']
+                    doc_id.append(i['文档id'])
+
+                if '文档名称' in i:
+                    fault_dict['文档名称'] = i['文档名称']
+                    doc_name.append(i['文档名称'])
+
+                fault_infos.append(fault_dict)
+
+        return set(hmcs), set(describs), set(systems), set(s_systems), set(excludes), set(objs), set(x_objects), set(plans),\
+               set(doc_id), set(doc_name), fault_infos, rels_des, rels_sys, sys_sys, rels_excludes, rels_objs, rels_obj_obj, sys_objs, plan_sys
+
+    '''建立节点'''
+
+    def create_node(self, label, nodes):
+        count = 0
+        nodess = []
+        for node_name in nodes:
+            # 检查node_name是否是三元组
+            if isinstance(node_name, (tuple, list)) and len(node_name) == 3:
+                node = Node(label, name=node_name[0], doc_id=node_name[1], doc_name=node_name[2])
+                nodess.append(node)
+                count += 1
+            # 如果是单个字符串,我们可以将其转为一个默认的doc_id和doc_name
+            elif isinstance(node_name, str):
+                node = Node(label, name=node_name, doc_id='default_id', doc_name='default_name')
+                nodess.append(node)
+                count += 1
+            else:
+                print(f"Warning: Invalid node format {node_name}")
+        return nodess
+
+    '''创建知识图谱中心故障的节点'''
+
+    def create_fault_nodes(self, fault_infos):
+        count = 0
+        nodes = []
+        for fault_dict in fault_infos:
+            node_properties = Node("Fault", name=fault_dict['HMC'], fault_des=fault_dict['故障描述'],
+                                   fault_excluds=fault_dict['维修策略'],
+                                   systems=fault_dict['系统'], s_sys=fault_dict['子系统'], fault_obj=fault_dict['成品'],
+                                   fault_x_obj=fault_dict['型号'],
+                                   plans=fault_dict['机型'])
+            node_properties = {k: v for k, v in node_properties.items() if v is not None or v != ''}
+            node = Node('Fault', **node_properties)
+            nodes.append(node)
+            count += 1
+        return nodes
+
+    '''创建知识图谱实体节点类型schema'''
+
+    def create_graphnodes(self):
+        hmcs, describs, systems, s_systems, excludes, objs, x_objects, plans, doc_id, doc_name, fault_infos, rels_des, \
+        rels_sys, sys_sys, rels_excludes, rels_objs, rels_obj_obj, sys_objs, plan_sys = self.read_nodes()
+        a = self.create_fault_nodes(fault_infos)
+        b = self.create_node('HMC', hmcs)
+        c = self.create_node('故障描述', describs)
+        d = self.create_node('系统', systems)
+        e = self.create_node('子系统', s_systems)
+        f = self.create_node('维修策略', excludes)
+        g = self.create_node('成品', objs)
+        h = self.create_node('型号', x_objects)
+        i = self.create_node('机型', plans)
+
+        return a + b + c + d + e + f + g + h + i
+
+    '''创建实体关系边'''
+
+    def create_graphrels(self):
+        hmcs, describs, systems, s_systems, excludes, objs, x_objects, plans, doc_id, doc_name, fault_infos, rels_des, \
+        rels_sys, sys_sys, rels_excludes, rels_objs, rels_obj_obj, sys_objs, plan_sys = self.read_nodes()
+        a = self.create_relationship('name', 'describ', rels_des, '故障描述', '故障描述')
+        b = self.create_relationship('name', 'systems', rels_sys, '系统', '系统')
+        c = self.create_relationship('name', 'excludes', rels_excludes, '维修策略', '维修策略')
+        d = self.create_relationship('name', 'object', rels_objs, '成品', '成品')
+        e = self.create_relationship('object', 'x_object', rels_obj_obj, '型号', '型号')
+        f = self.create_relationship('name', 'system', sys_sys, '子系统', '子系统')
+        g = self.create_relationship('name', 'object', sys_objs, '成品', '成品')
+        h = self.create_relationship('plan', 'system', plan_sys, '系统', '系统')
+
+        return a + b + c + d + e + f + g + h
+
+    '''创建实体关联边'''
+
+    def create_relationship(self, start_node, end_node, edges, rel_type, rel_name):
+        count = 0
+        # 去重处理
+        relationships = []
+        set_edges = []
+        for edge in edges:
+            if len(edge) == 2 and all(isinstance(item, str) for item in edge):
+                set_edges.append('###'.join(edge))
+            else:
+                print(f'warning: Invaild edge {edge}. Ignoring.')
+
+        for edge in set(set_edges):
+            edge = edge.split('###')
+            p = edge[0]
+            q = edge[1]
+            query = "match(p:%s),(q:%s) where p.name='%s'and q.name='%s' merge (p)-[rel:%s{name:'%s'}]->(q)" % (
+                start_node, end_node, p, q, rel_type, rel_name)
+            relationships.append(query)
+            try:
+                count += 1
+            except Exception as e:
+                print()
+        return relationships
+
+class ProgressBar:
+    def __init__(self, total, name='', mode=0):
+        self.total = total
+        self.name = name
+        modes = (
+            lambda n: f"进度|{'=' * n}{'>'}{'·' * (100 - n)}"[:-1] + f"| {n}% |",
+            lambda n: f"进度|{'█' * n:100s}| {n}% |",
+            lambda n: f"\033[31m{'♥' * n}{'♡' * (100 - n)}  进度{n}♥\033[0m",
+            lambda n: f"\033[46m进度{' ' * n}{n}% \033[44m{' ' * (100 - n)}\033[0m",
+        )
+        mode = 0 if mode > 3 else mode
+        self.mode = modes[mode]
+
+    def now(self, n):
+        if BAR:
+            n_ = 100 * n // self.total
+            print(f"\r{self.name}: {self.mode(n_)} [{n:05d} / {self.total}]", end='', flush=True)
+
+    def end(self, name):
+        print(name)
+
+BAR = True
+t0 = time()
+
+
+if __name__ == '__main__':
+    handler = FaultGraph()
+    graphnodes = handler.create_graphnodes()
+    graphrels = handler.create_graphrels()
+    print(f"匹配所用时间:{time() - t0:.1f}秒")
+    print('正在创建图谱 . . . . . .', end='')
+    graph = Graph("bolt://localhost:7687", auth=("neo4j", "fdx3081475970"))
+
+    dicts = {}
+    for i in graphnodes:
+        dicts[i['name']] = i
+    import numpy as np
+
+    len(np.array(graphrels))
+    p = ProgressBar(len(np.array(graphrels)), '匹配进度', mode=3)
+    s = 0
+    relationships = []
+    for i in graphrels:
+        p.now(s)
+        n = i.split(' ')[2].split("'")[1]
+        if re.match("^\d+$", n):
+            n = int(n)
+        m = i.split("'")[3]
+
+        rr = i.split('[')[-1].split(']')[0][4:].split("'")[1]
+        r = i.split('[')[-1].split(':')[1].split('{')[0]
+        relationships.append(Relationship(dicts[n], r, dicts[m], name=rr))
+        s += 1
+
+    print(f"匹配所用时间:{time() - t0:.1f}秒")
+    print('数据正在导入数据库......')
+
+    graph.create(Subgraph(nodes=graphnodes, relationships=relationships))
+
+    print('\r知识图谱数据库创建完成 !!')
+    print(f"总体所用时间:{time() - t0:.1f}秒")

BIN
rasa.db


+ 71 - 0
rasa_run.py

@@ -0,0 +1,71 @@
+import subprocess
+from pathlib import Path
+import sys
+import logging
+import shutil
+
+# 设置日志记录
+logging.basicConfig(level=logging.DEBUG, filename='rasa_run.log', filemode='w',
+                    format='%(name)s - %(levelname)s - %(message)s')
+
+# 获取 rasa 命令的路径
+rasa_path = r"D:\Anaconda3\envs\lq1\Scripts\rasa.exe"
+
+if not rasa_path:
+    logging.error("Rasa 命令未找到,请检查是否已正确安装")
+    print("Rasa 命令未找到,请检查是否已正确安装", file=sys.stderr)
+    sys.exit(1)  # 确保程序退出
+
+def get_resource_path(relative_path):
+    """获取资源的真实路径"""
+    if getattr(sys, 'frozen', False):
+        # 如果是打包后的exe程序,则获取临时目录下的资源路径
+        base_path = Path(sys._MEIPASS)
+    else:
+        # 否则,获取当前文件所在目录
+        base_path = Path(__file__).resolve().parent
+    return str(base_path / relative_path)
+
+def main():
+    # 获取模型路径(假设你有一个名为 'models' 的文件夹)
+    model_filename = "20241127-105650.tar.gz"
+    model_path = get_resource_path(f"models/{model_filename}")
+
+    # 获取 endpoints.yml 路径
+    endpoints_path = get_resource_path("endpoints.yml")
+
+    # 输出调试信息
+    logging.info(f"模型路径: {model_path}")
+    logging.info(f"端点配置路径: {endpoints_path}")
+
+    # 确保模型文件和 endpoints 文件存在
+    if not Path(model_path).is_file() or not Path(endpoints_path).is_file():
+        logging.error("模型文件或端点配置文件不存在")
+        print("模型文件或端点配置文件不存在", file=sys.stderr)
+        return
+
+    # 启动 Rasa 服务器
+    try:
+        logging.info("启动 Rasa 服务器...")
+        command = [
+            rasa_path,
+            'run',
+            '--model', model_path,
+            '--endpoints', endpoints_path  # 添加 endpoints 参数
+        ]
+        subprocess.run(command, check=True)  # 将 check=True 用于捕获错误
+    except subprocess.CalledProcessError as e:
+        logging.error(f"启动 Rasa 服务器失败: {e}")
+        print(f"启动 Rasa 服务器失败: {e}", file=sys.stderr)
+    except FileNotFoundError:
+        logging.error("Rasa 命令未找到,请检查是否已正确安装并包含在打包文件中")
+        print("Rasa 命令未找到,请检查是否已正确安装并包含在打包文件中", file=sys.stderr)
+    except Exception as e:
+        logging.exception("发生意外错误")
+        print(f"发生意外错误: {e}", file=sys.stderr)
+
+if __name__ == '__main__':
+    main()
+
+if __name__ == '__main__':
+    main()

+ 59 - 0
rasa_run_actions.py

@@ -0,0 +1,59 @@
+import sys
+import subprocess
+from pathlib import Path
+import logging
+
+# 设置日志记录
+logging.basicConfig(level=logging.DEBUG, filename='app.log', filemode='w',
+                    format='%(name)s - %(levelname)s - %(message)s')
+
+def resource_path(relative_path):
+    """ 获取附加资源的绝对路径 """
+    try:
+        # PyInstaller 创建临时文件夹,所有打包的文件都在此文件夹下
+        base_path = Path(sys._MEIPASS)
+    except Exception:
+        base_path = Path(__file__).resolve().parent
+
+    return str(base_path / relative_path)
+
+# 动态设置 Rasa SDK 的路径
+rasa_sdk_path = resource_path('Lib/site-packages/rasa_sdk')
+
+if rasa_sdk_path not in sys.path:
+    sys.path.append(rasa_sdk_path)
+
+from rasa_sdk.endpoint import run as sdk_run
+
+def main():
+    print("开始主函数...")
+    try:
+        # 启动 Rasa SDK 动作服务器
+        print("尝试使用 Rasa SDK Python API 启动动作服务器...")
+        action_package_name = "actions"  # 指定包含动作的包名称
+        sdk_run(action_package_name=action_package_name, port=5055)
+
+        print("动作服务器已启动,按 Enter 键退出。")
+        input()  # 等待用户输入
+    except Exception as e:
+        logging.exception("通过 Python API 启动动作服务器失败")
+        print(f"通过 Python API 启动动作服务器失败: {e}")
+
+        # 作为备选方案,尝试使用 Python 解释器启动 Rasa 动作服务器
+        try:
+            print("尝试使用 Python 解释器启动 Rasa 动作服务器...")
+
+            # 获取 Conda 环境中的 Python 解释器路径
+            conda_env_path = Path(resource_path('.')).parents[1]  # 调整路径以匹配你的实际情况
+            python_executable = conda_env_path / 'python.exe'
+            rasa_script = conda_env_path / 'Scripts' / 'rasa'
+
+            # 使用 Python 解释器运行 rasa run actions
+            subprocess.run([str(python_executable), str(rasa_script), 'run', 'actions'], check=True)
+        except Exception as e:
+            logging.exception("通过 Python 解释器启动 Rasa 动作服务器失败")
+            print(f"通过 Python 解释器启动 Rasa 动作服务器失败: {e}")
+            sys.exit(1)
+
+if __name__ == "__main__":
+    main()

+ 208 - 0
requirements.txt

@@ -0,0 +1,208 @@
+absl-py==0.12.0
+aio-pika==6.8.0
+aiofiles==0.7.0
+aiohttp==3.7.4
+aiormq==3.3.1
+alembic==1.4.3
+altgraph==0.17.4
+annotated-types==0.5.0
+anyio==3.7.1
+APScheduler==3.7.0
+astunparse==1.6.3
+async-generator==1.10
+async-timeout==3.0.1
+attrs==19.3.0
+Automat==20.2.0
+baidu-aip==2.2.18.0
+bidict==0.21.2
+boto3==1.18.32
+botocore==1.21.32
+cachetools==4.2.2
+certifi==2021.5.30
+cffi==1.14.6
+chardet==3.0.4
+click==8.0.1
+cloudpickle==1.6.0
+colorama==0.4.4
+colorclass==2.2.0
+coloredlogs==15.0.1
+colorhash==1.0.3
+comtypes==1.1.10
+constantly==15.1.0
+croniter==1.0.15
+cryptography==3.4.8
+cssselect==1.1.0
+cycler==0.10.0
+decorator==4.4.2
+dm-tree==0.1.6
+dnspython==1.16.0
+docker==5.0.0
+docopt==0.6.2
+english==2020.7.0
+et-xmlfile==1.1.0
+exceptiongroup==1.2.0
+fastapi==0.103.2
+fbmessenger==6.0.0
+Flask==2.0.1
+Flask-SocketIO==4.3.1
+future==0.18.2
+gast==0.3.3
+gitdb==4.0.7
+GitPython==3.1.20
+google-auth==1.10.1
+google-auth-oauthlib==0.4.5
+google-pasta==0.2.0
+grpcio==1.39.0
+h11==0.9.0
+h2==3.2.0
+h5py==2.10.0
+hpack==3.0.0
+httpcore==0.11.1
+httplib2==0.19.1
+httptools==0.3.0
+httpx==0.15.4
+humanfriendly==9.2
+hyperframe==5.2.0
+hyperlink==21.0.0
+idna==2.10
+importlib-metadata==4.8.1
+incremental==21.3.0
+isodate==0.6.0
+itemadapter==0.4.0
+itemloaders==1.0.4
+itsdangerous==2.0.1
+jieba==0.42.1
+Jinja2==3.0.1
+jmespath==0.10.0
+joblib==0.15.1
+jsonpickle==2.0.0
+jsonschema==3.2.0
+kafka-python==1.4.7
+Keras-Preprocessing==1.1.2
+kiwisolver==1.3.2
+lxml==4.6.3
+Mako==1.1.5
+Markdown==3.3.4
+MarkupSafe==2.0.1
+matplotlib==3.3.4
+mattermostwrapper==2.2
+monotonic==1.6
+multidict==4.7.6
+neo4j==5.15.0
+neotime==1.7.4
+networkx==2.5.1
+numpy==1.18.5
+oauth2client==4.1.3
+oauthlib==3.1.1
+openpyxl==3.1.2
+opt-einsum==3.3.0
+packaging==20.0
+pamqp==2.3.0
+pandas==1.3.5
+pansi==2020.7.3
+parsel==1.6.0
+pefile==2023.2.7
+pika==1.2.0
+Pillow==8.3.1
+priority==1.3.0
+prompt-toolkit==2.0.10
+Protego==0.1.16
+protobuf==3.17.3
+psycopg2-binary==2.8.6
+py2neo==2020.1.1
+pyasn1==0.4.8
+pyasn1-modules==0.2.8
+PyAudio @ file:///C:/Users/86183/Desktop/PyAudio-0.2.11-cp37-cp37m-win_amd64.whl
+pycparser==2.20
+pydantic==2.5.3
+pydantic_core==2.14.6
+PyDispatcher==2.0.5
+pydot==1.4.2
+pygame==2.5.2
+Pygments==2.10.0
+pyinstaller==5.13.2
+pyinstaller-hooks-contrib==2024.4
+PyJWT==2.1.0
+pykwalify==1.8.0
+pymongo==3.10.1
+pyOpenSSL==20.0.1
+pyparsing==2.4.7
+pypiwin32==223
+pyreadline==2.1
+pyrsistent==0.18.0
+pyTelegramBotAPI==3.8.3
+python-crfsuite==0.9.7
+python-dateutil==2.8.2
+python-editor==1.0.4
+python-engineio==4.2.1
+python-socketio==4.6.0
+pyttsx3==2.90
+pytz==2021.1
+pywin32==227
+pywin32-ctypes==0.2.2
+PyYAML==5.4.1
+questionary==1.4.0
+queuelib==1.6.2
+rasa==2.6.2
+rasa-sdk==2.8.1
+rasa-x==0.40.1
+redis==3.5.3
+regex==2020.9.27
+requests==2.25.1
+requests-oauthlib==1.3.0
+requests-toolbelt==0.9.1
+rfc3986==1.5.0
+rocketchat-API==1.15.0
+rsa==4.0
+ruamel.yaml==0.16.13
+ruamel.yaml.clib==0.2.6
+s3transfer==0.5.0
+sanic==20.9.0
+Sanic-Cors==0.10.0.post3
+sanic-jwt==1.7.0
+Sanic-Plugins-Framework==0.9.5
+scikit-learn==0.24.2
+scipy==1.7.1
+Scrapy==2.5.0
+sentry-sdk==0.19.5
+service-identity==21.1.0
+six==1.16.0
+sklearn-crfsuite==0.3.6
+slackclient==2.9.3
+smmap==4.0.0
+sniffio==1.2.0
+SQLAlchemy==1.3.12
+starlette==0.27.0
+tabulate==0.8.9
+tensorboard==2.6.0
+tensorboard-data-server==0.6.1
+tensorboard-plugin-wit==1.8.0
+tensorflow==2.3.4
+tensorflow-addons==0.12.0
+tensorflow-estimator==2.3.0
+tensorflow-hub==0.10.0
+tensorflow-probability==0.11.1
+termcolor==1.1.0
+terminaltables==3.1.0
+threadpoolctl==2.2.0
+tqdm==4.59.0
+twilio==6.50.1
+Twisted==21.7.0
+twisted-iocpsupport==1.0.1
+typeguard==2.12.1
+typing_extensions==4.7.1
+tzlocal==2.1
+ujson @ file:///C:/Users/86183/Desktop/ujson-1.35-cp37-cp37m-win_amd64.whl
+urllib3==1.25.7
+uvicorn==0.22.0
+w3lib==1.22.0
+wcwidth==0.2.5
+webexteamssdk==1.6
+websocket-client==1.2.1
+websockets==11.0.3
+Werkzeug==2.0.1
+wincertstore==0.2
+wrapt==1.12.1
+yarl==1.6.3
+zipp==3.5.0
+zope.interface==5.4.0

+ 21 - 0
story_graph.dot

@@ -0,0 +1,21 @@
+digraph  {
+0 [class="start active", fillcolor=green, fontsize=12, label=START, style=filled];
+"-1" [class=end, fillcolor=red, fontsize=12, label=END, style=filled];
+1 [class=active, fontsize=12, label=action_session_start];
+2 [class=active, fontsize=12, label=fault_hmc_form];
+3 [class=active, fontsize=12, label=FindTheCorrespondingFault_hmc];
+4 [class=active, fontsize=12, label=fault_obj_form];
+5 [class=active, fontsize=12, label=FindTheCorrespondingFault_obj];
+6 [class="intent dashed active", label="  ?  ", shape=rect];
+7 [class="intent active", fillcolor=lightblue, label=202310150010, shape=rect, style=filled];
+8 [class="intent active", fillcolor=lightblue, label="路由器属于哪个子系统", shape=rect, style=filled];
+0 -> "-1"  [class="", key=NONE, label=""];
+0 -> 1  [class=active, key=NONE, label=""];
+1 -> 7  [class=active, key=0];
+2 -> 3  [class=active, key=NONE, label=""];
+3 -> 8  [class=active, key=0];
+4 -> 5  [class=active, key=NONE, label=""];
+5 -> 6  [class=active, key=NONE, label=""];
+7 -> 2  [class=active, key=0];
+8 -> 4  [class=active, key=0];
+}

+ 47 - 0
test_qa.py

@@ -0,0 +1,47 @@
+import urllib.request
+import urllib.parse
+import json
+import string
+import jieba
+import ssl
+import pytest
+
+def talk(keyword):
+    target = r'http://api.qingyunke.com/api.php?key=free&appid=0&msg='
+
+    if keyword == "exit":
+        return "不聊算了,拜拜"
+
+    try:
+        tmp = target + keyword
+        url = urllib.parse.quote(tmp, safe=string.printable)
+
+        # 创建 SSL 上下文
+        context = ssl.create_default_context()
+        context.check_hostname = False
+        context.verify_mode = ssl.CERT_NONE
+
+        with urllib.request.urlopen(url, context=context) as page:
+            html = page.read().decode("utf-8")
+            res = json.loads(html)
+
+            content = res.get('content', '')
+            if not content:
+                return "没有获取到回复"
+
+            # 分词处理
+            jieba.add_word('菲菲')
+            words = jieba.cut(content, cut_all=False)
+            answers = ''.join(['Friday' if word == '菲菲' else word for word in words])
+
+            # 替换特殊字符
+            answers = answers.replace('{br}', '\n').replace('&quot;', '"')
+
+            # 添加更多处理逻辑
+            answers = answers.replace('你好', '嗨')  # 示例:将“你好”替换成“嗨”
+            if '再见' in answers:
+                answers = answers.replace('再见', '下次见!')  # 示例:将“再见”替换成“下次见!”
+
+            return answers
+    except Exception as e:
+        return f"发生错误: {str(e)}"

+ 91 - 0
tests/test_stories.yml

@@ -0,0 +1,91 @@
+#### This file contains tests to evaluate that your bot behaves as expected.
+#### If you want to learn more, please see the docs: https://rasa.com/docs/rasa/testing-your-assistant
+
+stories:
+- story: happy path 1
+  steps:
+  - user: |
+      hello there!
+    intent: greet
+  - action: utter_greet
+  - user: |
+      amazing
+    intent: mood_great
+  - action: utter_happy
+
+- story: happy path 2
+  steps:
+  - user: |
+      hello there!
+    intent: greet
+  - action: utter_greet
+  - user: |
+      amazing
+    intent: mood_great
+  - action: utter_happy
+  - user: |
+      bye-bye!
+    intent: goodbye
+  - action: utter_goodbye
+
+- story: sad path 1
+  steps:
+  - user: |
+      hello
+    intent: greet
+  - action: utter_greet
+  - user: |
+      not good
+    intent: mood_unhappy
+  - action: utter_cheer_up
+  - action: utter_did_that_help
+  - user: |
+      yes
+    intent: affirm
+  - action: utter_happy
+
+- story: sad path 2
+  steps:
+  - user: |
+      hello
+    intent: greet
+  - action: utter_greet
+  - user: |
+      not good
+    intent: mood_unhappy
+  - action: utter_cheer_up
+  - action: utter_did_that_help
+  - user: |
+      not really
+    intent: deny
+  - action: utter_goodbye
+
+- story: sad path 3
+  steps:
+  - user: |
+      hi
+    intent: greet
+  - action: utter_greet
+  - user: |
+      very terrible
+    intent: mood_unhappy
+  - action: utter_cheer_up
+  - action: utter_did_that_help
+  - user: |
+      no
+    intent: deny
+  - action: utter_goodbye
+
+- story: say goodbye
+  steps:
+  - user: |
+      bye-bye!
+    intent: goodbye
+  - action: utter_goodbye
+
+- story: bot challenge
+  steps:
+  - user: |
+      are you a bot?
+    intent: bot_challenge
+  - action: utter_iamabot