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.

You may also want to look at the Conversation Designer which allows a working skill to be generated based on a sample conversation. This is convenient way of rapidly developing a working skill to use as a prototype. See The Conversation Designer.

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.


Description of yaml_sections.png follows

The dialog flow is laid out as follows:

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"
...
Note

In platform version previous to 20.12, the dialog flow starts off with the metadata node, which contains a platformVersion node. Starting with platform version 20.12 these nodes are deprecated.

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.
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 the 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.
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.

Description of default_flow.png follows

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.

Tip:

Click Validate as you write your dialog flow to check for syntax errors and to apply best practices.

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 at 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. The Dialog Engine proceeds 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 one of the states named in the System.Intent's 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. Here are some solutions when users follow to the "unhappy" path, because their actions do not correspond to the current dialog flow state.
Scenario Solution
Instead of tapping buttons, the user responds inappropriately 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 tells 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 an explicit option to temporarily leave the skill they are engaged with to do something in a second skill within the same digital assistant. For example, if they are in a shopping skill and they have made some selections, you could display a button that enables them 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.

    Note

    When you use system.textReceived this way,system.text is the only variable that you can define in the postback payload. Any other variables in the payload are ignored.
  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.

Note

When you use an explicit invocation in the system.text component, the user may see the message with that button twice:
  • When they are presented the button to navigate to the other skill.
  • When they complete the flow in the other skill.
If you don't want the message to appear the second time, use an implicit invocation in the system.text component instead of explicit invocation. An implicit invocation is an utterance that matches well with a given skill without using the skill's invocation name (or a variant of the invocation name with different spacing or capitalization).
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

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


Description of skill-skill-routing-explicit.png follows

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


Description of skill-skill-routing-intent.png follows

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}
Custom Entity Values When useFullEntityMatches is Set to true

Starting with version 20.12, if you have set the useFullEntityMatches property to true on a System.ResolveEntities or System.CommonResponse component, any custom entity values that are set in the component are stored as JSON objects instead of as strings. In turn, the JSON objects have properties that you can access for the entity value in the language of the current conversation, the entity value in the skill's primary language, and the orignal string that the user entered that was matched to the entity.This behavior is the same as with built-in entities, which are always stored as JSON objects.

Referencing Entity Values in Multi-Language Skills

When you have multi-language skills, you should make sure the component that is resolving your entities has useFullEntityMatches set to true. This enables you to write the following kinds of expressions:

  • Expressions that reference the entity value in the language of the conversation. You can use such expressions to do things such as display the entity values to users in buttons, confirmation messages, etc.

    For such expressions, you append value to get the value in the language of the conversation. For example, if you have defined a context variable, pizza, and want to reference the PizzaSize entity value in a prompt, you would use the following in the expression: pizza.value.PizzaSize.value (instead of just pizza.value.PizzaSize).

  • Expressions that reference the value of the entity in the primary language. The primary language value serves as a kind of key for the corresponding values in all of the entity's languages. You can reference this value for business logic purposes without having to worry about which language the conversation is in.

    For these expressions, you append primaryLanguageValue (e.g. pizza.value.PizzaSize.primaryLanguageValue).

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.

User-scoped variables, which you define within the individual states, not in the context node, are prefixed with user. The checklastorder state in the following excerpt from the PizzaBotWithMemory dialog flow, includes the user.lastsize variable that retains the pizza size from the previous user session. 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.
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, locale, 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 skill 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}
Get the user’s time zone (offset in milliseconds). ${profile.timezoneOffset}

These pre-defined variables are set automatically from values that the messaging platform passes in. The values differ by messaging platform and some messaging platforms may not provide them. For digital assistant as agent integrations, for example, the profile.firstName, profile.lastName, and profile.email have values only if the user was signed in to Oracle Service Cloud, or if a user filled out fields for the first name, last name, and email address on the Oracle Service Cloud chat launch page before requesting a chat. For the Oracle Web Client, these profile values must be set with the initUserProfile property or the updateUser method.

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.

