# Let's go further
Shall we? Don't be scared, I'm just going to show you the content of some files and explain what they do and how they work.
# Talk files
Pro tip
If you want your assistant to feel natural, be natural. Write her answers how you speak, be friendly, use slang, provide multiple ways of answering
These files are used by Alice to respond to your requests and should by all means contain at least 2 different ways of answering, optimal would be 5 at least, so she doesn't always answer the same.
We have different ways of defining those text strings.
# Structure
Pro tip
If you are using an IDE able to handle json schema, it is highly recommended using our Talk file Schema
# Example 1 - A list of strings
Let's take this example:
{
"busy": [
"Sorry, but I'm busy at the moment, please wait.",
"Wait a second, I'm busy at the moment.",
"Please wait, I'm kinda busy right now."
]
}
In this example, the key
is busy
and that key
is used by skills to retrieve the text to be spoken. It contains a list of 3 different strings, 3 different ways for Alice to choose from when answering. The choice is random, you cannot influence it, but it feels more natural if Alice doesn't always answer the same for the same question. This example is the very basic way of defining answers for a skill and is not the preferred way for skills.
# Example 2 - A dictionary of strings
Let's take a look at another example, the preferred way:
{
"confirmAddition": {
"default": [
"Ok, I have added this device.",
"I added this new device.",
"Sure, I just added it!"
]
}
}
In this example the key
is confirmAddition
but defines a dictionary this time. This dictionary is only defining the default
way of answering and is not respectful of users that might have the option use short answers
or night mode
enabled. In this case, a random default
string will be picked by Alice to answer. To respect the use short answers
or night mode
users, we can define the same as the following:
{
"confirmAddition": {
"default": [
"Ok, I have added this device!",
"I added this new device!"
],
"short": [
"Ok!",
"Done!"
]
}
}
In the case a user prefers a short answer or has turned on night mode
, Alice will pick a random answer from the short
list. Otherwise, she will randomly pick from the default
list.
# Variables
The time you will need variables in your answers will come very fast. Imagine answering for the weather, you cannot make 100 strings declaration for each possible temperatures! Two solutions exist.
# Unindexed variables
This is ok to use in case your string only contains one variable but should be avoided in case of more, simply because some languages may have to inverse the position of the variables.
{
"langSwitch": {
"default": [
"Ok, let's talk in {} then!",
"Sure, I can talk in {}",
"No problem, I'm switching to {}"
],
"short": [
"Ok",
"Yep"
]
}
}
As you can see, default strings all have a {}
weirdness... We call them curly braces
. Well, this is your variable! Alice will replace that by the content you tell her. In this example, you could ask her to speak in finnish, and she'd answer, randomly, with Ok, let's talk in finnish then!
# Indexed variables
This is better as we define what goes where, as sometimes languages don't use the same order. Or sometimes a different way of saying inverts the order of the variables.
{
"noDevice": {
"default": [
"Sorry, no {0} in room {1}",
"I can't, room {1} doesn't have any {0}"
]
}
}
By reading this you notice that our curly braces
now contain a number. They are the same variable as before but are indexed. If you take the first example, it could translate to Sorry, no satellite in room kitchen
and the second would translate to I can't, room kitchen doesn't have any satellite
. You surely have noticed that the room, and the device type variable are reversed in both examples.
Pro tip
Use punctuation for your strings! Even at the end of answers, just because it's correct, but also because most TTS do respect them and will speak more naturally if punctuated correctly. Talk files also support SSML markup. The <speak>
and breath
effects are auto-injected, you shouldn't add them! If the TTS engine the user is using doesn't support SSML, Alice will automatically remove the tags.
# Dialog templates
This is the "pièce of resistance", the beef! This defines your intents, utterances and your slots in a readable way. Alice will then use this file to train the NLU and ASR components!
Pro tip
If you are using an IDE able to handle json schema, it is highly recommended using our Dialogue Template Schema
# A basic file
A basic template look fairly simple, without any defined slot or intent
{
"skill": "HelloWorld",
"description": "This is my first skill",
"slotTypes": [],
"intents": []
}
Four mandatory fields:
skill
: the name of your skilldescription
: the description of your skill, what users will be able to read before installingslotTypes
: a list of slot definition, in this case emptyintents
: a list of intent definition, in this case empty
And that's it! Every skill must ship with at least this. In this example, it would be a skill without any interaction User -> Alice
# Let's define an intent
Ok, say we want the user to be able to say hello to Alice, we need to add an intent to our dialog template:
{
"skill": "HelloWorld",
"description": "This is my first skill",
"slotTypes": [],
"intents": [
{
"name": "HelloWorld_Hello",
"description": "The user says hello",
"enabledByDefault": true,
"utterances": [
"hello alice",
"hi alice",
"konitchiwa alice",
"hey alice",
"aloha alice"
],
"slots": []
}
]
}
This just defined an
intent
namedHelloWorld_Hello
. By standards, your intent names should always be prefixed with the skill name to avoid collisions with other skills.There's then the
description
of that intent, what it does.Comes
enabledByDefault
that can be eithertrue
orfalse
. An intent that is enabled by default can be caught at any time. If disabled, it will only be caught if your script previously activates it. It makes sense to disable intents like "YesOrNo" per example, and only activate it when needed.Next comes the
utterances
field, which are the ways of indicating your intent you teach to Alice. She will understand that you are using theintent
HelloWorld_Hello
if you say one of the four provided sentences. It is really important that you add up to at least 15 to 20 different ways of saying for a good training. The NLU then has enough data to start allowing small differences but still guess the correct intent at an acceptable percentage. Do not use punctuation signs and adding capital letters is of no use.The last filed is
slots
and is empty in the example.
# Add some variable to our utterances, aka slots
Imagine a shopping skill, and an intent to add an item to your shopping list. For sure, you won't define 10'000 utterances with all possible items to say. For that you will use slots
{
"skill": "HelloWorld",
"description": "This is my first skill",
"slotTypes": [
{
"name": "Item",
"matchingStrictness": null,
"automaticallyExtensible": true,
"useSynonyms": true,
"values": [
{
"value": "wheat"
},
{
"value": "hawaiian pizza",
"synonyms": [
"hawaii pizza",
"pizza hawaii"
]
}
]
}
],
"intents": [
{
"name": "HelloWorld_AddItem",
"description": "The user adds item to his shopping list",
"enabledByDefault": true,
"utterances": [
"add {hawaii pizza:=>Item} to my shopping list",
"add {wheat:=>Item} to my shopping list please",
"please add {wheat:=>Item} to my shopping list",
"can you please add {hawaiian pizza:=>Item} to my shopping list"
],
"slots": [
{
"name": "shopItem",
"description": null,
"required": false,
"type": "Item",
"missingQuestion": ""
}
]
}
]
}
# Slots declaration
Ok, we went a little higher in our file and added a slot definition
to our slotTypes
{
"name": "Item",
"matchingStrictness": null,
"automaticallyExtensible": true,
"useSynonyms": true,
"values": [
{
"value": "wheat"
},
{
"value": "hawaiian pizza",
"synonyms": [
"hawaii pizza",
"pizza hawaii"
]
}
]
}
name
: This is the slot type, that should NEVER be translatedmatchingStrictness
: This is a float, from 0 to 1 or null on how strict the NLU will be on catching the word if small differences are detected.automaticallyExtensible
: This allows or forbids, an automatic attempt at catching other words that are not defined in your values.useSynonyms
: Allows or forbids the use of synonymsvalues
: Defines your slot possible values- The first one is defined without synonym and the value would be
wheat
- The second one is defined with synonyms. The raw value would be
hawaiian pizze
even if you sayhawaii pizza
,hawaiian pizza
orpizza hawaii
. Thissynonyms
field only works ifuseSynonyms
is set totrue
That's it for our definition! We have some Prebuilt slots available. Always check if your slot does not already exist before implementing yours!
Now let's move to our
intent
:- The first one is defined without synonym and the value would be
# Utterances
{
"name": "HelloWorld_AddItem",
"description": "The user adds item to his shopping list",
"enabledByDefault": true,
"utterances": [
"add {hawaii pizza:=>Item} to my shopping list",
"add {wheat:=>Item} to my shopping list please",
"please add {wheat:=>Item} to my shopping list",
"can you please add {hawaiian pizza:=>Item} to my shopping list"
],
"slots": [
{
"name": "shopItem",
"description": null,
"required": false,
"type": "Item",
"missingQuestion": ""
}
]
}
Take a look at the utterances
. You surely see that weird syntax made of curly braces {hawaii pizza:=>Item}
. This is our slot! In this case you taught alice that hawaii pizza
is of slot type Item
, the one you defined just before! It is important to know that slots you use in your intents are first defined, but the value is also declared in the slot definition.
# Slot definition
We also have now defined the intent slot
itself:
{
"name": "ShopItem",
"description": null,
"required": false,
"type": "Item"
}
name
: This is the name of the slot that is returned to your script and should never be translateddescription
: If you want to add a description of what that slot isrequired
: Leave it at false, or you'll lose the ability to have Alice random texts!type
: Also never to be translated, this defines of what type that slot is and refers to theslot definition
on top of the file
# Skill configuration file
At some point, your skill will need some user dependant configuration, per example an api key, a threshold, login or a password. This is where skill configuration files come in the game!
As a dev you have to provide a skill configuration template file. Based on this file, when Project Alice installs your skill, a config file will be automatically created with your default provided values. If you happen to update your config template, let's say, to drop a login and password field in favour of an api key field, Project Alice will detect it and update the configuration file accordingly.
Pro tip
If you are using an IDE able to handle json schema, it is highly recommended using our Config file Schema
# The template file
The name of the file must be config.json.template
. Let's take a look at our HelloWorld skill that requires some configuration from the user:
{
"login": {
"defaultValue": "",
"dataType": "string",
"isSensitive": false,
"description": "Login for the HelloWorld database",
"beforeUpdate": "tryConnection",
"onUpdate": "reconnect"
},
"password": {
"defaultValue": "",
"dataType": "string",
"isSensitive": true,
"description": "Password for the HelloWorld database",
"beforeUpdate": "tryConnection",
"onUpdate": "reconnect"
},
"autoConnect": {
"defaultValue": true,
"dataType": "boolean",
"isSensitive": false,
"description": "Check this is you want HelloWorld to automatically connect to the database"
},
"retries": {
"defaultValue": 3,
"dataType": "integer",
"isSensitive": false,
"description": "How many times should the skill retry to connect in case of failure before giving up"
},
"databaseToUse": {
"defaultValue": "HelloWorld",
"dataType": "list",
"isSensitive": false,
"values": ["HelloWord", "ByeByeWorld", "HelloKitty"],
"description": "Choose which database to connect to"
},
"language": {
"defaultValue": "en",
"dataType": "list",
"isSensitive": false,
"values": {"English": "en", "Français": "fr", "Deutsch": "de"},
"description": "Choose what language to use"
},
"maxTries": {
"defaultValue": 3,
"dataType": "range",
"min": 1,
"max": 5,
"step": 1,
"isSensitive": false,
"description": "How many times should a user be allowed to fail authentication"
},
"notes": {
"defaultValue": "",
"dataType": "longstring",
"isSensitive": false,
"description": "Anything you would like to add?"
}
}
With the above example you have the full overview of what is supported. As you might have understood, we define each config by a name, this is the name you will use then in code to get the set value. We can then define a default value to use, a datatype and in case of lists, a list of value.
In our example:
- login is a string, the text will be visible to the user.
- password is a string, the text won't be visible thanks to the "isSensitive" option.
- autoConnect is a boolean, true or false, or on or off. This will display a checkbox.
- retries is an integer. An integer is a full number, such as 1, 5, 9563, with no trailing decimals.
- databaseToUse is a list of type list. It means it will display a dropdown field containing the defined values.
- language is a list of type dictionary. The difference with the above list, is that it will display the key (in this case "English" / "Français" / "Deutsch") in a dropdown field, but the value selected will be "en" or "fr" or "de". Useful when you have values to set that are not natural, the language example shows it well enough, it is nicer to display the full language name for the user to choose than a list with language codes.
- maxTries is a slider. It needs to have a default value, a minimum allowed value, a maximum allowed value and a step which defines the increment by which the value is modified by each slider step. This setting cannot be made sensitive.
- notes is a textarea, a text input that handles multiple lines.
So we have, as setting types:
- string
- longstring
- boolean
- integer
- list
- range
There are three more options you can add to any configuration!
"isSensitive": true|false
: If true, the setting's value won't be shown, but replaced with * as passwords do per exemple. Cannot be applied to range configuration."display": "hidden"
: Hides the configuration field for the user. What's the use then? Well, imagine a skill that requires an API key but to retrieve this api key, one needs his login and password. I'd make "login" and "password" visible configurations, so the user can fill them, and the api key be hidden. My skill would use the login and password fields to automatically retrieve the api key and set it for next uses. The user doesn't need to change or see that api configuration."beforeUpdate": "checkValue"
: Replace "checkValue" with any method name in your skill and that method will be triggered before the value update passes. The method must return a boolean."onUpdate": "reconnect"
: Replace "reconnect" with any method name in your skill and that method will be triggered every time the user updates that configuration field.
# The generated config file
When Alice loads her configuration she also loads all her active skills configurations. If there's a config template file available but no config file (config.json), she'll auto generate that said file! That file is git ignored, so it cannot be pushed on your repository, keeping your personal data safe. This is how the template example would look like:
{
"login": "",
"password": "",
"autoConnect": true,
"retries": 3,
"databaseToUse": "HelloWorld",
"language": "en",
"maxTries": 3,
"notes": ""
}
# Using the settings in my skill
Now that you have created your template, it's time to use those settings in your skill! Happy you, it's very easy!
configValue = self.getConfig('login')
That all! This will grab the login setting value, for your skill!
Another way to access the settings, for another skill per example:
configValue = self.ConfigManager.getSkillConfigByName('skillName', 'configName')
This will grab the setting configName from the skill skillName
And if you don't have access to self, in a class method per example:
configValue = SuperManager.getInstance().configManager.getSkillConfigByName('skillName', 'configName')
I let you guess what it does.... Same as above!
# Update a config value
If for some reason you need to programmatically update a setting value, say the api key is not valid anymore, you can do it easily!
self.updateConfig('configName', 'configNewValue')
or
self.ConfigManager.updateSkillConfigurationFile('skillName', 'configName', 'configNewValue')
or
SuperManager.getInstance().configManager.updateSkillConfigurationFile('skillName', 'configName', 'configNewValue')
# en.sample
Ever got frustrated because something you ask your assistant isn't understood? What if I told you that Alice can try to find a skill on our skills store that could fulfill your request?
There's no black magic behind this, simply a little work for the skill dev to make it work!
When a new version of your skill is published, our store is updated automagically. During this process, it also looks for files, in the dialogTemplate
directory, with the .sample
extension. The file is named after whatever language is contains, example en.sample
or de.sample
.
# Example
The structure of the file is really simple! It's a json file that contains a list... Let's check an example, for the DateDayTimeYear
skill
[
"what time is it",
"what's the date",
"what is the year",
"what is the day",
"tell me the time",
"tell me the date",
"time",
"date"
]
This file is named en.sample
and is placed in dialogTemplate
directory. Its content defines, or tries to guess, the most common ways a lambda user would ask or say to trigger your skill.
# Rules
There aren't many rules about this file but keep these in mind:
- Politeness forms are removed, so don't add
please
orcould you
or stuff like that. - The file is limited to 10 samples.
- Do not use punctuations.
- Try to think out of the box and find casual samples, like, everyday usage forms.
- Do not cover
disabledByDefault
intents, or answer intents such asyes/no
. - Think about it as
What do I definitely want my skill to react to?
Pro tip
Remember speakableName
from the installation file? Well, this is why it exists! Alice will use that speakableName
to propose your skill to download.