13 Implement Conversation Flows

With a well-designed model, you are ready to start building the conversation flows for your regular intents. Conversations are defined by a series of dialog flow steps in the Oracle Digital Assistant skill.

Dialog-Driven Conversations

Dialog-driven conversations collect the user information needed to complete a task by navigating a user through a series of dialog flow states. Each state is linked to a component to render bot responses and user prompts, or to handle conditions, logic, and other functionality.

With this approach, you develop the dialog flow like a film script that you derive from the use cases that you designed in the planning phase of your project. Because the individual steps in a conversation are controlled by the dialog flow, your dialog flow can quickly become quite large. To avoid dialog flows that are unmanageable in size, you should partition your use cases into different skills.

Use a Naming Convention for Dialog Flow State Names

Consider the names of dialog flow states as part of your documentation. If the name of the dialog flow state provides a context, code reviewers find it easier to understand what a dialog flow state does, and which conversation flow it belongs to. For example:

  • promptForOrderNumber
  • findOrder
  • cancelOrder

Also consider keeping related dialog flow states closely together within the BotML so it is easier to follow the course of actions in a conversation when reviewing your code.

Recommendation for User Input Components

Each dialog flow state references a single component to perform logical operations, to prompt users for input, or to display bot responses and messages. For user input and bot messages, the primary components are:

  • System.CommonResponse
  • System.ResolveEntities

There are multiple templates based on these components for building various sorts of input prompts and messages (where System.ResolveEntities is best for entity-based variables).

Best Practices for Using Variables

Variables contain the information that a skill collects from a user. Whenever possible, use variables of an entity type for the information you want to collect. Do this for the following reasons:

  • User input is validated, and users are re-prompted after an invalid data input.

  • Digital assistants handle non sequitur navigation, which means that users can start a new conversation while in an existing conversation without you needing to code for it.

  • For built-in entities wrapped in a composite bag entity and for custom entities, you can define prompts, a validation error message, and a disambiguation prompt (that is automatically shown by the System.CommonResponse and System.ResolveEntities components).

  • A variable of an entity type can be slotted from initial user messages if you associate the entity with the intent and use the nlpResultVariable property on user interface components.

Best Practices for State Transitions

The transitions node on a component state defines the dialog flow states a conversation continues with based on the outcome of the current state. The order in which transition is evaluated is as follows:

  • If an action transition is matched to a component’s output, and if no error has occurred, then the conversation continues with the state that is mapped to the matching action.

  • If no action transition is defined for a component outcome, and if no error has occurred, then the transition follows the next transition if defined. If no next transition is defined (and no global transition handler is defined), the flow continues with the state immediately below the current state (also known as an “empty” transition).

  • If no transition defined on the local dialog flow state matches a component response, then the system looks for default transitions. Default transitions are defined at the top of the dialog flow and allow you to define a shared dialog flow state to handle transition of a specific type, e.g. error, next, or an action string. If you define a default transition for next transitions, you avoid inadvertent empty transitions.

  • If no local dialog flow state transition and no default transition is found, then an empty transition occurs.

  • In the case of component errors, which could happen due to misconfiguration at design time or runtime, the error transition on the local dialog flow state is followed. If no error transition is found on the local dialog flow state, then an error handler defined as a default transition is looked up. If no error handler can be found, then the system error handler is called.

Our recommendations for defining transitions are:

  • To avoid error prone empty navigation from happening, always define a next transition on the local dialog flow state or as a default transition. The next transition is also followed if a component value gets slotted from the initial user message.

  • Define an error handler as a default transition. If a specific error needs to be handled differently from other errors, you can override the default transition by creating an error transition on the corresponding dialog flow state.

Using Keywords on Action Items

The System.CommonResponse component and custom components allow you to define keywords for your action items. With a keyword, users can invoke an action by sending a shortcut like a number or an abbreviation. This means that they don't have to press an action item, which they couldn't anyway using a text-only channel or voice, or type the full text of the displayed action item label.

Consider the following when defining keywords using the System.CommonResponse component or custom components, including entity event handlers.

  • Keywords don’t have to have the same text shown in the label.

  • Keywords are case-insensitive. You do not have to define a keyword in all possible case-sensitive variants.

  • If you plan to build bots for multiple languages, then your keywords cannot be defined in English only. To support multilingual keywords, you can reference a resource bundle string from the keyword property of the System.CommonResponse component (${rb('resource_key_name')}). The resource bundle string then contains a comma-separated list of keywords.

  • Provide visual clues indicating that keywords can be used, such as by adding an index number in front of the label.

    For example, an action menu to submit or cancel an order could be defined with the following labels: "1. Send", "2. Cancel". And the keywords could be defined as "1,1.,send" and "2,2.,cancel". So, to cancel an order, the user could type "cancel", "2", or "2.".

    Note:

    Note that in this case “send” and "cancel" also need to be defined as keywords because the labels are “1. Send” and “2. Cancel”, not just “Send” an "Cancel".
  • Keywords don't work if used in a sentence. If, for example, a user writes "I’d prefer to cancel my order", "cancel" is not detected as a keyword. If you expect your users to use conversation instead of keywords to select from an option, then consider NLU-based action menus as explained in the next section.

