xatkit-bot-platform / xatkit

The simplest way to build all types of smart chatbots and digital assistants
https://xatkit.com
Eclipse Public License 2.0
176 stars 23 forks source link

Question: Where can I find documentation? #13

Closed mohamed-benali closed 4 years ago

mohamed-benali commented 4 years ago

Where can I find documention about what can be done with Xatkit? Explanations for "follows", "context" and others functionalities. I have seen them in the examples, but I have not seen them in the documentation.

I ask because, now, I need to represent states. I think that this can be achieved by using "follows" and "context", but I am not sure how they work. So there is any documentation that I can read to understand what I can do with Xatkit?

gdaniel commented 4 years ago

All the documentation we have right now is in the wiki. Indeed there is a lack of documentation for the language, I'll add it when I have some time. Let me know if you want help on a specific use case (with an execution model example), I'll be happy to give a hand to achieve what you want to do. I'll post in this thread when I'll have news regarding the documentation.

jcabot commented 4 years ago

Hi Mohamed,

As Gwendal says , and while we improve the documentation, please feel free to get in touch for specific questions.

Follows defines a partial order so that an intent is only matched after another one has been matched before.

mohamed-benali commented 4 years ago

Thank you for your response. I have seen the following code in https://github.com/xatkit-bot-platform/xatkit-runtime/wiki/Multi-Platform-Example

intent SpecifyRepository follows OpenIssue {
    inputs {
        "Repository MyRepo"
        "(Repository:repoName=@any)"
    }
    creates context Repository {
        sets parameter repoName from fragment "MyRepo" (entity any)
    }
}

intent SpecifyOwner follows SpecifyRepository {
    inputs {
        "(Repository:repoOwner=@any)"
    }
}

I would like to know how the following fragments work

"(Repository:repoOwner=@any)"

and

inputs {
    "Repository MyRepo"
    "(Repository:repoName=@any)"
}
creates context Repository {
    sets parameter repoName from fragment "MyRepo" (entity any)
}
mohamed-benali commented 4 years ago

I also would like to know how I should implement conditionals split and join.

An example of what i mean could be a user that uses the chatbot to know what he/she has to do. -The Chatbot asks the price. -The user writes the price. Internally, the chatbot decides, based on the price, the branch that follows. -The chatbot explains the task based on the price. -Then the chatbot "joins" the branches. So no matter the branch chosen, the chatbot will end in the same intent.

Basically i need two things: -How can i define a Intent that 'follows' two Intents. -How can i change the Intent where the Chatbot is(with code).

I think that with 'Context' i can pass the price. But i dont know how i can change the intent/state. I also think that using 'follows' i can do the Split&Join. So if an Intent can has two follows, i can simulate the join branches. Something like:

intent Test5 follows Test1, Test2, Test3{
    inputs {
        "After that, what i do?"
    }
}
mohamed-benali commented 4 years ago

More important. The general idea of what i want to achieve is that I want the Chatbot to simulate a BPMN diagram. So the user can ask about the process. Something like: Captura The nodes, would represent the Intents.

As the documentation is not completed, i have to ask. It is possible, right now, to achieve it with Xatkit?

gdaniel commented 4 years ago

Hi,

First of all, the example you are referring to is outdated (the up-to-date information is now centralized in our main wiki). Specifically, the syntax "(Repository:repoOwner=@any)" is not supported anymore.

Context definition and manipulation

The fragment

intent MyIntent {
  inputs {
    "Repository MyRepo"
  }
  creates context Repository {
    sets parameter repoName from fragment "MyRepo" (entity any)
  }
}

Means that you define an intent with 1 training sentence ("Repository MyRepo"). This intent creates the context Repository, with 1 parameter repoName extracted from the fragment "MyRepo" from your training sentence. The entity specifies the type of information extracted from the fragment, in our case any, but it could be number, date, etc.

In practice this means that your intent will recognize the sentences looking like "Repository ", and put the extracted value () in the context parameter Repository.repoName.

Now, from your bot's execution rules you can access this context parameter, using the following syntax:

