article by Frank Nimphius, October 2019
Oracle Digital Assistant orchestrates individual chatbots (skills) to a unified chatbot experience. In this architecture, skills are individual chatbots that represent modular building blocks and perform a specific task or set of tasks. The role of the digital assistant is to "host" skills and intelligently route user messages to the skills and intents that fit the best.
Although skills are independent chatbots, they may need to exchange information when executed in the context of a digital assistant. A usecase e.g. is to share an access token obtained in an authorization skill that then is used in other skills to perform backend service access. This way you don't need to implement the authorization logic in all skills but just one. Another use case is where skills implement related tasks: one skill to perform a banking transaction whereas a second tracks the transaction.
In this article, I'll explain how to use Oracle Digital Assistant user-scope variables to exchange information between skills. I'll also explain how to use custom skill parameters to avoid dependencies in the skill BotML code. The design goals are
- Skills should not depend on implementation details of other skills
- Skills should be designed for stand-alone use and not as a dependent detail of a master
- Information is shared using user scope variables. The name of the user scope variables should be defined by the digital assistant designer, not the skill developer
About User-Scope Variables
You create user-scope variables in Oracle Digital Assistant using a System.SetVariable component with a variable name you prefix with "user." (e.g. user.token). User-scope variables are saved in a database and live beyond the ending of a conversation. The end of a conversation is implemented through a return transition on a dialog flow state and ensure all variables defined in a dialog flow are reset. If you run a skill in the context of digital assistant, then user-scope variables are available to all skills within this digital assistant. The scope of user-scope variables is bound to the channel and the user, which means if a user starts the same digital assistant from another channel, then the user-scope variable needs to be re-set.
Code example:
setSharedToken:
component: "System.SetVariable"
properties:
value: "1234567ABC"
variable: "user.token"
The BotML code shown above sets a value of 1234567ABC to the user-scope variable with the name "token". To access the information saved in the user-scope variable, you use
${user.token}
Note: user-scope variables are accessible from custom components as well
About Custom Skill Parameters
The problem with the previously shown BotML code is that the name of the user-scope variable is statically defined in the BotML of the skill. In order to use this approach for the exchange of information between skills, communication is required between the skill developer and the digital assistant developer. The use of the user-scope parameter is not obvious to the digital assistant developer when adding a skill to a digital assistant. This is where custom skill parameters, and especially those that are exposed to digital assistant, become useful.
Custom skill parameters are explained in the following TechExchange articles:
TechExchange Quick-Tip: How to remote-control skill bots in Oracle Digital Assistant through parameterization
TechExchange Quick-Tip: Follow Best Practices By Keeping External Configurations Out of Your Dialog Flow
So, to avoid static user-scope variable names to be defined in BotML, you could use custom skill parameters with a "da." prefix in its name to pass the name of the user-scope variable at runtime. In addition, because the skill parameter is displayed in the digital assistant configuration for the skill, it is obvious to the digital assistant developer what this token is used for. Using a custom skill parameter you basically create an API contract.
Custom skill parameters can be accessed through an expression in the BotML code in one of two ways
1. ${system.config.parameter_name}
2. ${system.config.da.parameter_name}
The difference between option 1 and option 2 is that option 1 references a skill parameter that is local to a skill, whereas option 2 references a skill parameter that is exposed to digital assistant.
About the Challenge (Useful Know-How)
The challenge with using skill parameters to dynamically pass the names for user-scope variables to create in a skill is to find the right Apache FreeMarker expression for reading from and writing to the user-scope variable.
To write to a user-scope variable, you use the System.SetVariable component as shown in an earlier example. To read the name for the user-scope variable to write to from a custom skill parameter, you need to change the System.SetVariable setting as shown below.
setSharedToken:
component: "System.SetVariable"
properties:
value: "1234567ABC"
variable: "user.${system.config.da.parameter_name}"
You cannot make the "user." prefix of the user-scope variable part of the skill parameter value. So instead, you set the "user." prefix as a static string and then add an expression to read from the skill parameter. If the skill parameter value was "token", then the value "1234567ABC" would be written to "user.token".
To read the value of a dynamically defined user-scope variable name, for example in an output test component, you use the following expression
printToken:
component: "System.Output"
properties:
text: "Authorization token: ${user[system.config.da.tokenVariableName]}"
transitions:
next: "name_of_next_dialog_flow_state"
Notice the use of the square brackets [ … ] in the Apache FreeMarker expression. Basically, the following to expressions would do the same thing
${user.token}
${user['token']}
So using the square brackets, you can resolve an expression within resolving an expression, which is the secret sauce to this tech tip.
How to Create a Custom Skill Parameter
You define the custom skill parameter in the skill settings, selecting the Configuration tab (Note that the image below doesn't show the description field of the custom skill parameter, which also is displayed to the digital assistant designer).
The token value as the name of he user-scope parameter can be overridden in the digital assistant panel. It is set to a value so the skill can be used stand-alone, which is one of the design goals when building skills.
All skills that you want to share the user-scope variable value with need to have a custom skill parameter defined for it. Ideally the name of the skill parameters is the same. However, this is not a mandatory requirement for as long as it is obvious (through the parameter description) to the digital assistant developer what a specific skill parameter is for.
Notice the name of the skill parameter da.tokenVariableName. The ".da" prefix ensures the parameter to be visible for the skill in digital assistant.
How to Set Skill Parameter Values in Digital Assistant
As shown in the image below, the skill parameter is displayed to the digital assistant developer to keep the default value ("token" in the example), or to change it (linkedInToken" in this image). If you change the value of the parameter in one skill, you need to make sure that the skill parameter in the other skills are changed to the same value.
A Runtime Example in Pictures: Access Token Sample
A common usecase for sharing values between skills are access tokens. Two skills are used in the example for this article: A skill that uses the System.OauthAccountLink component to obtain an authorization token from LinkedIn, and a skill that displays the authorization token passed from the authorizing skill
Ps.: I know, there is a typo in the name of the first skill. But names don't matter for this example 😉
Both skills have a custom skill parameter da.tokenVariableName with a default value "token" defined defined. The skill that obtains the authorization token has the following BotML defined
Note: The System.OAuthAccuntLink component writes the authorization token that was obtained from LinkedIn to a dialog flow variable "authToken"
Both skills are added to a digital assistant in Oracle Digital Assistant. The da.tokenVariableName value was changed to "linkedInToken" for both skills.
The images below show the conversation and the token sharing at runtime:
1. The user message "Please sign me in to LinkedIn" is understood by the authorization skill. The first dialog flow state it navigates to is associated to the System.OauthAccountLink component to perform a redirect to the LinkedIn authorization endpoint.
2. The OAuth2 authorization provider displays a login screen or, if the session already has been authenticated to the provider, a dialog to grant authorization (OAuth2 is a technique to grant clients access to resources. Authentication is not a primary usecase of it). Once the Allow button is pressed, LinkedIn calls the Oracle Digital assistant callback URL to pass the authorization token back.
3. The authorizing skill in the example saves the token in the user-scope variable and then prints the token content so we have it easy to compare the token saved in the variable with the token printed in the other skill.
4. The user message "Please show the shared token" is understood by the second configured skill (which is routed to by the intelligent router in digital assistant). The BotML of the second skill is shown below
5. The code in the image above saves the token read from the user-scope variable into a dialog flow variable with the name "token". This step is not required, but makes the BotML code easier to read (in my opinion). It then navigates to a print state to display the token read from the user-scope variable. The result is shown in the image below.
6. The proof that the token is passed from one skill to another as a shared information is shown in the digital assistant conversation designer (see image below). As you can see, the navigation is from the "printToken" state in the fist skill to the "intent" state in the second skill.
A Known Issue
The use of "user.${system.config.da.parameter_name}" in a System.SetVariable component is flagged as an error when using the Validate function in the dialog flow editor. This however is more of a false positive when validating the BotML document than a problem at runtime.
Alternative Solutions
In a previous TechExchange article I explained how a skill can call another skill through the use of the System.CommonResponse component and the system.textReceived action. The call sends an explicit navigation message (containing the receiving skills invocation name) to the called skill. If the information to share between skills can be extracted from the incoming message (e.g. a RegEx entity, a value list entity, a DATE entity etc.) then you can use this "vehicle" to share information between the caller and the callee.
Summary
This article explains how to use user-scope variables in Oracle Digital Assistant to share information between skills in a digital assistant. To avoid coupling of skills, the name of the user-scope variable is passed as a custom skill parameter, which allows the digital assistant developer to set the user-scope variable name to a name of her choice. For this, in addition to a custom skill parameter, you need to use the Apache Freemarker expression explained in this article in the BotML.
Related Content
TechExchange: All 2-Minutes Oracle Digital Assistant Tech Tip Videos on YouTube
TechExchange Quick-Tip: How-to Call a Skill from another Skill And How to Avoid Dependencies