10 The Dialog Flow Definition

The dialog flow definition is the model for the conversation itself, one that lets you choreograph the interaction between a skill and its users.

Using the Skill Builder, you define the framework of the user-skill exchange in OBotML, Digital Assistant’s own implementation of YAML. This is a simple markup language, one that lets you describe a dialog both in terms of what your skill says and what it does.

The Dialog Flow Structure

Your OBotML definition is divided into three main parts: context, defaultTransitions, and states. You define the variables that are available across the session within the context node. The definition of the flow itself is described in the states section. The dialog flow starts off with the metadata node, where the platformVersion identifies the platform version of Oracle Digital Assistant. Always enclose the version number in double quotes ("1.1"). The dialog flow is laid out as follows:
metadata:
  platformVersion: "1.1"
main: true
name: "HelloKids"
context:
  variables:
    variable1: "entity1"
    variable2: "error"
...
States      
    state1:
      component: "a custom or built-in component" 
      properties: 
        property1: "component-specific property value"
        property2: "component-specific property value"
      transitions:
        actions:
          action1: "value1"
          action2: "value2"
    state2:
      component: "a custom or built-in component" 
      properties: 
        property1: "component-specific property value"
        property2: "component-specific property value"
      transitions:
        actions:
          action1: "value1"
          action2: "value2"
...

The context Node

The variables that you define within the context node can be primitive types like int, string, boolean, double, or float. You can define a variable as a map, which is a JSON object, or you can use variables to describe error handling.

As illustrated by the following snippet from the PizzaBot dialog flow definition, you can name variables for built-in or custom entities (which in this case, are the PizzaSize and PizzaCrust variables). Along with built-in entities and the custom entities, you can also declare a variable for the nlpresult entity, which holds the intent that's resolved from the user input. These variables are scoped to the entire flow. How Do I Write Dialog Flows in OBotML? tells you how to assemble the different parts of the dialog flow. You can also scope user variable values to enable your bot to recognize the user and persist user preferences after the first conversation. User-Scoped Variables describes these variables.
metadata:
  platformVersion: "1.1"
main: true
name: "PizzaBot"
context:
  variables:
    size: "PizzaSize"
    type: "PizzaType"
    crust: "PizzaCrust"
    iResult: "nlpresult"

The defaultTransitions Node

You can set transitions in two places: as part of the component definitions in the dialog flow's states, or in the defaultTransitions node. This node sets the global navigation. For example:
defaultTransitions
  next: "..."
  error: "..."
  actions:
    action_name1: "..."
    action_name2: "..."
   
The default transition acts as a fallback in that it gets triggered when there are no transitions defined within a state, or the conditions required to trigger a transition can't be met.
Use the defaultTransitions node to define routing that allows your skill bot to gracefully handle unexpected user actions. In particular, you can use it to enable your skill bot to react appropriately when a user taps an option in a previous reply instead of one of the options presented in the bot's current (and more appropriate) reply. As shown by NONE action in the following snippet, you can configure this transition to route to a state that handles all of the unexpected actions.
defaultTransitions:
  error: "globalErrorHandler"
 ...
globalErrorHandler:
  component: System.Switch
  properties:
   source: "${system.errorState}"
   values:
   - "getOrderStatus"
   - "displayOrderStatus"
   - "createOrder"
  transitions:
    actions:
      NONE: "unhandledErrorToHumanAgent"
      getOrderStatus: "handleOrderStatusError"
      displayOrderStatus: "handleOrderStatusError"
      createOrder: "handleOrderStatusError"

The states Node

You define each bit of dialog and its related operations as a sequence of transitory states, which manage the logic within the dialog flow. To cue the action, each state node within your OBotML definition names a component that provides the functionality needed at that point in the dialog. States are essentially built around the components. They contain component-specific properties and define the transitions to other states that get triggered after the component executes.
  state_name:
    component: "component_name"
    properties:
      component_property: "value"
      component_proprety: "value"
    transitions:
      actions:
        action_string1: "go_to_state1"
        action_string2: "go_to_state2"
A state definition might include the transitions that are specific to the component or the standard next, error, actions, or return transitions (which are described in Flow Navigation and Transitions) that you can define for any component. Transitions set within states can be overriden by the global transitions defined in the defaultTransitions node.
The PizzaBot includes a sequence of state nodes that verify a customer’s age. These states include components that take the user-supplied integer value, check it, and then output a text string as appropriate. To start off the process, the askage state’s component requests the user input then moves on to the checkAge state, whose AgeChecker component validates the user input. Here, the dialog is at a juncture: its transitions key defines the block or allow states. If the allow state is triggered, then the user can continue on. The subsequent state definitions will track the user input to preserve the user’s context until she completes her order. If the user input causes the AgeChecker component to trigger the block action, however, then conversation ends for the under-age user because the dialog transitions to the underage state.
metadata:
platformVersion: "1.1"
main: true
name: "PizzaBot"
context:
  variables:
    size: "PizzaSize"
    type: "PizzaType"
    crust: "PizzaCrust"
    cheese: "CheeseType"
    iResult: "nlpresult"
