Note:

Starting in Release 22.04, the Visual Flow Designer is available. The Visual Flow Designer has a number of advantages over the YAML-based editor, including the ability to create modular flows and to design with much less code. If you are designing a new skill, you should use the Visual Flow Designer instead of the YAML editor that's documented in this tutorial. See the Create a Dialog Flow with the Oracle Visual Flow Designer tutorial.

Before You Begin

This 70-minute tutorial shows you the structure of the dialog flow definition and how to build one.

What Do You Need?

  • Access to Oracle Digital Assistant
  • The WineSKill_Materials.zip file, which includes the starter skill, reference files, and a finished version of the skill. Download this file and then unzip it to your local system.

Explore the Starter Skill

The first thing you need to do is import the starter wine skill into your Oracle Digital Assistant instance so that you can see what you're working with.

Import the Skill

  1. Log into Oracle Digital Assistant.
  2. Click menu icon in the top left corner to open the side menu.
  3. Expand Development and then click Skills.
  4. Hide the menu by clicking menu icon again.
  5. Click Import Skill (located at the upper right).
    Description of  follows
    Description of the illustration import_skill.png
  6. Browse to, and then select, Wine_Skill_Starter.zip from the extracted WineSKill_Materials file. Then click Open.
  7. Click Upgrade to migrate the skill to the current version of Oracle Digital Assistant.
    Description of  follows
    Description of the illustration upgrade_option.png
  8. Enter a new version number in the the Create New Version of the Skill dialog.
  9. Choose the latest version of the platform. Then click Create.
    Description of  follows
    Description of the illustration create_new_version_dialog.png
  10. Click Train.
    Description of  follows
    Description of the illustration train.png
  11. Select Trainer Tm and then click Submit. It may take a few minutes for training to complete, but when it does, you'll be able to explore the basic capabilities of the skill in the next step.
    Description of  follows
    Description of the illustration choose_training_model_dialog.png

Review the Intents, Entities and Dialog Flow

Before we begin building the dialog flow, we're going to take a quick look at the intents, entities and the existing nodes in dialog flow that have already been defined.

  1. First, let's take a look at the intents. If it's not already selected, click Intents Intent icon in the left navbar. This skill uses regular intents (order.wine and tellme.about.wine) and an answer intent which describes this tutorial.
    Description of  follows
    Description of the illustration intents_page.png
    While order.wine is a transactional intent for ordering wine, both tellme.about.wine and ans.aboutSample answer questions. Although they perform the same role, they use different means to deliver answers. ans.aboutSample is an answer intent that always returns the same answer to the user. Its text is static. The content returned by the tellme.about.wine intent, on the other hand, is dynamic because it returns an answer based on the user input. For example, if a user asks about Chardonnay, this intent returns an answer specific to Chardonnay.
  2. Take a look at the kind of phrases that resolve to each of these intents using the Utterance Tester, which you open by clicking Test Utterances (located at the upper left of the Intents page).
    Description of test_utterances.txt  follows
    Description of the illustration try_it_out.png
    Enter the following phrases in the Utterance field, then click Test. Click Reset after each round. When you're done, click Close The Close button.
    For this intent... Enter these phrases
    ans.aboutSample
    1. What's this skill about
    2. What will I learn from this tutorial?
    3. What will I learn
    order.wine 2
    1. I want to buy wine
    2. I want to buy red wine
    3. I want to buy Chardonnay
    tellme.about.wine
    1. I have a question about wine
    2. I have a question about white wine
    3. Tell me about Riesling

    Description of  follows
    Description of the illustration intent_tester.png
    The dialog flow that you're going to build will branch by intent type (purchasing wines and asking questions about wines) and by entity values (for example, red wines and white wines).
  3. Click Entities Entities Menu Icon in the left navbar to take a look at the entities for matching both the wine category and wine type from the user input.
    Description of  follows
    Description of the illustration entities_page.png
    These entities have prompts and error messages defined for them that return variable values using Apache FreeMarker Template Language (FTL) expressions. Before we move on the dialog flow definition (our last stop before we begin creating our dialog flows), let's take a look at a couple of these expressions that have been defined for the list.orderSizes entity:
    • This prompt references a variable called selectedWine:
      ${selectedWine.value} is a great choice! How many bottles do you like to order
    • This error message displays the invalid input from the user message:
      ${system.message.messagePayload.text} is not a valid shipment size.
      Description of  follows
      Description of the illustration entity_prompts.png
  4. Click Flows The dialog flow editor icon in the left navbar.
  5. Take a look at the following variable definitions in the context node. You don't have to memorize these, but you should be aware of them since they're going to be referenced throughout the dialog flow definition that you're going to build:
    • The orderSizes, wineTypes, redWines, and whiteWines variable definitions for the entities.
    • The declaration for the selectedWine variable that stores the user input that's referenced in the in the list.order entity prompts that you just looked at.
    • The redWineCard and whiteWineCard map variables for populating the content of the wine menus on cards (more on them in a minute).
    • The chatType variable, whose value branches the dialog between the wine-ordering and question-answering dialog flows, which are built around the order.wine and tellme.about.wine intents.
    • The rb variable, which enables access to the wine description strings defined in resource bundles.
    • The iResult variable that holds the value that the NLP (Natural Language Processing) framework that's resolved from the user input.
    • Description of  follows
      Description of the illustration context_node.png
  6. In the states node, take a look at the buildRedWineMenu and buildWhiteWineMenu states that provide the text and images for the red and white wine menus. In the real world, a wine skill would connect users to a vast inventory of wines and would be accessed through a REST call. For the purposes of this tutorial, however, we're using mock data that's managed by Apache FreeMarker expressions.
    Description of this image follows
    Description of the illustration states_node.png

The dialog is comprised of a series of states, which generally (though not always) correspond with a step in a conversation. Each state is based on a component that executes a function that supports the conversation in some way. Some components output UI elements like value lists or text messages. Others execute logic based on the varaible values have been set (or not set) from the user input. The component defined for the buildRedWineMenu and buildWhiteWineMenu states, for example, is the System.SetVariable component, which sets the entity value for the wine type so that the appropriate wine list can be loaded.

