BaseChatbot


level_3


What is it

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.

How to use it

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.

Chatbot interface

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.

Implementing replyTo

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.

ReplyReaction

As an answer to a phrase, a Chatbot will create a ReplyReaction. A reply reaction contains at least:

  • The ReplyPriority, used by the chat to determine which ReplyReaction is chosen for a given user sentence.
  • The 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:

ChatbotReaction

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.

Managing availability incidents or planned non listening states with isAvailableToReply

level_6

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.

Optional methods to implement

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.

Getting started

Creating a simple greeting chatbot

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);
            }
        }
    }
}