【问题标题】:Get the active dialog id in botframework Python - Dispatch model with multiple Dialog and QnA Maker获取 botframework Python 中的活动对话框 ID - 具有多个对话框和 QnA Maker 的调度模型
【发布时间】:2020-10-20 14:58:33
【问题描述】:

我的机器人处理传入的用户消息并根据意图采取行动。对于不需要用户和机器人之间多次传递的简单的一次性答案,它运行良好。现在,当我想实现一个对话框时,它变得越来越难了。一旦用户发送消息并触发对话框,机器人就会要求用户输入。用户提供输入后,LUIS 将再次处理此输入以了解意图。但是,我不希望这种情况发生,因为这会放弃正在进行的 Dialog。

我想了解是否有某种方法可以检测对话框是否正在进行以及它是什么对话框。如果我知道这一点,我可以跳过 LUIS 意图检查部分并将逻辑定向到 Dialog。我使用Multi-turn Prompt 作为此基础。以下是我的预期。

from botbuilder.core import ActivityHandler, TurnContext, MessageFactory, IntentScore, ConversationState, UserState
from botbuilder.schema import ChannelAccount,Attachment
from botbuilder.ai.luis import LuisApplication,LuisPredictionOptions,LuisRecognizer
from botbuilder.ai.qna import QnAMakerEndpoint,QnAMakerOptions,QnAMaker
from typing import List
import os
import json
import os.path
from usecases import SimpleUseCase
import requests

from botbuilder.dialogs import Dialog, DialogSet, DialogTurnStatus
from helpers.dialog_helper import DialogHelper
from dialogs import UserProfileDialog



class Bot(ActivityHandler):

    def __init__(
        self,
        conversation_state: ConversationState,
        user_state: UserState
        # dialog: Dialog,
    ):
        if conversation_state is None:
            raise TypeError(
                "[DialogBot]: Missing parameter. conversation_state is required but None was given"
            )
        if user_state is None:
            raise TypeError(
                "[DialogBot]: Missing parameter. user_state is required but None was given"
            )
        # if dialog is None:
        #     raise Exception("[DialogBot]: Missing parameter. dialog is required")

        self.conversation_state = conversation_state
        self.user_state = user_state
        # self.dialog = dialog###Change based on dialog

        luisApp = LuisApplication("","","https://abcd.cognitiveservices.azure.com/")
        luisOptions = LuisPredictionOptions(include_all_intents=True,include_instance_data=True)
        self.LuisRecog = LuisRecognizer(luisApp,luisOptions)
        self.qnaMaker = QnAMaker(QnAMakerEndpoint("","",""))        
    

    async def on_members_added_activity(
        self, members_added: List[ChannelAccount], turn_context: TurnContext
    ):
        for member in members_added:
            # Greet anyone that was not the target (recipient) of this message.
            # To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
            if member.id != turn_context.activity.recipient.id:
                welcome_card = self.create_adaptive_card_attachment()
                response = MessageFactory.attachment(welcome_card)
                await turn_context.send_activity(response)

    # Load attachment from file.
    def create_adaptive_card_attachment(self):
        relative_path = os.path.abspath(os.path.dirname(__file__))
        path = os.path.join(relative_path, "cards/welcomeCard.json")
        with open(path) as in_file:
            card = json.load(in_file)

        return Attachment(
            content_type="application/vnd.microsoft.card.adaptive", content=card
        )

    async def greetings(self,turn_context):
        await turn_context.send_activity(MessageFactory.text("from inside greetings"))

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

        # Save any state changes that might have ocurred during the turn.
        await self.conversation_state.save_changes(turn_context)
        await self.user_state.save_changes(turn_context)


    async def on_message_activity(self, turn_context: TurnContext):
        luisResult = await self.LuisRecog.recognize(turn_context) 
        print(luisResult.get_top_scoring_intent())
        intent = LuisRecognizer.top_intent(luisResult,min_score=0.40)

        if turn_context.activity.text.lower() == 'hi':
            await self.greetings(turn_context)
            await turn_context.send_activity(MessageFactory.text("message sent from send greetings.."))
        else:
### Here I want to be able to understand if there is any dialog that is already present on the dialog stack and 
### the dialog id. If we can get that details, I can then call the run_dialog method with the  dialog id 
### instead of routing the request based on the intent. Without knowing if a dialog is already 
### running, every input from user is again processed which is not desired.
            if intent == 'UserProfileIntent':
                await DialogHelper.run_dialog(UserProfileDialog(self.user_state),turn_context,self.conversation_state.create_property("DialogState"))
            else:
                answers = await self..get_answers(turn_context)                
                await turn_context.send_activity(MessageFactory.text(answers[0].answer))

                

这在某种程度上与 this question 相关,尽管在 Github here 上进行了讨论,但我认为我的问题/要求有所不同。我试图了解是否有办法在其他 SDK 中获取当前活动对话框,但找不到任何有用的信息。

上述问题的作者已经回答了他的方法。虽然它是一个很好的,它只是部分符合我的要求。当意图数量很少并且仅使用 Luis 时,答案很有用。我的用例有 50 多个意图,并且列表会不断增加。我需要继续在主对话框中添加对话框。如果我能够识别活动对话框,我将能够动态调用 run_dialog 方法。

此外,当用户输入的意图为“无”时,我使用 QnA Maker 来回答用户。当我尝试将 QnA Maker 也实现到对话框中时,我最终会遇到以下某些错误。

ma​​in_dialog.py

from botbuilder.dialogs import (
    ComponentDialog,
    WaterfallDialog,
    WaterfallStepContext,
    DialogTurnResult,
)
from botbuilder.dialogs.prompts import (
    TextPrompt,
    NumberPrompt,
    ChoicePrompt,
    ConfirmPrompt,
    AttachmentPrompt,
    PromptOptions,
    PromptValidatorContext,
)
from botbuilder.dialogs.choices import Choice
from botbuilder.core import MessageFactory, UserState
from dialogs import NameDialog,UserProfileDialog,QADialog
from botbuilder.ai.qna import QnAMaker,QnAMakerEndpoint
import json

class MainDialog(ComponentDialog):
    def __init__(self, luis, intent, recognizer_result,user_state, turn_context):
        super(MainDialog, self).__init__(MainDialog.__name__)

        self.luis = luis
        self.intent = intent
        self.recognizer_result = recognizer_result
        self.user_state = user_state
        self.turn_context = turn_context

        self.add_dialog(NameDialog(self.user_state))
        self.add_dialog(UserProfileDialog(self.user_state))
        self.add_dialog(QADialog(self.user_state))
        self.add_dialog(
            WaterfallDialog(
                "main_dialog_id", [self.main_step]
            )
        )

        self.initial_dialog_id = "main_dialog_id"

    async def main_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
        # dialog_detail = self.luis.get_entities(self.intent, self.recognizer_result)
        if self.intent == "name":
            return await step_context.begin_dialog(NameDialog.__name__)
        elif self.intent == "place":
            return await step_context.begin_dialog(UserProfileDialog.__name__)
        elif self.intent == "AGE":
            entities = None
            if self.recognizer_result.entities is not None:
                entities = json.dumps(self.recognizer_result.entities.get("number"))
            return await step_context.begin_dialog(NameDialog.__name__,entities[0])
            # await self.turn_context.send_activity(MessageFactory.text("age intent"))
        elif self.intent == "None":                                    
     ###This part ends up in error even though I close the dialog immediately after sending the answer and I'm not sure if it's the right way to do it.        
            answers = await QnAMaker(QnAMakerEndpoint("","","")).get_answers(self.turn_context)
            if answers and len(answers) > 0:
                await self.turn_context.send_activity(MessageFactory.text(answers[0].answer))
            else:
                await self.turn_context.send_activity(MessageFactory.text("Sorry I cant help you with that !!"))

            await step_context.end_dialog()

bot.py