For this tutorial, you'll be focusing on the states node, where you will add states directly after the buildWhiteWineMenu state using the Components menu, which you access by clicking + Add Component (located at the upper right of the editor). The Add Component dialog populates the dialog flow with templates that contain component-specific properties.

Description of this image follows
Description of the illustration add_components_button.png
You'll choose from different categories of templates, which include the ones used for creating user input forms, outputting messages, and controlling the execution path based on the variable values that have been set.
Description of this image follows
Description of the illustration add_component_menu.png

The indentation from the left margin and spacing are crucial for maintaining valid syntax. While we'll help you maintain the proper alignment with the code snippets that you paste into the editor, you should periodically click Validate to check for errors using the Skill Validator.

Description of this image follows
Description of the illustration validate_option.png

You can access this tool by clicking either View validation results in the error message or by clicking Findings after you click Validate. Correcting errors may be as simple as adding or deleting a space.

Description of this image follows
Description of the illustration skill_validator.png

Begin the Conversation

Create the Welcome State

You'll use this state to create a simple string message that displays immediately after users ask a question or begin their orders. You're going to use the System.Output component to output this message.

  1. Click + Add Component.
  2. Choose User Interface.
    Description of this image follows
    Description of the illustration add_components_user_interface.png
  3. Click Output.
  4. Select buildWhiteWineMenu from the Insert After State menu.
  5. Clear Include template comments.
  6. Click Insert Component.
    Description of this image follows
    Description of the illustration add_components_dialog.png
  7. Update the output state as follows:
    • Change output to welcome.
    • Define the text property as
       "Welcome to 'Winery Chats'. I'm 'Grapy', a chatbot with which you can order the best wines in the world or get your wine-specific questions answered. So, let me help you too."
    • User's are not meant to reply to this message, so set keepTurn to true to prevent users from sending messages before the flow can transition to the next state, getIntent where the user input gets resolved to an intent.
    • Click Return to insert a new line.
    • Add the following transitions definition. The transitions: node must align with the properties node (four spaces from the left margin). The next: "getIntent" property should be six spaces from the left margin. You can enter these manually, or copy and paste the following snippet into your dialog flow definition after positioning your cursor at the left margin.
          transitions:
            next: "getIntent"

      Tip:

      As a rule of thumb, you should position your cursor at the left margin when pasting in the transition code snippets provided in this tutorial.
    When you're done, the welcome state should look like this:
      welcome:
        component: "System.Output"
        properties:
          text: "Welcome to 'Winery Chats'. I'm 'Grapy', a chatbot with which you can order the best wines in the world or get your wine-specific questions answered. So, let me help you too."
          keepTurn: true
        transitions:
          next: "getIntent"

Create the NLP State

In this step, you're going to create the NLP (natural langauge processing) state that sets the navigation through the dialog flow based on the intent resolution and entity extraction. To create this state, you'll add the System.Intent component, which evaluates the user input.

  1. Click + Add Component.
  2. Choose Language > Intent.
  3. Choose welcome from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the intent state as follows:
    • Change intent to getIntent.
    • Set the variable property to "iResult".
    • Delete the properties that you don't need: optionsPrompt, botName, botVersion, sourceVariable, autonumberPostbackActions, and footerText. When you're done, you should be left with only the variable and daIntercept properties.
    • Click Return to create a new line below the actions node.
    • In the actions node (directly beneath transitions), replace unresolvedIntent with the following actions. These entries should be eight spaces from the left margin. You can enter them manually, or paste them into your dialog defition after positioning your cursor at the left margin.
              tellme.about.wine: "setChatToExplainType"
              order.wine: "setChatToOrderType"
              unresolvedIntent: "handleBotNotUnderstanding"
    When you're done, the getIntent state should look like this:
      getIntent:
        component: "System.Intent"
        properties:
          variable: "iResult"       
          daIntercept: "always" 
        transitions:
          actions:
            tellme.about.wine: "setChatToExplainType"
            order.wine: "setChatToOrderType"
            unresolvedIntent: "handleBotNotUnderstanding"

Set the Chat Type to Order

The branching between the order wine and question execution paths is based on the value matched for the chatType variable (order or question) that has been resolved from the user input in the getIntent state. In this step, you're going add a state that sets the chatType variable to order when the order.wine action is triggered. The component that sets the value as order is the System.setVariable component.

  1. Click + Add Component.
  2. Choose Variables > Set variable.
  3. Choose getIntent from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the setVariable state as follows:
    • Change setVariable to setChatToOrderType.
    • Set the variable property to "chatType".
    • Set the value property to "order" to indicate that wine ordering execution path.
    • If needed, click Return to add a new line below the value property. On this line, add the following transitions node. The transitions: node should be four spaces from the left margin so that it aligns with the properties node. The next: "startDetermineWineType" property should be six spaces from the left margin. You can enter these manually, or paste the following snippet into your dialog definition after positioning the cursor at the left margin.
          transitions:
            next: "startDetermineWineType"
    When you're done, the setChatToOrderType state should look like this:
      setChatToOrderType:
        component: "System.SetVariable"
        properties:
          variable: "chatType"
          value: "order"
        transitions:
          next: "startDetermineWineType"

Set the Wine Type

Although you will create separate execution paths for the tellme.about.wine and order.wine intents, there are states that are common to both. Roughly speaking, the sequence of these common states is:

  1. Detect the wine type
  2. Select a wine type based on wine color or name
  3. Branch the dialog flow based on chat type

