Hi all!
I am struggling to get the conversation going again after pausing it.
Does anyone have a solution for me or does anyone see what I am doing wrong?
The Objective:
What I want to achieve is the following. I have created a bot for Facebook messenger. Here I want to give the user the option to chat with a live agent. Then the agent can indicate that the conversation is finished, then the conversation goes back to the bot.
It comes down to the following. The user talks to the bot and indicates they want to speak to a live agent. The bot hands over control of the conversation to the Facebook Page Inbox, from here a live agent can take over the conversation. When the live agent is finished the live agent hands the conversation back to the bot. The bot is the primary owner of the conversation the live agent is the secondary owner. This approach works well. Sources: Handover Protocol - Messenger Platform - Documentation - Facebook for Developers
For example:
User:
I want to speak to a live agent.
Bot:
Okay I’ll connect you through. You can now write down message for the live agent.
User:
What are your opening hours?
Live agent:
We are open from 9:00 to 17:00.
User:
Okay, thank you.
Live agent:
I’m handing the conversation back to the bot.
The Problem:
When the above conversation takes place, the following happens in practice. From the moment the bot has handed over the conversation property to the Facebook Page Inbox (live agent), the bot no longer receives messages. This is controlled on the side of Facebook. Facebook temporarily stops passing on messages from that moment on. From the moment the conversation is given back to the bot, the bot receives all the messages sent during this conversation and the bot tends to answer the messages from itself.
For example:
User:
I want to speak to a live agent.
Bot:
Okay I’ll connect you through. You can now write down message for the live agent.
User:
What are your opening hours?
Live agent:
We are open from 9:00 to 17:00.
User:
Okay, thank you.
Live agent:
I’m handing the conversation back to the bot.
[Problem starts]
Bot:
Our opening hours are from 9:00 am to 5:00 pm.
[Bot response to user message: What are your opening hours?]
Bot:
You’re welcome. Is there anything else I can help you with?
[Bot response to user message: Okay, thank you.]
Problem Analysis:
What happens here is that the bot receives the user’s messages and replies to them as new messages. So user messages are answered twice, first by the live agent and later by the bot. Ideally, the bot ignores these user messages.
Proposed Solution:
The approach that seems most logical to me is inspired by this post: Resume Conversation after Pause - #2 by Arjaan
What I understand from reading through and searching the forum is that the solution is to pause the conversation and then resume it once the bot is the primary owner of the conversation. What I understand is that the bot ignores the users messages during the time the conversation is paused and only responds again when the conversation is resumed. If my understanding is correct, this is the primary function of ConversationPaused() and ConversationResumed().
So in theory all good. What’s the problem, why don’t you implement this?
Well this is where I need some help, I just can’t get it done. I’ve been stuck on this for two weeks now.
I need to implement this on a server where I run my bot on Kubernetes installed with a Helm Chart. But I can’t even get it to work locally.
What did I try?
1. Resume the conversation with a FollowupAction
actions.py
class PauseConversation(Action):
def name(self) -> Text:
return "action_pause_conversation"
async def run(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict,
) -> List:
text = "pause conversation..."
dispatcher.utter_message(text=text)
return [
ConversationPaused(),
FollowupAction(name="action_resume_conversation")
]
class ResumeConversation(Action):
def name(self) -> Text:
return "action_resume_conversation"
async def run(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict,
) -> List:
text = "resume conversation..."
dispatcher.utter_message(text=text)
return [ConversationResumed()]
rules.yaml
- rule: Pause and resume with FollowupAction
steps:
- intent: pause_conversation
- action: action_pause_conversation
Result:
Conversation is paused, but the FollowupAction is not executed. Which I don’t understand, because the return value of “action_pause_conversation” = [{'event': 'pause', 'timestamp': None}, {'event': 'followup', 'time
stamp': None, 'name': 'action_resume_conversation'}]
Can someone explain to me why the follow up action is not executed?
2. Pause and Resume the conversation from the same CustomAction.
actions.py
class PauseConversation(Action):
def name(self) -> Text:
return "action_pause_conversation"
async def run(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict,
) -> List:
text = "pause conversation..."
dispatcher.utter_message(text=text)
return [
ConversationPaused(),
FollowupAction(name="action_resume_conversation")
ConversationResumed()
]
class ResumeConversation(Action):
def name(self) -> Text:
return "action_resume_conversation"
async def run(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict,
) -> List:
text = "resume conversation..."
dispatcher.utter_message(text=text)
return []
rules.yaml
- rule: Pause and resume with FollowupAction
steps:
- intent: pause_conversation
- action: action_pause_conversation
Result:
At first, this works as expected. But when I take a closer look, I see something unexpected. If I change the method and add this check to “action_resume_conversation”:
paused = tracker.is_paused()
dispatcher.utter_message(text=f"Is tracker paused? {paused}").
actions.pyclass PauseConversation(Action):
def name(self) -> Text:
return "action_pause_conversation"
async def run(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict,
) -> List:
text = "pause conversation..."
dispatcher.utter_message(text=text)
return [
ConversationPaused(),
FollowupAction(name="action_resume_conversation")
ConversationResumed()
]
class ResumeConversation(Action):
def name(self) -> Text:
return "action_resume_conversation"
async def run(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict,
) -> List:
paused = tracker.is_paused()
dispatcher.utter_message(text=f"Is tracker paused? {paused}").
text = "resume conversation..."
dispatcher.utter_message(text=text)
return []
The bot response:
pause conversation…
Is tracker paused? False
resume conversation…
I expect the bot here would say:
pause conversation…
Is tracker paused? True
resume conversation…
Can someone explain to me why the tracker is not paused?
3. Pause and Resume the conversation from rules.yaml.
actions.py
class PauseConversation(Action):
def name(self) -> Text:
return "action_pause_conversation"
async def run(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict,
) -> List:
text = "pause conversation..."
dispatcher.utter_message(text=text)
return [ConversationPaused()]
class ResumeConversation(Action):
def name(self) -> Text:
return "action_resume_conversation"
async def run(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict,
) -> List:
text = "resume conversation..."
dispatcher.utter_message(text=text)
return [ConversationResumed()]
rules.yaml
- rule: Pause and resume with FollowupAction
steps:
- intent: pause_conversation
- action: action_pause_conversation
- action: action_resume_conversation
Result:
The conversation is paused, but “action_resume_conversation” is not executed. Which I don’t understand, because it is implemented as a rule.
Can someone explain to me why “action_resume_conversation” is not executed even though it is a rule?
4. Resume the conversation with POST request.
Finally, when the conversation is paused I try to resume via a post request, but again I make little progress. This is my code which I run locally:
def resume_conversation(sender_id):
url = f"http://localhost:5005/conversations/{sender_id}/tracker/events"
headers = {"Content-Type": "application/json"}
data = '[{"event": "resume"}, {"event": "followup", "name": "action_resume_conversation"}]'
return requests.post(url=url, data=data, headers=headers)
Can someone explain to me what I do wrong here?