blob: 2403fc589b766c465b7aa753ae665c17194f7096 [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 <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2>
<p>
Let's develop the first NLPCraft example to familiarize ourselves with the main workflow of NLPCraft.
We'll put together a NLI-powered home light switch prototype 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 just on understanding
the natural language commands.
</p>
</section>
<section id="new_project">
<h2 class="section-title">New Project <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2>
<p>
We'll use <a href="/tools/script.html">NLPCraft CLI</a> to perform all management operations.
Run <code>bin/nlpcraft.sh</code> script to create a new project stub:
</p>
<nav>
<div class="nav nav-tabs" role="tablist">
<a class="nav-item nav-link active" data-toggle="tab" href="#nav-cmd" role="tab">Command</a>
<a class="nav-item nav-link" data-toggle="tab" href="#nav-out" role="tab">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 class="script">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
<i class="fab fa-fw fa-linux"></i> and <code>nlpcraft.cmd</code>
for <i class="fab fa-fw fa-windows"></i>.
</li>
</ul>
<div class="bq info">
<p>
<i class="fas fa-lightbulb"></i> Learn more about <a href="/tools/script.html">NLPCraft CLI</a>.
</p>
</div>
</section>
<section id="data_model">
<h2 class="section-title">Data Model <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2>
<p>
In NLPCraft a data model is 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.
</p>
<p>
Open <code>src/main/resources/<b>light_switch.yaml</b></code> file and replace its content with
the following model declaration:
</p>
<pre class="brush: js, highlight: [6, 22, 23, 26, 33, 41, 52]">
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;FLOOR_OPT&gt;"
macro: "{upstairs|downstairs|{1st|2nd|3rd|4th|5th|top|ground} floor|_}"
- name: "&lt;TYPE&gt;"
macro: "{room|closet|attic|loft|{store|storage} {room|_}}"
- name: "&lt;LIGHT&gt;"
macro: "{all|_} {it|them|light|illumination|lamp|lamplight}"
enabledBuiltInTokens: []
permutateSynonyms: true
sparse: true
elements:
- id: "ls:loc"
description: "Location of lights."
synonyms:
- "&lt;ENTIRE_OPT&gt; &lt;FLOOR_OPT&gt; {kitchen|library|closet|garage|office|playroom|{dinning|laundry|play} &lt;TYPE&gt;}"
- "&lt;ENTIRE_OPT&gt; &lt;FLOOR_OPT&gt; {master|kid|children|child|guest|_} {bedroom|bathroom|washroom|storage} {&lt;TYPE&gt;|_}"
- "&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)={has(tok_groups(), 'act')} term(loc)={tok_id() == 'ls:loc'}*"
</pre>
<p>There are number of important points here:</p>
<ul>
<li>
<code>Line 6</code> defines several macros that are used later on throughout the model's elements
to shorten the synonym declarations. Note how macros coupled with option groups
shorten overall synonym declarations 1000:1 vs. manually listing all possible word permutations.
</li>
<li>
<code>Lines 22, 23</code> define model properties that allow for multi-word synonyms in this model
to be
<a class="not-code" target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModelView.html#isSparse()">sparse</a> and
<a class="not-code" target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModelView.html#isPermutateSynonyms()">permutate</a> them for better detection. These two properties generally enable a free-form
natural language comprehension.
</li>
<li>
<code>Lines 26, 33, 41</code> define three model elements: the location of the light, and actions to turn
the light on and off. Action elements belong to the same group <code>act</code> which
will be used in our intent (<code>line 42</code>). Note that these model elements are defined mostly
through macros we have provided above.
</li>
<li>
On <code>line 52</code> we define a non-conversational intent <code>ls</code> that requires
one action (a token belonging to the group <code>act</code>) and optional list of light locations
(zero or more tokens with ID <code>ls:loc</code>) - by default we assume the entire house as a default location.
</li>
</ul>
<p>
Next, let's go ahead and add model's logic which we'll write in Scala with Java and Kotlin versions provided 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">Scala <img src="/images/scala-logo-h16.png" alt=""></a>
<a class="nav-item nav-link" data-toggle="tab" href="#nav-java" role="tab">Java <img src="/images/java2-h20.png" alt=""></a>
<a class="nav-item nav-link" data-toggle="tab" href="#nav-kotlin" role="tab">Kotlin <img src="/images/kotlin-h18.png" alt=""></a>
</div>
</nav>
<div class="tab-content">
<div class="tab-pane fade show active" id="nav-scala" role="tabpanel">
<pre class="brush: scala, 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(_.getOriginalText()).mkString(", ")
// Add HomeKit, Arduino or other integration here.
// By default - 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 - 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: scala">
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 - 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 <code>line 5</code> 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>
<code>Line 6</code> uses <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentRef.html">@NCIntentRef</a>
annotation to reference the intent defined in our model (<code>light_switch.yaml</code>, <code>line 42</code>).
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>
<code>Line 7</code> uses <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentSample.html">@NCIntentSample</a>
annotation that is used for <a href="#test">auto-testing</a> of the model.
</li>
</ul>
</li>
<li>
<code>Lines 22 and 23</code> 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 <code>line 20</code> is invoked. It
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 finish this implementation.
</li>
</ul>
<div class="bq info">
<p>
<i class="fas fa-lightbulb"></i> Learn more about <a href="/data-model.html">Data Model</a> and <a href="/intent-matching.html">Intents</a>.
</p>
</div>
</section>
<section id="build_project">
<h2 class="section-title">Build Project <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2>
<p>
Once the model ready, run the Maven build:
</p>
<pre class="brush: bash">
$ cd ~/LightSwitch
$ mvn clean package
</pre>
</section>
<section id="server">
<h2 class="section-title">Start Server <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2>
<p>
Start local REST server, if it hasn't been started already:
</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">Command</a>
<a class="nav-item nav-link" data-toggle="tab" href="#nav-srv-out" role="tab">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>
<div class="bq info">
<p>
<i class="fas fa-lightbulb"></i> Learn more about <a href="/server-and-probe.html">Server <span class="amp">&</span> Data Probes</a>.
</p>
</div>
</section>
<section id="test">
<h2 class="section-title">Testing <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2>
<p>
Recall the <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentSample.html">@NCIntentSample</a>
annotation we have used in our code next to intent reference.
</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> and
<a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentSampleRef.html">@NCIntentSampleRef</a> annotations
and their corresponding callback methods, submitting each sample input
sentences from these annotations 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>
Run model auto-validator:
</p>
<nav>
<div class="nav nav-tabs" role="tablist">
<a class="nav-item nav-link active" data-toggle="tab" href="#nav-test-cmd" role="tab">Command</a>
<a class="nav-item nav-link" data-toggle="tab" href="#nav-test-out" role="tab">Output <i class="fa fa-desktop output"></i></a>
</div>
</nav>
<div class="tab-content">
<div class="tab-pane fade show active" id="nav-test-cmd" role="tabpanel">
<pre class="brush: bash">
$ bin/nlpcraft.sh test-model --cp=~/LightSwitch/target/classes --mdls=demo.LightSwitchModel
</pre>
</div>
<div class="tab-pane fade show" id="nav-test-out" role="tabpanel">
<p></p>
<p>
<img style="max-width: 910px !important;" class="img-fluid" alt="" src="/images/first_example_fig4.png">
</p>
</div>
</div>
</section>
<section>
<h2 class="section-title">Congratulation! 👌 <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></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>