Description of default_card_text_only.png follows

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.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:
      variables:
        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.channelType 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.channelType=='twilio')?then('true','false')}"
    You can conditionally enable auto numbering by referencing the user message channel. For example:
    setAutoNumbering:
      component: "System.SetVariable"
      properties
        variable: autoNumeringPostbackActions
        value: "${(system.channelType=='twilio')?then('true','false')}"
    
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

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},<#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.value}"
      ...

    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"
  • 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.).

  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 Enable Insights 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 skill is in a production environment, be sure to turn the Skill Conversation Log 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.

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

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

If you want to be able to set the value for a skill's parameter in the digital assistant that you add the skill to, preface the parameter name with da. (including the dot (.)). For example, you could use da.faHostName as the name for a parameter for a host name.

Using this approach, you can define parameters with the same names in multiple skills, add all of those skills to a digital assistant, and then set the values for the shared parameters in one place in the digital assistant.

To access a parameter from a custom component, define an input parameter in the custom component and then pass the value of the skill parameter to it.

Note

You can't set the values of custom parameters (or any other variables prefixed with system.config directly in the dialog flow or in custom components.

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.

Secure Parameters

If your skill relies on a parameter, the value of which you don't want to be visible to others who are developing that skill or versions or clones that skill, you can designate that parameter as a secure parameter. Anybody who then navigates to the Settings page in the skill can see the name of the parameter, but not the value.

If you export the skill, the value of the parameter is not included in the export.

To create a secure parameter:

  • In the Create Parameter dialog, select Secure from the Type dropdown.

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 and you want to update the value that is used by the digital assistant, you need to do so in the digital assistant. Otherwise, the digital assistant will keep using the value that the parameter had at the time when it was added to the digital assistant. Changing the value in the skill will only affect the skill if it is used standalone or if it is later added to a different digital assistant.

Set the Value for a Parameter in Digital Assistant

After a skill has been published and has been added to a digital assistant, you can set the value in the digital assistant for any of the skill's parameters that are prefixed with da. (including the period (.)).

If a "da." parameter with the same name is defined in multiple skills in the digital assistant, the value of that parameter is shared between the skills in the digital assistant.

To set the value for a parameter in a 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.

Group Chats

On channels for Microsoft Teams and Slack, it is possible to make skills and digital assistants available for group chats, where multiple human participants can collaborate with a digital assistant in the same conversation.

Group chats are good for situations where you need a skill to perform actions or provide information for a group of human participants. In a group chat:

  • All aspects of the conversation are visible to all participants.
  • The skill only responds to messages in which the user includes a mention of the Microsoft bot or Slack app that represents the skill. This means that the human participants in a group chat can exchange messages among themselves without involving the skill. For example, they may want to have a discussion to agree on what to request of the skill before they make that request.
  • By default, the skill will not mediate between users. It will respond to input in the order received as if it was all coming from the same person. For example, if one user contradicts another user, the bot will not try to reconcile the two, unless some logic has been programmed in a custom component to do so (but it would probably need to have logged in users that the bot could explicitly track).
  • Any query made to a skill within a group chat is based on the identity (and, in the case of skills that require authentication, the corresponding credentials) of the user in the group chat who made the query.

You can also set up the skill to require all participants in a group chat to be authenticated.

For the group chat to work, you need to set up a Microsoft Teams or Slack channel.

If you are using a Microsoft Teams channel, make sure that you have selected the appropriate bot scopes to enable group chat. You can select Team and/or Group Chat (the latter of which is a user-defined group).

If you are using Slack as the channel, make sure that the Slack app has the following event subscriptions:

  • message.im
  • app_mention
  • message.mpim
  • message.channels

Once a channel is set up for the skill or digital assistant to be used in the group chat, users can add the corresponding Microsoft bot or Slack app to a group. In the conversation, they can invoke the bot or app with a user mention.

User Authorization in Group Chats

In skills that you target for group chats, you may wish to enforce user authorization. Since multiple users may access the skill in a session, you need to configure the skill to properly handle authorization for each user, not just the user who first encounters the authorization state. You do this by using:

  • The requiresAuthorization property on states where you want to enforce authorization. This ensures that a user can't join the chat when it is at such a state until that user is authorized.
  • The system.authorizeUser transition action to direct unauthorized users to the appropriate authorization state when they try to join a chat at a secured state. Typically you would use this as a default transition.
  • The system.requestedState variable to identify the state at which an unauthorized user tried to join a conversation (and, therefore, the state where they should be redirected after successful authorization).
  • The system.message.channelConversation.groupConversation Boolean property, which you can refer to when you need to determine whether the conversation is a group chat.