If you’re wondering how to create index-based keywords for dynamically-created action items, here are the choices:

  • Enable auto-numbering on the System.CommonResponse component. Auto-numbering is a property you set that then leads to the component adding the keyword to the list of keywords and the index to the label.

  • Use Apache FreeMarker expressions to add the index number and/or to reference a resource bundle string that contains the action item value in its name.

Consider NLU-Based Action Menus

Action menus usually use action items a user can press to perform navigation to a specific conversation or to submit, confirm, or cancel an operation. When an action item is pressed, a message is sent to the skill with an optional payload for updating variables and an action string to determine the dialog flow state to transition to.

If a user types a message that does not match the action item label, or a keyword defined for an action item, then the next transition is followed. For example, imagine a pair of action items that use Send expense report and Cancel expense report as labels. If a user types or says "Yes, please send", the next transition is triggered instead of the action item marked with Send expense report. The reason this would happen is because an implementation that requires users to press a button or action item is not conversational!

To create robust and truly conversational action menus, you need to create them based on value-list entities, where the list values indicate the action to follow and the synonyms define possible keywords a user would use in a message to invoke an action.

To do this, you first create a value list entity, for which you then define a variable in the dialog flow. You can then use the System.CommonResponse or System.ResolveEntities component to display the list of options. The variable you create must be configured as the value of the variable property of the component. In this way, the next transition is triggered when the user types "Yes, please send" and navigates to a dialog flow state that checks the value stored in the variable.

The value stored in the variable is one of the values defined in the value-list entity. Using a System.Switch component, you can define the next dialog flow state the conversation continues with.

If the user enters a message that is not in the value-list entity or as one of its synonyms, the action menu is invoked again. Since you used a value-list enitity, you can use the error message defined for the entity to help users understand what is expected of them. Also, because you can configure multiple prompts for the value-list entity, you can display alternative prompts and even messages that gradually reveal additional information, including information about how to cancel the action menu from displaying.

If you create resource bundle string names for the values in the value-list entity, then you can make sure the labels displayed on the action items can be translated using one of the following expressions:

  • ${rb(enumValue)}
  • ${rb(enumValue.value.primaryLanguageValue)} (if the fullEntityMatches property is set to true for the System.CommonResponse component)

To dynamically set the values on an action item, the System.CommonResponse component is easier to work with. If you are comfortable programming entity event handlers, the use of the System.ResolveEntities is also possible.

With NLU-based action menus, users can press an action item or type a message that doesn’t have to be the exact match of an action label or keyword.

Interrupting a Current Conversation for a New Conversation

A frequently asked question is how to configure a conversation so that users can start a new or different conversation when prompted for input. However, this question is more a design decision that you need to make than about technical know-how. Here are the design considerations:

  • Is your skill exposed through a digital assistant? If so, the skill participates in the non sequitur routing of the digital assistant, which routes messages to another skill or another intent in the same skill if the user message could not be successfully validated for the current dialog flow state. To make this non sequitur navigation happen, make sure user input gets validated against an entity-based variable.

  • Is your skill exposed as a stand-alone skill? If so, then there is no built-in non sequitur navigation, and you need to design for it. To do this you use an entity-based variable for the dialog flow state that you want to allow users to branch into a new conversation. You then set the maxPrompts property of the user input component to 1 and configure the cancel action transition to start a new conversation. It would be a mistake to directly point the cancel transition to the intent state as it most likely would cause an endless loop. So, before navigating back into the intent state, make sure you use a dialog flow state configured with the System.ResetVariables component to reset the nlpresult type variable and other variables needed for the conversation.

Note:

While creating standalone skills that you expose directly on a channel is an option, we don't recommend it. The main reason is that you miss out on all the conversational and development benefits you get from using a digital assistant. Some of the benefits you would miss out on are:
  • Non sequitur navigation, which is the ability of digital assistant to suspend a current conversation to temporarily change the topic to another conversation.

  • Modular development that allows you to partition your development effort into multiple skills, allowing incremental development and improvements to your bot.

  • Automatic handling of help requests.

  • Reuse of commonly needed skills like frequently asked questions, small talk, and agent integration.