Detect the Wine Type

    In this step, you'll create the first of the common states, which, based on the NLP results stored in the iResult property, routes to states that output option lists for selecting a specific wine, a wine category, or a wine type. The System.Switch component, which you're going to add next, determines this routing by evaluating the stored results.

  1. Click + Add Component.
  2. Choose Control > Switch.
  3. Choose setChatToOrderType from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change switch to startDetermineWineType.
    • Remove the variable property.
    • Set the source property to the following FreeMarker epression that evaluates the stored input for entities:
       "${iResult.value.entityMatches['list.redWines']?has_content?then('redWine',iResult.value.entityMatches['list.whiteWines']?has_content?then('whiteWine', 'none'))}"

      This Apache FreeMarker expression checks if the name of the wine has been included in the user's initial message to the skill. The expression evaluates the NLP results stored in the iResult variable for values matching the list.redWines and list.whiteWines entities. If the name of a red or white wine is present, the redWine or whiteWine actions are triggered and the dialog transitions to the selectRedWine and selectWhiteWine states. If the user's input doesn't specify a wine type, the NONE action is triggered and the dialog flow transitions to the checkForWineColor state.

    • For the values property, change "option1" to "redWine". Then change "option2" to "whiteWine".
    • For actions (located in the transitions node) replace "option1" with:
              redWine: "selectRedWine"
      Then replace "option2" with
              whiteWine: "selectWhiteWine"
    • Define the NONE action as "checkForWineColor":
              NONE: "checkForWineColor"
    • Note:

      Each of these actions should be eight spaces from the left margin.
    When you're done, the startDetermineWineType state should look like this:
      startDetermineWineType:
        component: "System.Switch"
        properties: 
          source: "${iResult.value.entityMatches['list.redWines']?has_content?then('redWine',iResult.value.entityMatches['list.whiteWines']?has_content?then('whiteWine', 'none'))}"
          values:
          - "redWine"
          - "whiteWine"
        transitions:
          actions:
            redWine: "selectRedWine"
            whiteWine: "selectWhiteWine" 
            NONE: "checkForWineColor"

Set the Wine Type

    In step, you're going to add a pair of options lists that enable users to select red and white wines. You'll add this UI by defining the System.CommonResponse component because it can render a list of values when multiple values exist. In this case, the displayed values are returned from of values defined for the list.redWines and list.whiteWines entities. The entity values are accessed using the enumValue property.

    Description of this image follows
    Description of the illustration shortcut_menu.png

    Note:

    While you can create a simple list of options using the System.List component, the System.CommonResponse component provides greater customization.

    1. Click + Add Component.
    2. Choose User Interface > Common response - composite bag.
    3. Choose startDetermineWineType from the Insert After State menu.
    4. Clear Include template comments.
    5. Click Insert Component.
    6. Change resolveCompositeBag to selectRedWine.
    7. Set the "variable" property to "redWines". (You may need to insert a space before adding this value.) The value for the redWines variable can be set explicitly when the user selects an option or enters the name of the wine, or implicitly when the value is set (or slotted) from the input that's processed by the NLP (natural language processing) framework and stored in the in the iResult variable.
    8. Set the "nlpResultVariable" to "iResult". With this setting, the redWines variable value will be slotted from by the NLP framework if the intial user message contains the name of a red wine.
    9. Set useFullEntityMatches to false. If this were a multi-lingual skill, you'd set this property to true, but for the purposes of this tutorial, it's set to false.
    10. Set maxPrompts to 2, allowing users two attempts to provide a valid value. When the value for the redWines variable has not been set, users can either choose an option, enter text for the wine name, or enter the shortcut (b for Merlot, for example). When the text doesn't match any of the values or synonyms defined for the list.redWines, the skill outputs the error message defined for the list.redWines entity along with the prompts that have been defined for the entity:
      I am sorry. "${system.message.messagePayload.text}" does not seem to be a valid option. After the second attempt, the dialog transitions to the cancelOrder state, ending the conversation.
    11. Delete the following properties:
      • "transitionAfterMatch"
      • "cancelPolicy"
      • "autoNumberPostbackActions"
    12. Under the actions: node (located within the metadata node):
      • Replace the default definition for the label property with
        "${(enumValue?index?number +1)?lower_abc}. ${enumValue}"
        For example:
                  actions:
                  - label: "${(enumValue?index?number +1)?lower_abc}. ${enumValue}"
        With this change, the label displays the name of the a wine preceded by leading character that's a shortcut. It's a customization that the System.CommonResponse component allows you to do that you can't with a regular System.List component, which outputs a simple list.
      • The keyword allows you to define quick-access short cuts for buttons, so set the keyword property to
        "${(enumValue?index?number +1)?lower_abc}, ${enumValue}"
      • In the payload node, change the name of the varName property (which is located under variables property) to redWines. Then define the value as "${enumValue}":
        redWines: "${enumValue}"
    13. In the transitions node (located at the bottom of the state):
      • Click Return to insert a new line below transitions.
      • Add a next transition that's triggered when users enter valid input. The next transition should be six spaces from the left menu so that it aligns with the actions node directly beneath it.
      • Define the next transition as "saveSelectedWine". You can add this manually, or paste in the following snippet after positioning your cursor at the left margin.
              next: "saveSelectedWine"
      • In the actions node, remove the match property.
      • Set the cancel transition to "cancelOrder". This transition is triggered when users enter invalid input or when they tap the Cancel button.
                cancel: "cancelOrder"
      When you're done, the state should look like this:
        selectRedWine:
          component: "System.CommonResponse"
          properties:
            processUserMessage: true
            variable: "redWines"
            nlpResultVariable: "iResult"       
            useFullEntityMatches: false      
            maxPrompts: 2
            metadata: 
              responseItems:         
              - type: "text"  
                text: "${system.entityToResolve.value.prompt}" 
                footerText:
                visible: 
                  expression:            
                  entitiesToResolve:
                    include:
                    exclude:    
                  channels:
                    include:
                    exclude:  
                  onInvalidUserInput:  
                actions:
                - label: "${(enumValue?index?number +1)?lower_abc}. ${enumValue}"
                  type: "postback"
                  iteratorVariable: "system.entityToResolve.value.enumValues"
                  keyword: "${(enumValue?index?number +1)?lower_abc}, ${enumValue}"
                  payload:
                    variables:
                      redWines: "${enumValue}"
                  visible: 
              globalActions: 
              - label: "Show Previous"
                type: "postback" 
                visible:
                  expression: "${system.entityToResolve.value.needShowPreviousButton}"
                payload:
                  action: "system.showPrevious"
                  variables: 
                    ${system.entityToResolve.value.rangeStartVar}: ${system.entityToResolve.value.previousRangeStart} 
              - label: "Show More"
                type: "postback" 
                visible:
                  expression: "${system.entityToResolve.value.needShowMoreButton}"
                payload:
                  action: "system.showMore"
                  variables: 
                    ${system.entityToResolve.value.rangeStartVar}: ${system.entityToResolve.value.nextRangeStart} 
              - label: "Cancel"
                type: "postback" 
                visible:
                  onInvalidUserInput: true
                payload:
                  action: "cancel"
          transitions:
            next: "saveSelectedWine"    
            actions:
              cancel: "cancelOrder"
    14. Repeat the previous steps to create the selectWhiteWine state, which is where the dialog navigates to when the user input mentions white wine. This state is the same as the selectRedWine state that you just created except for the following:
      • In the Components dialog, choose selectRedWine from the Insert After menu.
      • Change resolveCompositeBag to selectWhiteWine.
      • Set the variable property to "whiteWines".
      • Change the varName property name to whiteWines (whiteWines: "${enumValue}").
      When you're done, the selectWhiteWine state should look like this:
        selectWhiteWine:
          component: "System.CommonResponse"
          properties:
            processUserMessage: true
            variable: "whiteWines"
            nlpResultVariable: "iResult"      
            useFullEntityMatches: false      
            maxPrompts: 2
            metadata: 
              responseItems:         
              - type: "text"  
                text: "${system.entityToResolve.value.prompt}" 
                footerText:
                visible: 
                  expression:            
                  entitiesToResolve:
                    include:
                    exclude:    
                  channels:
                    include:
                    exclude:  
                  onInvalidUserInput:  
                actions:
                - label: "${(enumValue?index?number +1)?lower_abc}. ${enumValue}"
                  type: "postback"
                  iteratorVariable: "system.entityToResolve.value.enumValues"
                  keyword: "${(enumValue?index?number +1)?lower_abc}, ${enumValue}"
                  payload:
                    variables:
                      whiteWines: "${enumValue}"
                  visible: 
              globalActions: 
              - label: "Show Previous"
                type: "postback" 
                visible:
                  expression: "${system.entityToResolve.value.needShowPreviousButton}"
                payload:
                  action: "system.showPrevious"
                  variables: 
                    ${system.entityToResolve.value.rangeStartVar}: ${system.entityToResolve.value.previousRangeStart} 
              - label: "Show More"
                type: "postback" 
                visible:
                  expression: "${system.entityToResolve.value.needShowMoreButton}"
                payload:
                  action: "system.showMore"
                  variables: 
                    ${system.entityToResolve.value.rangeStartVar}: ${system.entityToResolve.value.nextRangeStart} 
              - label: "Cancel"
                type: "postback" 
                visible:
                  onInvalidUserInput: true
                payload:
                  action: "cancel"
          transitions:
            next: "saveSelectedWine"    
            actions:
               cancel: "cancelOrder"

