Skip to content
This repository was archived by the owner on Jun 24, 2020. It is now read-only.

Commit 12753b7

Browse files
author
pheel
authored
Merge pull request #46 from botfront/disambiguation2
Disambiguation
2 parents 183678b + fa57054 commit 12753b7

File tree

11 files changed

+586
-108
lines changed

11 files changed

+586
-108
lines changed

README.md

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,105 @@
33
![PyPI](https://img.shields.io/pypi/v/rasa-addons.svg)
44
![Travis](https://img.shields.io/travis/mrbot-ai/rasa-addons.svg)
55

6-
A set of 🚀🚀🚀 components to be used with Botfront and/or Rasa
6+
A set of 🚀🚀🚀 components to be used with Botfront and/or Rasa.
77

88
[Botfront](https://github.com/botfront/botfront) is an open source chatbot platform built with Rasa.
99

10+
## Disambiguation Policy
1011

12+
This policy implements fallback and suggestion-based disambiguation.
13+
14+
It works with actions ``rasa_addons.core.actions.ActionBotfrontDisambiguation``, ``rasa_addons.core.actions.ActionBotfrontDisambiguationFollowup`` and ``rasa_addons.core.actions.ActionBotfrontDisambiguationFollowup``.
15+
16+
### Example usage
17+
18+
```
19+
policies:
20+
...
21+
- name: rasa_addons.core.policies.DisambiguationPolicy
22+
fallback_trigger: 0.30
23+
disambiguation_trigger: '$0 < 2 * $1'
24+
deny_suggestions: 'deny_suggestions'
25+
n_suggestions: 3
26+
excluded_intents:
27+
- ^chitchat\..*
28+
disambiguation_title:
29+
en: "Sorry, I'm not sure I understood. Did you mean..."
30+
fr: "J'ai mal compris. Voulez-vous dire..."
31+
intent_mappings:
32+
basics.yes:
33+
en: "Yes"
34+
fr: "Oui"
35+
basics.no:
36+
en: "No"
37+
fr: "Non"
38+
I_need_help:
39+
en: "Help me"
40+
fr: "Aidez-moi"
41+
I_come_from:
42+
en: "I come from {from}"
43+
fr: "Je viens de {from}"
44+
want_shirt:
45+
en: "I want a {color} shirt"
46+
fr: "Je veux un chandail {color}"
47+
deny_suggestions:
48+
en: "Something else"
49+
fr: "Autre chose"
50+
...
51+
```
52+
53+
### Parameters
54+
55+
##### fallback_trigger
56+
57+
Float (default ``0.30``): if confidence of top-ranking intent is below this threshold, fallback is triggered. Fallback is an action that utters the template ``utter_fallback`` and returns to the previous conversation state.
58+
59+
##### disambiguation_trigger
60+
61+
String (default ``'$0 < 2 * $1'``): if this expression holds, disambiguation is triggered. (If it has already been triggered on the previous turn, fallback is triggered instead.) Here this expression resolves to "the score of the top-ranking intent is below twice the score of the second-ranking intent". Disambiguation is an action that lets the user to choose from the top-ranking intents using a button prompt.
62+
63+
In addition, an 'Other' option is shown with payload defined in ``deny_suggestions`` param is shown. It is up to the conversation designer to implement a story to handle the continuation of this interaction.
64+
65+
##### deny_suggestions
66+
67+
String: the intent associated in the payload for the 'Other' option.
68+
69+
##### n_suggestions
70+
71+
Int (default 3): the maximum number of suggestions to display (excluding the 'Other' option).
72+
73+
##### excluded_intents
74+
75+
List (regex string): any intent (exactly) matching one of these regular expressions will not be shown as a suggestion.
76+
77+
##### disambiguation_title
78+
79+
Dict (language string -> string): localized disambiguation message title.
80+
81+
##### intent_mappings
82+
83+
Dict (intent string -> language string -> string): localized representative button title for intents. If no title is defined for a given intent, the intent name is rendered instead. These titles support entity substitution: any entity name enclosed in curly brackets (``{entity}``) will be filled with entity information from the user utterance.
84+
85+
_Important:_ The title for the 'Other' option is also defined here.
86+
87+
## Mapping Policy
88+
89+
This policy implements regular expression-based direct mapping from intent to action.
90+
91+
### Example usage
92+
93+
```
94+
policies:
95+
...
96+
- name: rasa_addons.core.policies.BotfrontMappingPolicy
97+
triggers:
98+
- trigger: '^map\..+'
99+
action: 'action_botfront_mapping'
100+
extra_actions:
101+
- 'action_myaction'
102+
...
103+
```
104+
105+
### ActionBotfrontMapping
106+
107+
The default action ActionBotfrontMapping takes the intent that triggered the mapping policy, e.g. ``map.my_intent`` and tries to generate the template ``utter_map.my_intent``.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from rasa_addons.core.actions.action_botfront_mapping import ActionBotfrontMapping
2+
from rasa_addons.core.actions.action_botfront_disambiguation import ActionBotfrontDisambiguation, ActionBotfrontDisambiguationFollowup, ActionBotfrontFallback
3+
4+
from typing import List, Text, Optional, Dict, Any
5+
6+
def actions() -> List["Action"]:
7+
return [
8+
ActionBotfrontMapping(),
9+
ActionBotfrontDisambiguation(),
10+
ActionBotfrontDisambiguationFollowup(),
11+
ActionBotfrontFallback()
12+
]
13+
14+
actions_bf = {a.name(): a for a in actions()}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from rasa.core.events import (
2+
UserUtteranceReverted,
3+
UserUttered,
4+
ActionExecuted,
5+
Event,
6+
SlotSet,
7+
)
8+
from rasa.core.actions.action import Action, ActionUtterTemplate, create_bot_utterance
9+
10+
from typing import Any, List, Text, Dict, Optional
11+
import logging
12+
import copy
13+
14+
from requests.auth import HTTPBasicAuth
15+
16+
logging.basicConfig(level="WARN")
17+
logger = logging.getLogger()
18+
19+
class ActionBotfrontDisambiguation(Action):
20+
def name(self):
21+
return "action_botfront_disambiguation"
22+
23+
async def run(
24+
self,
25+
output_channel: "OutputChannel",
26+
nlg: "NaturalLanguageGenerator",
27+
tracker: "DialogueStateTracker",
28+
domain: "Domain",
29+
) -> List[Event]:
30+
message = None
31+
for event in list(tracker.events)[::-1]:
32+
logger.debug(event)
33+
if isinstance(event, SlotSet) and event.key == "disambiguation_message":
34+
message = event.value
35+
break
36+
if message:
37+
return [create_bot_utterance({ "text": message["title"], "buttons": message["buttons"] })]
38+
else:
39+
return []
40+
41+
class ActionBotfrontDisambiguationFollowup(Action):
42+
def name(self) -> Text:
43+
return "action_botfront_disambiguation_followup"
44+
45+
async def run(
46+
self,
47+
output_channel: "OutputChannel",
48+
nlg: "NaturalLanguageGenerator",
49+
tracker: "DialogueStateTracker",
50+
domain: "Domain",
51+
) -> List[Event]:
52+
53+
revert_events = [
54+
UserUtteranceReverted(),
55+
UserUtteranceReverted(),
56+
ActionExecuted(action_name="action_listen"),
57+
]
58+
59+
last_user_event = None
60+
for event in list(tracker.events)[::-1]:
61+
if isinstance(event, UserUttered):
62+
last_user_event = copy.deepcopy(event)
63+
last_user_event.parse_data["intent"]["confidence"] = 1.0
64+
break
65+
if last_user_event:
66+
revert_events += [last_user_event]
67+
68+
return revert_events
69+
70+
class ActionBotfrontFallback(ActionUtterTemplate):
71+
def name(self) -> Text:
72+
return "action_botfront_fallback"
73+
74+
def __init__(self):
75+
super(ActionBotfrontFallback, self).__init__("utter_fallback", silent_fail=True)
76+
77+
async def run(self, output_channel, nlg, tracker, domain):
78+
79+
evts = await super(ActionBotfrontFallback, self).run(
80+
output_channel, nlg, tracker, domain
81+
)
82+
if (len(tracker.events) >= 4 and
83+
isinstance(tracker.events[-4], ActionExecuted) and
84+
tracker.events[-4].action_name ==
85+
"action_botfront_disambiguation"):
86+
return evts + [UserUtteranceReverted(), UserUtteranceReverted()]
87+
else:
88+
return evts + [UserUtteranceReverted()]
89+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from rasa.core.events import Event
2+
from rasa.core.actions.action import Action, ActionUtterTemplate, create_bot_utterance
3+
4+
from typing import Any, List, Text, Dict, Optional
5+
import logging
6+
import copy
7+
8+
from requests.auth import HTTPBasicAuth
9+
10+
logging.basicConfig(level="WARN")
11+
logger = logging.getLogger()
12+
13+
class ActionBotfrontMapping(Action):
14+
def name(self):
15+
return 'action_botfront_mapping'
16+
17+
async def run(
18+
self,
19+
output_channel: "OutputChannel",
20+
nlg: "NaturalLanguageGenerator",
21+
tracker: "DialogueStateTracker",
22+
domain: "Domain",
23+
) -> List[Event]:
24+
"""Append 'utter_' to intent name and generates from that template"""
25+
26+
events = []
27+
response_name = 'utter_' + tracker.latest_message.intent["name"]
28+
events += [create_bot_utterance(m) for m in await nlg.generate(response_name, tracker, output_channel.name(),
29+
language=output_channel.language)]
30+
31+
return events
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from rasa_addons.core.policies.disambiguation import BotfrontDisambiguationPolicy
2+
from rasa_addons.core.policies.mapping import BotfrontMappingPolicy

0 commit comments

Comments
 (0)