blob: b7eac9c8fe2a2bfa1913748551b57e908bd61f72 [file] [log] [blame]
---
active_crumb: First Example
layout: documentation
id: first_example
---
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="col-md-8 second-column">
<section id="setup">
<h2 class="section-title">Overview</h2>
<p>
In the next 15 minutes we'll develop a prototypical light switch that can be controlled through the natural language.
We'll keep <a target=_ href="https://cloud.google.com/speech-to-text/">speech-to-text conversion</a> and integration
with <a target=_ href="https://developer.apple.com/homekit/">HomeKit</a> or
<a href="https://www.arduino.cc/" target=_>Ardunio</a> outside of this example - and concentrate only on understanding the natural
language commands. At the end, we should be able to say <code>"Turn the lights off in the guest bedroom"</code>
or <code>"Could you please switch off all the lights?"</code>.
</p>
<p>
NOTE: for this example we'll use Scala and Linux, however, the example works exactly the same
way with any other JVM language or operating system.
</p>
</section>
<section id="new_project">
<h3 class="section-title">New Project</h3>
<p>
We'll use <a href="/tools/script.html">NLPCraft CLI</a> to perform all management operations.
Run <code>bin/nlpcraft.sh</code> script from NLPCraft installation directory to create a new project stub (the script is located in <code>bin</code> sub-directory):
</p>
<nav>
<div class="nav nav-tabs" role="tablist">
<a class="nav-item nav-link active" data-toggle="tab" href="#nav-cmd" role="tab" aria-controls="nav-home" aria-selected="true">Command</a>
<a class="nav-item nav-link" data-toggle="tab" href="#nav-out" role="tab" aria-controls="nav-home" aria-selected="true">Output <i class="fa fa-desktop output"></i></a>
</div>
</nav>
<div class="tab-content">
<div class="tab-pane fade show active" id="nav-cmd" role="tabpanel">
<pre class="brush: bash">
$ bin/nlpcraft.sh gen-project --baseName=LightSwitch --outputDir=~ --lang=scala --pkgName=demo
</pre>
</div>
<div class="tab-pane fade show" id="nav-out" role="tabpanel">
<p></p>
<p>
<img class="img-fluid" alt="" src="/images/first_example_fig1.png">
</p>
</div>
</div>
<p>
<b>NOTES:</b>
</p>
<ul>
<li>
New project created in <code>/home/LightSwitch</code> directory.
</li>
<li>
<code>gen-project</code> command defaults to Maven as its built tool.
</li>
<li>
Run <code>bin/nlpcraft.sh help --cmd=gen-project</code> to get a full help on <code>gen-project</code> command.
</li>
<li>
<a href="/tools/script.html">NLPCraft CLI</a> is available as <code>nlpcraft.sh</code> for Unix/Linux/MacOS and <code>nlpcraft.cmd</code> for Windows.
</li>
</ul>
</section>
<section id="data_model">
<h2 class="section-title">Data Model</h2>
<p>
In NLPCraft a data model is simply an implementation of <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModel.html">NCModel</a>
interface. You can move most of the model configuration out to an external JSON or YAML file and use
<a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModelFileAdapter.html">NCModelFileAdapter</a> to
load this external configuration when creating a new model. Note that this is a canonical pattern to build
models in NLPCraft and it allows you cleanly separate the logic of the model from its declarative configuration.
</p>
<p>
Open <code>src/main/resources/<b>light_switch.yaml</b></code> file and replace its default content with
the following model declaration:
</p>
<pre class="brush: js, highlight: [16, 23, 31]">
id: "nlpcraft.lightswitch.ex"
name: "Light Switch Example Model"
version: "1.0"
description: "NLI-powered light switch example model."
macros:
- name: "&lt;ACTION&gt;"
macro: "{turn|switch|dial|let|set|get|put}"
- name: "&lt;KILL&gt;"
macro: "{shut|kill|stop|eliminate}"
- name: "&lt;ENTIRE_OPT&gt;"
macro: "{entire|full|whole|total|*}"
- name: "&lt;LIGHT&gt;"
macro: "{all|*} {it|them|light|illumination|lamp|lamplight}"
enabledBuiltInTokens: [] # This example doesn't use any built-in tokens.
elements:
- id: "ls:loc"
description: "Location of lights."
synonyms:
- "&lt;ENTIRE_OPT&gt; {upstairs|downstairs|*} {kitchen|library|closet|garage|office|playroom|{dinning|laundry|play} room}"
- "&lt;ENTIRE_OPT&gt; {upstairs|downstairs|*} {master|kid|children|child|guest|*} {bedroom|bathroom|washroom|storage} {closet|*}"
- "&lt;ENTIRE_OPT&gt; {house|home|building|{1st|first} floor|{2nd|second} floor}"
- id: "ls:on"
groups:
- "act"
description: "Light switch ON action."
synonyms:
- "&lt;ACTION&gt; {on|up|*} &lt;LIGHT&gt; {on|up|*}"
- "&lt;LIGHT&gt; {on|up}"
- id: "ls:off"
groups:
- "act"
description: "Light switch OFF action."
synonyms:
- "&lt;ACTION&gt; &lt;LIGHT&gt; {off|out}"
- "{&lt;ACTION&gt;|&lt;KILL&gt;} {off|out} &lt;LIGHT&gt;"
- "&lt;KILL&gt; &lt;LIGHT&gt;"
- "&lt;LIGHT&gt; &lt;KILL&gt;"
- "no &lt;LIGHT&gt;"
intents:
- "intent=ls term(act)={groups @@ 'act'} term(loc)={id == 'ls:loc'}*"
</pre>
<p>
Notice three model elements that we'll use later in our intent definition (lines 16, 23, and 31):
</p>
<ul>
<li><code>ls:loc</code></li>
<li><code>ls:on</code></li>
<li><code>ls:off</code></li>
</ul>
<p>
Model element <code>ls:loc</code> defines a location where we want to control the lights. Model
elements <code>ls:on</code> and <code>ls:off</code> define corresponding "on" and "off" lights
actions. We'll use these elements in our model's intent-based matching logic.
</p>
<p>
Next, let's go ahead and add model's logic which we'll write in Scala (with Java and Kotlin versions attached for reference).
Open <code>src/main/scala/demo/<b>LightSwitch.scala</b></code> file and replace its content with the following
code:
</p>
<nav>
<div class="nav nav-tabs" role="tablist">
<a class="nav-item nav-link active" data-toggle="tab" href="#nav-scala" role="tab" aria-controls="nav-home" aria-selected="true">Scala <img src="/images/scala-logo.png" height="20px" alt=""></a>
<a class="nav-item nav-link" data-toggle="tab" href="#nav-java" role="tab" aria-controls="nav-home" aria-selected="true">Java <img src="/images/java2.png" height="20px" alt=""></a>
<a class="nav-item nav-link" data-toggle="tab" href="#nav-kotlin" role="tab" aria-controls="nav-home" aria-selected="true">Kotlin <img src="/images/kotlin.png" height="20px" alt=""></a>
</div>
</nav>
<div class="tab-content">
<div class="tab-pane fade show active" id="nav-scala" role="tabpanel">
<pre class="brush: java, highlight: [5, 6, 7, 22, 23]">
package demo
import org.apache.nlpcraft.model.{NCIntentTerm, _}
class LightSwitchModel extends NCModelFileAdapter("light_switch.yaml") {
@NCIntentRef("ls")
@NCIntentSample(Array(
"Turn the lights off in the entire house.",
"Switch on the illumination in the master bedroom closet.",
"Get the lights on.",
"Please, put the light out in the upstairs bedroom.",
"Set the lights on in the entire house.",
"Turn the lights off in the guest bedroom.",
"Could you please switch off all the lights?",
"Dial off illumination on the 2nd floor.",
"Please, no lights!",
"Kill off all the lights now!",
"No lights in the bedroom, please.",
"Kill the illumination now!"
))
def onMatch(
@NCIntentTerm("act") actTok: NCToken,
@NCIntentTerm("loc") locToks: List[NCToken]
): NCResult = {
val status = if (actTok.getId == "ls:on") "on" else "off"
val locations =
if (locToks.isEmpty)
"entire house"
else
locToks.map(_.meta[String]("nlpcraft:nlp:origtext")).mkString(", ")
// Add HomeKit, Arduino or other integration here.
// By default - just return a descriptive action string.
NCResult.text(s"Lights '$status' in '${locations.toLowerCase}'.")
}
}
</pre>
</div>
<div class="tab-pane fade show" id="nav-java" role="tabpanel">
<pre class="brush: java">
package demo;
import org.apache.nlpcraft.model.*;
import java.util.List;
import java.util.stream.Collectors;
public class LightSwitchJavaModel extends NCModelFileAdapter {
public LightSwitchJavaModel() {
// Loading the model from the file in the classpath.
super("lightswitch_model.yaml");
}
@NCIntentRef("ls")
@NCIntentSample({
"Turn the lights off in the entire house.",
"Switch on the illumination in the master bedroom closet.",
"Get the lights on.",
"Lights up in the kitchen.",
"Please, put the light out in the upstairs bedroom.",
"Set the lights on in the entire house.",
"Turn the lights off in the guest bedroom.",
"Could you please switch off all the lights?",
"Dial off illumination on the 2nd floor.",
"Please, no lights!",
"Kill off all the lights now!",
"No lights in the bedroom, please.",
"Light up the garage, please!",
"Kill the illumination now!"
})
NCResult onMatch(
@NCIntentTerm("act") NCToken actTok,
@NCIntentTerm("loc") List&lt;NCToken&gt; locToks) {
String status = actTok.getId().equals("ls:on") ? "on" : "off";
String locations = locToks.isEmpty() ?
"entire house" :
locToks.stream().map(t -> (String)t.meta("nlpcraft:nlp:origtext"))
.collect(Collectors.joining(", "));
// Add HomeKit, Arduino or other integration here.
// By default - just return a descriptive action string.
return NCResult.text("Lights are [" + status + "] in [" + locations.toLowerCase() + "].");
}
}
</pre>
</div>
<div class="tab-pane fade show" id="nav-kotlin" role="tabpanel">
<pre class="brush: java">
package demo
import org.apache.nlpcraft.model.*
import java.util.stream.Collectors
class LightSwitchKotlinModel : NCModelFileAdapter("lightswitch_model.yaml") {
@NCIntentRef("ls")
@NCIntentSample(
"Turn the lights off in the entire house.",
"Switch on the illumination in the master bedroom closet.",
"Get the lights on.",
"Lights up in the kitchen.",
"Please, put the light out in the upstairs bedroom.",
"Set the lights on in the entire house.",
"Turn the lights off in the guest bedroom.",
"Could you please switch off all the lights?",
"Dial off illumination on the 2nd floor.",
"Please, no lights!",
"Kill off all the lights now!",
"No lights in the bedroom, please.",
"Light up the garage, please!",
"Kill the illumination now!"
)
fun onMatch(
@NCIntentTerm("act") actTok: NCToken,
@NCIntentTerm("loc") locToks: List&lt;NCToken&gt;
): NCResult {
val status = if (actTok.id == "ls:on") "on" else "off"
val locations = if (locToks.isEmpty()) "entire house" else locToks.stream()
.map { t: NCToken -> t.meta("nlpcraft:nlp:origtext") as String }
.collect(Collectors.joining(", "))
// Add HomeKit, Arduino or other integration here.
// By default - just return a descriptive action string.
return NCResult.text("Lights are [" + status + "] in [" + locations.toLowerCase() + "].")
}
}
</pre>
</div>
</div>
<p>
<b>NOTES:</b>
</p>
<ul>
<li>
On line 5 we use <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModelFileAdapter.html">NCModelFileAdapter</a>
to load its configuration from the external <code>light_switch.yaml</code> YAML file.
</li>
<li>
Our data model uses intent-based matching of the user input. Intent is a template and a callback for when
a template is matched against user input:
<ul>
<li>
Line 6 uses <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentRef.html">@NCIntentRef</a>
annotation to reference the intent defined in our model (it can also be defined in place).
The intent matches
two parts: first should be any token belonging to <code>act</code> group (i.e. "on" or "off" action),
and the second should be a zero or more tokens with ID equal to <code>ls:loc</code> (i.e. optional list of locations).
</li>
<li>
Line 7 uses <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentSample.html">@NCIntentSample</a>
annotation that is used for auto-testing of the model. See below the <a href="#test">testing</a>
section for details.
</li>
</ul>
</li>
<li>
Lines 22 and 23 use <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntent.html">@NCIntentTerm</a>
annotations to connect the matched result with a callback method formal parameters.
</li>
<li>
When our intent is selected as a matching winner the method <code>onMatch(...)</code> on line 20 is invoked. It simply
returns a text response that indicates the light status at a requested location. That's where you can
also add <a target=_ href="https://developer.apple.com/homekit/">HomeKit</a>,
<a href="https://www.arduino.cc/" target=_>Ardunio</a> or other integrations to make a real
lightswitch in your home.
</li>
<li>
Chapter <a href="/data-model.html">Data Model</a> provides detailed explanation on how data models work.
</li>
</ul>
</section>
<section id="build_project">
<h3 class="section-title">Build Project</h3>
<p>
Once we have our model ready let's go to <code>~/LightSwitch</code> directory and run the Maven build:
</p>
<pre class="brush: bash">
$ cd ~/LightSwitch
$ mvn clean package
</pre>
<p>
At this stage we have our project built and we are ready to start testing.
</p>
</section>
<section id="server">
<h3 class="section-title">Start Server</h3>
<p>
Run the following command to start local REST server, if it hasn't been started already, from the NLPCraft installation directory:
</p>
<nav>
<div class="nav nav-tabs" role="tablist">
<a class="nav-item nav-link active" data-toggle="tab" href="#nav-srv-cmd" role="tab" aria-controls="nav-home" aria-selected="true">Command</a>
<a class="nav-item nav-link" data-toggle="tab" href="#nav-srv-out" role="tab" aria-controls="nav-home" aria-selected="true">Output <i class="fa fa-desktop output"></i></a>
</div>
</nav>
<div class="tab-content">
<div class="tab-pane fade show active" id="nav-srv-cmd" role="tabpanel">
<pre class="brush: bash">
$ bin/nlpcraft.sh start-server
</pre>
</div>
<div class="tab-pane fade show" id="nav-srv-out" role="tabpanel">
<p></p>
<p>
<img class="img-fluid" alt="" src="/images/first_example_fig2.png">
</p>
</div>
</div>
<p>
<b>NOTES:</b>
</p>
<ul>
<li>
REST server is a "fore-and-forget" component that you generally need to start only once.
</li>
<li>
Run <code>bin/nlpcraft.sh help --cmd=start-server</code> to get a full help on this command.
</li>
<li>
<a href="/tools/script.html">NLPCraft CLI</a> is available as <code>nlpcraft.sh</code> for Unix/Linux/MacOS and <code>nlpcraft.cmd</code> for Windows.
</li>
</ul>
</section>
<section id="test">
<h3 class="section-title">Testing</h3>
<p>
Remember the <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentSample.html">@NCIntentSample</a>
annotation we have used in our Scala code next to intent definition?
</p>
<p>
Part of the <a href="/tools/test_framework.html">test framework</a>, the auto-validator class <a
target="javadoc"
href="/apis/latest/org/apache/nlpcraft/model/tools/test/NCTestAutoModelValidator.html">NCTestAutoModelValidator</a> takes one or more model IDs
(or class names) and performs validation. Validation consists of starting an <a href="/tools/embedded_probe.html">embedded probe</a> with a given model,
scanning for <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentSample.html">@NCIntentSample</a> annotations
and their corresponding callback methods, submitting each sample input
sentences from <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentSample.html">@NCIntentSample</a>
annotation and checking that resulting intent matches the intent the sample was attached to.
Note that auto-testing does not require any additional code to be written - the class gathers all required information from the model
itself.
</p>
<p>
As always, you can launch model auto-validator as any other Java class but we'll use <code>nlpcraft.sh</code>
script to do it more conveniently:
</p>
<pre class="brush: bash">
$ bin/nlpcraft.sh test-model --cp=~/LightSwitch/target/classes --mdls=demo.LightSwitch
</pre>
<p>
<b>NOTES:</b>
</p>
<ul>
<li>
Run <code>bin/nlpcraft.sh help --cmd=test-model</code> to get a full help on this command.
</li>
<li>
<a href="/tools/script.html">NLPCraft CLI</a> is available as <code>nlpcraft.sh</code> for Unix/Linux/MacOS and <code>nlpcraft.cmd</code> for Windows.
</li>
</ul>
<p>
Look at the output of this command and you will see the test results for all our sample utterances:
</p>
<p>
<img style="max-width: 910px !important;" class="img-fluid" alt="" src="/images/first_example_fig4.png">
</p>
<p>
To play with this project change the model and/or the mode logic, <a href="#build_project">build</a> project again,
and run the <a href="#test">auto-test</a>.
</p>
</section>
<section>
<h2 class="section-title">Congratulation! 👌</h2>
<p>
You've created your first data model, started the
REST server and tested the model using built-in model auto-validator.
</p>
</section>
</div>
<div class="col-md-2 third-column">
<ul class="side-nav">
<li class="side-nav-title">On This Page</li>
<li><a href="#new_project">New Project</a></li>
<li><a href="#data_model">Data Model</a></li>
<li><a href="#build_project">Build Project</a></li>
<li><a href="#server">Start Server</a></li>
<li><a href="#test">Testing</a></li>
{% include quick-links.html %}
</ul>
</div>