Set the Wine Color

The dialog transitions to this state when the user's input does not include specifics as to wine type or color. To end up at this state, a user would have entered something like "I want some wine." For this state, you're going add a System.CommonResponse component that displays the values defined for the list.wine.types entity

  1. Click + Add Component.
  2. Choose User Interface > Common response - composite bag.
  3. Choose selectWhiteWine from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Change resolveCompositeBag to checkForWineColor.
  7. Set the "variable" property to "wineTypes".
  8. Set the "nlpResultVariable" to "iResult". With this setting, the redWines variable value will be slotted from the intent engine if the intial user message contains the name of a red wine.
  9. Set useFullEntityMatches to false.
  10. Set maxPrompts to 2 (allowing users 2 attempts to provide a valid value).
  11. Delete the following properties:
    • "transitionAfterMatch"
    • "cancelPolicy"
    • "autoNumberPostbackActions"
  12. Under the actions: node (located within the metadata node), replace the label value with "${enumValue}". For example:
              - label: "${enumValue}"
  13. In the payload node, change the varName property, which is located under variables property name to wineTypes. Then define the value as "${enumValue}":
    wineTypes: "${enumValue}"
  14. In the transitions node (located at the bottom of the state):
    • Click Return to insert a new line below transitions.
    • Add the following next transition that's triggered when users enter valid input. This transition must be six spaces from the left margin so that it aligns with the actions node directly beneath it. You can add this manually, or copy and paste the following snippet after positioning your cursor at the left menu.
            next: "determineWineMenu"
    • In the actions node, remove the match property.
    • Set the cancel transition that's triggered when user enter invalid input or when they tap the Cancel button. Set the transition to "cancelOrder":
              cancel: "cancelOrder"
    When you're done, the checkForWineColor state should look like this:
      checkForWineColor:
        component: "System.CommonResponse"
        properties:
          processUserMessage: true
          variable: "wineTypes"
          nlpResultVariable: "iResult"       
          useFullEntityMatches: false      
          maxPrompts: 2
          metadata: 
            responseItems:         
            - type: "text"  
              text: "${system.entityToResolve.value.prompt}" 
              footerText:
              visible: 
                expression:            
                entitiesToResolve:
                  include:
                  exclude:    
                channels:
                  include:
                  exclude:  
                onInvalidUserInput:  
              actions:
              - label: "${enumValue}"
                type: "postback"
                iteratorVariable: "system.entityToResolve.value.enumValues"
                keyword: 
                payload:
                  variables:
                    wineTypes: "${enumValue}"
                visible: 
            globalActions: 
            - label: "Show Previous"
              type: "postback" 
              visible:
                expression: "${system.entityToResolve.value.needShowPreviousButton}"
              payload:
                action: "system.showPrevious"
                variables: 
                  ${system.entityToResolve.value.rangeStartVar}: ${system.entityToResolve.value.previousRangeStart} 
            - label: "Show More"
              type: "postback" 
              visible:
                expression: "${system.entityToResolve.value.needShowMoreButton}"
              payload:
                action: "system.showMore"
                variables: 
                  ${system.entityToResolve.value.rangeStartVar}: ${system.entityToResolve.value.nextRangeStart} 
            - label: "Cancel"
              type: "postback" 
              visible:
                onInvalidUserInput: true
              payload:
                action: "cancel"
        transitions:
          next: "determineWineMenu"    
          actions:
            cancel: "cancelOrder"

