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.
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 + Component menu. See Dialog Flow Syntax for tips on setting variables and
defining states.
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:
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:
Typically (though not always), you’d define an
nlpresultvariable 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 in YAML Mode, you can declare an
nlpresult variable in the flow’s context node
to hold the resolved intent (iResult: "nlpresult" ). The
potential outcome is defined by one of the states named in the
System.Intent's actions node. 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 70% or higher (which is the default confidence threshold
value). See also How Confidence Threshold Works and Tune Intent Resolution Before Publishing.
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.
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!\"medium\"} 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.
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
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 Transition, 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 for Common Response Components ). 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:
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 Transition 🔗
You use the next transition to specify the default next
state. 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, define a next action within the
defaultTransition node.
context:
variables:
name: "string"
defaultTransitions:
next: "nextRules"
states:
getName:
component: "System.Text"
properties:
prompt: "What's your name please?"
variable: "name"
transitions:
next: "printName"
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:
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 Out-of-Order Actions Are Detected.
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 from a YAML
Dialog Flow 🔗
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 in a YAML dialog flow, 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:
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.
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.
When you use an explicit invocation in the
system.text variable, 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 variable 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.
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:
User-Scoped Variables in YAML
Dialog Flows 🔗
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.
Uses the stored value to switch from one state to another.
Auto-Numbering for Text-Only
Channels in YAML Dialog Flows 🔗
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. 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).
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.
Set Auto-Numbering for YAML
Dialog Flows 🔗
For YAML dialog flows, 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:
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.
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
.
You can conditionally enable auto-numbering by setting the autoNumberPostbackActions variable with the current channel. For example:
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 in YAML Dialog Flows 🔗
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:
Add the textOnly: "boolean" variable to the context
node.
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.