Use Case - Integrating a Copilot Chatbot with Nimbus via Direct Line API

In this use case, we describe briefly how to configure Copilot in Copilot Studio for integration with Nimbus. We describe how Nimbus Workflows can interact with the Copilot and respond to customer's messages. The Copilot can understand the intent of your customer and we see how to implement agent handover. The Copilot also keeps the information of a whole conversation. Referencing to something a customer has written before in the same conversation will be recognized by the Copilot.

Preconditions

 Contact Center Service Licensing is required to make use of Nimbus Features such as the the chat modality.

A Microsoft Copilot Studio License is required to create and deploy the copilot in your organization. Please note that this subscription and related cost are not handled or included within Nimbus. For more information, refer to the official Microsoft Copilot Studio site

 

INC Icon Legend Accordion

Show Icon Legend

💡 = A hint to signal learnings, improvements or useful information in context. 🔍 = Info points out essential notes or related page in context.
☝ = Notifies you about fallacies and tricky parts that help avoid problems. 🤔 = Asks and answers common questions and troubleshooting points.
❌ = Warns you of actions with irreversible / data-destructive consequence. ✅ = Intructs you to perform a certain (prerequired) action to complete a related step.
 
 

Create Your Copilot Bot

🔎This variant of connecting Nimbus with Copilot is based on the bot framework Direct Line 3.0 API described here Key concepts in the Bot Framework Direct Line API 3.0 - Azure AI Bot Service - Bot Service | Microsoft Learn

 
  1. Create your bot in Copilot Studio
  2. Deploy the bot.
  3. Optional: If you use any website as knowledge activate Orchestration.
  4. Define your topics. You need one escalation topic. It is generally created out of the box, under Topics → System.       
  5. Add a fixed text response to it. We do not need more. Nimbus will receive this text and check if it contains the exact agent handover text string. If so, Nimbus will route the conversation to an agent.       
  6. In the settings of the bot, go to “Security”
  7. Then first go to Authentication and set it to Non Autentication
  8. Then go back to “Security”  and now select “Web channel security”.       
  9. Copy one of the secret keys.       
     

Create the Nimbus Workflow

Overview: 

  1. Initialize AIAnswerGenerated parameter
  2. Check initial message and branch:
    1. if there is an initial message, take it as the AIQuestion
    2. If not ask for input.
  3. Trigger Event for the Power Automate Flow.  
  4. Check and Wait for the answer and display.
  5. Check if the answer from the bot was the intent to speak to an agent. Use a fixed phrase set within the topic in Copilot.
  6. Agent handover and queue handling

 

Detailed Workflow steps

In the initial Check Parameter we verify if an initial message was made from the chat window. For that we simply check if there is anything inside the message. If so we will take the right exit. 
If not we create a message to say Hi and then use the Collect Information where you type in your question.
The AIAnserIsGenerated is there for us to check if we got a response back from Copilot Studio. That's why in the next Check Parameter we check if the answer is true. If not we add the message with the three dots again. To mimic that something is happening.
After we got the message from Copilot back we use the Message element to show it. Plus we check if the person was entering “I want to talk to an agent” if so we will send it to the queue. You will see how to configure that in the Copilot section.
Otherwise we go back to the initial Collect Information for AIQuestion
 
 

Create the Power Automate Flow

Power Automate Flow Overview

 

 
 

 

Part 1: Prepare the variables

Description Screenshot

Within the Connector Flow Actions start with to the UpdatedParameter Trigger Events on the Nimbus trigger “When a task changes state”. 

 

Within the settings set the trigger condition to the AIQuestion parameter using @equals(triggerOutputs()?['body/UpdatedParameterName'], 'AIQuestion'), as we want to run the flow when a question has been asked by the user.

Then we need to initialize variables to use in the flow. 

 

  • DirectLineSecret1: Type: String
    Holds the copied secret from the Copilot Studio
  • GeneratedAnswerFromBot: Type: String
    Empty, will hold the generated answer
  • ConversationToken: Type: String
    Holds an existing conversation token from a previous request. Will be set with Nimbus Task Information 
  • ConversationId: Type: String
    Holds an existing conversation id from a previous request. Will be set with Nimbus Task Information 
  • LenActivitiesBeforeQuestion: Type: Integer
    Is a helper variable to check for new generated replies
    Default Value: 0
  • LenActivitiesAfterQuestion: Type: Integer
    Is a helper variable to check for new generated replies
  • Activities: Type: Array
    Is a variable that will hold the bot's responses 

 

💡In the next step we set the variables holding information of a previous/ongoing chat conversation ConversationToken and ConversationId.

To set the ConversationId we filter the array of the Nimbus task;

  • From: @{triggerOutputs()?['body/taskInformation/customContextParameters']}
  • Filter Query: item()?['Name'] "is equal to" ConversationId

We then store it into the variable using a Set variable

  • Name: ConversationId
  • Value:@first(body('Filter_array_ConversationId'))?['Value']

Set the ConversationToken

 

We filter the array of the Nimbus task

  • From: @{triggerOutputs()?['body/taskInformation/customContextParameters']}
  • Filter Query: item()?['Name'] “is equal to” ConversationToken

We store it into the variable using a Set variable

  • Name: ConversationToken
  • Value: @first(body('Filter_array-token'))?['Value']