Set the Selected Wine Menu

If users select a wine color, the value is saved in the wineTypes variable. This state, which is built around the System.Switch component, branches the conversation using this value.
  1. Click + Add Component.
  2. Choose Control > Switch.
  3. Choose checkForWineColor from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change switch to determineWineMenu.
    • Set the variable property to "wineTypes".
    • Remove the unneeded source property.
    • In the values node, change "option1" to "red wine". Then change "option2" to "white wine".
            - "red wine"
            - "white wine"
    • For the actions node (located under transitions), change option1 and option2 to red wine: "selectRedWine" and white wine: "selectWhiteWine", respectively.
    • Define the NONE transition as "checkForWineColor".
              red wine: "selectRedWine"
              white wine: "selectWhiteWine"
              NONE: "checkForWineColor"
    When you're done, the determineWineMenu state should look like this:
      determineWineMenu:
        component: "System.Switch"
        properties:
          variable: "wineTypes"
          values:
          - "red wine"
          - "white wine"
        transitions:
          actions:
            red wine: "selectRedWine"
            white wine: "selectWhiteWine"
            NONE: "checkForWineColor"

Save the Wine Type

The user-selected wine are saved in two separate variables (whiteWines or redWines) becuase they have been declared for two separate value list entities (list.redWines and list.whiteWines). To keep the Apache FreeMarker expressions simple, we're going to consolidate these values into a single variable called selectedWine where either wine selection can be saved. To set the value, you're going to add a System.SetVariable component.

  1. Click + Add Component.
  2. Choose Variables > Set variable.
  3. Choose determineWineMenu from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change setVariable to saveSelectedWine.
    • In the properties node, set the variable property to "selectedWine".
    • Define the value property with the following Apache FreeMarker expression:
       "${redWines.value?hasContent?then(redWines.value, whiteWines.value)}"

      This expression sets the value for the selectedWine variable by first checking for the redWines variable. If this variable has not been set, the expression sets the whiteWines variable with the name of the wine.

    • Click Return to create a new line. Then add the following transitions node. The transitions: node should be four spaces from the left margin so that it aligns with the properties node. The next: "branchToOrderOrQuestionFlow" property should be six spaces from the left margin. You can add this manually, or you can paste this snippet into the dialog flow dialog definition after positioning your cursor at the left margin.
          transitions:
            next: "branchToOrderOrQuestionFlow"
    When you're done, the state should look like this:
      saveSelectedWine:
        component: "System.SetVariable"
        properties:
          variable: "selectedWine"
          value: "${redWines.value?hasContent?then(redWines.value, whiteWines.value)}"
        transitions:
          next: "branchToOrderOrQuestionFlow"
    

Branch the Dialog Flow by Chat Type

Up to this point, the conversation flows for ordering wine and asking questions about wine are the same. Because they have different outcomes, we need to branch the flow. To do this, we're going base the branching on the value set for the chatType variable (set in the setChatToOrderType state), which can be either order or question.

Note:

No validation is required because both the order and question values are set programatically. It's either one or the other. If the value is not order, then it must be question.

To perform the branching logic, we're going to define a System.ConditionEquals component to branch the flow by comparing the value set (or not set) for the chatType variable against a value of order. If the two are different, the flow transitions to the tellme.about.wine execution path.
  1. Click + Add Component.
  2. Choose Control > Condition equals.
  3. Choose saveSelectedWine from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change statename to branchToOrderOrQuestionFlow.
    • Set the variable property to "chatType".
    • Set the value property to "order".
    • In the actions node, set the equal property to "askShippingSize". Then set the notequal property to "showAnswerToQuestion".
    When you're done, the branchToOrderOrQuestionFlow state should look like this:
      branchToOrderOrQuestionFlow:
        component: "System.ConditionEquals"
        properties:
          variable: "chatType"
          source:
          value: "order"
        transitions:
          actions:
            equal: "askShippingSize" 
            notequal: "showAnswerToQuestion"

Create the Wine Ordering Flow

Select the Order Size

For users to select the order size, we are using the System.ResolveEntities a component. It's similar to the System.CommonResponse component, but has fewer options. While it's easier to use, its customization options are limited.

  1. Click + Add Component.
  2. Choose User Interface > Resolve entities.
  3. Choose branchToOrderOrQuestionFlow from the Insert After State menu.
  4. Switch on Remove Contents.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change resolveEntities to askShippingSize.
    • Set the variable property to "orderSizes".
    • Remove the following properties:
      • nlpResultVariable
      • transitionAfterMatch
      • cancelPolicy
      • autoNumberPostbackActions
      • headerText
      • footerText
    • Set useFullEntityMatches to false.
    • Set the MaxPrompts property to 2 to limit the users' attempts at entering values that match the values or synonyms defined for the list.orderSizes entity to two tries. While this state presents users with a list of options, users may exceed the number of attempts if they instead enter text that is not valid. When the skill receives an invalid value, it returns the value to the user through the system.message.messagePayload.text property in the entity's error message that displays in the prompt dialog:
      ${system.message.messagePayload.text} is not a valid shipment size.  
      When users enter an invalid value for the second time, the dialog flow transitions to the askCancelOrder state, which you're going to define as a transition for this state.
    • In the transitions node, first click Return to insert a line, then add a next transition that's triggered when the skill receives a valid value and moves to the summarizeOrder state. The next: "summarizeOrder" action should be six spaces from the left margin so that it aligns with the actions node directly beneath it. You can add this transition manually, or paste the following snippet into your dialog definition after you position the cursor at the left margin.
            next: "summarizeOrder"
    • In the actions node, remove match.
    • Define the cancel action as cancel: "askCancelOrder" that's triggered when users enter an incorrect value twice.
    When you're done, the state should look like this:
      askShippingSize:
        component: "System.ResolveEntities"
        properties:
          variable: "orderSizes"
          useFullEntityMatches: false       
          maxPrompts: 2
          showMoreLabel:
          showPreviousLabel:
        transitions:
          next: "summarizeOrder"
          actions:
            cancel: "askCancelOrder"

