DSUL.ai (v1)¶
Digital Service Univeral Language.
A language created by Prisme.ai to describe applications.
This language is universal, transparent and intuitive. Prisme.ai has the ambition to make DSUL a new standard of connected Virtual Assistant creation.
Sommaire¶
- Hello World
- Section Intents.when
- Section Intents.parameters
- Section Entities
- Sections Intents.outputContexts & Intents.inputContexts
- Section Intents.do
- Section Resources
- Translations
Hello World¶
Intents:
HelloWorld:
when:
match phrases:
- Salut
- bonjour
do:
- say text: Bienvenue à toi !
Section Intents.when¶
It is this section that defines when an intent is triggered. Two mechanisms are available: match phrases et match events.
match phrases¶
match phrases allows to detect an intent with training sentences:
Intents:
Welcome Intent:
when:
match phrases:
- Salut
- bonjour
Each time a sentence similar to these is sent by the user, the intent associated workflow (Intent.do) will be executed.
match events¶
Rather than waiting for a user sentence, it is also possible to automatically trigger intents with event buttons :
Intents:
- name: step.one
...
do:
- say event:
text: Accéder à l'étape 2
value: STEP_2
In order to be triggered as soon as this STEP_2 event is emitted, another intent can declare it in match events :
Intents:
- name: step.one
when:
match phrases:
- je veux accéder à l'intent secrète !
do:
- say text: "Voici ce que je peux te proposer :"
- say event:
text: Accéder à l'étape 2
value: STEP_2
- name: step.two
when:
match events:
- STEP_2
do:
- Text: Bravo !
As explained in section Intents.do, it is also possible to emit events without any user intervention.
Intents.parameters¶
Within training phrases (when.match phrases section), it is possible to specify parameters that would be extracted from user messages in order to gather complementary details about their request.
Each of these parameters must be described in Intents.*.parameters section:
- name : parameter name, the one training phrases & workflow responses will refer to
- entity : indicates the entity's name, which must be detailed in Entities section if it's a custom entity. Differents builtin entities are automatically provided to extract generic kinds of information (numbers, dates, duration, ... exhaustive list within Entities section)
- required : configures whether this is a required parameter (false by default)
- questions : indicates a few alternative questions to ask if the parameter is missing although required is set to true
- displayEntityValues : when set to true, displays every possible values as clickable buttons
Intents:
I_LIKE_FRUIT:
when:
match phrases:
- j'aime la [poire](fruit)
- j'aime bien les [pommes](fruit)
- j'aime les fruits
parameters:
- name: fruit
entity: Fruit
required: true
questions:
- Quel fruit tu aimes ?
do:
- say text: je suis vraiment ravi d'apprendre que tu aimes les $fruit !
Each parameter expected by training phrases must be specified as follows :
- Parameters' value must be enclosed in square brackets (poire, pommes)
- Parameters' name must be enclosed in parenthesis right next to the closing bracket
In the example above, one training phrase does not contain any parameter : j'aime les fruits
If someone would send this sentence or an equivalent formulation without the expected parameter inside, and since required is true, the application would automatically ask one of the defined questions : Quel fruit tu aimes ?
As the entity name (Fruit) is not prefixed with ggwg/, we know that is a custom entity described in Entities section.
Entities¶
An entity corresponds to a category of information that might be extracted from user messages, and Prisme.ai provides some builtin entities available to all intentions :
- ggwg/amount-money : Detects amounts of money : "20€", "15€", ...
- ggwg/date-time : Detects date ("Friday, October 23th"), and more informal expressions as well :"Friday", "next week", ...
- ggwg/duration : Detects durations : "1h", "during 20 minuts", "3 months", ...
- ggwg/number : Detects numbers : "1", "525", "18", ...
- ggwg/ordinal : Detects ordinal numbers : "premier", "troisième", "second"
- ggwg/percentage : Detects percentages : "15%", "20 %", ...
- ggwg/temperature : Detects temperatures : "15°C"
- ggwg/any : Detects anything that fits right where the training phrase is expecting the parameter
If one needs an additional entity, it can be specified within Entities :
Entities:
Fruit:
automaticallyExtensible: false
useSynonyms: true
values:
fr:
- value: poire
synonyms:
- poires
- value: banane
- value: pomme
synonyms:
- pome
Main options :
- automaticallyExtensible : false by default, this allows any value to be considered as the entity even if it's not part of the configured values
- useSynonyms : true by default, this allows to bind a list of synonyms to a value. Even if the user types one of the synonyms, the intent would always receive the standardized value (values.value)
- values : Only mandatory setting, this is where you will list every possible values, categorized by language
In case of a multilingual assistant, automaticallyExtensible and useSynonyms options can be set differently depending on the language :
Entities:
Fruit:
automaticallyExtensible:
fr: false
en: true
useSynonyms:
fr: true
en: false
Structure of values :
- First depth level of values indicate needed languages' code (fr, en, ...)
- Each language structure contains a list of value, where each of them have optional synonyms
- Important : synonyms option must be exactly aligned with the beginning of value, otherwise the file could not be parsed
Intents.outputContexts & Intents.inputContexts¶
Every intent maintains a context where all retrieved parameters are saved. By default, an intent's context expires as soon as we jump to another intent.
However, any intent might expose its context to the following ones for a specified lifetime (as a number of messages):
Intents:
topup.ask:
parameters:
- name: amount
entity: ggwg/amount-money
outputContexts:
TopUp: 3
Any intent can now declare TopUp as an input context in order to access amout parameter, as long as they are triggered during the 3 first messages after topup.ask :
Intents:
topup.confirm:
inputContexts:
- TopUp
Moreover, an intent specifying inputContexts cannot be triggered by training phrases if the input contexts have not been emitted.
Intents:
topup.ask:
outputContexts:
TopUp: 3
when:
match phrases:
- recharge mon forfait de [5 €](amount)
- je veux recharger [10 €](amount) sur mon forfait
parameters:
- name: amount
entity: ggwg/amount-money
required: true
questions:
- de combien voulez-vous recharger votre forfait ?
do:
- say text: Confirmez-vous la demande de rechargement de $amount € sur votre forfait ?
- say button:
text: Oui
value: Je confirme
topup.confirm:
inputContexts:
- TopUp
when:
match phrases:
- Je confirme
parameters:
- name: amount
entity: ggwg/amount-money
do:
- say text: Très bien, $amount € ont bien été rechargés sur votre forfait.
Important : An intent receiving a context must indicate again the parameters name & entity in order to use them.
User authentication¶
In order to authenticate webchat users, we can pass authenticating data to the webchat injection script's originalRequest parameter. Any intent can access these data by declaring originalContext in both inputContexts and parameters :
Intents:
hello:
inputContexts:
- originalRequest
when:
match phrases:
- Salut
- Bonjour
parameters:
- name: originalRequest
entity: ggwg/any
do:
- say text: Bonjour $originalRequest.firstName !
In the webchat injection script, these user data would be passed as follows :
<script type="text/javascript" src="//cdn.gogowego.com/wegobot_inject.js" charset="UTF-8"></script>
<script type="text/javascript">
injectWegobot(
{
"botId": "bVXMBVIjIV",
"originalRequest": {
"firstName": "Peter"
}
...
}
</script>
Section Intents.Do¶
Visual responses¶
Every visual responses are prefixed with say :
Intents:
show_responses:
...
do:
- say text: Montre moi plus d'actualités
- say random text:
- Premiere formulation
- Deuxieme formulation
- say richText:
- |-
<h3>Texte enrichi</h3>
<p>Sur plusieurs lignes</p>
- say random richText:
- <h3>Premiere possibilité de texte enrichi</h3>
- <h3>Deuxieme possibilité de texte enrichi</h3>
- say button:
text: Plus d'actu
value: Montre moi plus d'actualités
- say card:
title: Titre card
text: 'Texte en dessous'
image: https://.../url_image.png
buttons:
- say button:
text: Titre Bouton 1 card
value: Valeur bouton 1 card
- say link:
text: Titre lien
value: 'https://www.google.com'
Event buttons¶
The first manner to trigger an event is to send an event button to the user with say event :
Intents:
My_Intent:
...
do:
- say event:
text: Plus d'actu
value: MORE_NEWS
As soon as the user clicks this button, MORE_NEWS event will be emitted, trigerring any intent that declared MORE_NEWS in when.match events.
It is also possible to send custom data within event payload :
Intents:
My_Intent:
...
parameters:
- name: category
entity: ggwg/any
required: true
- name: page
entity: ggwg/number
required: true
do:
- say event:
text: Plus d'actu
value: MORE_NEWS
payload:
- type: 'parameters'
value:
category: $category
page: $page
ShowMoreNews:
when:
match events:
- MORE_NEWS
parameters:
- name: category
entity: ggwg/any
- name: page
entity: ggwg/number
do:
...
Emit an event without user interaction¶
We can also automatically trigger an event as soon as a given intent is run, without manual intervention from the user :
Intents:
My_Intent:
...
do:
- say text: Vous allez être redirigé vers une nouvelle intent !
- jump: MORE_NEWS
Intent_2:
when:
match events:
- MORE_NEWS
...
As soon as My_Intent required parameters are fulfilled and the workflow executed, the jump would automatically trigger Intent_2 intent.
Forms¶
Forms let you ask the user various informations & send them to another intent through event buttons.
Forms are displayed using say form response, which have the following 2 required fields :
- parameters : describes the informations to ask, identical to the Intents parameters field in its base version as shown below
- submit : submit button, for now it can only be an event button
Intents:
Soumettre:
when:
match phrases:
- remplir le formulaire
do:
- say form:
parameters:
- name: firstName
question: Prénom
entity: ggwg/any
- name: age
question: Age
entity: ggwg/number
submit:
say event:
text: Envoyer
value: PROCESS_FORM
ValidationFormulaire:
when:
match events:
- PROCESS_FORM
parameters:
- name: firstName
required: true
entity: ggwg/any
- name: age
entity: ggwg/number
do:
- say text: Tu t'appelles donc $firstName et tu as $age ans
- call write dataflow: Ages
Parameters accept these other options :
- validator : custom validations. May include a pattern sub structure configuring a regex
- HTMLAttributes : html attributes directly injected in the input
validator.pattern structure :
- value : the regex itself
- message : error message to show on validation failure
Channels customization¶
Intents.do can be adapted for each available channel :
Intents:
Welcome Intent:
description: un intent qui dit bonjour
when:
match phrases: '${i18n.hello}'
do:
- on default:
- say text: default channel
- on web:
- say text: Depuis le web
- on telephony:
- say text: depuis un appel
- on speaker:
- say text: depuis un speaker (alexa & cie)
- on messenger:
- say text: depuis messenger
- on whatsapp:
- say text: depuis whatsapp
- on slack:
- say text: depuis slack
Conditions¶
do:
- if:
condition: age < 18
then:
- say text: Tu es donc mineur !
- else if:
condition: age > 50
then:
- say text: En voilà un avec de la bouteille !
- else:
- say text: Bravo, te voilà majeur !
- say text: Ce texte sera affiché quelque soit la condition exécutée. Il doit toujours se situer après les conditions et jamais avant.
Every usual responses and actions might be used within these conditions.
Channels customization can be used within conditions as well, but the inverse is not possible.
We can also combine multiple sub conditions using and and or operators :
- if:
condition: age > 18 and age < 50
More comparison operators are available :
- a > b : a greather than b
- a < b : a lower than b
- a == b : a equals to b
- a != b : a not equals to b
- a exists : parameter a is defined
- a not-exists : parameter a is undefined
Actions & Ressources¶
Ressources are differents services which can be created and shared between bots from your Prisme.ai dashboard. Except for Cron, every resources provide one or more actions.
Each action name is prefixed by a call, and those that do not need any other parameter than the corresponding resource name can be written on a single line :
...
- call myAction: myResourceName
And, in its customizable form :
...
- call myAction:
resource: myResourceName
parameter1: value1
When saving a DSUL model containing actions, new resources are linked to their internal id inside an automatically created Resources section. This Resources section must not be manually edited, as it could break existing actions.
Actions result display & Repeat
Although most actions returning data provide a responses parameter configuring how these data are visually formatted, it is sometimes necessary to separate the action execution time from when its result is displayed.
In this regard, some actions have an output parameter specifying the name of a context variable in which to store their result, so that it can be displayed later on within the workflow.
When this result is a list (as it might be the case for webbook or dataflow actions), we need to loop on each entry of this list using a repeat block :
Intents:
MyIntent:
...
do:
- call myAction:
resource: myResourceName
output: resultVariable
- repeat on $resultVariable:
- say text: $text
For more complex or mulit-step processes, these repeat blocks can themselves nest new actions and repeats.
Searchengine¶
Action search¶
The only searchengine action transmits user message to the searchengine and automatically formats matching web pages as cards.
Intents:
Fallback Intent:
isFallback: true
do:
- call search: MySearchengine
Here, we call search within Fallback Intent workflow so that the searchengine will take over only when the application cannot process the request itself.
An option onFailure let you configure a workflow to execute when no results are found :
Intents:
Fallback Intent:
isFallback: true
do:
- call search:
resource: MySearchengine
onFailure:
- say text: I could not find anything relevant to your question ...
- say link:
text: Go to website
value: http://...
API¶
Action webhook¶
The only webhook action simply makes an HTTP call to the underlying route :
Intents:
Call_API:
when:
match phrases:
- Appelle mon webhook
- Lance le webhook
do:
- call webhook: MyAPI
In its simplest form as above, the request configuration is entirely driven by its resource.
However, all API resource options can be overriden on the action level :
- parameters : object of parameters to inject as GET parameters and inside the request body (unless body is specified)
- method : GET, POST, PUT, DELETE. Defaults to POST
- body : custom request body
- headers : HTTP deaders
- auth: sub object containing username and password to configure HTTP Basic Auth
- JSONPathFilter : JSON path to extract from the returned response
- output : name of the variable in which to store the extracted result
If method is not GET and body is not specified, the request body is automatically built as follows :
interface request {
timestamp: string; // DateTime of the call
sessionId: string; // Session identifier
bot: {
id: string; // Wizard identifier
};
user: { // Current user
id: string; // User ID
location: string; // Location of the user if authorized by the user
platform: string; // User agent platform
conversationSize: number; // Number of messages in the conversation
conversationId: string; // ConversationId: string; // ConversationId identifier
};
userMessage: { // User message
event: string; // If the user's message is an event, its name
text: string; // If it's text, its content
payload: {}; // If the message contains an arbitrary object, this one
}
originalRequest: {}; // Object of arbitrary parameters passed to the injection script
lang: string; // User's browser language
query: string; // Request sent by the user
event: string; // Event sent by the user
fulfillment: { // Responses generated by the workflow before arriving at the function
stream: {
type: string;
resourceId: string;
}[];
intent: { // Intention corresponding to the user's request
name: string;
confidence: number; // Match index
inputs: {}; // Value of intention parameters
};
contexts: { // Contexts
lifespan: string; // Lifetime
required: boolean; // If required
name: string; // Name
parameters: {}; // Associated parameters
scope: string; // Perimeter
}[];
endConversation: boolean; // If the convesation is over
}
Tuning request inputs¶
With simple GET parameters (i.e http://.../?foo=contentOfMyVariable):
- call webhook:
resource: MyAPI
method: GET
parameters:
foo: $myVariable
With a custom request body :
- call webhook:
resource: MyAPI
method: POST
parameters:
foo: $myVariable
body:
params:
foo: $myVariable
Output¶
If output is not specified, extracted result is considered as standard Prisme.ai objects in order to leave full control over the responses to be created to the underlying API.
However, if the API does not return Prisme.ai standards compliant responses, we can store its response with output and JSONPathFilter in order to manage visual responses with DSUL / Prisme.ai dashboard :
Intents:
Call_API:
when:
match phrases:
- Appelle mon webhook
- Lance le webhook
do:
- call webhook:
resource: MyAPI
output: names
JSONPathFilter: .result.data
- repeat on $names:
- say card:
title: $name
text: "$tel"
Instead of directly displaying result, one might need to process returned data further using a function.
Dataflow¶
Action read dataflow¶
Intents:
recipes.ideas:
description: ask for some recipes ideas
when:
match phrases:
- Idée d'un plat à cuisiner ?
- Que pourrais-je cuisiner ?
- Je ne sais pas quoi faire pour manger
parameters:
- name: ingredient
required: true
entity: Ingredient
questions:
- De quel ingrédient disposes-tu ?
do:
- call read dataflow:
resource: MyIngredientsDataflow
query:
ingredient: $ingredient
responses:
- say card:
title: $recipe
text: Il faut $count $ingredient
As for webhook, it is also possible to save its result in a context variable and display it later on :
Intents:
recipes.ideas:
description: ask for some recipes ideas
when:
match phrases:
- Idée d'un plat à cuisiner ?
- Que pourrais-je cuisiner ?
- Je ne sais pas quoi faire pour manger
parameters:
- name: ingredient
required: true
entity: Ingredient
questions:
- De quel ingrédient disposes-tu ?
do:
- call read dataflow:
resource: MyIngredientsDataflow
query:
ingredient: $ingredient
output: recipes
- repeat on $recipes:
- say card:
title: $recipe
text: Il faut $count $ingredient
An option onFailure let you configure a workflow to execute when no results are found :
- call read dataflow:
resource: MyIngredientsDataflow
query:
ingredient: $ingredient
output: recipes
onFailure:
- say text: Sorry, I did not find anything matching $ingredient ...
```
### Action write dataflow
Insert a new dataflow row, containing every declared parameters :
```yaml
Intents:
ingredients.new:
description: Enregistre un nouvel ingrédient
when:
match phrases:
- j'ai acheté un [oeuf](ingredient)
parameters:
- name: ingredient
entity: Ingredient
- name: count
entity: ggwg/number
defaultValue: 1
do:
- call write dataflow: MyIngredientsDataflow
- say text: J'ai retenu que tu as maintenant un $ingredient !
Action update dataflow¶
Updates a row from the dataflow with every declared parameters :
Intents:
ingredients.delete:
description: Supprime un ingrédient
when:
match phrases:
- Ma quantité d'[oeuf](ingredient) a changé !
parameters:
- name: ingredient
entity: Ingredient
- name: count
entity: ggwg/number
required: true
questions:
- Combien en as-tu ?
do:
- call update dataflow:
resource: MyIngredientsDataflow
query:
ingredient: $ingredient
- say text: J'ai retenu que tu as maintenant $count $ingredient !
Action delete dataflow¶
Delete a row from the dataflow :
Intents:
ingredients.delete:
description: Supprime un ingrédient
when:
match phrases:
- je n'ai plus d'[oeuf](ingredient)
parameters:
- name: ingredient
entity: Ingredient
do:
- call delete dataflow:
resource: MyIngredientsDataflow
query:
ingredient: $ingredient
- say text: J'ai retenu que tu n'as plus de $ingredient !
Function¶
Action function¶
Allows you to execute arbitrary javascript code to generate a response or a value to be used in the workflow.
Intents:
Welcome Intents:
when:
MatchPhrases:
- Hi
do:
- say text: Execute a function
- say event:
text: Go !
value: CALL_FUNCTION
RunFunction:
when:
MatchEvents:
- CALL_FUNCTION
do:
- call function:
resource: MyFunction
output: myVariable
- say text: Function returns $myVariable
Translations¶
In an application, 4 differents elements might be translated :
- Training phrases
- Required parameters' questions
- Responses
- Entities
Nested translations¶
For each of the previously quoted elements, the first translation method consists in duplicating linguistic structures in a substructure for each needed language :
Settings:
languages:
- fr
- en
Intents:
MyCountry:
when:
match phrases:
fr:
- Je viens du [Portugal](country)
- Je suis originaire de [France](country)
- Mon pays d'origine est le ...
- Mon pays d'origine est le [Maroc](country)
en:
- I'm from [Portugal](country)
- Hello, I'm coming from [France](country)
- Hello, I'm coming from [Morocco](country)
parameters:
- name: country
entity: Country
required: true
displayEntityValues: true
questions:
fr:
- D'où viens-tu ?
- De quel pays viens-tu ?
en:
- Where are you from ?
- Where do you come from ?
do:
fr:
- say text: Et c'est beau le $country ?
en:
- say text: Is $country beautiful to see ?
Entities:
Country:
values:
fr:
- value: France
- value: Portugal
- value: Maroc
en:
- value: France
- value: Portugal
- value: Morocco
Settings.languages is important, indicating which languages are supported by the application.