from botbuilder.core import ActivityHandler, TurnContext, MessageFactory, IntentScore, ConversationState, UserState, StatePropertyAccessor
from botbuilder.schema import ChannelAccount,Attachment
from botbuilder.ai.luis import LuisApplication,LuisPredictionOptions,LuisRecognizer
from botbuilder.ai.qna import QnAMakerEndpoint,QnAMakerOptions,QnAMaker
from typing import List
import os
import json
import os.path
from usecases import SimpleUseCase
import requests

from botbuilder.dialogs import Dialog, DialogSet, DialogTurnStatus, DialogContext
from helpers.dialog_helper import DialogHelper
from dialogs import UserProfileDialog,NameDialog,MainDialog



class Bot(ActivityHandler):

    def __init__(
        self,
        conversation_state: ConversationState,
        user_state: UserState
        # dialog: Dialog,
    ):
        if conversation_state is None:
            raise TypeError(
                "[DialogBot]: Missing parameter. conversation_state is required but None was given"
            )
        if user_state is None:
            raise TypeError(
                "[DialogBot]: Missing parameter. user_state is required but None was given"
            )
        # if dialog is None:
        #     raise Exception("[DialogBot]: Missing parameter. dialog is required")

        self.conversation_state = conversation_state
        self.user_state = user_state
        # self.dialog = dialog###Change based on dialog

        luisApp = LuisApplication("","","")
        luisOptions = LuisPredictionOptions(include_all_intents=True,include_instance_data=True)
        self.LuisRecog = LuisRecognizer(luisApp,luisOptions)
      

    async def on_members_added_activity(
        self, members_added: List[ChannelAccount], turn_context: TurnContext
    ):
        for member in members_added:
            # Greet anyone that was not the target (recipient) of this message.
            # To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
            if member.id != turn_context.activity.recipient.id:
                welcome_card = self.create_adaptive_card_attachment()
                response = MessageFactory.attachment(welcome_card)
                await turn_context.send_activity(response)

    # Load attachment from file.
    def create_adaptive_card_attachment(self):
        relative_path = os.path.abspath(os.path.dirname(__file__))
        path = os.path.join(relative_path, "cards/welcomeCard.json")
        with open(path) as in_file:
            card = json.load(in_file)

        return Attachment(
            content_type="application/vnd.microsoft.card.adaptive", content=card
        )

    async def greetings(self,turn_context):
        await turn_context.send_activity(MessageFactory.text("from inside greetings"))

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

        # Save any state changes that might have ocurred during the turn.
        await self.conversation_state.save_changes(turn_context)
        await self.user_state.save_changes(turn_context)


    async def on_message_activity(self, turn_context: TurnContext):
        luisResult = await self.LuisRecog.recognize(turn_context) 
        print(luisResult.get_top_scoring_intent())
        intent = LuisRecognizer.top_intent(luisResult,min_score=0.40)


        if turn_context.activity.text.lower() == 'hi':
            await self.greetings(turn_context)
            await turn_context.send_activity(MessageFactory.text("message sent from send greetings.."))
        else:
            # if intent != "None":
            dialog = MainDialog(self.LuisRecog, intent, luisResult,self.user_state,turn_context)
            await DialogHelper.run_dialog(dialog,turn_context,self.conversation_state.create_property("DialogState"))

                

None 意图和 Q&A Maker 处理用户问题时出错。此外,我还没有尝试过 QnA 对话框,看看它们是否在此设置中工作。

如果我们能够以某种方式访问​​当前活动的对话框 id,这样我们就可以通过创建一个意图到对话框的映射来轻松地根据 id 切换对话框。

感谢this question的作者分享他的解决方案。

使用调度方法

import os
from typing import List
import json

from azure.cognitiveservices.language.luis.runtime.models import LuisResult, IntentModel

from botbuilder.ai.luis import LuisApplication, LuisRecognizer, LuisPredictionOptions
from botbuilder.ai.qna import QnAMaker, QnAMakerEndpoint
from botbuilder.core import ActivityHandler, TurnContext, RecognizerResult, MessageFactory, UserState, ConversationState
from botbuilder.schema import ChannelAccount, Attachment

from config import DefaultConfig