Create the Cancel Order Confirmation Message

  1. Click + Add Component.
  2. Choose User Interface > Common Response.
  3. Choose Common response - composite bag.
  4. Choose askShippingSize from the Insert After menu.
  5. Clear Include template components.
  6. Click Insert Component.
  7. Update the state as follows:
    • Change resolveCompositeBag to askCancelOrder.
    • Set the variable property to "yesNo", the variable declared for the built-in Yes_No entity which validates a yes or a no response.
    • Remove the nlpResultVariable property.
      • nlpResultVariable
      • transitionAfterMatch
      • cancelPolicy
      • autoNumberPostbackActions
    • Set useFullEntityMatches to false.
    • Set the MaxPrompts property to 2 to limit the users' attempts at entering valid yes or no responses to two tries.
    • For the text property (located in the metadata node under responseItems), replace the expression defined for the text property with the following string:
      "I cannot ship wine without an order Size. Do you want to cancel this order (yes/no)?"
    • In the actions node (within transitions), set the match property to "checkCancelOrderOrNot".
    • Set cancel to "cancelOrder".
  8. Because you only need this component to output a prompt, you can delete the following nodes and their contents (which is everything between footerText and transitions):
    • visible
    • actions
    • globalActions
    When you're done, the askCancelOrder state should look like this:
      askCancelOrder:
        component: "System.CommonResponse"
        properties:
          processUserMessage: true
          variable: "yesNo"
          useFullEntityMatches: false       
          maxPrompts: 2      
          metadata: 
            responseItems:         
            - type: "text"  
              text: "I cannot ship wine without an order Size. Do you want to cancel this order (yes/no)?"
              footerText:        
        transitions:
          actions:
            match: "checkCancelOrderOrNot"
            cancel: "cancelOrder"

Confirm the Order Status

Create a state that checks whether the user has opted to cancel or continue. For this state, you'll define a System.ConditionEquals component to compare the values.

  1. Click + Add Component.
  2. Choose Control > Condition equals.
  3. Choose askCancelOrder from the Insert After menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change conditionEquals to checkCancelOrderOrNot.
    • Delete the unneeded variable variable.
    • Set the source property to
      "${yesNo.value.yesno}"
    • Set the value property to "YES".
    • In the actions node, set eqaul to "cancelOrder". Then set notequal to "askShippingSize".
    When you're done, the state should look like this:
      checkCancelOrderOrNot:
        component: "System.ConditionEquals"
        properties:
          source: "${yesNo.value.yesno}"
          value: "YES"
        transitions:
          actions:
            equal: "cancelOrder"
            notequal: "askShippingSize"
    

Create the Cancel Order Message

In this state, we're going to enable the skill to output a simple message by defining a System.Output component.

  1. Click + Add Component.
  2. Choose User Interface > output.
  3. Choose checkCancelOrderOrNot from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change output to cancelOrder.
    • Change the text property to
      "Our winery chat just ended here. If you wish, we can start over again. If not, then have a good day, cheers - your friend Grapy"
    • Add the following transitions node with a return statement to end the conversation and clear all the values. (You may need to click Return to add a new line.) The transitions node should be four spaces from the left margin and should align with the properties node. The return: "done" statement should be six spaces from the left margin. You can either enter it manually, or paste in the following snippet after positioning your cursor at the left margin.
          transitions:
            return: "done"
      
    When you're done, the cancelOrder state should look like this:
      cancelOrder:
        component: "System.Output"
        properties:
          text: "Our winery chat just ended here. If you wish, we can start over again. If not, then have a good day, cheers - your friend Grapy"
          keepTurn: false
        transitions:
          return: "done"
    

Confirm the Order

To finalize the order conversation, we need to display a confirmation message. This message uses the wine cards to display a picture of the selected wine, the total cost of the order based on the number of bottles, and the the price of an individual bottle. To enable the display of the picture and the calculation for the order, you'll define this state using Apache FreeMarker expressions that evaluate the input and create the display by referencing the properties of the redWineCard and whiteWineCard variables that are populated with the mock data and images. These properties, which include image and price, are set by the buildRedWineMenu and buildWhiteWineMenu states at the beginning of the states node.

Description of this image follows
Description of the illustration wine_menu_properties.png
The expression finds the value stored in the selectedWine variable and then reads the image property and price properties:
  • redWineCard.value[selectedWine].image
  • redWineCard.value[selectedWine].price
  1. Click + Add Component.
  2. Choose User Interface > Common Response - attachment.
  3. Choose cancelOrder from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change attachmentResponse to summarizeOrder.
    • In the responseItems node, add a row beneath type: "attachment".
    • Enter headerText: in the blank row. This entry should be ten spaces from the left margin so that it aligns with type in the row above. Define headerText as "Order Summary":
                headerText: "Order Summary"
    • Remove the attachmentTitle node.
    • Replace with the attachmentUrl definition with:
      "${redWineCard.value[selectedWine]?has_content?then(redWineCard.value[selectedWine].image, whiteWineCard.value[selectedWine].image)}"
    • Define the footerText property with the following Apache FreeMarker expression:
       "You ordered ${orderSizes.value} of ${selectedWine.value} for ${redWineCard.value[selectedWine]?has_content?then(redWineCard.value[selectedWine].price, whiteWineCard.value[selectedWine].price)}$ per bottle."
    • Remove the visible node entirely.
    • Add the following transitions node with a return statement to end the conversation and clear all the values. The transitions node should be four spaces from the left margin and should align with the properties node. The return: "done" statement should be six spaces from the left margin. You can either enter it manually, or paste in the following snippet after positioning your cursor at the left margin.
          transitions:
            return: "done"
    When you're done, the summarizeOrder state should look like this:
      summarizeOrder:
        component: "System.CommonResponse"
        properties:
          processUserMessage: false
          keepTurn: false
          metadata: 
            responseItems:         
            - type: "attachment" 
              headerText: "Order Summary"
              attachmentType: "image"           
              attachmentUrl: "${redWineCard.value[selectedWine]?has_content?then(redWineCard.value[selectedWine].image, whiteWineCard.value[selectedWine].image)}"            
              footerText: "You ordered ${orderSizes.value} of ${selectedWine.value} for ${redWineCard.value[selectedWine]?has_content?then(redWineCard.value[selectedWine].price, whiteWineCard.value[selectedWine].price)}$ per bottle."
        transitions:
          return: "done"
    
    