on intent MyIntent do
  if(context.get("Repository").get("repoName").equals( [...] ) ) {
    SlackPlatform.Reply("Branch 1")
  } else {
    SlackPlatform.Reply("Branch 2")
  }

Note that you can use context values in if condition to do the kind of branching you explain in your other message (in your example this context value would be the price specified by the user). Xatkit embeds xbase as its expression language, so you can benefit out of the box of branching, loops, etc

Follows & context requirement

Here is the full syntax of our running example:

intent MyIntent {
  inputs {
    "Repository MyRepo"
  }
  creates context Repository with lifespan 5 {
    sets parameter repoName from fragment "MyRepo" (entity any)
  }
}

Note the presence of with lifespan 5, that is not specified in the original example since it's the default value. This means that the context Repository (and all its parameters) is created and "alive" for 5 interactions. An interaction in Xatkit is a user input received, so, for the next 5 message received the expression context.get("Repository").get("repoName") will return the value matched by the intent (unless it has been overriden by another intent).

We can now create a new intent that will require this context to be matched:

intent MyIntent2 {
  requires context Repository
  inputs {
    "another input"
  }
}

Here the requires context Repository forces the underlying NLP engine to check if a context Repository is "alive" for the current session (i.e. if it has been created in the last 5 interactions by another intent). Note that you can require multiple contexts, in this case you need to duplicated the line requires context XXX for each one.

In our example we are now sure that MyIntent2 follows MyIntent, because it cannot be matched if the context Repository is not set. Since this syntax is a bit heavy we have defined some syntactic sugar for simple use cases:

intent MyIntent2 follows MyIntent {
  [...]
}

Here the semantic is almost the same as the example before, but some things are happening under the hood:

As a summary, the follows construct should be seen as a lightweight version of the creates context/requires context constructs. It is less powerful, cannot be fully configured, but do the job quite well for simple use cases.

Additional remarks

You can create as many context as you want in an intent (with and without parameters):

intent MyIntent {
  inputs {
    "Repository MyRepo"
  }
  creates context Repository {
    sets parameter repoName from fragment "MyRepo" (entity any)
  }
  creates context AnotherContext
}

And you can use them to enable specific conversation paths in your bot using the requires context construct. This should be the key point for your BPMN to bot translation. Yet, note that you cannot specify complex expressions in requires context construct (it is not possible to specify something like requires context XXX or YYY, it may be the case in a future release).

For the following example:

intent Test5 follows Test1, Test2, Test3{
    inputs {
        "After that, what i do?"
    }
}

I'd proceed like that:

intent Test1 {
  input { ... }
  creates context Questionable 
  creates context ...
}
intent Test2 {
  input { ... }
  creates context Questionable
  creates context ...
}
[...]
intent Test5 {
  requires context Questionable
  inputs {
    "After that, what I do?"
  }
}

This way you ensure that the question "After that, what I do?" can be asked after each intent. Of course this may need to be polished, but I hope you get the general idea.

Finally, context parameters are always overwritten: this means that if you set the same context parameter in multiple intents only the last one can be retrieved from the context.get(...) expression. E.g. if the intent MyIntent is matched two times, the first one from "Repository ABC", and the second one from "Repository XYZ", the expression context.get("Repository").get("repoName") will be evaluated as "XYZ".

I hope this gives you a better idea of what can be done with Xatkit and how to manage contexts. I'd add that querying a BPMN process using a Xatkit bot is certainly possible (we have worked internally on this some time ago), but requires some work to carefully define the intents and their order.

Let me know if you have any additional question/remark.

Gwendal

mohamed-benali commented 4 years ago

Thank you for your explanations.

I would like to know if there is a way to change the actual Intent using code. So at any moment, i can change the actual intent. So it is posible to change the intent where is the Chatbot without the user's response?

Also i would like to have automatic tests. So can i simulate the user's response via code? Something like:

ChatPlatform.USER.Reply("BLA BLA BLA")
gdaniel commented 4 years ago

An intent is not a state of the bot, it's an abstraction of what the user says. Think about it as an event more than a state. If you want to test the bot's reaction to a specific intent you'll have to forge one by yourself and send it manually to Xatkit internals:

[Create an EventInstance object]
xatkitCore.getExecutionService().handleEventInstance(eventInstance, session);

You can look at the example from this test case to see how to create an EventInstance object. Note that in this specific case you are also responsible of the XatkitSession content (and thus the associated context variable), so it's quite a lot of work to make it work.

Another solution is to write your tests at the IntentProvider level (which seems to be more aligned with what you want to do with automated tests). In this case you can take a look at this class (assuming you are using Slack). It contains some test cases that simulate incoming messages from Slack to check that the recognized intents are correct.

If you are using the ReactPlatform I don't have an out of the box example to show, a colleague tested his bot using Selenium and seems to be happy with the result.

jcabot commented 4 years ago

Thanks Gwendal for this very detailed responses. @mohamed-benali I think the best you could do at this point is to spend some time playing with Xatkit and creating some example bots to see if the above explanations are enough for you to keep going.

Then we can worry about more advanded stuff like the tests.

And I hope you don't forget to star and watch the repo and share Xatkit with your colleagues!!

mohamed-benali commented 4 years ago

Thanks you for your responses. I watched the repo :)

I would like to know if there is a way to erase/remove a context before the lifespan ends(for example, when entering into an intent).

I ask because I'm trying to do a query over the following BPMN to know who does a task. imagen

Test is done by Empleat Test2 is done by Departament

I created two intents that represents the tasks, Test and Test2. And then i created two intents more, that represents WHO does the tasks: SUBJECT_Empleat, SUBJECT_Departament.

My idea is that SUBJECT_Empleat can only be matched if the Intent Test has been matched. The same for SUBJECT_Departament and Test2

So when the user asks "Who does this task?", the correspondent intent will be matched.

I got it working, but it has an issue. The problem is that the two contexts are overlapped. Because in both cases, they are triggered by the same question: "Who does this task?". So after matching Test2, there are two contexts living(one for SUBJECT_Empleat and another for SUBJECT_Departament). And here comes the mistake, when the user asks "Who does this task?", the Chatbot says Empleat instead of Departament. And if the question is repeated, then the Chatbot replies correctly (Departament).

Here are two photos of what i explained above:

Captura de pantalla de 2020-01-06 01-49-25

Captura de pantalla de 2020-01-06 01-49-45

In summary, i would like to know if there is a way to erase/remove a context before the lifespan ends(for example, when entering into an intent).

I also would like to know if my approach is correct or if in Xatkit already exists a functionality that helps doing it.

As the code is small, I will put it here. It may clarify what i said before. .intent

intent Test {
    inputs {
        "testing"
    }
    creates context CONTEXT_Test with lifespan 4
    creates context SUBJECT_CONTEXT_Empleat with lifespan 4
}

intent Test2 {
    requires context CONTEXT_Test
    inputs {
        "testing"
    }
    creates context CONTEXT_Test2 with lifespan 4
    creates context SUBJECT_CONTEXT_Departament with lifespan 4
}

intent SUBJECT_Departament {
    requires context SUBJECT_CONTEXT_Departament
    inputs {
        "Who does this task?"
        "Who does it?"
        "Who performs this task"
    }
}

intent SUBJECT_Empleat {
    requires context SUBJECT_CONTEXT_Empleat
    inputs {
        "Who does this task?"
        "Who does it?"
        "Who performs this task"
    }
}

.execution

on intent Test do
    ChatPlatform.Reply("Intent: Test")

on intent Test2 do
    ChatPlatform.Reply("Intent: Test2")

on intent SUBJECT_Departament do
    ChatPlatform.Reply("Departament")

on intent SUBJECT_Empleat do
    ChatPlatform.Reply("Empleat")

on intent Default_Fallback_Intent do
    ChatPlatform.Reply("Sorry I didn't get it")
mohamed-benali commented 4 years ago

Also, I would like to know if i can create a context in execution.

mohamed-benali commented 4 years ago

I have another question: Can I, in creates context, set a parameter with a hardcoded value? So i have the posibility to not take it from the user input.

