最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Problem with Automatic Chaining of Follow-Up Events in Dialogflow - Stack Overflow

programmeradmin3浏览0评论

So I am developing an AI agent chatbot for a bank website. I am using dialogflow and Python with FastAPI to create the endpoints and the webhook for the dialogflow fulfillment. There is an intent that collects the bank ID, another one after it that verifies the bank ID, and then if valid, it asks integrity questions, if not, it prompts "Bank ID is invalid". So the bank ID verification and Integrity Questions shoul trigger automatically and not wait for a user prompt message. I already achieved and when I test it inside dialogflow, it works. However, when I test it using Postman or on the frontend, it prompts the default static responses of the intent instead of the webhook responses. Why do you think this is happening?

This is my endpoints.py file:

@router.post("/webhook")
async def dialogflow_webhook(request: dict):
    session = request.get("session")
    query_result = request.get("queryResult", {})
    intent_name = query_result.get("intent", {}).get("displayName", "")
    parameters = query_result.get("parameters", {})

    if intent_name == "Bank ID Collection":
        bank_id = parameters.get("bank_id")
        if not bank_id:
            return {
                "fulfillmentText": "I didn't get your bank ID. Could you please provide it?"
            }
        
        # Trigger a follow-up event for an automatic transition to bank ID verification.
        return {
            "fulfillmentText": f"Got your bank ID ({bank_id}). Please wait while I verify it.",
            "followupEventInput": {
                "name": "BANK_ID_VERIFICATION_EVENT",
                "languageCode": "en-US",
                "parameters": {"bank_id": bank_id}
            }
        }

    elif intent_name == "Bank ID Verification":
        bank_id = parameters.get("bank_id")
        if not bank_id:
            return {
                "fulfillmentText": "I didn't get your bank ID. Could you please provide it?"
            }
        is_valid = bank_client.check_bank_id(bank_id)
        if is_valid:
            return {
                "fulfillmentText": "Your bank ID is valid. Please answer the following security questions.",
                "followupEventInput": {
                    "name": "INTEGRITY_QUESTIONS",
                    "languageCode": "en-US",
                    "parameters": {"bank_id": bank_id}
                },
                "outputContexts": [
                    {
                        "name": f"{session}/contexts/awaiting_integrity",
                        "lifespanCount": 5,
                        "parameters": {"bank_id": bank_id}
                    }
                ]
            }
        else:
            return {
                "fulfillmentText": "The bank ID provided is invalid. Please try again."
            }

    elif intent_name == "Integrity Questions":
        # Check if we have the maiden name; if not, ask for it.
        maiden_name = parameters.get("maiden_name")
        if not maiden_name:
            return {
                "fulfillmentText": "What is your mother's maiden name?",
                "outputContexts": [
                    {
                        "name": f"{session}/contexts/awaiting_maiden_name",
                        "lifespanCount": 5,
                        "parameters": parameters
                    }
                ]
            }
        else:
            # Once the maiden name is provided, ask for the first pet's name.
            return {
                "fulfillmentText": "What was the name of your first pet?",
                "outputContexts": [
                    {
                        "name": f"{session}/contexts/awaiting_first_pet",
                        "lifespanCount": 5,
                        "parameters": parameters
                    }
                ]
            }

    elif intent_name == "First Pet Answer":
        # This branch handles the user's answer to the first pet question.
        first_pet = parameters.get("first_pet")
        if not first_pet:
            return {
                "fulfillmentText": "I didn't catch the name of your first pet. Could you please say it again?"
            }
        # You can add more logic here if needed.
        return {
            "fulfillmentText": "Good! I have verified your identity. How can I help you today?",
        }

    else:
        return {"fulfillmentText": "I'm not sure how to handle that."}


@router.post("/detect_intent")
async def detect_intent(payload: dict):
    """
    Detects user intent by calling Dialogflow's detect_intent API.
    It automatically chains follow-up events by looping until no event is returned.
    Accepts an optional "contexts" field to preserve conversation state.
    """
    from google.cloud import dialogflow_v2 as dialogflow
    from google.cloud.dialogflow_v2.types import QueryParameters, Context, EventInput
    from google.protobuf.struct_pb2 import Struct
    from google.protobuf.json_format import MessageToDict
    import asyncio

    def safe_convert(message):
        """
        Safely converts a protobuf message (or a map-like object) to a dict.
        """
        if message is None:
            return {}
        try:
            if hasattr(message, "DESCRIPTOR"):
                return MessageToDict(message)
            else:
                return dict(message)
        except Exception:
            return dict(message)

    try:
        # Extract fields from payload
        session_id = payload.get("session", "default-session")
        query_text = payload.get("queryText")
        language_code = payload.get("languageCode", "en-US")
        if not query_text:
            raise HTTPException(status_code=400, detail="queryText must be provided")

        # Get Dialogflow project ID from environment
        project_id = os.getenv("DIALOGFLOW_PROJECT_ID")
        if not project_id:
            raise HTTPException(
                status_code=500,
                detail="Dialogflow project ID is not configured in the environment"
            )

        # Create session client and session path
        session_client = dialogflow.SessionsClient()
        session_path = f"projects/{project_id}/agent/sessions/{session_id}"

        # Process any provided contexts
        contexts_payload = payload.get("contexts", [])
        contexts = []
        for ctx in contexts_payload:
            full_context_name = f"{session_path}/contexts/{ctx.get('name')}"
            parameters_struct = Struct()
            params = ctx.get("parameters", {})
            if params:
                for key, value in params.items():
                    parameters_struct[key] = value
            context_obj = Context(
                name=full_context_name,
                lifespan_count=ctx.get("lifespanCount", 5),
                parameters=parameters_struct
            )
            contexts.append(context_obj)
        query_params = QueryParameters(contexts=contexts) if contexts else None

        # Build initial text query input and request
        text_input = dialogflow.TextInput(text=query_text, language_code=language_code)
        query_input = dialogflow.QueryInput(text=text_input)
        request_obj = {"session": session_path, "query_input": query_input}
        if query_params:
            request_obj["query_params"] = query_params

        # Loop: call detect_intent repeatedly if a follow-up event is returned
        while True:
            response = session_client.detect_intent(request=request_obj)
            # Wait a short time to ensure Dialogflow has updated the state
            await asyncio.sleep(0.5)
            # Convert webhook payload safely
            webhook_payload = safe_convert(response.query_result.webhook_payload)
            # Check if a follow-up event exists
            if webhook_payload and "followupEventInput" in webhook_payload:
                followup_event = webhook_payload["followupEventInput"]
                event_name = followup_event.get("name")
                event_language_code = followup_event.get("languageCode", language_code)
                event_params = followup_event.get("parameters", {})

                # Build an EventInput using the event info
                event_parameters = Struct()
                for key, value in event_params.items():
                    event_parameters[key] = value

                event_input = EventInput(
                    name=event_name,
                    language_code=event_language_code,
                    parameters=event_parameters
                )
                new_query_input = dialogflow.QueryInput(event=event_input)
                # Update the request_obj with the event input for the next call
                request_obj = {"session": session_path, "query_input": new_query_input}
                if query_params:
                    request_obj["query_params"] = query_params
                # Loop again to process the follow-up event
                continue
            # No follow-up event; break out of loop
            break

        # Process output contexts and parameters for final response
        output_contexts = []
        for ctx in response.query_result.output_contexts or []:
            output_contexts.append({
                "name": ctx.name,
                "lifespanCount": ctx.lifespan_count,
                "parameters": safe_convert(ctx.parameters)
            })
        parameters = safe_convert(response.query_result.parameters)
        webhook_payload = safe_convert(response.query_result.webhook_payload)

        return {
            "queryText": response.query_result.query_text,
            "fulfillmentText": response.query_result.fulfillment_text,
            "webhookPayload": webhook_payload,
            "intent": response.query_result.intent.display_name,
            "parameters": parameters,
            "outputContexts": output_contexts,
        }

    except Exception as e:
        print("Error in detect_intent endpoint:", e)
        raise HTTPException(status_code=500, detail=str(e))

发布评论

评论列表(0)

  1. 暂无评论