Part 2: Create a new conversation

Now we want to check if there is an existing conversation. 

  • If this conversation starts, we need to open a new one on the Direct Line. 
  • If there is an existing ongoing conversation we do not open anything new. 

💡Note:  in this example we do not handle expired tokens, we should implement it in the true case but omitted it for simplicity.

Overview

Steps

Description Screenshot

First we create a Scope to then put the Condition in there. 

We then need to check if an old ConversationId is existing. The respective variable is holding the information.

In the FALSE branch we will now request the Copilot Direct Line API to get a new conversation starting.

 

Use a HTTP Element to get a token

  • URI: https://directline.botframework.com/v3/directline/tokens/generate
  • Method: POST
  • Headers:       
    Authorization: Bearer @{variables('DirectLineSecret1')}

Parse the response body using Parse JSON

 

{
    "type": "object",
    "properties": {
        "conversationId": {
            "type": "string"
        },
        "token": {
            "type": "string"
        },
        "expires_in": {
            "type": "integer"
        }
    }
}

Now use an HTTP element to start the conversation

  • URI: https://directline.botframework.com/v3/directline/conversations
  • Method: POST
  • Headers:       
    Authorization: Bearer @{variables('DirectLineSecret1')}

Parse the response body using Parse JSON

 

{
    "type": "object",
    "properties": {
        "conversationId": {
            "type": "string"
        },
        "token": {
            "type": "string"
        },
        "expires_in": {
            "type": "integer"
        },
        "streamUrl": {
            "type": "string"
        }
    }
}
Store the ConversationID from the response into the variable
Store the ConversationToken from the response into the variable

Part 3: Interact with the AI model and update the Nimbus task

Now that we have a conversation going, we can send the question from the chat to the bot. 

  • We will check how many activities exist and recheck again and again until we discovered a new response from the bot. 
  • We then leave the loop and update the Nimbus task with the reply, the conversation id and the conversation token.

Overview

Steps

Description Screenshot

Use an HTTP Element with the following setting to send the question to the bot

  • URI: https://directline.botframework.com/v3/directline/conversations/@{variables('ConversationId')}/activities
  • Method: GET
  • Authorization Bearer @{variables('ConversationToken')}
  • Body:       
    {
    "locale": "en-UK",
    "type": "message",
    "from": {
    "id": "user1"
    },
    "text": "@{triggerOutputs()?['body/UpdatedParameterValue']}"
    }

Use another HTTP Element to get all activities of the conversation

 

  • URI: https://directline.botframework.com/v3/directline/conversations/@{variables('ConversationId')}/activities
  • Method: GET
  • Authorization Bearer @{variables('ConversationToken')}

 

Put the response into the Activities array variable using a Set variable Element  

@{body('GetActivities')?['activities']}

 

Then filter the array to return only activities of type “message”

 

We want to count the message activities before the question was answered. This is done with another variable.

Name: LenActivitiesBeforeQuestion       
Value: @length(body('Filter_array_messages'))

Now, add a Do until element to the flow and…

  • … loop until (got to advanced mode and copy it)
    @less(@{variables('LenActivitiesBeforeQuestion')},@{variables('LenActivitiesAfterQuestion')})
  • Count: 60
  • Timeout: PT1H

 

💡"PT1H" means the loop will continue to iterate until the specified condition is fulfilled 

 or the loop runs for a maximum of 1 hour.

Within the loop we do the same actions again: we get the list of activities. Set the array,   filter the array on type “message”  and then set the variable LenActivitiesAfterQuestion to get the final length of the response array.

Finally, we set the GeneratedAnswerFromBot to the text of the last (thus latest) message from the array.

 

 

Use another HTTP Element to get all activities of the conversation

 

  • URI: https://directline.botframework.com/v3/directline/conversations/@{variables('ConversationId')}/activities
  • Method: GET
  • Authorization Bearer @{variables('ConversationToken')}

Put the response into the Activities array variable using a Set variable Element  

@{body('GetActivitiesAgain')?['activities']}

Filter the array to return only items with type “message”

  • From: @{variables('Activities')}
  • @{item()?['type']} is equal to message

Now set the variable

Name: LenActivitiesAfterQuestion        
Value: @length(body('Filter_array_Messages2'))

Set the value of the variable “GeneratedAnswerFromBot” to the text of the response

Name: GeneratedAnswerFromBot       
Value: @last(body('Filter_array_Messages'))?['text']

Finally, we can update the Nimbus Task using the TaskId from the trigger and setting the following Custom Context Parameters:

[
                                                                                                                                            {
                                                                                                                                            "name": "AIAnswerIsGenerated",
                                                                                                                                            "value": "True"
                                                                                                                                            },
                                                                                                                                            {
                                                                                                                                            "name": "AIAnswer",
                                                                                                                                            "value": variables('GeneratedAnswerFromBot')
                                                                                                                                            },
                                                                                                                                            {
                                                                                                                                            "name": "ConversationId",
                                                                                                                                            "value": variables('ConversationId')
                                                                                                                                            },
                                                                                                                                            {
                                                                                                                                            "name": "ConversationToken",
                                                                                                                                            "value": variables('ConversationToken')
                                                                                                                                            }
                                                                                                                                            ]

Table of Contents