Skip to content

Commit

Permalink
Merge pull request #42 from MycroftAI/enhancement/regex
Browse files Browse the repository at this point in the history
Better examples of using Intents with Adapt
  • Loading branch information
KathyReid committed Mar 14, 2018
2 parents f5bdf47 + ecb560c commit a55d309
Showing 1 changed file with 141 additions and 0 deletions.
141 changes: 141 additions & 0 deletions _pages/introduction-developing-skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,147 @@ def stop(self):

In the above code block, the [`pass` statement](https://docs.python.org/2/reference/simple_stmts.html#the-pass-statement) is used as a placeholder; it doesn't actually have any function. However, if the **Skill** had any active functionality, the stop() method would terminate the functionality, leaving the *Skill** in a known good state.

#### Intents and regular expressions (regex)

In the examples above, we walked through how to use phrases in a `.voc` file to build an `Intent` using _entities_. In this section, we expand on how `Intents` are built, and introduce _multiple entities_, and _regular expressions_.

Throughout this section, we will be using examples from the [Date and Time Skill](https://github.com/MycroftAI/skill-date-time).

##### How .voc files are used to handle Intents

At the top of your **Skill** file, you will have a line that looks like this:

`from adapt.intent import IntentBuilder`

This tells your **Skill** to import the `IntentBuilder` class from Adapt. Adapt is an Intent-handling engine. Its job is to understand what a user Speaks to Mycroft, and to pass that information to a **Skill** for handling.

Different **Skills** require different information from the user. For example, the **Skill** to change the color of Mycroft's eyes just has one parameter - `color`. That parameter is mandatory - because you can't change the color of Mycroft's eyes without knowing what color to change them to.

Later in your **Skill** file, you will call `IntentBuilder`, with one or more parameters. The parameters can be either `required` or `optional`.

For example, here is the `@intent_handler` _decorator_ used in the [Date and Time Skill](https://github.com/MycroftAI/skill-date-time). It has three parameters; two are `required` and one is `optional`.

```python

@intent_handler(IntentBuilder("")
.require("Query")
.require("Time")
.optionally("Location")
)

```

This call is then interpreted by the Adapt Intent Parser.

Internally, Adapt uses a function called `register_entity`, and tries to register `entities` based on the parameters passed to `IntentBuilder`. There are several ways that Adapt can register `entities`.

If we were building `Intent`s manually, we would do something like this:

```python

locations = [
"Seattle",
"San Francisco",
"Tokyo"
]

for loc in locations:
engine.register_entity(loc, "Location")

```

But what if we want to support more `locations`? Or make the `location` available to the **Skill** to use as a parameter in an API call?

First, Adapt will look in `.voc` files to try and register an `Intent`. For example, in the [Date and Time Skill](https://github.com/MycroftAI/skill-date-time), in the `vocab` directory, you will see several `.voc` files. Note that they each correspond to one of the parameters passed to `Intentbuilder()`.

```bash

pi@mark_1:/opt/mycroft/skills/skill-date-time/vocab/en-us $ ls -las
total 24
4 drwxr-xr-x 2 mycroft mycroft 4096 Feb 15 14:17 .
4 drwxr-xr-x 4 mycroft mycroft 4096 Feb 15 14:17 ..
4 -rwxr-xr-x 1 mycroft mycroft 8 Feb 15 14:17 Date.voc
4 -rwxr-xr-x 1 mycroft mycroft 12 Feb 15 14:17 Display.voc
4 -rwxr-xr-x 1 mycroft mycroft 9 Feb 15 14:17 Query.voc
4 -rwxr-xr-x 1 mycroft mycroft 5 Feb 15 14:17 Time.voc

```

If we take a look inside each of these files, they contain only a single word each:

* `Date.voc` => "date"
* `Display.voc => "display"
* `Query.voc`=> "what"
* `Time.voc` => "time"

Now, remember back to `IntentBuilder` and the mandatory and optional parameters? Only `Query` and `Time` were mandatory. So if a user Spoke:

`"Hey Mycroft, **what** **time** is it?"`

then Adapt would match that **Utterance** to the [Date and Time Skill](https://github.com/MycroftAI/skill-date-time), by _registering_ the `Intent`, and within the **Skill**, this would be handled by the `handle_query_time()` function.

If the user Spoke:

`"Hey Mycroft, **display** the **time** "`

which function within the Date and Time Skill do you think would handle the **Utterance**?

_ANSWER: handle_show_time()_

But what about `Location`? There isn't a `.voc` file for `Location`, so how does Adapt register an `entity` for `Location` so that `Location` can be included in an **Utterance**, recognised as an `Intent`, and handled properly by the [Date and Time Skill](https://github.com/MycroftAI/skill-date-time)?

This is done using [regular expressions](https://docs.python.org/2/library/re.html).

In the [Date and Time Skill](https://github.com/MycroftAI/skill-date-time) directory, you will see a sub-directory called `regex`. This sub-directory follows the same file structure as the `voc` directory (eg. there will be an `en-us` directory inside), and contains a file called 'location.rx':

```bash

pi@mark_1:/opt/mycroft/skills/skill-date-time/regex/en-us $ ls -las
total 12
4 drwxr-xr-x 2 mycroft mycroft 4096 Feb 15 14:17 .
4 drwxr-xr-x 4 mycroft mycroft 4096 Feb 15 14:17 ..
4 -rwxr-xr-x 1 mycroft mycroft 28 Feb 15 14:17 location.rx

```

Inside `location.rx` is a _regular expression_:

`(at|in|for) (?P<Location>.*)`

Because a `.voc` file is not present for the `Location` parameter, Adapt will then search for an equivalent `.rx` file in the `regex` directory. Instead of being restricted to the specified words in the `.voc` file, Adapt can register `Intents` using _regular expressions_, and thus support a wider range of input from the user.

Can you think of another **Skill** where a _regular expression_ `Location` would be useful?

_ANSWER: [Weather Skill](https://github.com/MycroftAI/skill-weather)_

For those who are new to Python, the `regex` used is a [Python named _group_](https://docs.python.org/2/library/re.html#regular-expression-syntax). The name of the group is case-sensitive, and correlates with the variable name used to extract the named group value.

For example, in the [Date and Time Skill](https://github.com/MycroftAI/skill-date-time), we can see one of the functions uses `Location` as an optional parameter to the function.

[Link to the code snipped below](https://github.com/MycroftAI/skill-date-time/blob/master/__init__.py#L257
)

```python
@intent_handler(IntentBuilder("").require("Query").require("Time").
optionally("Location"))
def handle_query_time(self, message):
location = message.data.get("Location")
current_time = self.get_spoken_time(location)
if not current_time:
```

The `Location` value is extracted by calling `message.data.get("Location")`. If the named group was named differently, such as `TheUserLocation`, then this code would look like:

```python
@intent_handler(IntentBuilder("").require("Query").require("Time").
optionally("TheUserLocation"))
def handle_query_time(self, message):
location = message.data.get("TheUserLocation")
current_time = self.get_spoken_time(location)
if not current_time:

```

## Simplifying your Skill code with `intent_handler` _decorators_

Your **Skill** code can be simplified using the intent_handler() _decorator_. The major advantage in this approach is that the **Intent** is described together with the method that handles the **Intent**. This makes your code easier to read, easier to write, and errors will be easier to identify.
Expand Down

0 comments on commit a55d309

Please sign in to comment.