Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example to the new state machine architecture #415

Merged
merged 11 commits into from
Sep 10, 2024
39 changes: 39 additions & 0 deletions demo/state_based_question_answering/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## State-based Question Answering

In this demo, we show case the ability of Sherpa to integrate state management and dictionary-based actions to answer questions and identify relations in different entities mentioned during the conversation.

### State Machine
The state machine of the question answering action is shown below:

```mermaid
stateDiagram
Waiting: Waiting [do - waiting for questions]
[*] --> Waiting
Waiting --> AnswerQuestion: start question answering
AnswerQuestion --> Clarification: start clarification
Clarification --> Clarification: clarify question
Clarification --> Clarification: update belief
Clarification --> Clarification: retrieve belief
Clarification --> AnswerQuestion: finish clarification
AnswerQuestion --> Waiting: answer question
```

### Structure
* actions.py: actions used in the demo
* main.py: main file to run the demo
* states.py: states used in the demo

### Running the Demo
To run the demo, execute the following command in the terminal:

First, go to the demo folder:
```bash
cd demo/state_based_question_answering
```

Then, run the demo:
```bash
python main.py
```

Then you can start ask questions to the agent.
42 changes: 42 additions & 0 deletions demo/state_based_question_answering/actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

from sherpa_ai.actions.base import BaseAction
from sherpa_ai.events import Event, EventType
from sherpa_ai.memory.belief import Belief


class UserHelp(BaseAction):
"""
Ask the user for clarification on a question.
"""
args: dict = {"question": "str"}

def execute(self, question: str) -> str:
clarification = input(question)

return clarification


class Respond(BaseAction):
"""
Respond to the user with a message.
"""
args: dict = {"response": "str"}

def execute(self, response: str) -> str:
print(response)

return "success"


class StartQuestion(BaseAction):
"""
Waiting the user to ask a question.
"""
belief: Belief
args: dict = {}

def execute(self) -> str:
question = input()
self.belief.set_current_task(Event(EventType.task, "user", question))

return "success"
47 changes: 47 additions & 0 deletions demo/state_based_question_answering/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from states import add_qa_sm

from sherpa_ai.agents.qa_agent import QAAgent
from sherpa_ai.events import Event, EventType
from sherpa_ai.memory.belief import Belief
from sherpa_ai.models import SherpaChatOpenAI
from sherpa_ai.policies.react_policy import ReactPolicy

role_description = """You are a helpful agent help user to perform their task.

Your task is to choose from available actions and execute them to help with user's task.

ask clarification if needed.

If you make any assumptions, make sure to clarify them with the user.

You want the response to be concise yet informative and keep the conversation engaging.

Store any relevant clarification into the belief and use it to answer the question if necessary.

Never Ask question repetitively. Never make random assumption
"""


def main():
belief = Belief()
llm = SherpaChatOpenAI(model_name="gpt-4o-mini", temperature=0.7)

policy = ReactPolicy(
role_description=role_description,
output_instruction="Determine which action and arguments would be the best continuing the task",
llm=llm,
)
belief = add_qa_sm(belief)
belief.max_tokens = 100
belief.set_current_task(Event(EventType.task, "user", "Answer the question"))

# belief.state_machine.enter_state("Waiting")
qa_agent = QAAgent(llm=llm, belief=belief, num_runs=100, policy=policy)

belief.state_machine.start()
while True:
qa_agent.run()


if __name__ == "__main__":
main()
113 changes: 113 additions & 0 deletions demo/state_based_question_answering/states.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from actions import Respond, StartQuestion, UserHelp
from transitions.extensions import GraphMachine, HierarchicalGraphMachine

from sherpa_ai.actions.base import BaseAction
from sherpa_ai.actions.belief_actions import RetrieveBelief, UpdateBelief
from sherpa_ai.memory.belief import Belief
from sherpa_ai.memory.state_machine import SherpaStateMachine


def get_actions(belief: Belief) -> dict[str, BaseAction]:
start_question = StartQuestion(
name="start_question",
usage="Start the question answering process",
belief=belief,
)

clarify_question = UserHelp(
name="clarify_question",
usage="Ask questions to clarify the intention of user",
belief=belief,
)
answer_question = Respond(
name="answer_question",
usage="Answer the user's question based on the current context",
belief=belief,
)

update_belief = UpdateBelief(belief=belief)

retrieve_belief = RetrieveBelief(belief=belief)

actions = [
start_question,
clarify_question,
answer_question,
update_belief,
retrieve_belief,
]

return {action.name: action for action in actions}


def add_qa_sm(belief: Belief) -> Belief:
# Hierarchical version of the state machine
states = [
"Start",
{"name": "Waiting", "on_enter": "start_question"},
{
"name": "QA",
"children": ["Thinking", "Clarification", "Clarified"],
"initial": "Thinking",
},
]
initial = "Start"

transitions = [
{
"trigger": "start",
"source": "Start",
"dest": "Waiting",
},
{"trigger": "Start_question_answering", "source": "Waiting", "dest": "QA"},
{
"trigger": "Ask_for_clarification",
"source": "QA_Thinking",
"dest": "QA_Clarification",
},
{
"trigger": "Ask_for_clarification",
"source": "QA_Clarification",
"dest": "QA_Clarified",
"before": "clarify_question",
},
{
"trigger": "Update_belief",
"source": "QA_Clarified",
"dest": "QA_Clarification",
"before": "update_belief",
},
{
"trigger": "Retrieve_belief",
"source": "QA",
"dest": "QA",
"before": "retrieve_belief",
},
{
"trigger": "Finish_clarification",
"source": "QA_Clarification",
"dest": "QA_Thinking",
},
{
"trigger": "Answer_question",
"source": "QA_Thinking",
"dest": "Waiting",
"before": "answer_question",
},
]

action_map = get_actions(belief)

sm = SherpaStateMachine(
states=states,
transitions=transitions,
initial=initial,
action_map=action_map,
sm_cls=HierarchicalGraphMachine,
)

print(sm.sm.get_graph().draw(None))

belief.state_machine = sm

return belief
Binary file added docs/How_To/Tutorials/imgs/state_machine_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/How_To/Tutorials/imgs/state_machine_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/How_To/Tutorials/imgs/state_machine_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/How_To/Tutorials/imgs/state_machine_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/How_To/Tutorials/imgs/state_machine_5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/How_To/Tutorials/imgs/state_machine_6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading