blob: aa5284f6166fc0cf6e036202af6b47325ab6692e [file] [log] [blame]
---
active_crumb: Intent Matching
layout: documentation
id: intent_matching
---
<!--
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="overview">
<h2 class="section-title">Overview</h2>
<p>
<a href="/data-model.html#logic">Data Model</a> processing logic is
encoded in intents and their callbacks. Sections below will explain how to declare an intent and how to develop
a callback method when the intent is matched.
</p>
</section>
<section id="matching">
<h2 class="section-title">Intent-Based Matching</h2>
<p>
The intent matching is based on an idea of defining one or more templates for user input and let the
algorithm choose the best matching intent given the user input. Such template is called an <em>intent</em>.
Each intent defines a pattern of the user input and associated action to take - <em>a callback method to call</em> - when that pattern is detected
and selected as a best match. While selecting the best matching intent NLPCraft uses a fully deterministic, traceable, algorithm.
When more than one intent matches the user input - the system automatically selects the <em>most specific one</em> as its best match.
</p>
<p>
In NLPCraft intents are specified using the Java annotations that are attached to the callback methods. Note
that intents can either be defined in the annotation itself or in the external model YAML/JSON representation
and then referenced in annotation. Both methods are equally supported and their usage is the matter of
preference or convenience:
</p>
<table class="gradient-table">
<thead>
<tr>
<th>Annotation</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntent.html">@NCIntent</a></td>
<td>
This annotation defines an intent in-place on the method serving as its callback. It takes a string value
that specifies an intent using intent <a href="#syntax">DSL syntax</a>.
</td>
</tr>
<tr>
<td><a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentRef.html">@NCIntentRef</a></td>
<td>
This annotation allows to reference an intent defined in external JSON or YAML model definition.
Similarly to <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntent.html">@NCIntent</a>
annotation the intent should be defined using <a href="#syntax">DSL syntax</a>.
</td>
</tr>
<tr>
<td><a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentTerm.html">@NCIntentTerm</a></td>
<td>
This annotation marks a formal callback method parameter to receive term's tokens when the intent
to which this term belongs is selected as the best match.
</td>
</tr>
<tr>
<td><a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentSample.html">@NCIntentSample</a></td>
<td>
Annotation that provides one or more sample of the input that associated intent should match on.
Although this annotation is optional it's <b>highly recommended</b> to provide at least several samples per intent. There's no upper
limit on how many examples can be provided and typically the more examples the better for the built-in tools.
These samples serve documentation purpose as well as used in built-in model <a href="/tools/test_framework.html">auto-validation</a>
and and <a href="/tools/syn_tool.html">synonym suggesting</a> tools.
</td>
</tr>
</tbody>
</table>
<p>
Here's a couple of examples of intent declarations to illustrate the basics of intent declaration and usage.
</p>
<p>
An intent from
<a href="examples/light_switch.html">Light Switch</a> Scala example:
</p>
<pre class="brush: java">
&#64;NCIntent("intent=act term(act)={groups @@ 'act'} term(loc)={trim(id) == 'ls:loc'}*")
&#64;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."
))
def onMatch(
&#64;NCIntentTerm("act") actTok: NCToken,
&#64;NCIntentTerm("loc") locToks: List[NCToken]
): NCResult = {
...
}
</pre>
<p>
<b>NOTES:</b>
</p>
<ul>
<li>
The intent is defined in-place using <code>@NCIntent</code> annotation.
</li>
<li>
A term match is defined as one or more tokens. Term can be optional if its min quantifier is zero.
</li>
<li>
An intent <code>act</code> has two non-conversational terms: one mandatory term and another
that can match zero or more tokens with method <code>onMatch(...)</code> as its callback.
</li>
<li>
Terms is conversational if it uses <code>'~'</code> and non-conversational if it uses <code>'='</code>
symbol in its definition. If term is conversational, the matching algorithm will look into the conversation
context short-term-memory (STM) to seek the matching tokens for this term. Note that the terms that were fully or partially matched using tokens from
the conversation context will contribute a smaller weight to the overall intent matching weight since these terms are <em>less specific.</em>
Non-conversational terms will be matched using tokens found only in the current user input without looking at the conversation context.
</li>
<li>
Method <code>onMatch(...)</code> will be called if and when this intent is selected as the best match.
</li>
<li>
Note that terms have <code>min=1, max=1</code> quantifiers by default, i.e. one and only one.
</li>
<li>
First term defines any single token that belongs to the group <code>act</code>. Note that model elements
can belong to multiple groups.
</li>
<li>
Second term would match zero or more tokens with ID <code>ls:loc</code>. Note that we use function <code>trim</code>
on the token ID.
</li>
<li>
Note that both terms have IDs (<code>act</code> and <code>loc</code>) that are used in <code>onMatch(...)</code>
method parameters to automatically assign terms' tokens to the formal method parameters using <code>@NCIntentTerm</code>
annotations.
</li>
</ul>
<br/>
<p>
In the following <a href="examples/alarm_clock.html">Alarm Clock</a> Java example
the intent is defined in JSON model definition and referenced in Java code using <code>@NCIntentTerm</code>
annotation:
</p>
<pre class="brush: js, highlight: [19]">
{
"id": "nlpcraft.alarm.ex",
"name": "Alarm Example Model",
"version": "1.0",
"enabledBuiltInTokens": [
"nlpcraft:num"
],
"elements": [
{
"id": "x:alarm",
"description": "Alarm token indicator.",
"synonyms": [
"{ping|buzz|wake|call|hit} {me|up|me up|*}",
"{set|*} {my|*} {wake|wake up|*} {alarm|timer|clock|buzzer|call} {up|*}"
]
}
],
"intents": [
"intent=alarm term~{id=='x:alarm'} term(nums)~{id=='nlpcraft:num' && ~nlpcraft:num:unittype=='datetime' && ~nlpcraft:num:isequalcondition==true}[0,7]"
]
}
</pre>
<pre class="brush: java, highlight: [1]">
&#64;NCIntentRef("alarm")
&#64;NCIntentSample({
"Ping me in 3 minutes",
"Buzz me in an hour and 15mins",
"Set my alarm for 30s"
})
private NCResult onMatch(
NCIntentMatch ctx,
&#64;NCIntentTerm("nums") List&lt;NCToken&gt; numToks
) {
...
}
</pre>
<p>
<b>NOTES:</b>
</p>
<ul>
<li>
Intent is defined in the external JSON model declaration (see line 19 in JSON file).
</li>
<li>
This intent is referenced by annotation <code>@NCIntentRef("alarm")</code> with method <code>onMatch(...)</code>
as its callback.
</li>
<li>
This example defines an intent with two conversational terms both of which have to found for the
intent to match.
</li>
<li>
Terms is conversational if it uses <code>'~'</code> and non-conversational if it uses <code>'='</code>
symbol in its definition. If term is conversational, the matching algorithm will look into the conversation
context short-term-memory (STM) to seek the matching tokens for this term. Note that the terms that were fully or partially matched using tokens from
the conversation context will contribute a smaller weight to the overall intent matching weight since these terms are <em>less specific.</em>
Non-conversational terms will be matched using tokens found only in the current user input without looking at the conversation context.
</li>
<li>
Method <code>onMatch(...)</code> will be called when this intent is the best match detected.
</li>
<li>
Note that terms have <code>min=1, max=1</code> quantifiers by default.
</li>
<li>
First term is defined as a single mandatory (<code>min=1, max=1</code>) user token with ID <code>x:alarm</code>
whose element is defined in the model.
</li>
<li>
Second term is defined as a zero or up to seven numeric built-in <code>nlpcraft:num</code> tokens that
have unit type of <code>datetime</code> and are single numbers. Note that <a href="examples/alarm_clock.html">Alarm Clock</a>
model allows zero tokens in this term which would mean the current time.
</li>
<li>
Given data model definition above the following sentences will be matched by this intent:
<ul>
<li><code>Ping me in 3 minutes</code></li>
<li><code>Buzz me in an hour and 15mins</code></li>
<li><code>Set my alarm for 30s</code></li>
</ul>
</li>
</ul>
<h3 id="logic" class="section-sub-title">Matching Logic</h3>
<p>
In order to understand the intent matching logic lets review the overall user request processing workflow:
</p>
<figure>
<img class="img-fluid" src="/images/intent_matching1.png" alt="">
<figcaption><b>Fig. 1</b> User Request Workflow</figcaption>
</figure>
<ul>
<li>
<b>Step: 0</b><br>
<p>
Server receives REST call <code>/ask</code> or <code>/ask/sync</code> that contains the text
of the sentence that needs to be processed.
</p>
</li>
<li>
<b>Step: 1</b><br>
<p>
At this step the server attempts to find additional variations of the input sentence by substituting
certain words in the original text with synonyms from Google's BERT dataset. Note that server will not use the synonyms that
are already defined in the model itself - it only tries to compensate for the potential incompleteness
of the model. The result of this step is one or more sentences that all have the same meaning as the
original text.
</p>
</li>
<li>
<b>Step: 2</b><br>
<p>
At this step the server takes one or more sentences from the previous step and tokenizes them. This
process involves converting the text into a sequence of enriched tokens representing named entities.
This step also performs the initial server-side enrichment and detection of the
<a href="/data-model.html#builtin">built-in named entities</a>.
</p>
<p>
The result of this step is a sequence of converted sentences, where each element is a sequence
of tokens. These sequences are send down to the data probe that has requested data model deployed.
</p>
</li>
<li>
<b>Step: 3</b><br>
<p>
This is the first step of the probe-side processing. At this point the data probe receives one or more
sequences of tokens. Probe then takes each sequence and performs the final enrichment by detecting user-defined
elements additionally to the built-in tokens that were detected on the server during step 2 above.
</p>
</li>
<li>
<b>Step: 4</b><br>
<p>
This is an important step for understanding intent matching logic. At this step the data probe
takes sequences of tokens generated at the last step and comes up with one or more parsing
variants. A parsing variant is a sequence of tokens that is free from token overlapping and other parsing
ambiguities. Typically, a single sequence of tokens can produce one (always) or more parsing variants.
</p>
<p>
Let's consider the input text <code>'A B C D'</code> and the following elements defined in our model:
</p>
<pre class="brush: js">
"elements": [
{
"id": "elm1",
"synonyms": ["A B"]
},
{
"id": "elm2",
"synonyms": ["B C"]
},
{
"id": "elm3",
"synonyms": ["D"]
}
],
</pre>
<p>
All of these elements will be detected but since two of them are overlapping (<code>elm1</code> and
<code>elm2</code>) there should be <b>two</b> parsing variants at the output of this step:
</p>
<ol>
<li><code>elm1</code>('A', 'B') <code>freeword</code>('C') <code>elm3</code>('D')</li>
<li><code>freeword</code>('A') <code>elm2</code>('B', 'C') <code>elm3</code>('D')</li>
</ol>
<p></p>
<p>
Note that at this point the <em>system cannot determine which of these variants is the best one
for matching - there's simply not enough information at this stage</em>. It can only be determined
when each variant is matched against model's intents - which happens in the next step.
</p>
</li>
<li>
<b>Step: 5</b><br>
<p>
At this step the actual matching between intents and variants happens. Each parsing variant from the previous
step is matched against each intent. Each matching pair of a variant and an intent produce a match with a
<em>certain weight</em>. If there are no matches at all - an error is returned. If matches were found, the match
with the biggest weight is selected as a winning match. If multiple matches have the same weight, their
respective variants' weights will be used to further sort them out. Finally, the intent's callback from the winning match is
called.
</p>
<p>
Although details on exact algorithm on weight calculation are too complex, here's the general guidelines
on what determines the weight of the match between a parsing variant and the intent. Note that these rules
coalesce around the principle idea that the <b>more specific match always wins</b>:
</p>
<ul>
<li>
A match that captures more tokens has more weight than a match with less tokens. As a corollary, the match
with less free words (i.e. unused words) has bigger weight than a match with more free words.
</li>
<li>
Tokens for user-defined elements are more important than built-in tokens.
</li>
<li>
A more specific match has bigger weight. In other words, a match that uses token from the conversation
context (i.e short-term-memory) has less weight than a match that only uses tokens from the current request. In the same
way older tokens from the conversation produce less weight than the younger ones.
</li>
</ul>
</li>
</ul>
</section>
<section id="syntax">
<h2 class="section-title">Intent DSL</h2>
<p>
Regardless of how intent is defined - directly in <code>@NCIntent</code> annotation or in external model
declaration - it follow the exactly the same syntax (here's a full <a target=github href="https://github.com/apache/incubator-nlpcraft/blob/master/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/antlr4/NCIntentDsl.g4">ANTRL4 grammar</a>).
</p>
<p>
Intent DSL grammar can be informally described using the following example (order of individual declarations is important):
</p>
<pre class="brush: js">
intent=my_intent
ordered=true
flow='^(?:id1)(^:id2)*$'
term(term1)={group @@ 'my_group'}?
term(term2)~{trim(partId.partAlias.id) == 'token1:id'}[1,3]
</pre>
<dl>
<dt><code>intent=my_intent</code></dt>
<dd>
Mandatory intent ID. Any arbitrary unique string matching the following template: <code>(UNDERSCORE|[a-z]|[A-Z])+([a-z]|[A-Z]|[0-9]|COLON|MINUS|UNDERSCORE)*</code>
</dd>
<dt><code>ordered=true</code></dt>
<dd>
<em>Optional.</em>
Whether or not this intent is ordered. Default is <code>false</code>.
For ordered intent the specified order of terms is important for matching this intent.
If intent is unordered its terms can be found anywhere in the input text and in any order.
Note that ordered intent significantly limits the user input it can match. In most cases
the ordered intent is only applicable to processing formal strict grammar (like a programming language)
and unsuitable for natural language processing.
</dd>
<dt><code>flow='^(?:id1)(^:id2)*$'</code></dt>
<dd>
<p>
<em>Optional.</em> Dialog flow is a history of previously matched intents to match on. If provided,
the intent will match not only on the user input but also on the history of the previously matched
intents.
</p>
<p>
Dialog flow specification is a standard <a target=_blank href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/regex/Pattern.html">Java regular expression</a>.
The history of previously matched intents is presented as a space separated string of intent IDs that were
selected as the best match during the current conversation, in the chronological order with the most
recent matched intent ID being the first element in the string. Dialog flow regular expression
will be matched against that string representing intent IDs.
</p>
<p>
In this example, the <code>^(?:id1)(^:id2)*$</code> dialog flow regular expression defines that intent
should only match when the immediate previous intent was <code>id1</code> and no <code>id2</code> intents
are in the history. If history is <code>"id1 id3 id3"</code> - this intent will match. However, for
<code>"id1 id2"</code> or <code>"id3 id1"</code> history this dialog flow will not match.
</p>
<p>
Note that if dialog flow is defined and it doesn't match the history the terms of the intent won't be tested at all.
</p>
</dd>
<dt>
<code>term(term1)={group @@ 'my_group'}?</code><br>
<code>term(term2)~{trim(partId.partAlias.id) == 'token1:id'}[1,3]</code>
</dt>
<dd>
<p>
Term, also known as a slot, is a building block of the intent. Term has optional ID, predicate and quantifiers.
It can support conversation context if it uses <code>'~'</code> symbol or not if it uses <code>'='</code>
symbol in its definition. For conversational term the system will search for a match using tokens from
the current request as well as the tokens from conversation STM (short-term-memory). For a non-conversational
term - only tokens from the current request will be considered.
</p>
<p>
A term represents one or more tokens, sequential or not, detected in the user input. Intent has a list of terms
(always at least one) that all have to be found in the user input for the intent to match. Note that term
can be optional if its min quantifier is zero. Whether or not the order of the terms is important
for matching is governed by <code>ordered=true</code> parameter.
</p>
<p>
Term ID (<code>term1</code> and <code>term2</code>) is optional, and when provided, is used by <code>@NCIntentTerm</code>
annotation to link term's tokens to a formal parameter of the callback method. Note that term ID follows
the same lexical rules as intent ID.
</p>
<p>
Inside of curly brackets <code>{</code> <code>}</code> is a <a href="data-model.html#dsl">token DSL</a>
expression: <code>group @@ 'my_group'</code> and <code>trim(partId.partAlias.id) == 'token1:id'</code>.
Note that exactly the same syntax is used for token DSL as well as for intent DSL for defining a token predicate.
Consult <a href="data-model.html#dsl">token DSL</a> documentation for details on its syntax.
</p>
<p>
<code>?</code> and <code>[1,3]</code> define an inclusive quantifier for that term (how many time this term should appear
for it to be considered found). You can also use the following standard abbreviations:
</p>
<ul>
<li><code>*</code> is equal to <code>[0,∞]</code></li>
<li><code>+</code> is equal to <code>[1,∞]</code></li>
<li><code>?</code> is equal to <code>[0,1]</code></li>
<li>No quantifier defaults to <code>[1,1]</code></li>
</ul>
</dd>
</dl>
<div class="bq success">
<p>
<b>Token DSL</b>
</p>
<p>
Intent DSL is built on top of the <a href="data-model.html#dsl">Token DSL</a> that provides comprehensive support for
defining predicates over detected model elements.
Make sure to read about <a href="data-model.html#dsl">Token DSL</a> as well.
</p>
</div>
</section>
<section id="callback">
<h2 class="section-title">Intent Callback</h2>
<p>
Whether the intent is defined directly in <code>@NCIntent</code> annotation or indirectly via <code>@NCIntentRef</code>
annotation - it is always attached to a callback method:
</p>
<ul>
<li>
Callback can only be an instance method on the class implementing <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModel.html">NCModel</a>
interface.
</li>
<li>
Method must have return type of <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCResult.html">NCResult</a>.
</li>
<li>
Method can have zero or more parameters:
<ul>
<li>
Parameter of type <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentMatch.html">NCIntentMatch</a>,
if present, must be first.
</li>
<li>
Any other parameters (other than the first optional
<a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentMatch.html">NCIntentMatch</a>) must
have <code>@NCIntentTerm</code> annotation.
</li>
</ul>
</li>
<li>
Method must support reflection-based invocation.
</li>
</ul>
<p>
<code>@NCIntentTerm</code> annotation marks callback parameter to receive term's tokens. This annotations can
only be used for the parameters of the callbacks, i.e. methods that are annotated with <code>@NCIntnet</code> or
<code>@NCIntentRef</code>. <code>@NCIntentTerm</code> takes a term ID as its only mandatory parameter and
should be applied to callback method parameters to get the tokens associated with that term (if and when
the intent was matched and that callback was invoked).
</p>
<p>
Depending on the term quantifier the method parameter type can only be one of the following types:
</p>
<table class="gradient-table">
<thead>
<tr>
<th>Quantifier</th>
<th>Java Type</th>
<th>Scala Type</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>[1,1]</code></td>
<td><code>NCToken</code></td>
<td><code>NCToken</code></td>
</tr>
<tr>
<td><code>[0,1]</code></td>
<td><code>Optional&lt;NCToken&gt;</code></td>
<td><code>Option[NCToken]</code></td>
</tr>
<tr>
<td><code>[1,∞]</code> or <code>[0,∞]</code></td>
<td><code>java.util.List&lt;NCToken&gt;</code></td>
<td><code>List[NCToken]</code></td>
</tr>
</tbody>
</table>
<p>
For example:
</p>
<pre class="brush: java">
&#64;NCIntent("intent=id term(termId)~{id == 'my_token'}?")
private NCResult onMatch(
&#64;NCIntentTerm("termId") Optional&lt;NCToken&gt; myTok
) {
...
}
</pre>
<p><b>NOTES:</b></p>
<ul>
<li>
Conversational term <code>termId</code> has <code>[0,1]</code> quantifier (it's optional).
</li>
<li>
The formal parameter on the callback has a type of <code>Optional&lt;NCToken&gt;</code> because the
term's quantifier is <code>[0,1]</code>.
</li>
<li>
Note that callback doesn't have an optional <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentMatch.html">NCIntentMatch</a>
parameter.
</li>
</ul>
<h3 class="section-title"><code>NCRejection</code> and <code>NCIntentSkip</code> Exceptions</h3>
<p>
There are two exceptions that can be used by intent callback logic to control intent matching process.
</p>
<p>
When <a href="/apis/latest/org/apache/nlpcraft/model/NCRejection.html">NCRejection</a> exception is thrown by the callback it indicates that user input cannot
be processed as is. This exception typically indicates that user has not provided enough information in
the input string to have it processed automatically. In most cases this means that the user's input is
either too short or too simple, too long or too complex, missing required context, or is unrelated to the
requested data model.
</p>
<p>
<a href="/apis/latest/org/apache/nlpcraft/model/NCIntentSkip.html">NCIntentSkip</a> is a control flow exception to
skip current intent. This exception can be thrown by the intent callback to indicate that current intent
should be skipped (even though it was matched and its callback was called). If there's more than one intent
matched the next best matching intent will be selected and its callback will be called.
<p>
<p>
This exception becomes useful when it is hard or impossible to encode the entire matching logic using just
declarative intent DSL. In these cases the intent definition can be relaxed and the "last mile" of intent
matching can happen inside of the intent callback's user logic. If it is determined that intent in fact does
not match then throwing this exception allows to try next best matching intent, if any.
</p>
<p>
Note that there's a significant difference between <a href="/apis/latest/org/apache/nlpcraft/model/NCIntentSkip.html">NCIntentSkip</a>
exception and model's <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModel.html">onMatchedIntent(...)</a>
callback. Unlike this callback, the exception does not force re-matching of all intents, it simply
picks the next best intent from the list of already matched ones. The model's callback can force
a full reevaluation of all intents against the user input.
</p>
<div class="bq info">
<p>
<b>Intent DSL Expressiveness</b>
</p>
<p>
Note that usage of <code>NCIntentSkip</code> exception (as well as model's life-cycle callbacks) is a
required technique when you cannot express the desired matching logic with just intent DSL alone.
Intent DSL is a high-level declarative language and it does
not support programmable logic or other types of complex matching algorithms. In such cases, you can
define a broad intent that would <em>broadly match</em> and then define the rest of the more complex matching logic in the callback
using <code>NCIntentSkip</code> exception to effectively indicate when intent doesn't match and other
intents, if any, have to be tried.
</p>
<p>
There are many use cases where DSL is not expressive enough. For example, if you intent matching depends
on the date and time, financial market conditions, weather, state from external systems or current user geographical
location - you will need to use <code>NCIntentSkip</code>-based logic or model's callbacks to support
that type of matching.
</p>
</div>
<h3 class="section-title"><code>NCIntentMatch</code> Interface</h3>
<p>
<a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCIntentMatch.html">NCIntentMatch</a> interface
can be passed into intent callback as its first parameter. This interface provide runtime information
about the intent that was matched (i.e. the intent with which this callback was annotated with). Note also that
intent context can only be the 1st parameter in the callback, and if not declared as such - it won't be passed in.
</p>
</section>
<section id="model_callbacks">
<h2 class="section-title">Model Callbacks</h2>
<p>
<a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModel.html">NCModel</a> interface provides
several callbacks that are invoked before, during and after intent matching. They provide an opportunity to inject
user-provided cross-cutting concerns into a standard intent matching workflow of NLPCraft. Usage of these callbacks
is completely optional, yet they provide convenient joint points for logging, statistic collections, security
audit and validation, explicit conversation context management, model metadata updates, and many other aspects
that may depend on the standard intent matching workflow:
</p>
<table class="gradient-table">
<thead>
<tr>
<th>Callback</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="/apis/latest/org/apache/nlpcraft/model/NCModel.html#onParsedVariant(org.apache.nlpcraft.model.NCVariant)"><code>NCModel#<b>onParsedVariant(...)</b></code></a></td>
<td>
<p>
A callback to accept or reject a parsed variant. This callback is called before any other
callbacks at the beginning of the processing pipeline and it is called for each parsed variant.
Note that a given user input can have one or more possible different parsing variants. Depending on
model configuration a user input can produce hundreds or even thousands of parsing variants that
can significantly slow down the overall processing. This method allows to filter out unnecessary
parsing variants based on variety of user-defined factors like number of tokens, presence
of a particular token in the variant, etc.
</p>
</td>
</tr>
<tr>
<td><a href="/apis/latest/org/apache/nlpcraft/model/NCModel.html#onContext(org.apache.nlpcraft.model.NCContext)"><code>NCModel#<b>onContext(...)</b></code></a></td>
<td>
<p>
A callback that is called when a fully assembled query context is ready. This callback is called
after all <a href="/apis/latest/org/apache/nlpcraft/model/NCModel.html#onParsedVariant(org.apache.nlpcraft.model.NCVariant)"><code>onParsedVariant(...)</code></a>
callbacks are called but before any <a href="/apis/latest/org/apache/nlpcraft/model/NCModel.html#onMatchedIntent(org.apache.nlpcraft.model.NCIntentMatch)"><code>onMatchedIntent(...)</code></a>
are called, i.e. right before the intent matching is performed. It's called always once per user request processing.
Typical use case for this callback is to perform logging, debugging, statistic or usage collection,
explicit update or initialization of conversation context, security audit or validation, etc.
</p>
</td>
</tr>
<tr>
<td><a href="/apis/latest/org/apache/nlpcraft/model/NCModel.html#onMatchedIntent(org.apache.nlpcraft.model.NCIntentMatch)"><code>NCModel#<b>onMatchedIntent(...)</b></code></a></td>
<td>
<p>
A callback that is called when intent was successfully matched but right before its callback is called.
This callback is called after <a href="/apis/latest/org/apache/nlpcraft/model/NCModel.html#onContext(org.apache.nlpcraft.model.NCContext)"><code>onContext(...)</code></a>
is called and may be called multiple times depending on its return value. If <code>true</code> is
returned than the default workflow will continue and the matched intent's callback will be called.
However, if <code>false</code> is returned than the entire existing set of parsing variants will be
matched against all declared intents again. Returning false allows this method to alter the state
of the model (like soft-reset conversation or change metadata) and force the full re-evaluation
of the parsing variants against all declared intents. Note that user logic should be careful not
to induce infinite loop in this behavior.
</p>
<p>
Note that this callback may not be called at all based on the return value of
<a href="/apis/latest/org/apache/nlpcraft/model/NCModel.html#onContext(org.apache.nlpcraft.model.NCContext)"><code>onContext(...)</code></a> callback.
Typical use case for this callback is to perform logging, debugging, statistic or usage collection,
explicit update or initialization of conversation context, security audit or validation, etc.
</p>
</td>
</tr>
<tr>
<td><a href="/apis/latest/org/apache/nlpcraft/model/NCModel.html#onResult(org.apache.nlpcraft.model.NCIntentMatch,org.apache.nlpcraft.model.NCResult)"><code>NCModel#<b>onResult(...)</b></code></a></td>
<td>
<p>
A callback that is called when successful result is obtained from the intent callback and right
before sending it back to the caller. This callback is called after
<a href="/apis/latest/org/apache/nlpcraft/model/NCModel.html#onMatchedIntent(org.apache.nlpcraft.model.NCIntentMatch)"><code>onMatchedIntent(...)</code></a> is called.
Note that this callback may not be called at all, and if called - it's called only once. Typical
use case for this callback is to perform logging, debugging, statistic or usage collection,
explicit update or initialization of conversation context, security audit or validation, etc.
</p>
</td>
</tr>
<tr>
<td><a href="/apis/latest/org/apache/nlpcraft/model/NCModel.html#onRejection(org.apache.nlpcraft.model.NCIntentMatch,org.apache.nlpcraft.model.NCRejection)"><code>NCModel#<b>onRejection(...)</b></code></a></td>
<td>
<p>
A callback that is called when intent callback threw <a href="/apis/latest/org/apache/nlpcraft/model/NCRejection.html"><code>NCRejection</code></a> exception.
This callback is called after <a href="/apis/latest/org/apache/nlpcraft/model/NCModel.html#onMatchedIntent(org.apache.nlpcraft.model.NCIntentMatch)"><code>onMatchedIntent(...)</code></a> is called.
Note that this callback may not be called at all, and if called - it's called only once. Typical
use case for this callback is to perform logging, debugging, statistic or usage collection,
explicit update or initialization of conversation context, security audit or validation, etc.
</p>
</td>
</tr>
<tr>
<td><a href="/apis/latest/org/apache/nlpcraft/model/NCModel.html#onError(org.apache.nlpcraft.model.NCContext,java.lang.Throwable)"><code>NCModel#<b>onError(...)</b></code></a></td>
<td>
<p>
A callback that is called when intent callback failed with unexpected exception. Note that this
callback may not be called at all, and if called - it's called only once. Typical use case for
this callback is to perform logging, debugging, statistic or usage collection, explicit update
or initialization of conversation context, security audit or validation, etc.
</p>
</td>
</tr>
</tbody>
</table>
</section>
<section id="examples">
<h2 class="section-title">Intent Examples</h2>
<p>
Here's number of intent examples with explanations. These intents can be defined directly in <code>@NCIntent</code>
annotation or in external JSON or YAML mode definition.
</p>
<p>
<b>Example 1:</b>
</p>
<pre class="brush: js">
intent=id1
term~{id == 'x:id'}
term(nums)~{id == 'nlpcraft:num' && lowercase(~nlpcraft:num:unittype) == 'datetime'}[0,2]
</pre>
<p><b>NOTES:</b></p>
<ul>
<li>
Intent has ID <code>id1</code>.
</li>
<li>
Intent uses default conversational support (<code>true</code>) and default order (<code>false</code>).
</li>
<li>
Intent has two converstaional terms that have to be found for the intent to match. Note that second term is optional as it
has <code>[0,2]</code> quantifier.
</li>
<li>
First term matches any single token with ID <code>x:id</code>.
</li>
<li>
Second term can appear zero, once or two times and it matches token with ID <code>nlpcraft:num</code> with
<code>nlpcraft:num:unittype</code> metadata property equal to <code>'datetime'</code> string.
</li>
<li>
Function <code>lowercase</code> used on <code>nlpcraft:num:unittype</code> metadata property value. Note
that built-in functions can only be used on left values.
</li>
<li>
Note that since second term has ID (<code>nums</code>) it can be references by <code>@NCIntentTerm</code>
annotation by the callback formal parameter.
</li>
<li>
Read more on built-in functions and token metadata properties in <a href="data-model.html#dsl">Token DSL</a>
section.
</li>
</ul>
<br/>
<p>
<b>Example 2:</b>
</p>
<pre class="brush: js">
intent=id2
flow='id1 id2'
term={id == 'mytok' && signum(~score['best']) != -1}
term={(groups @@ 'actors' || groups @@ 'owners') && size(partAlias.~text) > 10}
</pre>
<p><b>NOTES:</b></p>
<ul>
<li>
Intent has ID <code>id2</code>.
</li>
<li>
Intent has dialog flow pattern: <code>'id1 id2'</code>. It expect sequence of intents <code>id1</code> and
<code>id2</code> somewhere in the history of previously matched intents in the course of the current conversation.
</li>
<li>
Intent has two non-conversational terms. Both terms have to be present only once (their implicit quantifiers are <code>[1,1]</code>).
</li>
<li>
First term should be a token with ID <code>mytok</code> and have metadata property <code>score</code> of type
map. This map should have a value with the string key <code>'best'</code>. <code>signum</code> of this map value
should not equal <code>-1</code>. Note that built-in functions (i.e. <code>signum</code>) can only be
used on the left values.
</li>
<li>
Second term should be a token that belongs to either <code>actors</code> or <code>owners</code> group.
It should have a part token whose <a href="data-model.html#dsl">Token DSL</a> expression should have alias <code>partAlias</code>. That
part token should have metadata property <code>text</code> of type string or collection. The length of
this string or collection should be greater than <code>10</code>.
</li>
<li>
Read more on built-in functions and token metadata properties in <a href="data-model.html#dsl">Token DSL</a>
section.
</li>
</ul>
<div class="bq success">
<p>
<b>Token DSL</b>
</p>
<p>
Intent DSL is built on top of the <a href="data-model.html#dsl">Token DSL</a> that provides comprehensive support for
defining predicates over detected model elements.
Make sure to read about <a href="data-model.html#dsl">Token DSL</a> as well.
</p>
</div>
</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="#overview">Overview</a></li>
<li><a href="#matching">Intent Matching</a></li>
<li><a href="#syntax">Intent DSL</a></li>
<li><a href="#callback">Intent Callback</a></li>
<li><a href="#model_callbacks">Model Callbacks</a></li>
<li><a href="#examples">Intent Examples</a></li>
{% include quick-links.html %}
</ul>
</div>