Skip to content

Commit

Permalink
merged upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
tmbo committed Jul 25, 2023
2 parents b55f99a + 76ce844 commit 933dad3
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 25 deletions.
25 changes: 10 additions & 15 deletions rasa/cdu/command_generator/command_prompt_template.jinja2
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Your task is to analyze the current conversation context and start new business processes that we call flows and to extract slots to advance active flows.
Your task is to analyze the current conversation context and generate a list of actions to start new business processes that we call flows, to extract slots, or respond to small talk and knowledge requests.

These are the flows that can be started, with their description and slots:
{% for flow in available_flows %}
flow: {{ flow.name }}: {{ flow.description }}
{{ flow.name }}: {{ flow.description }}
{% for slot in flow.slots -%}
slot: {{ slot.name }}{% if slot.description %} ({{ slot.description }}){% endif %}
{% endfor %}
Expand Down Expand Up @@ -35,23 +35,18 @@ The user just said """{{ user_message }}""".
Based on this information generate a list of actions you want to take. Your job is to start flows and to fill slots where appropriate. Any logic of what happens afterwards is handled by the flow engine. These are your available actions:
* Slot setting, described by "SetSlot(slot_name, slot_value)". An example would be "SetSlot(recipient, Freddy)"
* Starting another flow, described by "StartFlow(flow_name)". An example would be "StartFlow(transfer_money)"
* Starting another flow, and already filling a slot for that flow. An example would be "StartFlow(transfer_money), SetSlot(recipient, Freddy)"
* Setting a slot for the current flow, before starting a new flow and setting a slot for it. An example would be "SetSlot(confirmation, True), StartFlow(transfer_money), SetSlot(recipient, Joe)"
* Cancelling the current flow, described by "CancelFlow()"
* Asking the user to choose from two or more possible flows. An example would be ClarifyNextStep(transfer_money, list_transactions, check_balance)
* Taking no action because the user's message has nothing to do with the available flows. An example would be AllowInterruption(chitchat) if the user is making small talk, or AllowInterruption(information) if the user has a question unrelated to the available flows.
* Handing off to a human, in case the user seems frustrated or explicitly asks to speak to one. An example would be HumanHandoff()
* Taking no action because the user's message does not invite a further response. For example, if the user asks for more time, or simply makes a comment.
* Disambiguating which flow should be started when there are multiple likely candidates. An example would be Disambiguate(transfer_money, list_transactions, check_balance)
* Responding to a non-task-oriented user message, described by "ChitChat()".
* Responding to a user message that requires additional knowledge from a knowledge database, described by "KnowledgeAnswer()"
* Handing off to a human, in case the user seems frustrated or explicitly asks to speak to one, described by "HumanHandoff()".

Write out the actions you want to take for the last user message, one per line, in the order they should take place.
===
Write out the actions you want to take, one per line, in the order they should take place.
Do not fill slots with abstract values or placeholders.
Only use information provided by the user.
Only start a flow if it's completely clear what the user wants. Imagine you were a person reading this message. If it's not 100% clear, clarify the next step.
Don't be overly confident. Take a conservative approach and clarify before proceeding.
If the user asks for two things which seem contradictory, clarify before starting a flow.
If a user does multiple things in one message, you can output multiple actions. An example would be if the user fills a slot but also makes some smalltalk: "SetSlot(cuisine, chinese), AllowInterruption(chitchat)"
Acknowledge everything a user does in a message.
Strictly adhere to the provided action types listed above.
Focus on the last message and take it one step at a time.
Use the previous conversation steps only to aid understanding.
The action list:

Your action list:
32 changes: 22 additions & 10 deletions rasa/cdu/command_generator/llm_command_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
SetSlotCommand,
CancelFlowCommand,
StartFlowCommand,
HumanHandoffCommand,
ListenCommand,
CorrectSlotCommand,
)

from rasa.core.policies.flow_policy import FlowStack
Expand Down Expand Up @@ -146,17 +149,19 @@ def predict_commands(
)
flow_prompt = self.render_template(message, tracker, flows_without_patterns)
structlogger.info(
"llm_command_generator.process.prompt_rendered", prompt=flow_prompt
"llm_command_generator.predict_commands.prompt_rendered", prompt=flow_prompt
)
action_list = self._generate_action_list_using_llm(flow_prompt)
structlogger.info(
"llm_command_generator.process.actions_generated", action_list=action_list
"llm_command_generator.predict_commands.actions_generated",
action_list=action_list,
)
commands = self.parse_commands(action_list, tracker, flows_without_patterns)
commands = self.parse_commands(action_list)
structlogger.info(
"llm_command_generator.process.finished",
"llm_command_generator.predict_commands.finished",
commands=commands,
)

return commands

@staticmethod
Expand All @@ -169,9 +174,7 @@ def is_hallucinated_value(value: str) -> bool:
}

@classmethod
def parse_commands(
cls, actions: Optional[str], tracker: DialogueStateTracker, flows: FlowsList
) -> List[Command]:
def parse_commands(cls, actions: Optional[str]) -> List[Command]:
"""Parse the actions returned by the llm into intent and entities."""
if not actions:
return [ErrorCommand()]
Expand All @@ -182,12 +185,17 @@ def parse_commands(
r"""SetSlot\(([a-zA-Z_][a-zA-Z0-9_-]*?), ?\"?([^)]*?)\"?\)"""
)
start_flow_re = re.compile(r"StartFlow\(([a-zA-Z_][a-zA-Z0-9_-]*?)\)")
cancel_flow_re = re.compile(r"CancelFlow")
interruption_flow_re = re.compile(r"AllowInterruption")
cancel_flow_re = re.compile(r"CancelFlow\(\)")
chitchat_re = re.compile(r"ChitChat\(\)")
knowledge_re = re.compile(r"KnowledgeAnswer\(\)")
humand_handoff_re = re.compile(r"HumandHandoff\(\)")
listen_re = re.compile(r"Listen\(\)")

for action in actions.strip().splitlines():
if m := slot_set_re.search(action):
slot_name = m.group(1).strip()
slot_value = m.group(2).strip()
# error case where the llm tries to start a flow using a slot set
if slot_name == "flow_name":
commands.append(StartFlowCommand(flow=slot_value))
elif cls.is_hallucinated_value(slot_value):
Expand All @@ -198,8 +206,12 @@ def parse_commands(
commands.append(StartFlowCommand(flow=m.group(1).strip()))
elif cancel_flow_re.search(action):
commands.append(CancelFlowCommand())
elif interruption_flow_re.search(action):
elif chitchat_re.search(action) or knowledge_re.search(action):
commands.append(HandleInterruptionCommand())
elif humand_handoff_re.search(action):
commands.append(HumanHandoffCommand())
# elif listen_re.search(action):
# commands.append(ListenCommand())

return commands

Expand Down

0 comments on commit 933dad3

Please sign in to comment.