Snips Workshop: Mental Calculation Quiz, slides
Largely inspired by the times tables quiz, this skill poses simple arithmetic questions to test basic addition, subtraction, multiplication, and division.
The implementation of this App was developed for a workshop first given at LauzHack Days. It was then done at LauzHack 2018, Robotex 2018, ThingsCon 2018, and Sorbonne HackAIthon 2019.
In the workshop, we first create an Assistant and a Mental Calculation App from the
Snips Console. We then complete INCOMPLETE_action-mental-calculation.py
in order to obtain the working code in action-mental-calculation.py
.
This action code will hopefully serve as a useful reference for others to create their own Apps!
Below, we first describe how you can deploy the Assistant (created on the Console) to a Raspberry Pi. Further below we describe some key ingredients when writing your own action code.
Before installing your App, you need to create an Assistant. An Assistant is (typically) a collection of Apps, but it can also consist of a single App, as is done in this workshop.
For installing an Assistant, it is recommended to use SAM so that you can avoid SSH'ing into your board frequently and copying files back and forth.
The assistant for this workshop can be downloaded here. The steps for creating this assistant can be found in the slides. Whether or not you have SAM, you can find instructions for deploying the assistant on the Snips official documentation. For completeness, we provide the steps for installing the assistant if you do not have SAM:
- Go to where the ZIP file was downloaded and copy it to the Raspberry Pi (change
<pi-name>
with the hostname of your Raspberry Pi or<pi-name>.local
with its IP address):$ scp assistant_proj_Y4y2q9exO5b.zip pi@<pi-name>.local:~
- SSH into your board. Default password is
raspberry
:From now on, the specified commands should be executed on the Raspberry Pi.$ ssh pi@<pi-name>.local
- Delete the previous assistant if there is one.
$ sudo rm -rf /usr/share/snips/assistant/
- Unzip the new Assistant in the following folder:
$ sudo unzip assistant_proj_o8AM7O4ormk.zip -d /usr/share/snips/
- The Assistant is now installed. Relaunch the various Snips components with the following command:
$ sudo systemctl restart 'snips-*'
Finally, we need to add the action code for our Mental Calculations App in the Assistant, in particular for the our Mental Calculation App.
Here you can find steps for deploying
your Apps with or without SAM. If you don't have SAM, you will need two Snips programs (snips-template
and
snips-skill-server
) and virtualenv
on your board as described here.
For the purpose of this workshop, the instructions below for installing the App assume you don't have SAM (but meet the above prerequisites). This workflow is presented as we have found it convenient when developing and debugging your application code.
- Create a remote repository (like this one) with your application code.
- SSH onto the board, e.g.
$ ssh pi@<pi-name>.local
- Navigate to the following directory.
$ cd /var/lib/snips/skills
- Clone the repository to this folder.
$ git clone https://github.com/ebezzam/snips-skill-mental-calculation.git
- Enter the repository and setup the virtual environment:
$ cd snips-skill-mental-calculation $ ./setup.sh
- Restart
snips-skill-server
to launch the new App:$ sudo systemctl restart snips-skill-server
- Run
snips-watch -vvv
and test out the App!
If you see the following message in the snips-watch -vvv
output:
The session was ended because one of the component didn't respond in a timely manner.
something is definitely wrong: either there is no action code for the detected intent or
the action code is "erroring out" due to possible bugs in the Python code. Other common
errors include not typing the correct intent names (which means you aren't subscribing to the
intents you want) and similarly not including the correct username
in the intent names.
- Your action scripts must begin with
action-*
. - Create a
requirements.txt
andsetup.sh
file as the ones in this repo. - Make your action script
action-*.py
andsetup.sh
executable, i.e. from the Terminal (for MacOS and Linux) run the following commands:
chmod +x action-*.py
chmod +x setup.sh
Essentially all scripts should start off like so (with the additonal libraries that you requires for your application):
#!/usr/bin/env python2
from hermes_python.hermes import Hermes
MQTT_IP_ADDR = "localhost"
MQTT_PORT = 1883
MQTT_ADDR = "{}:{}".format(MQTT_IP_ADDR, str(MQTT_PORT))
It is good practice to create global variables for your different intents, as is done in this skill:
INTENT_START = "bezzam:start_mental_calculations"
INTENT_ANSWER = "bezzam:give_mental_calculation"
INTENT_STOP = "bezzam:stop_lesson"
INTENT_DOES_NOT_KNOW = "bezzam:does_not_know_calculation"
where bezzam
should be replaced by your username on the Snips Console if you create your own intents.
Typically each intent will have its own function/callback that should be triggered when the intent is
identified. Our action code should "subscribe" to the various intents and declare what function/callback
should be used. This should be done in the action-*.py
script, as is done in this example (at the bottom
of the script):
with Hermes(MQTT_ADDR) as h:
h.subscribe_intent(INTENT_START, user_request_quiz) \
.subscribe_intent(INTENT_STOP, user_quits) \
.subscribe_intent(INTENT_DOES_NOT_KNOW, user_does_not_know) \
.subscribe_intent(INTENT_ANSWER, user_gives_answer) \
.start()
The above callbacks, e.g. user_request_quiz
, have two arguments:
hermes
: this object will allow us to continue/terminate interactions with the user.intent_message
: this object will contain useful info, e.g. slot entries, in order for our application code to respond appropriately.
In general, an intent's function/callback should perform the following:
-
Parse
intent_message
to perform the appropriate action based on the various slot values. The various slots can be read fromintent_message
like a dictionary. For instance, in this action code we can extract the number of questions the user would like as such:intent_message.slots.n_questions.first().value
We use
first()
to extract the first slot value that was identified. Alternatively, we can get a list of all identified slot values as such:intent_message.slots.n_questions.all()
It is very important that the member,
n_questions
in this case, matches the slot value as defined in the Console. -
After performing the necessary action for the intent, we can either continue the interaction/session with the user with
hermes.publish_continue_session
or end the current interaction/session withhermes.publish_end_session
. Both take as arguments:- The session ID (
hermes.sesssion_id
). - A string to respond to the user.
hermes.publish_continue_session
could also take a list of potential intents that could follow after the given detected intent. In this example, after starting the lesson (INTENT_START
), the session could continue to one of three possibilities:INTENT_ANSWER
INTENT_DOES_NOT_KNOW
INTENT_STOP
For this reason, we pass a list of these three intents to
hermes.publish_continue_session
(Line 106). - The session ID (
When coding interactions that involve storing some sort of status, it is useful to define a dictionary, e.g.
SessionsStates
in this example, where the key is the session ID. Remember to delete entries when you no longer need
to keep track of a session's status!
Here are some pointers, if you are not working with a pre-assembled Maker Kit, as would be provided at a workshop.
The overall steps, as described in more detail here are:
- Flash an SD card with Raspbian Stretch Lite, e.g. using Etcher
- Enable SSH by adding an empty
ssh
file at the root of theboot
volume on your SD card. - Configure network access: you can simply connect with an Ethernet cable or for WiFi edit the
etc/wpa_supplicant/wpa_supplicant.conf
file (after SSH'ing). More details below. - Install the Snips Platform by running these commands.
- Configure audio. This can also be done
with SAM:
sam setup audio
.
See here for instructions. The easiest way is to use the command line raspi-config tool:
$ sudo raspi-config
For getting on a WPA2 Enterprise network like eduroam
, you will have to dig a bit deeper. Edit the following file:
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
And add the following content (e.g. for EPFL):
ctrl_interface=/var/run/wpa_supplicant
network={
ssid="eduroam" # or "epfl"
key_mgmt=WPA-EAP
proto=WPA2
eap=PEAP
identity="gaspar@epfl.ch"
password="my_password"
anonymous_identity="anonymous@epfl.ch"
phase2="auth=MSCHAPV2"
ca_cert="/etc/ssl/certs/thawte_Primary_Root_CA.pem"
subject_match="CN=radius.epfl.ch"
priority=8
}
Protect your file with the rights 600. (read/write for root only)
$ chmod 600 /etc/wpa_supplicant/wpa_supplicant.conf
The following command starts the WPA supplicant (check the name of the interface)
$ wpa_supplicant -c /etc/wpa_supplicant/wpa_supplicant.conf -i wlan0
The following command starts the dhcp
$ dhclient wlan0
For more info, see here.