Model-Driven Conversations

Model-driven conversations are an extension of dialog-driven conversations. With model-driven conversations, you reduce the amount of dialog flow code you write, providing a mature and domain object focused navigation of bot-user interactions.

The idea behind what we refer to as model-driven conversation is to handle the conversation by resolving composite bag entities using the System.ResolveEntities or the System.CommonResponse component. Composite bag entities are similar to domain objects in that they group a set of entities to form a real-world object that represents an order, booking, customer, or the like.

Each entity in the composite bag is automatically resolved by the System.ResolveEntities or System.CommonResponse component, which means that all bot responses and prompts are generated for you without the need to create dialog flow states for each of them. With model-driven conversations, you write less dialog flow code and get more functionality.

Recommended Approach

Best practices for creating model-driven conversation are to use composite bag entities, entity event handlers, and the System.ResolveEntities component.

There is nothing wrong with using the System.CommonResponse component instead of System.ResolveEntities, but, thanks to entity event handlers, the System.ResolveEntities component is sufficient for most implementations.

  • Composite bag entities are domain objects that have bag items for each piece of information to collect from a user. Each bag item has prompts, error messages and a disambiguation prompt defined that is displayed if needed. For composite bag items that are based on value list entities, you can also display multi-select lists. A benefit of composite bag entities is their ability to collect user input for many of its bag items from a single user message. This functionality is called out-of-order extraction and is enabled by default.

  • The System.ResolveEntities component resolves entities by displaying prompts defined in the entity, validating user input, displaying validation error messages defined in the entity, and showing a disambiguation dialog when users provide more information than expected in a message. For composite bag entities, the System.ResolveEntities component renders user interfaces for all bag items in the composite bag entity in the order in which they are defined.

  • An Entity Event Handler is a JavaScript component that is registered with a composite bag entity and contains functions that are called by the System.ResolveEntities component when resolving the composite bag entity. This event-driven approach allows you to run custom code logic and even invoke remote REST services while a user is working in a composite bag entity. We will cover entity event handlers in more depth later in this guide.

How to Design Model-Driven Conversations

The best design for model-driven conversations is to minimize the number of bag items required by a composite bag entity. Imagine composite bag entities as individual conversational modules you chain up to a conversation.

Check with the user after each composite bag entity you resolve to give her the opportunity to continue or discontinue the conversation.

Of course, this shouldn't be implemented with a prompt like "should I continue" followed by a pair of "yes" and "no" buttons. Let your conversation designer create a less intrusive transition that ties two conversation modules together.

Resource Bundles for Messages and Prompts

As with all messages and prompts we strongly recommend the use of resource bundle strings for prompts and messages defined in composite entity bag items. Here are some examples for composite bag entities:

  • cbe.<entity_name>.bag_item_name.errorMessage

  • cbe.<entity_name>.bag_item_name.disambiguationMessage

  • cbe.<entity_name>.bag_item_name.prompt1

  • cbe.<entity_name>.bag_item_name.prompt2

Apache FreeMarker Best Practices

Apache FreeMarker is a powerful expression language that you can use in your dialog flows as well as in entity and skill configurations. However, Apache FreeMarker expressions become problematic when they get too complex, making them error-prone and difficult to use due to the lack of debugging options.

Our recommendation is to avoid complex multi-line Apache FreeMarker expressions and instead consider one of the following options:

  • Break up complex FreeMarker expressions by saving values in variables with short names before using them in the expression.

  • Use <#/if …> directives to improve readability for your FreeMarker expressions.

  • Use entity event handlers with composite bag entities to deal with complex validation code or when computing values to be assigned to a variable.

  • Check for null values for variables you reference using the ?has_content built-in expression. Provide a sensible default value if the expression resolves to false, e.g. ?has_content?then(…,<SENSIBLE_DEFAULT_VALUE>).

Checklist for Implementing Conversations

  • ☑ Partition your use cases into multiple skills to keep your dialog flow from becoming too large.
  • ☑ Choose sensible and descriptive names for your dialog flow states.
  • ☑ Always use a "next" transition for your dialog flow states.
  • ☑ Use entity-type variables.
  • ☑ Use the System.CommonResponse and System.ResolveEntities components for user input.
  • ☑ User input prompts for entity type variables should read the prompt from the entity.
  • ☑ Build model-driven conversations.
  • ☑ Build action menus from value-list entities.
  • ☑ Avoid complex Apache FreeMarker expressions.
  • ☑ Use resource bundles. No text message or prompt should be added directly to the dialog flow.