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
- Log into Oracle Digital Assistant.
- Click in the top left corner to open the side menu.
- Expand Development and then click Skills.
- Hide the menu by clicking again.
- Click Import Skill (located at the upper right).
- Browse to, and then select,
Wine_Skill_Starter.zip
from the extractedWineSKill_Materials
file. Then click Open. - Click Upgrade to migrate the skill to the current version of Oracle Digital Assistant.
- Enter a new version number in the the Create New Version of the Skill dialog.
- Choose the latest version of the platform. Then click Create.
- Click Train.
- 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.
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.
- First, let's take a look at the intents. If it's not already selected, click Intents in the left navbar. This skill uses regular intents (order.wine and tellme.about.wine) and an answer intent which describes this tutorial.
- 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).
Enter the following phrases in the Utterance field, then click Test. Click Reset after each round. When you're done, click Close .
For this intent... Enter these phrases ans.aboutSample - What's this skill about
- What will I learn from this tutorial?
- What will I learn
order.wine 2 - I want to buy wine
- I want to buy red wine
- I want to buy Chardonnay
tellme.about.wine - I have a question about wine
- I have a question about white wine
- Tell me about Riesling
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). - Click Entities in the left navbar to take a look at the entities for matching both the wine category and wine type from the user input.
- Click Flows in the left navbar.
- 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
, andwhiteWines
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
andwhiteWineCard
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 the illustration context_node.png - The
- In the
states
node, take a look at thebuildRedWineMenu
andbuildWhiteWineMenu
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 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.
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.
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.
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.
- Click + Add Component.
- Choose User Interface.
Description of the illustration add_components_user_interface.png - Click Output.
- Select buildWhiteWineMenu from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
Description of the illustration add_components_dialog.png - Update the
output
state as follows:- Change
output
towelcome
. - 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
totrue
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. Thetransitions:
node must align with theproperties
node (four spaces from the left margin). Thenext: "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.
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"
- Change
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.
- Click + Add Component.
- Choose Language > Intent.
- Choose welcome from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the intent state as follows:
- Change
intent
togetIntent
. - Set the
variable
property to"iResult"
. - Delete the properties that you don't need:
optionsPrompt
,botName
,botVersion
,sourceVariable
,autonumberPostbackActions
, andfooterText
. When you're done, you should be left with only thevariable
anddaIntercept
properties. - Click Return to create a new line below the
actions
node. - In the
actions
node (directly beneathtransitions
), replaceunresolvedIntent
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"
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"
- Change
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.
- Click + Add Component.
- Choose Variables > Set variable.
- Choose getIntent from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
setVariable
state as follows:- Change
setVariable
tosetChatToOrderType
. - 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 followingtransitions
node. Thetransitions:
node should be four spaces from the left margin so that it aligns with theproperties
node. Thenext: "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"
setChatToOrderType
state should look like this:setChatToOrderType: component: "System.SetVariable" properties: variable: "chatType" value: "order" transitions: next: "startDetermineWineType"
- Change
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:
- Detect the wine type
- Select a wine type based on wine color or name
- Branch the dialog flow based on chat type
Detect the Wine Type
- Click + Add Component.
- Choose Control > Switch.
- Choose setChatToOrderType from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
state as follows:
- Change
switch
tostartDetermineWineType
. - 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, theredWine
orwhiteWine
actions are triggered and the dialog transitions to theselectRedWine
andselectWhiteWine
states. If the user's input doesn't specify a wine type, theNONE
action is triggered and the dialog flow transitions to thecheckForWineColor
state. - For the
values
property, change"option1"
to"redWine"
. Then change"option2"
to"whiteWine"
. - For
actions
(located in thetransitions
node) replace"option1"
with:redWine: "selectRedWine"
Then replace"option2"
withwhiteWine: "selectWhiteWine"
- Define the
NONE
action as"checkForWineColor"
:NONE: "checkForWineColor"
Note:
Each of these actions should be eight spaces from the left margin.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"
- Change
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.
Set the Wine Type
- Click + Add Component.
- Choose User Interface > Common response - composite bag.
- Choose startDetermineWineType from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Change
resolveCompositeBag
toselectRedWine
. - Set the
"variable"
property to"redWines"
. (You may need to insert a space before adding this value.) The value for theredWines
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 theiResult
variable. - Set the
"nlpResultVariable"
to"iResult"
. With this setting, theredWines
variable value will be slotted from by the NLP framework if the intial user message contains the name of a red wine. - Set
useFullEntityMatches
tofalse
. If this were a multi-lingual skill, you'd set this property totrue
, but for the purposes of this tutorial, it's set tofalse
. - 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 thecancelOrder
state, ending the conversation. - Delete the following properties:
"transitionAfterMatch"
"cancelPolicy"
"autoNumberPostbackActions"
- Under the
actions
: node (located within themetadata
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 theSystem.CommonResponse
component allows you to do that you can't with a regularSystem.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 thevarName
property (which is located undervariables
property) toredWines
. Then define the value as"${enumValue}"
:redWines: "${enumValue}"
- Replace the default definition for the
- 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. Thenext
transition should be six spaces from the left menu so that it aligns with theactions
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 thematch
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"
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"
- Click Return to insert a new line below
- 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 theselectRedWine
state that you just created except for the following:- In the Components dialog, choose selectRedWine from the Insert After menu.
- Change
resolveCompositeBag
toselectWhiteWine
. - Set the
variable
property to"whiteWines"
. - Change the
varName
property name towhiteWines
(whiteWines: "${enumValue}"
).
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"
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.
Note:
While you can create a simple list of options using the System.List component, the System.CommonResponse component provides greater customization.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
- Click + Add Component.
- Choose User Interface > Common response - composite bag.
- Choose selectWhiteWine from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Change
resolveCompositeBag
tocheckForWineColor
. - Set the
"variable"
property to"wineTypes"
. - Set the
"nlpResultVariable"
to"iResult"
. With this setting, theredWines
variable value will be slotted from the intent engine if the intial user message contains the name of a red wine. - Set
useFullEntityMatches
tofalse
. - Set
maxPrompts
to 2 (allowing users 2 attempts to provide a valid value). - Delete the following properties:
"transitionAfterMatch"
"cancelPolicy"
"autoNumberPostbackActions"
- Under the
actions
: node (located within themetadata
node), replace thelabel
value with"${enumValue}".
For example:- label: "${enumValue}"
- In the
payload
node, change thevarName
property, which is located undervariables
property name towineTypes
. Then define the value as"${enumValue}"
:wineTypes: "${enumValue}"
- 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 theactions
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 thematch
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"
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"
- Click Return to insert a new line below
Set the Selected Wine Menu
If users select a wine color, the value is saved in thewineTypes
variable. This state, which is built around the System.Switch
component, branches the conversation using this value.
- Click + Add Component.
- Choose Control > Switch.
- Choose checkForWineColor from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
state as follows:
- Change
switch
todetermineWineMenu
. - 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 undertransitions
), changeoption1
andoption2
tored wine: "selectRedWine"
andwhite wine: "selectWhiteWine"
, respectively. - Define the
NONE
transition as"checkForWineColor".
red wine: "selectRedWine" white wine: "selectWhiteWine" NONE: "checkForWineColor"
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"
- Change
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.
- Click + Add Component.
- Choose Variables > Set variable.
- Choose determineWineMenu from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
state as follows:
- Change
setVariable
tosaveSelectedWine
. - In the
properties
node, set thevariable
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 theredWines
variable. If this variable has not been set, the expression sets thewhiteWines
variable with the name of the wine. - Click Return to create a new line. Then add the following
transitions
node. Thetransitions:
node should be four spaces from the left margin so that it aligns with theproperties
node. Thenext: "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"
state should look like this:
saveSelectedWine: component: "System.SetVariable" properties: variable: "selectedWine" value: "${redWines.value?hasContent?then(redWines.value, whiteWines.value)}" transitions: next: "branchToOrderOrQuestionFlow"
- Change
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.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.
- Click + Add Component.
- Choose Control > Condition equals.
- Choose saveSelectedWine from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
state as follows:
- Change
statename
tobranchToOrderOrQuestionFlow
. - Set the
variable
property to"chatType"
. - Set the
value
property to"order"
. - In the actions node, set the
equal
property to"askShippingSize"
. Then set thenotequal
property to"showAnswerToQuestion"
.
branchToOrderOrQuestionFlow
state should look like this:branchToOrderOrQuestionFlow: component: "System.ConditionEquals" properties: variable: "chatType" source: value: "order" transitions: actions: equal: "askShippingSize" notequal: "showAnswerToQuestion"
- Change
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.
- Click + Add Component.
- Choose User Interface > Resolve entities.
- Choose branchToOrderOrQuestionFlow from the Insert After State menu.
- Switch on Remove Contents.
- Click Insert Component.
- Update the
state as follows:
- Change
resolveEntities
toaskShippingSize
. - Set the variable property to
"orderSizes"
. - Remove the following properties:
nlpResultVariable
transitionAfterMatch
cancelPolicy
autoNumberPostbackActions
headerText
footerText
- Set
useFullEntityMatches
tofalse
. - 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 thesystem.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 theaskCancelOrder
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 anext
transition that's triggered when the skill receives a valid value and moves to thesummarizeOrder
state. Thenext: "summarizeOrder"
action should be six spaces from the left margin so that it aligns with theactions
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, removematch
. - Define the
cancel
action ascancel: "askCancelOrder"
that's triggered when users enter an incorrect value twice.
state should look like this:
askShippingSize: component: "System.ResolveEntities" properties: variable: "orderSizes" useFullEntityMatches: false maxPrompts: 2 showMoreLabel: showPreviousLabel: transitions: next: "summarizeOrder" actions: cancel: "askCancelOrder"
- Change
Create the Cancel Order Confirmation Message
- Click + Add Component.
- Choose User Interface > Common Response.
- Choose Common response - composite bag.
- Choose askShippingSize from the Insert After menu.
- Clear Include template components.
- Click Insert Component.
- Update the
state as follows:
- Change
resolveCompositeBag
toaskCancelOrder
. - 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
tofalse
. - 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 themetadata
node underresponseItems
), replace the expression defined for thetext
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 (withintransitions
), set thematch
property to"checkCancelOrderOrNot"
. - Set
cancel
to"cancelOrder"
.
- Change
- Because you only need this component to output a prompt, you can delete the following nodes and their contents (which is everything between
footerText
andtransitions
):visible
actions
globalActions
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.
- Click + Add Component.
- Choose Control > Condition equals.
- Choose askCancelOrder from the Insert After menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
state as follows:
- Change
conditionEquals
tocheckCancelOrderOrNot
. - 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 setnotequal
to"askShippingSize"
.
state should look like this:
checkCancelOrderOrNot: component: "System.ConditionEquals" properties: source: "${yesNo.value.yesno}" value: "YES" transitions: actions: equal: "cancelOrder" notequal: "askShippingSize"
- Change
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.
- Click + Add Component.
- Choose User Interface > output.
- Choose checkCancelOrderOrNot from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
state as follows:
- Change
output
tocancelOrder
. - 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.) Thetransitions
node should be four spaces from the left margin and should align with theproperties
node. Thereturn: "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"
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"
- Change
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.selectedWine
variable and then reads the image
property and price
properties:
redWineCard.value[selectedWine].image
redWineCard.value[selectedWine].price
- Click + Add Component.
- Choose User Interface > Common Response - attachment.
- Choose cancelOrder from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
state as follows:
- Change
attachmentResponse
tosummarizeOrder
. - In the
responseItems
node, add a row beneathtype: "attachment"
. - Enter
headerText:
in the blank row. This entry should be ten spaces from the left margin so that it aligns withtype
in the row above. DefineheaderText
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. Thetransitions
node should be four spaces from the left margin and should align with theproperties
node. Thereturn: "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"
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"
- Change
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 in the left navbar to take a quick look.
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.
- Click + Add Component.
- Choose Variable > Set variable.
- Choose summarizeOrder from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
state as follows:
- Change
setVariable
tosetChatToExplainType
. - 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 theproperties
node. (You may need to create a new line by clicking Return.) Thenext: "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"
state should look like this:
setChatToExplainType: component: "System.SetVariable" properties: variable: "chatType" value: "question" transitions: next: "confirmQuestion"
- Change
Create the Confirm Question Message
- Click + Add Component.
- Choose User Interface > Output.
- Choose setChatToExplainType from the Insert After State menu.
- Clear Include template comments.
- Click Insert Components.
- Update the
state as follows:
- Change
output
toconfirmQuestion
. - Set the
text
property to"Let me get your question answered."
- Set
keepTurn
totrue
to prevent the user from entering input before the skill can transition to thestartDetermineWineType
state. - Click Return after the
keepTurn
node to insert a new line. Then add the followingtransitions
node.transitions:
should be four spaces from the left margin so that it aligns with theproperties
node. Thenext: "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"
state should look like this:
confirmQuestion: component: "System.Output" properties: text: "Let me get your question answered." keepTurn: true transitions: next: "startDetermineWineType"
- Change
Output the Answer
- Click + Add Component.
- Choose User Interface > Output.
- Choose confirmQuestion from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
state as follows:
- Change
output
toshowAnswerToQuestion
. - Set the
text
property to"${rb(selectedWine.value)}"
- Set the
keepTurn
property totrue
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 followingtransitions
node.transitions:
should be four spaces from the left margin so that it aligns with the properties node. Thenext: "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"
state should look like this:
showAnswerToQuestion: component: "System.Output" properties: text: "${rb(selectedWine.value)}" keepTurn: true transitions: next: "goodbye"
- Change
Create the Farewell Message
- Click + Add Component.
- Choose User Interface > Output.
- Choose showAnswerToQuestion from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
state as follows:
- Change
output
togoodbye
. - 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 followingtransitions
node.transitions:
should be four spaces from the left margin so that it aligns with theproperties
node. Thereturn: "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"
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"
- Change
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
- Click + Add Component.
- Choose User Interface > Output.
- Choose goodbye from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
state as follows:
- Change
output
tohandleBotNotUnderstanding
. - 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 followingtransitions
node.transitions:
should be four spaces from the left margin so that it aligns with theproperties
node. Thereturn: "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"
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"
- Change
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:
- Navigate to the top of the dialog flow.
- Copy the following just above the
states
node.defaultTransitions: actions: system.outOfOrderMessage: "outOfOrderMessageHandler"
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 isbuildRedWineMenu
, 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')}"
- Click + Add Component.
- Choose Control > Conditional equals.
- Choose handleBotNotUnderstanding from the Insert After State menu.
- Clear Include template components.
- Click Insert Component.
- Update the
state as follows:
- Change
conditionEquals
tooutOfOrderMessageHandler
. - 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"
- Define
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"
- Change
Create the Navigation Not Allowed Message
- Click + Add Component.
- Choose User Interface > Output.
- Choose outOfOrderMessageHandler from the Insert After State menu.
- Clear Include template comments.
- Click Insert Component.
- Update the
state as follows:
- Change
output
tonavigationNotAllowedMessage
. - 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 followingtransitions
node.transitions:
should be four spaces from the left margin so that it aligns with theproperties
node. Thereturn: "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"
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"
- Change
Test the Skill
Now that your dialog is complete, you can test it out with the Skill Tester, which you access by clicking Preview
(located at the top right).
Tip:
Before you test out the skill, run Validate one more time. If the validator lists a lot of critical errors , delete the entire definition and replace it with the contesxt in the complete_dialog_flow file.- 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 withgetIntent
state because it's an answer intent, which only needs aSystem.Intent
component -- thegetIntent
state in our skill. When you ask about a wine, the execution path proceeds to thesetChatToExplainType
state (the start of the question-and-answer flow) from thegetIntent
state. - 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
- 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.
- 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.
Creating a Dialog Flow
F40549-03
April 2022
Copyright © 2022, Oracle and/or its affiliates.
Describes the basics of dialog flows
This software and related documentation are provided under a license agreement containing restrictions on use and disclosure and are protected by intellectual property laws. Except as expressly permitted in your license agreement or allowed by law, you may not use, copy, reproduce, translate, broadcast, modify, license, transmit, distribute, exhibit, perform, publish, or display any part, in any form, or by any means. Reverse engineering, disassembly, or decompilation of this software, unless required by law for interoperability, is prohibited.
If this is software or related documentation that is delivered to the U.S. Government or anyone licensing it on behalf of the U.S. Government, then the following notice is applicable:
U.S. GOVERNMENT END USERS: Oracle programs (including any operating system, integrated software, any programs embedded, installed or activated on delivered hardware, and modifications of such programs) and Oracle computer documentation or other Oracle data delivered to or accessed by U.S. Government end users are "commercial computer software" or "commercial computer software documentation" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations. As such, the use, reproduction, duplication, release, display, disclosure, modification, preparation of derivative works, and/or adaptation of i) Oracle programs (including any operating system, integrated software, any programs embedded, installed or activated on delivered hardware, and modifications of such programs), ii) Oracle computer documentation and/or iii) other Oracle data, is subject to the rights and limitations specified in the license contained in the applicable contract. The terms governing the U.S. Government's use of Oracle cloud services are defined by the applicable contract for such services. No other rights are granted to the U.S. Government.
This software or hardware is developed for general use in a variety of information management applications. It is not developed or intended for use in any inherently dangerous applications, including applications that may create a risk of personal injury. If you use this software or hardware in dangerous applications, then you shall be responsible to take all appropriate fail-safe, backup, redundancy, and other measures to ensure its safe use. Oracle Corporation and its affiliates disclaim any liability for any damages caused by use of this software or hardware in dangerous applications.
Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.
Intel and Intel Inside are trademarks or registered trademarks of Intel Corporation. All SPARC trademarks are used under license and are trademarks or registered trademarks of SPARC International, Inc. AMD, Epyc, and the AMD logo are trademarks or registered trademarks of Advanced Micro Devices. UNIX is a registered trademark of The Open Group.
This software or hardware and documentation may provide access to or information about content, products, and services from third parties. Oracle Corporation and its affiliates are not responsible for and expressly disclaim all warranties of any kind with respect to third-party content, products, and services unless otherwise set forth in an applicable agreement between you and Oracle. Oracle Corporation and its affiliates will not be responsible for any loss, costs, or damages incurred due to your access to or use of third-party content, products, or services, except as set forth in an applicable agreement between you and Oracle.