Something like:

creates context Repository {
    sets parameter repoName "my value"
  }
mohamed-benali commented 4 years ago

I have another question: There is a way to know which input has matched the actual Intent?

gdaniel commented 4 years ago

No you can't erase a context before the lifespan end, the only option you have is to change the lifespan value in the intent definition. This is by design: the context is immutable and contains intent-related information automatically extracted by the underlying NLP engine (and also from events, but it's not the topic here). More precisely: you cannot create, modify, or delete contexts and context parameters.

The issue in your example is that you define two intents for the same input: SUBJECT_Departament and SUBJECT_Empleat have the same training sentences, this will confuse the NLP engine.

A possible solution is to use the descriptive contexts CONTEXT_Test and CONTEXT_Test2 to filter the reply returned by the bot (see below). In this case these contexts have a lifespan of 2, meaning that they will be alive for the matched input and the next input:

Note that I also removed the contexts used to trigger the different versions of your previous SUBJECT intents, and created a new Askable context, that is set by both intents and can be used to trigger the WhoDoesIt intent. In this case the Askable context is re-created every time an intent is matched.

.intent

intent Test {
    inputs {
        "testing"
    }
        creates context Askable with lifespan 2
        context CONTEXT_Test with lifespan 2
}

intent Test2 {
    requires context CONTEXT_Test
    inputs {
        "testing"
    }
        creates context Askable with lifespan 2
    creates context CONTEXT_Test2 with lifespan 2
}

intent WhoDoesIt {
        requires context Askable
    inputs {
        "Who does this task?"
        "Who does it?"
        "Who performs this task"
    }
}

.execution

on intent Test do
    ChatPlatform.Reply("Intent: Test")

on intent Test2 do
    ChatPlatform.Reply("Intent: Test2")