from helpers.luis_helper import LuisDispatchResult
from helpers.dialog_helper import DialogHelper
from dialogs import NameDialog,UserProfileDialog,QADialog

class DispatchBot(ActivityHandler):
    def __init__(self, config: DefaultConfig,
                 conversation_state: ConversationState,
                 user_state: UserState):
        self.qna_maker = QnAMaker(
            QnAMakerEndpoint(
                knowledge_base_id=config.QNA_KNOWLEDGEBASE_ID,
                endpoint_key=config.QNA_ENDPOINT_KEY,
                host=config.QNA_ENDPOINT_HOST,
            )
        )

        # If the includeApiResults parameter is set to true, as shown below, the full response
        # from the LUIS api will be made available in the properties  of the RecognizerResult
        self.dialog_started = False
        self.app_id = config.LUIS_APP_ID
        self.api_key = config.LUIS_API_KEY
        self.host = "https://" + config.LUIS_API_HOST_NAME
        self.user_state = user_state
        self.conversation_state = conversation_state

        luis_application = LuisApplication(
            config.LUIS_APP_ID,
            config.LUIS_API_KEY,
            "https://" + config.LUIS_API_HOST_NAME,
        )
        luis_options = LuisPredictionOptions(
            include_all_intents=True, include_instance_data=True
        )
        self.recognizer = LuisRecognizer(luis_application, luis_options, True)

    async def on_members_added_activity(
        self, members_added: List[ChannelAccount], turn_context: TurnContext
    ):
        for member in members_added:
            # Greet anyone that was not the target (recipient) of this message.
            # To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
            if member.id != turn_context.activity.recipient.id:
                welcome_card = self.create_adaptive_card_attachment()
                response = MessageFactory.attachment(welcome_card)
                await turn_context.send_activity(response)

    # Load attachment from file.
    def create_adaptive_card_attachment(self):
        relative_path = os.path.abspath(os.path.dirname(__file__))
        path = os.path.join(relative_path, "cards/welcomeCard.json")
        with open(path) as in_file:
            card = json.load(in_file)

        return Attachment(
            content_type="application/vnd.microsoft.card.adaptive", content=card
        )



    async def on_message_activity(self, turn_context: TurnContext):
        # First, we use the dispatch model to determine which cognitive service (LUIS or QnA) to use.
        # recognizer_result = await self.recognizer.recognize(turn_context)
        # Top intent tell us which cognitive service to use.
        # intent = LuisRecognizer.top_intent(recognizer_result)
        topIntent,childIntent,childEntities = LuisDispatchResult().getLuisDispatchResult(self.app_id,self.api_key,self.host,turn_context.activity.text)

        # Next, we call the dispatcher with the top intent.
        await self._dispatch_to_top_intent(turn_context, topIntent,childIntent,childEntities)

    async def _dispatch_to_top_intent(
        self, turn_context: TurnContext, topIntent,childIntent,childEntities
    ):
        if topIntent == "l_myluisapp":
            await self._process_luis_queries(
                turn_context, childIntent,childEntities
            )
        elif topIntent == "q_mybotqna":
            await self._process_sample_qna(turn_context)
        else:
            await turn_context.send_activity(f"Dispatch unrecognized intent: {topIntent}.")

    async def _process_luis_queries(
        self, turn_context: TurnContext, childIntent,childEntities
    ):
        await turn_context.send_activity(
            f"LUIS intent selected {childIntent}."
        )

        await turn_context.send_activity(
            f"LUIS entities: {childEntities}."
        )
        
            
        if childIntent == "name":
            self.dialog_started = True
            dialog = NameDialog(self.user_state)
            await DialogHelper.run_dialog(dialog,turn_context,self.conversation_state.create_property("DialogState"))
        elif childIntent == "place":
            self.dialog_started = True
            dialog = UserProfileDialog(self.user_state)
            await DialogHelper.run_dialog(dialog,turn_context,self.conversation_state.create_property("DialogState"))
        elif childIntent == "AGE":
            self.dialog_started = True
            dialog = NameDialog(self.user_state)
            await DialogHelper.run_dialog(dialog,turn_context,self.conversation_state.create_property("DialogState"))
        else:
            await turn_context.send_activity(MessageFactory.text("No suitable intent detected"))


    async def _process_sample_qna(self, turn_context: TurnContext):
        results = await self.qna_maker.get_answers(turn_context)
        if results:
            await turn_context.send_activity(results[0].answer)
        else:
            await turn_context.send_activity(
                "Sorry, could not find an answer in the Q and A system."
            )

