A BaseChatbot
is an abstract class that is the parent class of all custom Chatbot
implementations.
This class must be inherited to implement your own Chatbot on the tablet.
See also API doc: BaseChatbot.
Create a class that inherits BaseChatbot
and implement the required methods, especially replyTo
.
An instance of this custom class can then be provided to the method that creates a Chat action, along with any other chatbot.
Warning
A custom Chatbot
requires an available remote speech recognition.
In order to handle properly any error or slowness, it is recommended to create a standard QiChatbot, able to provide a minimal vocal interaction to the robot and to catch the not understood cases.
For further details, see: Managing not understood cases.
It is also possible to implement a simple fallback behavior thanks to a ReplyReaction with ReplyPriority.FALLBACK.
For further details see: StandardReplyReaction.
See also Managing availability incidents or planned non listening states with isAvailableToReply in order to avoid incorrect conversation feedbacks.
To be used within a Chat
, a Chatbot
should at least implement:
replyTo(phrase, locale)
: Produces a ReplyReaction based on a given input phrase.acknowledgeHeard(phrase, locale)
: Informs the Chatbot
with the last phrase heard by the
robot. It will be called anytime a reply not given by this Chatbot
has been chosen.acknowledgeSaid(phrase, locale)
: Informs the Chatbot
with the last phrase said by the robot.
It will be called anytime a ChatbotReaction not given by this Chatbot
has been chosen.Extend BaseChatbot to implement your own Chatbot
, according to this interface.
See also API doc: Chatbot and BaseChatbot.
This method is called by the chat when the robot hears something recognized as a Phrase
. The returned value is a possible
reply that the chat may select as its answer, among other chatbots replies (if any).
The replyTo
implementation must return a StandardReplyReaction
which is the standard implementation of ReplyReaction
.
The ReplyReaction
that is returned may obviously be run as an uttered sentence, but it may also be any other action or
combination of actions, such as an animation synchronized with an uttered answer for example.
The different effective behaviors of the replies, according to the input phrase, may be provided by several implementations of
BaseChatbotReaction
that can be passed as a parameter of the returned StandardReplyReaction
.
If the chatbot has no answer for the input phrase, the implementation of
replyTo
must return null
.
val heard: String = phrase.text
return when {
heard == "hello" -> StandardReplyReaction(
MyChatbotReaction(qiContext, "Hi"),
ReplyPriority.NORMAL)
heard.contains("dance") -> StandardReplyReaction(
MyAnimatedChatbotReaction(qiContext),
ReplyPriority.NORMAL)
else -> StandardReplyReaction(
MyDefaultChatbotReaction(qiContext, heard),
ReplyPriority.FALLBACK)
}
String heard = phrase.getText();
if (heard.equals("hello")) {
return StandardReplyReaction(
new MyChatbotReaction(getQiContext(), "Hi"),
ReplyPriority.NORMAL);
} else if (heard.contains("dance")) {
return new StandardReplyReaction(
new MyAnimatedChatbotReaction(getQiContext()),
ReplyPriority.NORMAL);
} else {
return new StandardReplyReaction(
new MyDefaultChatbotReaction(getQiContext(), heard),
ReplyPriority.FALLBACK);
}
For further details, see: StandardReplyReaction.
As an answer to a phrase, a Chatbot
will create a ReplyReaction
.
A reply reaction contains at least:
ReplyPriority
, used by the chat to determine which ReplyReaction
is chosen for a given user sentence.ChatbotReaction
, executed if the chat chooses it to answer a given user
sentence.ReplyReaction
implementations to be used StandardReplyReaction
and StandardAutonomousReaction.
See also API doc:
A ChatbotReaction
should at least have the following interface:
runWith(SpeechEngine)
: runs the ChatbotReaction
. The ChatbotReaction
will most likely make the robot speak but can also make him move, display things
on the tablet and so on.stop()
: stops the ChatbotReaction
. The stop method
will be automatically called when Chat
action stopped. Developer should ensure
every action, robotic or not, will be stopped.Extend BaseChatbotReaction to implement your own ChatbotReaction
.
See also API doc: ChatbotReaction and BaseChatbotReaction.
By default isAvailableToReply
is set to true, meaning your chatbot is ready
to process any input.
If any kind of of technical incidents, such as a network shortage or a usage quota exceeded,
or if your chatbot cannot / should not answer to the user at the moment,
then you should set isAvailableToReply
to false. This way, the Chat will be able to manage
correctly the conversation feedbacks: if no chatbot is available, then the robot
will switch to a non-listening state.
For further details, see: Managing Conversation Feedbacks.
The acknowledgeHeard
and acknowledgeSaid
methods may be implemented if
necessary. Their default implementation is to do nothing.
These methods are called by the chat to inform your custom chatbot that another
chatbot’s reply has been chosen.
This is an opportunity for a chatbot to keep the context of the discussion, even if its own replies are not selected by the chat engine.
override fun acknowledgeHeard(phrase: Phrase, locale: Locale) {
Log.i(TAG, "Last phrase heard by the robot and whose selected answer is not mine: ${phrase.text}")
}
override fun acknowledgeSaid(phrase: Phrase, locale: Locale) {
Log.i(TAG, "Another chatbot answered: ${phrase.text}")
}
@Override
public void acknowledgeHeard(Phrase phrase, Locale locale) {
Log.i(TAG, "Last phrase heard by the robot and whose selected answer is not mine: " + phrase.getText());
}
@Override
public void acknowledgeSaid(Phrase phrase, Locale locale) {
Log.i(TAG, "Another chatbot answered: " + phrase.getText());
}
If the chat contains only one chatbot, these methods will never be called.
private const val TAG: String = "GreetingChatbot"
class GreetingChatbot(context: QiContext) : BaseChatbot {
private val greetings = mutableListOf<String>( "hello", "hi", "good morning", "good afternoon", "good evening" )
override fun replyTo(phrase: Phrase, locale: Locale): StandardReplyReaction {
return if (greetings.contains(phrase.text)) {
StandardReplyReaction(
MyChatbotReaction(qiContext, "Hello you"),
ReplyPriority.NORMAL)
} else {
StandardReplyReaction(
MyChatbotReaction(qiContext, "I can just greet you"),
ReplyPriority.FALLBACK)
}
}
override fun acknowledgeHeard(phrase: Phrase, locale: Locale) {
Log.i(TAG, "Last phrase heard by the robot and whose chosen answer is not mine: ${phrase.text}")
}
override fun acknowledgeSaid(phrase: Phrase, Locale locale) {
Log.i(TAG, "Another chatbot answered: ${phrase.text})
}
class MyChatbotReaction(context: QiContext, answer: String) : BaseChatbotReaction {
private val answer: String = "answer"
private var fsay: Future<Void>? = null
override fun runWith(speechEngine: SpeechEngine) {
val say: Say = SayBuilder.with(speechEngine)
.withText(answer)
.build()
fSay = say.async().run()
try {
fSay.get() // Do not leave the method before the actions are done
} catch (e: ExecutionException) {
Log.e(TAG, "Error during Say", e);
} catch (e: CancellationException) {
Log.i(TAG, "Interruption during Say");
}
}
override fun stop() {
fSay?.cancel(true)
}
}
}
public class GreetingChatbot extends BaseChatbot {
private static final String TAG = "GreetingChatbot";
static private List<String> greetings = Arrays.asList( "hello", "hi", "good morning", "good afternoon", "good evening" );
public GreetingChatbot(QiContext context) {
super(context);
}
@Override
public StandardReplyReaction replyTo(@NonNull Phrase phrase, Locale locale) {
if (greetings.contains(phrase.getText())) {
return new StandardReplyReaction(
new MyChatbotReaction(getQiContext(), "Hello you"),
ReplyPriority.NORMAL);
} else {
return new StandardReplyReaction(
new MyChatbotReaction(getQiContext(), "I can just greet you"),
ReplyPriority.FALLBACK);
}
}
@Override
public void acknowledgeHeard(Phrase phrase, Locale locale) {
Log.i(TAG, "Last phrase heard by the robot and whose chosen answer is not mine: " + phrase.getText());
}
@Override
public void acknowledgeSaid(Phrase phrase, Locale locale) {
Log.i(TAG, "Another chatbot answered: " + phrase.getText());
}
class MyChatbotReaction extends BaseChatbotReaction {
private String answer;
private Future<Void> fSay;
MyChatbotReaction(final QiContext context, String answer) {
super(context);
this.answer = answer;
}
@Override
public void runWith(SpeechEngine speechEngine) {
Say say = SayBuilder.with(speechEngine)
.withText(answer)
.build();
fSay = say.async().run();
try {
fSay.get(); // Do not leave the method before the actions are done
} catch (ExecutionException e) {
Log.e(TAG, "Error during Say", e);
} catch (CancellationException e) {
Log.i(TAG, "Interruption during Say");
}
}
@Override
public void stop() {
if (fSay != null) {
fSay.cancel(true);
}
}
}
}