| --- |
| 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: "<ACTION>" |
| macro: "{turn|switch|dial|let|set|get|put}" |
| - name: "<KILL>" |
| macro: "{shut|kill|stop|eliminate}" |
| - name: "<ENTIRE_OPT>" |
| macro: "{entire|full|whole|total|*}" |
| - name: "<LIGHT>" |
| 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: |
| - "<ENTIRE_OPT> {upstairs|downstairs|*} {kitchen|library|closet|garage|office|playroom|{dinning|laundry|play} room}" |
| - "<ENTIRE_OPT> {upstairs|downstairs|*} {master|kid|children|child|guest|*} {bedroom|bathroom|washroom|storage} {closet|*}" |
| - "<ENTIRE_OPT> {house|home|building|{1st|first} floor|{2nd|second} floor}" |
| |
| - id: "ls:on" |
| groups: |
| - "act" |
| description: "Light switch ON action." |
| synonyms: |
| - "<ACTION> {on|up|*} <LIGHT> {on|up|*}" |
| - "<LIGHT> {on|up}" |
| |
| - id: "ls:off" |
| groups: |
| - "act" |
| description: "Light switch OFF action." |
| synonyms: |
| - "<ACTION> <LIGHT> {off|out}" |
| - "{<ACTION>|<KILL>} {off|out} <LIGHT>" |
| - "<KILL> <LIGHT>" |
| - "<LIGHT> <KILL>" |
| - "no <LIGHT>" |
| 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<NCToken> 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<NCToken> |
| ): 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> |
| |
| |
| |
| |