只要从 Dispatch App 检测到意图和子意图,就会触发对话框。但是,一旦用户从第一个对话中向问题提供输入,这会导致无意图,对话就会以 Dispatch unrecognized intent: None 结束。在调用 end_dialog 之前,对话不应结束。我尝试通过将子对话框传递到上面的主对话框来做同样的事情,但它也导致相同的结果。我应该怎么做才能跳过意图检查或者我在这里做错了什么?

【问题讨论】:

  • 我正在研究这个,但在我深入研究之前,您是否研究过调度?它就像一个伞形 LUIS,您可以在其中检测 LUIS 意图与 QnA。一旦检测到最初的意图,您可以运行一个方法从那里启动组件对话框,并且在触发 end_dialog 之前它不会返回到您的父对话框。
  • 我尝试运行 Python Dispatch 示例,但它总是以 Dispatch 无法识别的意图结束 - 我已经配置了 LUIS 和 QnA 模型并更新了 config.py。 LUIS 模型中不存在分支逻辑 l_HomeAutomation/l_Weather/q_sample-qna 中使用的意图。我认为它们必须存在 bcz,意图来自 LUIS 识别器。我是否还需要安装和配置 Dispatch CLI 工具才能使其正常工作?
  • 能够设置调度!如果我遇到任何问题,我会试试看。
  • 我能够设置 Dispatch 并运行机器人。但我有个问题。 Dispatch 只是在 LUIS 和 QnA 之间拆分问题,实际上并没有给出 LUIS 意图。一旦它分裂,我们应该再次运行识别器以从 LUIS 中找到意图。我说的对吗?
  • 不一定。这正是调度示例所做的,以展示它的能力。

标签: python botframework azure-bot-service


【解决方案1】:

终于,我可以做我想做的事了。 Python SDK 和周围的社区不如 .net 成熟,这就是为什么它花费的时间比实际应该多得多。我在YouTubeGithub Repo 上关注了一个.Net 示例。观看此示例后,检查对话是否处于活动状态或看起来如此简单。下面是 Python 的实现。下面的代码所做的是,它在启动机器人时创建一个 DialogSet,然后创建一个 DialogContext。它检查是否存在任何可以在 on_message_activity 方法中运行的对话,如果为真,则继续旧对话,否则将消息发送到 LuisHelper 类以检测意图并根据意图启动对话。

机器人

import os
import json

from botbuilder.core import ActivityHandler, ConversationState, UserState, TurnContext, StatePropertyAccessor, MessageFactory
from botbuilder.dialogs import Dialog, DialogSet, DialogTurnStatus
from botbuilder.schema import Attachment,ChannelAccount
from helpers.luis_helper import LuisDispatchResult
from helpers.qna_helper import QnAHelper,QnAMakerEndpoint
from dialogs import NameDialog, UserProfileDialog
from typing import List
from botbuilder.ai.qna import QnAMaker,QnAMakerEndpoint, QnAMakerOptions

from config import DefaultConfig

CONFIG = DefaultConfig()