...

  askage:
    component: "System.Output"
    properties:
      text: "How old are you?"
    transitions: {}
  checkage:
    component: "AgeChecker"
    properties:
      minAge: 18
    transitions:
      actions:
        allow: "crust"
        block: "underage"
  crust:
    component: "System.List"
    properties:
      options: "Thick,Thin,Stuffed,Pan"
      prompt: "What crust do you want for your Pizza?"
      variable: "crust"
    transitions: {}
...
    underage:
    component: "System.Output"
    properties:
      text: "You are too young to order a pizza"
    transitions:
      return: "underage"

How Do I Write Dialog Flows in OBotML?

OBotML uses a simple syntax for setting variables and defining states. Because it’s a variant of YAML, keep the YAML spacing conventions in mind when you define the dialog flow. You don’t need to start from scratch. Instead, you can use the default dialog flow definition as a basic template.
Along with the metadata definition at the top of the dialog, the template already has the context and states nodes, so you can just delete the existing boilerplate and add your own content. To help you build state definitions that are syntactically sound, use the component templates in the Add Components menu. See Dialog Flow Syntax for tips on setting variables and defining states.

Note:

Click Validate and check the logger widow (This is an image of the debug icon.) as you write your dialog flow.

Dialog Flow Syntax

How Do I? Use this
Set variables that persist the context across the entire dialog flow?
Within the context node, use the following syntax: variablename: "variableType" For example:
main: true
name: "FinancialBotMainFlow"
context:
  variables:
    accountType: "AccountType"
    txnType: "TransactionType"
    txnSelector: "TransactionSelector"
    toAccount: "ToAccount"
    spendingCategory: "TrackSpendingCategory"
    paymentAmount: "string"

You can define variables as entities (like AccountType and ToAccount) and as primitives (paymentAmount: “string”).

Define an error handler for your skill?
Define the defaultTransitions node that points to a state that handles errors. Typically, you'd add this state and the end of your dialog flow definition. For example:
context:
  variables:
    iresult: "nlpresult"
defaultTransitions:
  next: "ImplicitTransitionDetected"
  error: "MyErrorState"
...
states:
...
  MyErrorState
    component: "System.Output"
    properties:
      text: "Problem detected in \"${system.errorState}\" state."
    transitions:
      return: "done"          
  

See Configure the Dialog Flow for Unexpected Actions.

Define a variable that holds the value for the resolved intent?
Within the context node, define a variable that names the nlpresult entity. As its name implies ("nlp" stands for Natural Language Processing), this entity extracts the intent resolved by the Intent Engine. Nearly all of the reference bots declare nlpresult variables. For example:
main: true
name: "FinancialBotMainFlow"
context:
  variables:
    iResult: "nlpresult"
Control the dialog flow based on the user input?

Typically (though not always), you’d define an nlpresult variable property for the System.Intent component that returns the result from the Intent Engine. See System.Intent.

In the following snippet from the FinancialBot dialog flow, the System.Intent component instructs the Dialog Engine to proceed based on the value returned by its nlpresult variable (iResult). As described in The Dialog Flow Structure, you can declare an nlpresult variable in the flow’s context node to hold the resolved intent (iResult:”nlpresult” ). The potential outcome, defined by the states named in the actions node, is also predicated on Confidence threshold property. You can set this optional property against the probabilistic score given by the Intent Engine. This definition for the System.Intent component tells the Dialog Engine to move on to the next state that matches a resolved intent whose accuracy rate at parsing user data is at least 40% or higher. See How Confidence Threshold Works.

Equip my skill to handle unresolved intents?

Define a state for the System.Intent’s unresolvedIntent action. unresolvedIntent is an intent that we provide for you to track the messages that couldn’t be resolved within the minimum confidence threshold. See Run Failure Reports to find out how to filter a quality report using this intent.

Example:
unresolvedIntent: "unresolved"
...
  unresolved:
    component: "System.Output"
    properties:
      text: "Sorry I don't understand that question!"
    transitions:
      return: "unresolved"