Build the Question and Answer Flow

With the ordering flow complete, you're ready to create the question and answer flow. The answers are read from resource bundles strings that use the name of the wine as a key. These answers are created as resource bundle strings that are referenced in the dialog flow definition, but are not part of the dialog flow. Click Resource Bundles The Resource Bundle icon in the left navbar to take a quick look.

Description of  follows
Description of the illustration resource_bundle_page.png

Note:

To keep this tutorial as simple as possible, we're only using resource bundles for the answer strings. However, when you build production-level skills, we recommend that you use resource bundles for all strings and labels. Resource bundles are not just for multi-lingual skills; they are a best practice because they centralize all of your text strings, making them easier to manage and easier to review for a consistent style and voice.

Set the Chat Type to Question

The state that you're going to create in this step starts the question and answer flow by setting the value of the chatType variable to question using a System.SetVariable component. Similar to the setChatToOrderType state, this state transitions to the startDetermineWineType state that ultimately routes to the branchToOrderOrQuestionFlow state.

  1. Click + Add Component.
  2. Choose Variable > Set variable.
  3. Choose summarizeOrder from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change setVariable to setChatToExplainType.
    • Set the variable property to "chatType".
    • Set the value property to "question".
    • Add the following transitions node. transitions: should be four spaces from the left margin so that it aligns with the properties node. (You may need to create a new line by clicking Return.) The next: "confirmQuestion" statement should be six spaces from the left margin. You can enter this manually, or paste the following snippet into your dialog flow definition after positioning your cursor at the left margin.
          transitions:
            next: "confirmQuestion"
      
    When you're done, the state should look like this:
      setChatToExplainType:
        component: "System.SetVariable"
        properties:
          variable: "chatType"
          value: "question"
        transitions:
          next: "confirmQuestion"
    

Create the Confirm Question Message

  1. Click + Add Component.
  2. Choose User Interface > Output.
  3. Choose setChatToExplainType from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Components.
  6. Update the state as follows:
    • Change output to confirmQuestion.
    • Set the text property to
      "Let me get your question answered."
    • Set keepTurn to true to prevent the user from entering input before the skill can transition to the startDetermineWineType state.
    • Click Return after the keepTurn node to insert a new line. Then add the following transitions node. transitions: should be four spaces from the left margin so that it aligns with the properties node. The next: "startDeterminWineType" statement should be six spaces from the left margin. You can enter this manually, or paste the following snippet into your dialog flow definition after positioning your cursor at the left margin.
          transitions:
            next: "startDetermineWineType"
      
    When you're done, the state should look like this:
      confirmQuestion:
        component: "System.Output"
        properties:
          text: "Let me get your question answered."
          keepTurn: true
        transitions:
          next: "startDetermineWineType"
    

Output the Answer

  1. Click + Add Component.
  2. Choose User Interface > Output.
  3. Choose confirmQuestion from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change output to showAnswerToQuestion.
    • Set the text property to
      "${rb(selectedWine.value)}"
    • Set the keepTurn property to true because users are expected (or allowed) to reply to the skill's answer.
    • Click Return after the keepTurn node to insert a new line. Then add the following transitions node. transitions: should be four spaces from the left margin so that it aligns with the properties node. The next: "goodbye" statement should be six spaces from the left margin. You can enter this manually, or paste the following snippet into your dialog flow definition after positioning your cursor at the left margin.
          transitions:
            next: "goodbye"
      
    When you're done, the state should look like this:
      showAnswerToQuestion:
        component: "System.Output"
        properties:
          text: "${rb(selectedWine.value)}"
          keepTurn: true
        transitions:
          next: "goodbye"
    

Create the Farewell Message

  1. Click + Add Component.
  2. Choose User Interface > Output.
  3. Choose showAnswerToQuestion from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change output to goodbye.
    • Set the text property to
      "Hope I could help. If you want, ask another question."
    • Click Return after the keepTurn node to insert a new line. Then add the following transitions node. transitions: should be four spaces from the left margin so that it aligns with the properties node. The return: "done" statement should be six spaces from the left margin. You can enter this manually, or paste the following snippet into your dialog flow definition after positioning your cursor at the left margin.
          transitions:
            return: "done"
    When you're done, the state should look like this:
      goodbye:
        component: "System.Output"
        properties:
          text: "Hope I could help. If you want, ask another question."
          keepTurn: false
        transitions:
          return: "done"      
    

Add Error-Handling

The skill needs to tell the user when it can't understand the input that's stored in getIntent state's iResult variable. When the NLP framework can't resolve the input to either the tellme.about.wine or order.wine intents, it resolves the input to a third intent, unresolvedIntent. This is a built-in intent for input that can't be resolved to any of the intents. When it's resolved, the dialog transitions to a state dedicated to handling errors, which you're creating in this step. For this skill, error handling means outputting a message that the skill can't understand the user input, clearing any variable values, and resetting the dialog flow at the beginning.

Add the Dedicated Error-Handling State

  1. Click + Add Component.
  2. Choose User Interface > Output.
  3. Choose goodbye from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change output to handleBotNotUnderstanding.
    • Set the text property to
      "Hmm, seems I did not quite understand. I hope my wine skills are better. You can try my wine skills by sending a message like 'I want to order wine'"
    • Click Return after the keepTurn node to insert a new line. Then add the following transitions node. transitions: should be four spaces from the left margin so that it aligns with the properties node. The return: "done" statement should be six spaces from the left margin. You can enter this manually, or paste the following snippet into your dialog flow definition after positioning your cursor at the left margin.
          transitions:
            return: "done"
    When you're done, the state should look like this:
      handleBotNotUnderstanding:
        component: "System.Output"
        properties:
          text: "Hmm, seems I did not quite understand. I hope my wine skills are better. You can try my wine skills by sending a message like 'I want to order wine'"
          keepTurn: false
        transitions:
          return: "done"
    