on intent WhoDoesIt do
        if(context.containsKey("CONTEXT_Test2")) {
          ChatPlatform.Reply("Departament")
        else if(context.containsKey("CONTEXT_Test")) {
          ChatPlatform.Reply("Empleat")
        }

on intent Default_Fallback_Intent do
    ChatPlatform.Reply("Sorry I didn't get it")

While this solution should work, I have to admit it's not really handy. If you want to store information manually I'd use the session (that can contain arbitrary key/value pairs) instead of contexts:

intent Test {
    inputs {
        "testing"
    }
        creates context Askable with lifespan 2
    creates context CONTEXT_Test with lifespan 4
}

intent Test2 {
    requires context CONTEXT_Test
    inputs {
        "testing"
    }
        creates context Askable with lifespan 2
    creates context CONTEXT_Test2 with lifespan 4
}

intent WhoDoesIt {
    requires context Askable
    inputs {
        "Who does this task?"
        "Who does it?"
        "Who performs this task"
    }
}

.execution

on intent Test do
        session.put("whoDoesIt", "Empleat")
    ChatPlatform.Reply("Intent: Test")

on intent Test2 do
        session.put("whoDoesIt", "Departament")
    ChatPlatform.Reply("Intent: Test2")

on intent WhoDoesIt do
    ChatPlatform.Reply(session.get("whoDoesIt") as String)

on intent Default_Fallback_Intent do
    ChatPlatform.Reply("Sorry I didn't get it")

In this version you store a session value with the key "whoDoesIt", that is set/erased in the execution rules Test and Test2. The rule WhoDoesIt access the value and prints it in the returned message. Note that session values does not have a lifespan, you have to manage them manually.

Other Q&A

Also, I would like to know if i can create a context in execution. No you can't, see the beginning of my comment, if you need to create and store information use the session instead of the context

I have another question: Can I, in creates context, set a parameter with a hardcoded value? No, even if it looks like a possible solution for your issue I don't think it should be the case, again, the session can do it for you.

I have another question: There is a way to know which input has matched the actual Intent? Yes, you can access the raw input from the intent variable (and also the recognition confidence) see below:

on intent Test do
intent.matchedInput // "testing"
intent.recognitionConfidence // [0 .. 1]

Note that the matchedInput value is the raw input from the user, so if you use a ML-based NLP engine such as DialogFlow it may not be exactly a training sentence. The recognitionConfidence is a percentage (always 100% for the RegExp NLP engine, different for DialogFlow), and can be used to trigger specific responses from the bot when the intent confidence is low.

mohamed-benali commented 4 years ago

Thank you for your response. You are helping me a lot.

mohamed-benali commented 4 years ago

Can i delete/remove a context if i create a context(with the same name) with lifespan 0?

gdaniel commented 4 years ago

To be fair I don't know, but for sure it's not how it is supposed to work, why do you want to remove a context? You can set the lifespan to 1 (or 2, I don't remember right now if the current interaction is counted in the lifespan or not) to make it visible only for the next intent.

I am interested to know more about your use case, deleting context may make sense in specific scenario.

mohamed-benali commented 4 years ago

My objective is to generate automatically a Chatbot using any BPMN. So being able to delete contexts gives me more control.

It could be used in order to do a "reset". For example, if i have a BPMN with multiple subproceses, and in a determined moment the chatbot is in the middle of one. If the user asks for another, i want to be able to "jump" at the begining of the subproces. So i may want to do a "reset" to be sure that there will not be conflicting contexts.

mohamed-benali commented 4 years ago

I have a question. There is way to prevent lifespan decreasing when no intent is matched(when default fallback intent is activated).

I ask because i'm having problems with the lifespan of my contexts. I set them to 2, but if the user says, twice, something that does not match any intent, then the context ends. I would like to prevent that, so if there is no input matched then the lifespan does not decrease.

I would also like to know if i can define a fallback intent for a specific intent. So i can have different fallback intents for different intents. Something like a "follow-up" fallback intent. Intent1 --> Fallback1 Intent2 --> Fallback2

mohamed-benali commented 4 years ago

I also tried creating a context with lifespan 0(to remove it). I tried it in Dialog Flow console and worked for me. It deletes the context properly. But when i try it in Xatkit, doesn't work, it feels like the context is still alive.

I would like to know why in Xatkit doesn't work. I also would like to know how i can make it work so it has the same behavior as Dialog Flow console(there is any option on Xatkit configuration i can change?).

im basically implementing this:

So what i have is this branch and merge structure. When it ends(after End is matched), i say "begin", so Begin is matched. Here comes the difference. In Xatkit the contexts are not erased after creating a context with lifespan 0. However, in Dialog Flow they are.

So i would like to know how i can have the same behavior as Dialog Flow.

I will put here the relevant code of my intent file. It may clarify what i mean. (the execution file is just a fixed response, so i will not put it here)

intent Begin {
    inputs {
        "begin"
    }
    creates context CONTEXT_Begin with lifespan 5

    creates context CONTEXT_Price with lifespan 0
    creates context CONTEXT_Price_Question with lifespan 0
    creates context CONTEXT_Test2 with lifespan 0
}

intent Price {
    requires context CONTEXT_Begin
    inputs {
        "next"
    }
    creates context CONTEXT_Price with lifespan 5
}

intent Price_YES {
    requires context CONTEXT_Price
    inputs {
            "yes"
        }
    creates context CONTEXT_Price_Question with lifespan 5
}

intent Price_NO {
    requires context CONTEXT_Price
    inputs {
        "no"
    }
    creates context CONTEXT_Price_Question with lifespan 5
}

intent End {
    requires context CONTEXT_Price
    requires context CONTEXT_Price_Question
    inputs {
        "next"
    }
    creates context CONTEXT_Test2 with lifespan 5
}
mohamed-benali commented 4 years ago

I will also leave a capture: image

What surprises me is that when Xatkit is started. If i go to DialogFlow console i see the following: image

So the intents are well uploaded. And if i execute them(on DialogFlow console) it works properly. But dont work properly on Xatkit.

So i would like to know how if there is any configuration on Xatkit to solve this issue.

mohamed-benali commented 4 years ago

I also would like to ask if there is a way to do faster the intent registration on Dialog Flow. So it takes less time.