Enable components to access variable values?
Use the .value property in your expressions (${crust.value} ). To substitute a default value, use ${variable.value!\"default value\"} . For example, thick is the default value in ${crust.value!\"thick\"}. For example:
context:
  variables:
    size: "PizzaSize"
    confirm: "YES_NO"
    ...
  confirmState:
    component: "System.List"
      properties:
      options: "Yes,No"
      prompt: "You ordered a ${size.value} pizza. Is this correct?"
      variable: "confirm"
...

Use the Apache FreeMarker default operator (${variable.value!\"default value\"}) if it’s likely that a null value will be returned for a variable. You can use this operator wherever you define variable replacement in your flow, like the value definitions for variables used by system and custom components, or the variables that name states in a transitions definition. See Defining Value Expressions for the System.Output Component.

Save user values for return visits?
Within a state definition, add a variable definition with a user. prefix. See Built-In Components for Setting User Values. For example:
 checklastorder:
    component: "System.ConditionExists"
    properties:
      variable: "user.lastpizza"

To find out more about user variables, see the dialog flow for the PizzaBotWithMemory reference bot.

Exit a dialog flow and end the user session.

Use a return transition.

Example:
  printBalance:
    component: "BalanceRetrieval"
    properties:
      accountType: "${accountType.value}"
    transitions:
      return: "printBalance"
Flow Navigation and Transitions

You can set the dialog engine on a specific path within the dialog flow by setting the transitions property for a state. Transitions describe how the dialog forks when variable values are either set or not set. They allow you to plot the typical route through the conversation (the “happy” flow) and set alternate routes that accommodate missing values or unpredictable user behavior.

The transition definition depends on your flow sequence and on the component.
To do this... ...Use this transition
Set a default sequence in the dialog flow. To enable the Dialog Engine to move to the next state in the dialog, use an empty transition (transitions: {}) or omit a transitions definition altogether. Because they're not explicit, empty transitions make your dialog flow definition difficult to troubleshoot when it's not executing as expected. The skill throws errors when the final state in the dialog flow (that is, the state at the bottom) has an empty transition because the Dialog Engine won't know what to do next.
Specify the next state to be executed. Set a next transition (next: "statename"), to instruct the Dialog Engine to jump to the state named by the next key. As noted in next and Default Transitions, you can add a next transtion to any state except for the ones that have a return transition.
Reset the conversation. Use a return transition to clear any values set for the context variables and resets the dialog flow. You can give this transition any string value.
  unresolved:
    component: "System.Output"
    properties:
      text: "Sorry! I don't understand that question!"
    transitions:
      return: "unresolved"
Defining a return: "done" transition terminates the user session and positions the Dialog Engine at the beginning of the flow.
Trigger conditional actions. Define actions keys to trigger the navigation to a specific state. When a component completes its processing, it returns an action string that instructs the Dialog Engine where to go next. If you don’t define any action keys, then the Dialog Engine relies on the default transition or a next transition (if one exists). You can define as many actions as needed. Some built-in components have specific actions. For example, a component like System.MatchEntity that evaluates an Apache FreeMarker expression, uses match and nomatch actions. System.OAuthAccountLink has textReceived, pass, and fail actions, and the user interface components use their own actions (described in Transitions ). Use the component templates as a guide. You can define an actions transition on any state except for ones that have a return transition.
Handle errors. Components sometimes throw errors. This often happens because of a system-related problem or failure (invalid passwords, invalid hostnames, or communications errors). Setting an error transition that names an error-handling state allows your skill to gracefully handle problems:
transitions:
    error: "handleMe"
If you don’t set an error transition, then the skill outputs the Unexpected Error Prompt (Oops! I’m encountering a spot of trouble) and terminates the session. You can define an error transition within any state except for ones that have a return transition.
Some components have an error defined as an action. These built-in error transitions handle component-specific errors:
transitions:
  actions:
    error: "handleMe"
You can use different types of transitions in the same state. In the following example, the Dialog Engine navigation is based on an action or an error. When the component doesn't evaluate to either one, then the Dialog Engine follows the next transition:
state_name:
  component: "component name"
  properties:
    component_property: "value"
    component_proprety: "value"
  transitions:
    next: "go_to_state"
    error: "go_to_error_handler"
    actions:
      action_string1: "go_to_state1"
      action_string2: "go_to_state2"

Note:

While you can define more than one transition, the return transition is an exception: you can't combine a return transition with the error, next or actions transitions.
next and Default Transitions

The default, or empty, transition ({}) is an implicit transition that directs the Dialog Engine to the next state after the component executes successfully. It can suffice for strictly linear progressions that process expected user input, but not for more complicated (and realistic) user interactions. In the latter case, empty transitions can result in unintended routing and, because they are implicit (they don't return a value), they make errors harder to trace. For example, when you define the transition for the final state in the flow as an empty transition, the skill will throw an exception after the component executes because it won't know what to do next. The action is pending. In the Insights Pathing report, this error might indicate that an execution path didn't complete successfully even though it did, despite the error.

To avoid these problems, use the next transition instead of the empty transition. Further, use the next transition whenever there's no return, error, or actions defined. When a state combines error, actions, and next transitions, the next transition only gets triggered when the component can't return a string that satisfies either of the error or actions transitions.

To ensure that a next transition gets triggered whenever errors or actions can't, or where the dialog flow has empty transitions (or no transitions at all), define a next action within the defaultTransition node. In the following snippet, the next: "nextRules" global action triggers a next transition for the getName state, which lacks a transition. It allows the flow to move on from the getName state to the nextRules state:
context:
  variables:

    name: "string"

defaultTransitions:
  next: "nextRules"

states:
  
  getName:
    component: "System.Text"
    properties:
      prompt: "What's your name please?"
      variable: "name"
      #empty transition as there is no transitions: element defined


  printName:
    component: "System.Output"
    properties:
      text: "Hello ${name.value}." 
    transitions:
      return: "done"


  nextRules:
    component: "System.Output"
    properties:
      text: "Hello ${name.value}. I told you! Next transitions rule the game!" 
    transitions:
      return: "done"
Configure the Dialog Flow for Unexpected Actions
When designing your dialog flow, you typically start modeling the “happy” flow, the path that the user is most likely to follow.
Scenario Solution
Instead of tapping buttons, the user responds inappropriately in this situation by entering text. To enable your bot to handle this gracefully, route to a state where the System.Intent component can resolve the text input, like textReceived: Intent in the following snippet from the CrcPizzaBot:
ShowMenu:
 component: System.CommonResponse
 properties:
   metadata: ...
   processUserMessage: true
 transitions:
   actions:
     pizza: OrderPizza
     pasta: OrderPasta
     textReceived: Intent
Users scroll back up to an earlier message and tap its options, even though they’re expected to tap the buttons in the current response.
By default, Digital Assistant handles out-of-order messages, but you can override or customize this behavior, as described in How Does Digital Assistant Detect Unexpected Actions?.
context:
  variables:
    iresult: "nlpresult"
defaultTransitions:
  next: "ImplicitTransitionDetected"
  error: "MyErrorState"
  actions:
    system.outOfOrderMessage: "HandleUnexpectedAction"

...

  HandleOutOfOrderMessage:
    component: "System.Switch"
    properties:
      variable: "system.actualState"
      values:
        - "ShowMenu"
        - "OrderPizza"
        - "AskPizzaSize"
    transitions:
      actions:
        NONE: "ActionNoLongerAvailable"
        ShowMenu: "${system.actualState}"
For example, adding a default system.outofOrderMessage transition informs the Dialog Engine to transition to a single state that handles all of the out-of-order messages, such as the HandleUnexpectedAction state in the OBotML snippet above. You can use different approaches to create this state:
  • You can use the System.Output or System.CommonResponse component that outputs a message like “Sorry, this option is no longer available” along with a return: "done" transition to invalidate the session so that the user can start over. For example:
    ActionNoLongerAvailable:
        component: "System.Output"
        properties:
          text: "Sorry, this action is no longer available"
        transitions:
          return: "done"
    
  • Using a System.Switch component, you can enable your bot to honor some of the request actions by transitioning to another state. Depending on the factors involved in honoring the request, you may need to create a custom component to implement the routing.

Call a Skill from Another Skill

There might be times when you want to provide users the option to temporarily leave the skill they are engaged with to do something in a second skill within the digital assistant. For example, if they are in a shopping skill and they have made some selections, they may want to jump to a banking skill (to make sure that they have enough money for the purchase) and then return to the shopping skill to complete their order.

To address this, you can configure an action in a skill to initiate interaction with a different skill in the same digital assistant and then return to the original flow.

Here's how it works:

  1. You use the System.CommonResponse component to present the user a button to do something in another skill.

    The button is based on a postback action, in which you configure the payload to provide an utterance that is directed toward the target skill. Ideally, that utterance should contain the invocation name of the target skill (i.e. be an explicit invocation) in order to maximize the likelihood that routing to that skill will occur. By doing this, you essentially hard-code an utterance to trigger the desired intent.

    Here's the format of that code:

    
      component: "System.CommonResponse"
      properties:
        metadata:
        ...
          responseItems:
            - type: "cards"
              ...
              actions:
                  ...
                - label: "Press me to switch to different skill"
                  type: "postback"
                  payload:
                    action: "system.textReceived"
                    variables:
                      system.text: "utterance with invocation name that you want passed to the digital assistant"
                    ...

    By using a system.textReceived action and specifying the text in the system.text variable, you ensure that the postback is treated as a user message that can be properly routed by the digital assistant.

  2. You set the textReceived transition to the state containing the System.Intent component.
    
      transitions:
        actions:
          ....
          textReceived: "Name of the state for the System.Intent component"

    This is necessary to ensure that the digital assistant provides an appropriate fallback response if the digital assistant does not contain the target skill.

    For this to work, the skill's System.Intent component must have its daIntercept property set to "always" (which is the default).

If the target skill is in the digital assistant, the digital assistant recognizes the explicit invocation, takes control of the request (which would normally be handled by the component), and routes the request to the target skill's System.Intent component. Once the flow in the target skill is finished, the user is returned to the calling skill.

If the target skill is not in the digital assistant (or the calling skill is exposed without a digital assistant), the calling skill's System.Intent component is invoked and the intent should resolve to unresolvedIntent.

Tip:

To handle the case where the target skill's invocation name is changed when it is added to a digital assistant, you can use a custom parameter to pass in the skill's invocation name in the system.text variable.

For example, you could create a parameter called da.CrcPizzaCashBankInvocationName in the pizza skill and give it a default value of CashBank. You could then reference the parameter like so:

system.text: "ask  ${system.config.daCrcPizzaFinSkillInvocationName}, what is my balance"

If the invocation name of the skill is changed, you simply change the value of the custom parameter to match the new invocation name.

See Custom Parameters.

Example: Call a Skill from Another Skill

For example, here is an intent for ordering pizza (OrderPizza) that gives the user the option to check their bank account balance before completing their order. The account balance is provided by a different skill (CashBank). If the user selects the Check Balance option, the text "ask CashBank, what is my balance" is posted back to the digital assistant and the user is routed to the appropriate intent within the CashBank skill.

OrderPizza:
  component: "System.CommonResponse"
  properties:
    metadata:
    ...
      responseItems:
        - type: "cards"
          headerText: "Our pizzas:"
          cardLayout: "vertical"
          name: "PizzaCards"
          actions:
            - label: "More Pizzas"
              ...
            - label: "Check bank account balance"
              type: "postback"
              payload:
                action: "system.textReceived"
                variables:
                  system.text: "ask CashBank, do I have enough money?"
                ...
    processUserMessage: true
  transitions:
    actions:
      order: "AskPizzaSize"
      more: "OrderPizza"
      textReceived: "Intent" # where the value of textReceived is the name CashBank's System.Intent state
  ...

Assuming your pizza skill is in the same digital assistant as the Cash Bank skill, here's what it should look like if you open the digital assistant in the tester, access the pizza skill, and then click the Check bank account balance.

Description of skill-skill.png follows
Description of the illustration skill-skill.png

In the Routing tab of the tester, you can see that the explicit invocation has been recognized and is given preference:



Further down, you can see that the Check Balance intent of the Cash Bank skill is matched:



Apache FreeMarker Template Language Syntax

You can use Apache FreeMarker Template Language (FTL) to access variable values, show or hide response items, find key words in a user message, print a list of values, or display arrays that are sorted by a specific data attribute. The basic syntax for these value expressions is ${...}. You can incorporate FTL into the property definitions for various components, such as System.SetVariable and System.Output.

Note:

As illustrated by the text and rendered metadata properties of the System.CommonResponse, you can also define the expressions using the if directive (<#if>...</#if>).
To do this... ...Do this
Read values from context variables. Add the value property using dot notation:
${variablename.value}
For example:
${MyEmail.value}
Read values from context variables defined by complex entities. Use dot notation to add an additional property:
${variablename.value.property}
For example:
${MyMoney.value.totalCurrency}
If you use an expression like ${MyMoney} in a System.Output component, you will see all the properties of the referenced currency JSON object.
Return the set of values that are defined for a value list entity. Use the built-in type and enumValues variable in the following syntax:
${variablename.type.enumValues}
For example, for a list value entity like AccountType (whose savings, checking, and credit card values are constant and predefined for the user), you’d store these values in the accountType variable using ${accountType.type.enumValues}:
  accounts:
    component: "System.List"
    properties:
      options: "${accountType.type.enumValues}"
      prompt: "Which account?"
      variable: "accountType"
    transitions: {} 
When the user taps one the buttons, the skill stores the corresponding value in the accountType variable.
Use built-ins for strings, arrays (sequences), numbers, and dates. See Apache FreeMarker Reference. Follow the value property with a question mark (?) and the operation name:
${variable.value?ftl_function}
  • string operations:
    toLowercase:
      component: "System.SetVariable"
      properties:
        variable: "userstring"
        value: "${userstring.value?lower_case}"
      transitions: {}
    
  • array operations:
    setArrayCount:
      component: "System.SetVariable"
      properties:
        variable: "count"
        value: "${person.value?size?number}"
  • number operations:
    ${negativeValue.value?round}
  • time and date operations:
    printDateFound:
      component: "System.Output"
      properties:
        text: "Date found is: ${theDate.value.date?long?number_to_date?string.short}"
    
Concatenate FTL expressions. String the operations together using a question mark (?):
${variable.value?ftl_function1?ftl_function2}

User-Scoped Variables

When the conversation ends, the variable values that were set from the user input are destroyed. With these values gone, your skill users must resort to retracing their steps every time they return to your skill. You can spare your users this effort by defining user-scope variables in the dialog flow. Your skill can use these variables, which store the user input from previous sessions, to quickly step users through the conversation.

Unlike the session-wide variables that you declare in the context node at the start of the flow, you do not need to declare user-scoped. Any reference to a variable name that is prefixed with user. is treated as a user-scoped variable. As shown in the following dialog flow excerpt from the PizzaBotWithMemory dialog flow, these variables are identified by the user. prefix (such as user.lastsize in the checklastorder state). The user. variable persists the user ID. That ID is channel-specific, so while you can return to a conversation, or skip through an order using your prior entries on skills that run on the same channel, you can’t do this across different channels.
metadata:
  platformVersion: "1.0"
main: true
name: "PizzaBot"
parameters:
  age: 18
context:
  variables:
    size: "PizzaSize"
    type: "PizzaType"
    crust: "PizzaCrust"
    iResult: "nlpresult"
    sameAsLast: "YesNo"
states:
  intent:
    component: "System.Intent"
    properties:
      variable: "iResult"
    transitions:
      actions:
        OrderPizza: "checklastorder"
        CancelPizza: "cancelorder"
        unresolvedIntent: "unresolved"
  checklastorder:
    component: "System.ConditionExists"
    properties:
      variable: "user.lastsize"
    transitions:
      actions:
        exists: "lastorderprompt"
        notexists: "resolvesize"
  lastorderprompt:
    component: "System.List"
    properties:
      options: "Yes,No"
      prompt: "Same pizza as last time?"
      variable: "sameAsLast"
    transitions: {}
  rememberchoice:
    component: "System.ConditionEquals"
    properties:
      variable: "sameAsLast"
      value: "No"
    transitions:
      actions:
        equal: "resolvesize"
        notequal: "load"
...


  load:
    component: "System.CopyVariables"
    properties:
      from: "user.lastsize,user.lasttype,user.lastcrust"
      to: "size,type,crust"
    transitions: {}
Built-In Components for Setting User Values

Define the value property of the following components with expressions like “${user.age.value}” to set stored user values.

Component Uses
System.SetVariable Sets the stored user value.
System.ResetVariables Resets a stored user value.
System.CopyVariables Copies in the stored user value.
System.Output Outputs the stored user value as text.
System.ConditionExists Checks if the user-scoped variable is already in context.
System.ConditionEquals Checks for the user-scoped variable.
System.Switch Uses the stored value to switch from one state to another.

User Context

The profile property enables your bot to recognize a user’s name, local, and local time using the profile settings from the chat client. For example:
  Greeting:
    component: "System.Output"
      properties:
      text:  "Hello ${profile.firstName}, how can I help you today?"
    transitions:
      next: Intent

This is an image of the profile.firstName in the output.
Use these pre-defined variables to output context-specific for the bot user.
To do this... Use this...
Get the first name of the bot user. ${profile.firstName}
Get the last name of the bot user. ${profile.lastName}
Get the bot user’s locale. ${profile.locale} and ${profile.languageTag}. Use these variables to translate your bot to the user’s language.
Get the user’s time zone (offset in milliseconds). ${profile.timezoneOffset}

Auto-Numbering for Text-Only Channels

The auto-numbering framework enables your skill bot to run on text-only channels because it prefixes buttons and list options with numbers. When users can’t use tap gestures, they can still trigger the button’s postback actions by entering a number. For example, when the CrcPizzaBot runs in a channel that supports buttons, it displays Pizzas and Pastas.
A runtime image of options in non-text only channel.
But when it runs on a text-only channel, it renders the Pizza and Pasta options as text and prefixes them with sequential numbers (1. Pizza 2. Pasta).
A runtime image of options in a text-only channel.

Auto-numbering isn’t limited to text-only channels; enabling it where buttons are supported add another way for users to input their choices. For examples, users can either tap Pizza or enter 1.
A runtime image of auto-numbering with buttons.

How Do Cards Render on Text-Only Channels?

Both the System.CommonResponse and Q&A render responses as cards. When your skill runs in a text-only channel, some of the card response item properties behave differently. Here are some things to keep in mind.

  • There is no vertical or horizontal scrolling (behaviors set by the cardLayout option). All cards render within a single message bubble, which can include a header and footer. The card title and description properties are separated by a new line character. You can number the card's title proprety.

  • Hyperlinks are still supported in text-only channels, with the address configured for cardUrl property rendered within the bubble along with the title and description properties, which are separated by new line character.

  • Images specified by the imageURL property are rendered.

  • The label text for action buttons displays (though the buttons themselves are not rendered). Users can enter the text, or, if auto-numbering is enabled, they can enter the corresponding number instead for added convenience.


Set Auto-Numbering for Text-Only Channels

You can set the auto-numbering feature on a global scale (meaning that it affects all components named in your dialog flow definition) or at the component level for the components that trigger postback actions, namely the System.List, System.CommonResponse, System.ResolveEntities, System.QnA, System.Webview, System.OAuthAccountLinkComponent, and System.OAuth2AccountLinkComponent components.

To automatically prefix the options with sequential numbers:
  1. In the context node, add autoNumberPostbackActions: "boolean". This, like textOnly and autoTranslate, is a common variable that can be used across all of your bots.
    context:
      variables:
        pizzaSize: "PizzaSize"
        pizzaType: "PizzaType"
        pizzaCrust: "PizzaCrust"
        pizzaCheese: "CheeseType"
        autoNumberPostbackActions: "boolean"
        iResult: "nlpresult"
  2. Set the autoNumberPostbackActions property to true:
      type:
        component: "System.List"
        properties:
          prompt: "What Type of Pizza do you want?"
          options: "${pizzaType.type.enumValues}"
          variable: "pizzType"
          autoNumberPostbackActions: "true"
          footerText: "Enter a number or tap your selection."
        transitions: {}
    If you need to override auto-numbering for a particular component (either a system component or a custom component), set the autoNumberPostbackActions property to false. To override auto-numbering for a specific postback action in the System.CommonResponse, add a skipAutoNumber property and name the action.

    Note:

    Because auto-numbering gets applied through server-side processing, it only works on postback actions, not for the client-side URL actions. Consequently, components that render two button actions, one URL action, and one postback action result in a suboptimal user experience because of the inconsistent numbering of the various UI elements. For the OAuth components that render both a login URL action and a cancel postback action, only the cancel action is prefixed with a number. To maintain consistency in cases like this, set the autoNumberPostbackActions property to false .
  3. You can conditionally enable auto-numbering by setting the autoNumberPostbackActions variable with the current channel. For example:
    setAutoNumbering:
      component: "System.SetVariable"
      properties:
        variable: "autoNumberPostbackActions" 
        value: "${(system.message.channelConversation.channelType=='facebook')?then('true','false')}"
    Once you’ve set the autoNumberPostbackActions variable, you can reference it to modify the prompt text:
    prompt: "Hello ${profile.firstName}, this is our menu today<#if autoNumberPostbackActions.value>. Make your choice by entering the menu option number</#if>:"
    Likewise, you can conditionalize the footer text:
    footerText: <#if autoNumberPostbackActions.value>"Make your choice by entering the menu option number."</#if>
Render Content for Text-Only Channels

You can show or hide channel-specific messages when you reference the textOnly variable in the dialog flow-branching components like System.ConditionEquals or System.Switch. Before you can branch your flow based on text-only messages, you need to declare textOnly as a context variable and then set its value. Here are the basic steps:
  1. Add the textOnly: “boolean” variable to the context node.
    context:
    autoNumberingPostbackActions: "boolean"
    textOnly: "boolean"
  2. Reference textOnly in the variable setting components, like System.SetVariable and System.Switch

  3. Use the system.message property to expose the complete user message. The following snipppet shows how to set a boolean within the system.message expression that indicates whether a text-only channel (Twilio, in this case) is in use or not.
    setTextOnly:
      component: "System.SetVariable"
      properties:
        variable: "textOnly" 
        value: "${(system.message.channelConversation.channelType=='twilio')?then('true','false')}"
  4. Because the textOnly variable is true or false, you can enable auto-numbering only when the skill rungs on text-only channels. For example:
    setAutoNumbering:
      component: "System.SetVariable"
      properties
        variable: autoNumeringPostbackActions
        value: "$(textOnly)

You can also reference textOnly to modify parts of the text message or a button label depending on the channel.

Optimize Cards on Text-Only Channels with Keywords

Most cards have a single action, such as the CRCPizzaBot's Order Now button and a global action like More for loading the next card in the carousel. As illustrated in How Do Cards Render on Text-Only Channels?, the label for each action gets auto-numbered when the skill runs on SMS/text-only channels. On these channels, a set of cards is represented in a single bubble, which can become long and therefore difficult to read. You can avoid this by configuring postback actions that aren't associated with the action labels, but are executed by user keywords (1,2,3, cheese, or more, for example).


Description of cards_text_only.png follows
Description of the illustration cards_text_only.png

You can hide the action labels when your skill runs on text-only channels using these general guidelines.

In the metadata Property:
  • Define the keywords property. In the following CRCPizzaBot snippet, the ${pizza.name} expression set for the keyword property defines a keyword for each pizza name:
      OrderPizza:
        component: "System.CommonResponse"
        properties:
          metadata:
            keywords:
              - keyword: "${pizzas.name}
                visible:
                  expression: "${textOnly}"

    These keywords are only added when textOnly is true. Enable numbering for the keywords by setting autoNumberPostbackActions: true.

In the card metadata:
  • Define the title property. In the following snippet, an expression uses the FreeMarker index variable to prefix a number to the title (returned by ${pizzas.name} when the textOnly variable value is true). This means that when a customer enters more, the skill will load another message bubble containing the next set of pizzas starting at Number 5 (rangeSize: "4").
    cards:
            - title: "${(textOnly=='true')?then((pizzas?index+1)+'. ','')}${pizzas.name}"
              description: "${pizzas.description}"
              imageUrl: "${pizzas.image}"
              name: "PizzaCard"
              iteratorVariable: "pizzas"
              rangeStart: "${cardsRangeStart}"
              rangeSize: "4"
  • Configure the card actions to render only in text-only channels. In the following snippet, the card actions ("Order" and "More Pizzas") are only displayed when the textOnly variable value is false:
            - label: "More Pizzas"
              keyword: "more"
              type: "postback"
              skipAutoNumber: true
              visible:
                expression: "<#if cardsRangeStart?number+4 < pizzas.value?size && textOnly=='false'>true<#else>false</#if>"
  • Add a footer that displays only when the textOnly variable value is true.
    footerText: "<#if textOnly=='true'>Enter a pizza number to make your choice<#if cardsRangeStart?number+4 < pizzas.value?size>, or type 'more' to see more pizzas</#if></#if>"
Extract Keywords from Messages
While the component triggers a postback when users enter a number, you can extend your skill to support broader input like First, or let's try the 1st item. To do this, create a variable for the keyword phrases:
  LoadNumberKeywords:
    component: "System.SetVariable"
    properties:
      variable: "numberKeywords"
      value:
        - keywords: "first,1st,one"
        - keywords: "second,2nd,two"
        - keywords: "third,3rd,three"
        - keywords: "fourth,4th,four"
        - keywords: "fifth,5th,five"
Reference the variable (numberKeywords in the following snippet) in the keyword property:
  OrderPizza:
    component: "System.CommonResponse"
    properties:
      metadata:
        keywords:
          - keyword: "${pizzas.name},<#if pizzas?index <numberKeywords.value?size>${numberKeywords.value[pizzas?index].keywords}</#if>,<#if pizzas?index==cardsRangeStart?number+[cardsRangeStart?number+3,pizzaCount.value?number-cardsRangeStart?number-1]?min>last</#if>"
            visible:
              expression: "${textOnly}"
            payload:
              action: "order"
              variables:
                orderedPizza: "${pizzas.name}"
                orderedPizzaImage: "${pizzas.image}"
            iteratorVariable: "pizzas"
    ...
In this definition, the last keyword is based on the current range start. It's set to the last pizza currently display, based on the number of times that the customer has entered more.

Test the Dialog Flow

Once you have a valid dialog flow, you can test your skill bot as a whole. Be sure to validate the syntax before you test the bot.

To test the dialog flow:
  1. Click Skill Tester (This is an image of the Bot Tester icon from the left navbar.) in the left navbar.

  2. Enter a test phrase or utterance and then click Send. Click This is an image of the attachment upload icon.to test an attachment response rendered by the System.CommonResponse component.

  3. Click Conversation to see the traversal through the states.

    During testing, you can export the conversation log for offline analysis by:
    1. Turning on Skill Conversation in Settings > General.
    2. Choosing Skill Conversation Log and a time period in the Export Conversation Log dialog (accessed from the Export Conversation Log option in the tile menu). When your bot is in a production environment, be sure to turn the Skill Conversation option in the Settings page off to prevent the database from running out of space.

Custom Parameters

In your skill, you can define customer parameters to be referenced from dialog flows. After you have published the skill, you can change the values of these parameters (though you can not change other parameter details or add or delete parameters).

Custom parameters are exposed as system.config variables for use in the dialog flow definition.

Parameters that begin with da. can be used by all the skills that are registered in the same digital assistant. You can also set the value for such parameters in the digital assistant.

For example, you can add the da.faHostName, da.faPort, and vendorId parameters, and then use them in your dialog flow like this:

createVendorUrl:
  component: "System.SetVariable"
  properties:
    variable: "vendorBaseUrl"
    value: "http://${system.config.da.faHostName}:${system.config.da.faPort}/vendors/${system.config.vendorId}"

Similarly, you can access these parameters from custom components. For example, you could reference da.faHostName from a custom component with this call:

conversation.variable("system.config.da.faHostName")

Create a Custom Parameter

  1. Click icon to open the side menu to open the side menu, select Development > Skills, and select your skill.
  2. In the skill’s left navigation, click left navbar Settings icon..
  3. Click the Configuration tab.
  4. Click New Parameter and fill in the fields of the dialog that appears.

Modify the Value for a Custom Parameter in a Published Skill

Once you have published a skill, you can't add or delete custom parameters, but you can change their values. To do so:

  1. Click icon to open the side menu to open the side menu, select Development > Skills, and select your skill.

  2. In the skill’s left navigation, click left navbar Settings icon..

  3. Select the Configuration tab.
  4. Select the parameter, click Edit, and enter the updated value.

Note:

If you have added a skill with a parameter that is prefaced with da to a digital assistant, you can only update its value in the digital assistant.

Set the Value for a Shared Parameter in Digital Assistant

After a skill has been published and has been added to a digital assistant, you can set the value for any shared parameters (those that are prefixed with da.) in the digital assistant:

  1. Click icon to open the side menu to open the side menu, select Development > Digital Assistants, and select your digital assistant.
  2. In the digital assistant's left navigation, click Skills icon.
  3. Select one of the skills that uses the parameter.
  4. Scroll down to the Parameters section of the page and enter a value for the parameter.

    The updated parameter value will be applied for all skills that use the parameter.