class Bot(ActivityHandler):
    def __init__(
        self,
        conversation_state: ConversationState,
        user_state: UserState
    ):
        if conversation_state is None:
            raise Exception(
                "[DialogBot]: Missing parameter. conversation_state is required"
            )
        if user_state is None:
            raise Exception("[DialogBot]: Missing parameter. user_state is required")


        self.conversation_state = conversation_state
        self.user_state = user_state

        self.accessor = self.conversation_state.create_property("DialogState")
        self.dialog_set = DialogSet(self.accessor)
        self.dialog_context = None

        self.app_id = CONFIG.LUIS_APP_ID
        self.api_key = CONFIG.LUIS_API_KEY
        self.host = "https://" + CONFIG.LUIS_API_HOST_NAME

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

        # Save any state changes that might have occurred during the turn.
        await self.conversation_state.save_changes(turn_context, False)
        await self.user_state.save_changes(turn_context, False)


    async def on_members_added_activity(
        self, members_added: List[ChannelAccount], turn_context: TurnContext
    ):
        for member in members_added:
            # Greet anyone that was not the target (recipient) of this message.
            # To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
            if member.id != turn_context.activity.recipient.id:
                welcome_card = self.create_adaptive_card_attachment()
                response = MessageFactory.attachment(welcome_card)
                await turn_context.send_activity(response)

    # Load attachment from file.
    def create_adaptive_card_attachment(self):
        relative_path = os.path.abspath(os.path.dirname(__file__))
        path = os.path.join(relative_path, "cards/welcomeCard.json")
        with open(path) as in_file:
            card = json.load(in_file)

        return Attachment(
            content_type="application/vnd.microsoft.card.adaptive", content=card
        )

    async def on_message_activity(self, turn_context: TurnContext):        
        
        self.dialog_context = await self.dialog_set.create_context(turn_context)
        results = await self.dialog_context.continue_dialog()

        if results.status == DialogTurnStatus.Empty:
            topIntent,childIntent,childEntities = LuisDispatchResult().getLuisDispatchResult(self.app_id,self.api_key,self.host,turn_context.activity.text)
            print(topIntent,childIntent,childEntities )
            await self._dispatch_to_top_intent(turn_context, topIntent,childIntent,childEntities)

    async def _dispatch_to_top_intent(
        self, turn_context: TurnContext, topIntent,childIntent,childEntities
    ):
        if topIntent == "l_myluisapp":
            await self._process_luis_queries(
                turn_context, childIntent,childEntities
            )
        elif topIntent == "q_mybotqna":
            await self._process_sample_qna(turn_context)
        else:
            await turn_context.send_activity(f"Dispatch unrecognized intent: {topIntent}.")

    async def _process_luis_queries(
        self, turn_context: TurnContext, childIntent,childEntities
    ):

        if childIntent == "name":
            dialog = NameDialog(self.user_state)
            if not await self.dialog_set.find(dialog.id):
                self.dialog_set.add(dialog)            
            await self.dialog_context.begin_dialog(dialog.id)
        elif childIntent == "place":
            dialog = UserProfileDialog(self.user_state)
            if not await self.dialog_set.find(dialog.id):
                self.dialog_set.add(dialog)            
            await self.dialog_context.begin_dialog(dialog.id)
        elif childIntent == "AGE":
            dialog = NameDialog(self.user_state)
            if not await self.dialog_set.find(dialog.id):
                self.dialog_set.add(dialog)            
            await self.dialog_context.begin_dialog(dialog.id)
        else:
            await turn_context.send_activity(MessageFactory.text("No suitable intent detected, Checking QnA..."))
            await self._process_sample_qna(turn_context)
            

    async def _process_sample_qna(self, turn_context: TurnContext):

        results = await QnAMaker(QnAMakerEndpoint(CONFIG.QNA_KNOWLEDGEBASE_ID,CONFIG.QNA_ENDPOINT_KEY,CONFIG.QNA_ENDPOINT_HOST),QnAMakerOptions(score_threshold=CONFIG.QNA_THRESHOLD)).get_answers(turn_context)

        # results = await self.qna_maker.get_answers(turn_context)

        if results:
            await turn_context.send_activity(results[0].answer)
        else:
            await turn_context.send_activity(
                "Sorry, could not find an answer in the Q and A system."
            )

app.py

from quart import Quart, request, Response
from botbuilder.core import (
    BotFrameworkAdapterSettings,
    ConversationState,
    MemoryStorage,
    UserState,
)
from botbuilder.schema import Activity

from bot import Bot

from adapter_with_error_handler import AdapterWithErrorHandler

app = Quart(__name__, instance_relative_config=True)
# app.config.from_object("config.DefaultConfig")