Enforce User Authorization for Group Chats

Here are the general steps for enforcing authorization for each user in a group chat:

  1. Assuming that you will want to enforce authentication for the majority of the skill's states, configure the skill as a whole to require authorization by default. 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. Click the Configuration tab.
    4. Set the Requires Authorization property to true.

    Doing this sets the default value for the requiresAuthorization property. This value can be overridden on individual states.

  2. For any states that do not require authorization, add the requiresAuthorization property and set it to false. For example:
      greetingMessage:
        component: System.Output
        requiresAuthorization: false
        ...
    Note

    Alternatively, you can set the skill's Requires Authorization property to false and then set the requiresAuthorization property to individual states to true.
  3. Add a System.OAuthAccountLink or System.OAuth2AccountLink component in the dialog flow for user authentication.

    Use user-scoped variables to store the access token and authenticated user ID. Otherwise, the authorization info each user who joins the chat would override the authorization info the preceding user who joined the chat.

    For the pass transition, use the system.requestedState system variable to return the user to the state where they tried to enter the conversation.

    For the fail transition action, use an expression with the system.message.channelConversation.groupConversation property. This enables you to direct the conversation to one state if the failed authorization happens in a group chat and a different state if it's not a group chat.

    For example:

      login:
        component: "System.OAuth2AccountLink"
        properties:
          prompt: "${profile.firstName}, please login"
          authenticationService: "GoogleAuthService"
          redirectUri: "<redirect_URI>"
          accessTokenVariableName: "user.accessToken"
          authenticatedUserVariableName: "user.authenticatedUserId"
        transitions:
          actions:
            pass : "${system.requestedState}"
            fail : "${system.message.channelConversation.groupConversation?then('failInGroup','fail')}"        textReceived: "intent"
  4. Add the states for authorization failure. In this snippet:
    • failInGroup handles the case where the failed authorization happens in a group chat. In this case, the user might not be able to authenticate, but the conversation can continue with the other participants.
    • fail handles the case where the failed authorization happens in an individual chat, in which case the conversation can simply end.
      failInGroup:
        component: "System.CommonResponse"
        requiresAuthorization: false
        properties:
          metadata:
            responseItems:
              - type: "text"
                text: "Sorry ${profile.firstName}, you are not authorized to use this skill"
                channelCustomProperties:
                  - channel: "slack"
                    properties:
                      ephemeral: true
        transitions:
          next: ${system.requestedState}
    
       fail:
        component: System.Output
        requiresAuthorization: false
        properties:
          text: "Sorry, you are not authorized to use this skill"
        transitions:
          return: done
    Note

    The ephemeral custom channel property in the above snippet ensures that the message is only displayed to the user who is triggering that response. This custom channel property is only available for Slack channels.
  5. Add a default transition in the dialog flow with a system.authorizeUser action that transitions to the user authentication state. For example:
    defaultTransitions:
      actions:
        system.authorizeUser: "login"

    This default transition is triggered when a not-yet-authorized user tries to join the conversation and reaches a state that requires authorization.

What Users Need to Know About Group Chats

When you create a skill that is intended to work in a group chat, you should make sure that users of the skill are aware of the following:

  • To involve the skill (or digital assistant) in the group chat, you address the corresponding Microsoft Teams bot or Slack app with a user mention (@bot_name).
  • For every message that you enter that you expect a response from the bot, you need to include the user mention of the bot.
  • If your message is intended only for the other human participants, don't include a user mention for the bot. The bot will ignore such messages.
  • In Slack group chats, the chats need to be held in the thread where the bot was first introduced in the conversation. If someone responds outside the thread of the conversation, it creates a new conversation that is independent of the previous one.
  • When a participant in the group chat makes a request of the bot, the bot is aware of which user made the request. Therefore, where appropriate, the response to the request is based on the requester's identity and credentials. If a different participant then makes a request, the response to that request is based on that second participant's identity and credentials.
  • All users that have successfully joined a group chat can see all of the bot's responses. It is the responsibility of the participants in the group chat to not elicit information from the bot that other participants aren't supposed to see.