Enable Out-of-Order Message Handling

Because chat messengers don't hide the message history, users can scroll back through the conversation and then select an option in an older message that is no longer in focus. In our skill, for example, you might scroll back through the prior messages to revise your order. An out-of-order user action of this sort is a typical real-world scenario that our skill needs to support. To enable the skill to handle this gracefully, you're going to implement a global navigation rule that directs the dialog flow to a subflow that defines how the skill should react when it encounters out-of-order input.

You can define a global navigation rule in the defaultTransitions node, which is near the top of the dialog flow definition and use it to set the behavior the next, error, or component action transitions when you don't want an individual state to be handling them.

In this step, we're going to add the defaultTransitions node, but instead of using it for our flow's next and error transitions, we're going to define it for out-of-order message handling using the system.outOfOrderMessage transition. This transition will point to the first state of the message handling subflow, which we're also going to create in this step.

Create the Default Transition:

  1. Navigate to the top of the dialog flow.
  2. Copy the following just above the states node.
    defaultTransitions:
      actions:
        system.outOfOrderMessage: "outOfOrderMessageHandler"
Description of this image follows
Description of the illustration follows.

Create the Error-Handling State

While we want to allow out-of-order messages in the middle of the conversation, we don't want to allow this behavior all of the time. Specifically, we don't allow it after an order has been confirmed or canceled, after a question has been answered, or when no current conversation exists. This state, which is the start of the out-of-order message handling subflow, determines when users can still revise their orders and when they'll have to restart the conversation by referencing the following built-in system variables:
  • ${system.actualState}, which holds the name of the prior state.
  • ${system.expectedState}, which holds the name of the current dialog state. It's referenced in the Apache FreeMarker expression to determine if the conversation is under way. This expression tests if the current state is buildRedWineMenu, the first state of the dialog flow. If so, then no navigation can be performed because an actual skill-user conversation has not begun.
    "${(system.expectedState == 'buildRedWineMenu')?then('disAllowNavigation','allowNavigation')}"

  1. Click + Add Component.
  2. Choose Control > Conditional equals.
  3. Choose handleBotNotUnderstanding from the Insert After State menu.
  4. Clear Include template components.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change conditionEquals to outOfOrderMessageHandler.
    • Set the source property to:
       "${(system.expectedState == 'buildRedWineMenu')?then('disAllowNavigation','allowNavigation')}"
    • Set the value property to "allowNavigation".
    • In the transitions node
      • Define equal as
         "${system.actualState}"
      • Define notequal as
         "navigationNotAllowedMessage"
    When you're done, the outOfOrderMessageHandler state should look like this:
      outOfOrderMessageHandler:
        component: "System.ConditionEquals"
        properties:
          variable: 
          source: "${(system.expectedState == 'buildRedWineMenu')?then('disAllowNavigation','allowNavigation')}"
          value: "allowNavigation"
        transitions:
          actions:
            equal: "${system.actualState}"
            notequal: "navigationNotAllowedMessage"
    

Create the Navigation Not Allowed Message

  1. Click + Add Component.
  2. Choose User Interface > Output.
  3. Choose outOfOrderMessageHandler from the Insert After State menu.
  4. Clear Include template comments.
  5. Click Insert Component.
  6. Update the state as follows:
    • Change output to navigationNotAllowedMessage.
    • Set the text property to
      "I can't change the direction at this point in the conversation. Please start a new chat for any new orders or questions you have."
    • Click Return after the keepTurn node to insert a new line. Then add the following transitions node. transitions: should be four spaces from the left margin so that it aligns with the properties node. The return: "done" statement should be six spaces from the left margin. You can enter this manually, or paste the following snippet into your dialog flow definition after positioning your cursor at the left margin.
          transitions:
            return: "done"      
    When you're done, the state should look like this:
      navigationNotAllowedMessage:
        component: "System.Output"
        properties:
          text: "I can't change the direction at this point in the conversation. Please start a new chat for any new orders or questions you have."
          keepTurn: false
        transitions:
          return: "done"
    

Test the Skill

Now that your dialog is complete, you can test it out with the Skill Tester, which you access by clicking Preview Skill tester icon (located at the top right).

Description of this image follows
Description of the illustration follows.
The Skill Tester not only lets you chat with your skill, but it also lets you monitor the dialog traversal as the conversations progress using the execution paths rendered in the Conversation tab.
Description of this image follows
Description of the illustration test_answer_intent.png

Tip:

Before you test out the skill, run Validate one more time. If the validator lists a lot of critical errors This is an image of the debug icon, delete the entire definition and replace it with the contesxt in the complete_dialog_flow file.

  1. Test the answer flows. Click Reset after each one:
    • What is this skill about?
    • What do I learn in this tutorial?
    • What will I learn?
    • I have a question about wine
    • I have a question about white wine
    • Tell me about Riesling
    • Note:

      There are two answer execution paths. When you ask about the skill itself, the execution path ends with getIntent state because it's an answer intent, which only needs a System.Intent component -- the getIntent state in our skill. When you ask about a wine, the execution path proceeds to the setChatToExplainType state (the start of the question-and-answer flow) from the getIntent state.
  2. Test the wine ordering flow by starting off conversations with these phrases. Click Reset after each completed conversation:
    • I want to buy wine
    • I want to order red wine
    • I want to buy Chardonnay
  3. Test the out of-order-message handling:
    • Enter I want to order wine.
    • Choose red wine.
    • Choose b (Merlot).
    • When prompted for the order size, scroll back and enter to the wine menu and choose Cabernet Sauvingnon.
    • Choose the order size.
    • After the confirmation message, scroll back to the order size menu and choose an option.
  4. Test out the error flow:
    • Enter I want a pizza. Click Reset.
    • Enter I want red wine.
      • Instead of choosing an option from the menu, enter e.
      • Enter Pizza.

Learn More