【Day27】建立一个 QA Bot

今天要来跟各位一起解析 QnA Maker Bot,以下简称 QA Bot。

今天是参考 官方范例程序码

bots/qna_bot.py

class QnABot(ActivityHandler):
    def __init__(
        self,
        conversation_state: ConversationState,
        user_state: UserState,
        dialog: Dialog,
    ):
        self.conversation_state = conversation_state
        self.user_state = user_state
        self.dialog = dialog

    async def on_turn(self, turn_context: TurnContext):
        await super().on_turn(turn_context)
        await self.conversation_state.save_changes(turn_context)
        await self.user_state.save_changes(turn_context)

    async def on_members_added_activity(
        self, members_added: List[ChannelAccount], turn_context: TurnContext
    ):
        for member in members_added:
            if member.id != turn_context.activity.recipient.id:
                await turn_context.send_activity("Hello and welcome!")

    async def on_message_activity(self, turn_context: TurnContext):
        await DialogHelper.run_dialog(
            self.dialog,
            turn_context,
            self.conversation_state.create_property("DialogState"),
        )

这里大家不知道有没有发现一件事情,就是这个范例明明有用到 Dialog,但是却没有看到像昨天一样的 .py 档。这是因为 Microsoft 专门帮 QnA Maker 写了一个 dialog.py 档,放在 SDK 里了。

qnamaker_dialog.py

qnamaker_dialog.py

    def __init__(
        self,
        knowledgebase_id: str,
        endpoint_key: str,
        hostname: str,
        no_answer: Activity = None,
        threshold: float = DEFAULT_THRESHOLD,
        active_learning_card_title: str = DEFAULT_CARD_TITLE,
        card_no_match_text: str = DEFAULT_CARD_NO_MATCH_TEXT,
        top: int = DEFAULT_TOP_N,
        card_no_match_response: Activity = None,
        strict_filters: [Metadata] = None,
        dialog_id: str = "QnAMakerDialog",
    ):
        """
        Initializes a new instance of the QnAMakerDialog class.

        :param knowledgebase_id: The ID of the QnA Maker knowledge base to query.
        :param endpoint_key: The QnA Maker endpoint key to use to query the knowledge base.
        :param hostname: The QnA Maker host URL for the knowledge base, starting with "https://" and
        ending with "/qnamaker".
        :param no_answer: The activity to send the user when QnA Maker does not find an answer.
        :param threshold: The threshold for answers returned, based on score.
        :param active_learning_card_title: The card title to use when showing active learning options
        to the user, if active learning is enabled.
        :param card_no_match_text: The button text to use with active learning options,
        allowing a user to indicate none of the options are applicable.
        :param top: The maximum number of answers to return from the knowledge base.
        :param card_no_match_response: The activity to send the user if they select the no match option
        on an active learning card.
        :param strict_filters: QnA Maker metadata with which to filter or boost queries to the
        knowledge base; or null to apply none.
        :param dialog_id: The ID of this dialog.
        """
        super().__init__(dialog_id)


        self.knowledgebase_id = knowledgebase_id
        self.endpoint_key = endpoint_key
        self.hostname = hostname
        self.no_answer = no_answer
        self.threshold = threshold
        self.active_learning_card_title = active_learning_card_title
        self.card_no_match_text = card_no_match_text
        self.top = top
        self.card_no_match_response = card_no_match_response
        self.strict_filters = strict_filters


        self.maximum_score_for_low_score_variation = 0.95


        self.add_step(self.__call_generate_answer)
        self.add_step(self.__call_train)
        self.add_step(self.__check_for_multiturn_prompt)
        self.add_step(self.__display_qna_result)

从这个 dialog 的建构函式来看,用的是 waterfall dialog,若要自己客制化步骤,则加在

        ## 若要新增步骤,则是加在这里,并在底下定义新的函式
        self.add_step(self.__newSTEP)

        self.add_step(self.__call_generate_answer)
        self.add_step(self.__call_train)
        self.add_step(self.__check_for_multiturn_prompt)
        self.add_step(self.__display_qna_result)

config.py

接下来再回到 官方范例程序码 的 config.py。

import os

class DefaultConfig:
    """ Bot Configuration """

    PORT = 3978
    APP_ID = os.environ.get("MicrosoftAppId", "")
    APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
    QNA_KNOWLEDGEBASE_ID = os.environ.get("QnAKnowledgebaseId", "")
    QNA_ENDPOINT_KEY = os.environ.get("QnAEndpointKey", "")
    QNA_ENDPOINT_HOST = os.environ.get("QnAEndpointHostName", "")

要使用哪一个 QnA Maker 的 Knowledge Base(知识库) 则是在这边设定。

app.py

# Create ConversationState and UserState
STORAGE = MemoryStorage()
CONVERSATION_STATE = ConversationState(STORAGE)
USER_STATE = UserState(STORAGE)

# Create the main dialog and bot
DIALOG = QnAMakerDialog(
    CONFIG.QNA_KNOWLEDGEBASE_ID,
    CONFIG.QNA_ENDPOINT_KEY,
    CONFIG.QNA_ENDPOINT_HOST,
)
BOT = QnABot(CONVERSATION_STATE, USER_STATE, DIALOG)

这个范例的 Storage 一样也是用 In-memory Storage,并且也可以看到 Dialog 是用官方 SDK 的 class 来实作。


若要回顾 QnA Maker 的学理及操作,请参考:

以上是今天尝试解析 QA Bot 的程序码,我们明天见。


<<:  【零基础成为 AI 解梦大师秘笈】Day27 - 周易解梦之人工智慧(8)

>>:  Day27 深入解析Elasticsearch Query DSL Range query

AI - 海关图图片侦测判别

1.建立资料库 ----- 建立资料库 CREATE DATABASE [PicTest] ON P...

Day 26. Zabbix 实际报警案例分享 - 机器服务被关机

今天跟大家分享关机与服务中断的警报,如果有仔细观察 Problem: /etc/passwd has...

Day27 Redis架构实战-Redis丛集Slot分流机制

Redis丛集Slot分流机制 # Redis丛集配置 client | V -----------...

[Day9]-字典(dict)

基本字典 字典的元素是以”key:value”的方式储存的,可以利用key去找到对应的value,...

挑选命题的要件 | ML#Day4

过去那一段时间,直到感受做不出明确的实作成果,和夥伴才意识到题目有问题。 经验整理一下,或许可以用几...