from config import DefaultConfig

CONFIG = DefaultConfig()

# Create adapter.
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
SETTINGS = BotFrameworkAdapterSettings(CONFIG.APP_ID, CONFIG.APP_PASSWORD)

# Create MemoryStorage, UserState and ConversationState
MEMORY = MemoryStorage()
USER_STATE = UserState(MEMORY)
CONVERSATION_STATE = ConversationState(MEMORY)

# Create adapter.
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
ADAPTER = AdapterWithErrorHandler(SETTINGS, CONVERSATION_STATE)


# Create dialogs and Bot
BOT = Bot(CONVERSATION_STATE, USER_STATE)



# Listen for incoming requests on /api/messages
@app.route("/api/messages", methods=["POST"])
async def messages():
    # Main bot message handler.
    if "application/json" in request.headers["Content-Type"]:
        body = await request.json
    else:
        return Response("", status=415)

    activity = Activity().deserialize(body)
    auth_header = (
        request.headers["Authorization"] if "Authorization" in request.headers else ""
    )

    try:
        await ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
        return Response("", status=201)
    except Exception as exception:
        raise exception


@app.route("/", methods=["GET"])
async def homepage():
    try:
        return "Yes I'm working brother."
    except Exception as exception:
        raise exception
    
if __name__ == "__main__":
    try:
        app.run()  # nosec debug
    except Exception as exception:
        raise exception

示例对话框 - 名称对话框

from botbuilder.dialogs import (
    ComponentDialog,
    WaterfallDialog,
    WaterfallStepContext,
    DialogTurnResult,
)
from botbuilder.dialogs.prompts import (
    TextPrompt,
    NumberPrompt,
    ChoicePrompt,
    ConfirmPrompt,
    AttachmentPrompt,
    PromptOptions,
    PromptValidatorContext,
)
from botbuilder.dialogs.choices import Choice
from botbuilder.core import MessageFactory, UserState

from data_models import Name


class NameDialog(ComponentDialog):

    def __init__(self, user_state: UserState):
        super(NameDialog, self).__init__(NameDialog.__name__)

        self.name_accessor = user_state.create_property("Name")

        self.add_dialog(
            WaterfallDialog(
                WaterfallDialog.__name__,
                [
                    self.name_step,
                    self.summary_step
                ],
            )
        )
        self.add_dialog(
            TextPrompt(TextPrompt.__name__)
        )

        self.initial_dialog_id = WaterfallDialog.__name__


    async def name_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:

        return await step_context.prompt(
            TextPrompt.__name__,
            PromptOptions(prompt=MessageFactory.text("My name is Hidimbi. What's yours ?")),
        )
        # User said "no" so we will skip the next step. Give -1 as the age.
        # return await step_context.next(-1)


    async def summary_step(
        self, step_context: WaterfallStepContext
    ) -> DialogTurnResult:
        if step_context.result:
            # Get the current profile object from user state.  Changes to it
            # will saved during Bot.on_turn.
            
            msg = f"Great name {step_context.result}."


            await step_context.context.send_activity(MessageFactory.text(msg))

        else:
            await step_context.context.send_activity(
                MessageFactory.text("Thanks. Your data is not saved")
            )

        # WaterfallStep always finishes with the end of the Waterfall or with another
        # dialog, here it is the end.
        return await step_context.end_dialog()

    @staticmethod
    async def material_prompt_validator(prompt_context: PromptValidatorContext) -> bool:
        # This condition is our validation rule. You can also change the value at this point.
        return (
            prompt_context.recognized.succeeded
            and 0 < prompt_context.recognized.value < 150
        )

【讨论】:

  • 这种方法让我只执行一次对话框。我第二次尝试相同的对话框时,它抛出了 DialogSet.add(): A dialog with an id of 'NameDialog' already added exception!
  • 更新代码以修复上述 dialogset.add() 问题。关键是,一旦对话框被添加到对话框集中,它总是可用的,只需通过调用它的名称来触发,我之前没有这样做。
猜你喜欢
  • 2017-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多