Genie Flow state machine principles
The backbone of the Genie Flow framework is defined by the State Machine. The Genie Flow State
Machine is based on the package Python State Machine
and the GenieStateMachine
is a direct subclass of the StateMachine
class that is defined there.
The GenieStateMachine
class implements the base logic of the Genie Flow. A State Machine defines
the different states (nodes in a graph) and the transitions (edges in the graph) that can be made
between these states.
Question and Answer example
A simple Question and Answer dialogue flow would look like this:
stateDiagram-v2
direction LR
[*] --> intro
intro --> ai_creates_response: user_input
ai_creates_response --> user_enters_query: processing_done
user_enters_query --> ai_creates_response: user_input
intro: Intro
ai_creates_response: AI Creates Response
user_enters_query: User Enters Query
Here, it becomes apparent that a dialogue is played between an "AI" and a human actor. The
dialogue starts with the initial state (colour coded) called Intro
. From that initial state,
the only transition that can be made is the one called user_input
, which transitions into
Ai creates response
. From there, a transition called ai_extraction
brings the state
machine into the state called User enters query
. From there, the only transition is again
a user_input
transition, back to the Ai creates response
.
This example shows a number of foundational elements:
- states
- A state machine contains a number of states between which the state machine can transition. A machine can only be in one state at any single time. The transitions that can be made from one state to another are predefined and fixed. Only one initial state is defined. It is the state where a newly instantiated state machine starts.
- transitions
- From any state, there can be zero, one or more transitions into other states. If there are zero states, that state is a final state, of which there can be multiple, or none, as in the Q and A example. Every transition has the name of an event, which is the event that will trigger that particular transition.
- events
- An event is what makes the state machine transition from one state to the next. In Genie Flow, events are either sent through the API or received from the internal workings, for instance, when an LLM has finished rendering, and its output is ready for processing.
Question and Answer example with conditions
This first example gives a good impression of the elements that are relevant for creating a Genie Flow application. The dialogue, however, is simplistic and never-ending. Now, consider the following flow diagram:
stateDiagram-v2
direction LR
[*] --> intro
intro --> ai_creates_response: user_input
ai_creates_response --> user_enters_query: processing_done
user_enters_query --> ai_creates_response: user_input
intro: Intro
ai_creates_response: AI Creates Response
user_enters_query: User Enters Query
It is almost the same Question and Answer flow, except that we have now introduced conditions.
There are now two ai_extraction
event transitions from the state Ai creates response
. One
that has the connotation user_wants_to_quit
and the other with !user_wants_to_quit
.
Indicating that the user wants to quit, or the user does not want to quit, indicated by
the !
mark, pronounced as "not".
So, when the LLM determines the user wants to quit, we want the flow to go to the state called "Outro". If not, we follow the normal path towards the state "User enters query".
A similar thing has been done with the user_input
events out of the state "User enters query".
Here we have another condition called user_says_stop
that tells the state machine to either
go to the "Ai creates response", in the case where "Not user says stop" (!user_says_stop
),
or to the state "Outro" in the case where "user says stop" (user_says_stop
).
These conditions are a first step towards creating more complex dialogues. It enables us to make different paths through the dialogue.
Question and Answer example with data capture
So far, we have not yet seen how data that is entered by the user is captured and stored. Now imagine the following Genie Flow:
In this flow, the user is asked for their name. That username is extracted and stored in the
data model. The extraction is done through an LLM. The response of that LLM should be either
the name of the user or the term UNDEFINED
. In the latter case, the user is asked again to
state their name (state Need to retry
). The condition name_is_defined
ensures the state
machine directs the user towards the Welcome message or to retry.
One new element on the state Ai extracts name
is the exit / on_exit_ai_extracts_name
method.
This is the method that is called when the state machine exists the state Ai extracts name
.
So this is when the LLM has conducted its extraction, and the response is available. This is the
moment during the conversation that the programmer has control to change values in the data
model attached to the state machine.
- actions
- When entering or exiting states, the programmer has control over what happens. Typically, these moments are used to capture responses from the LLM or users and adapt the content of an attached data model object.
Genie Flow templating
The final concept that is introduced by Genie Flow is the idea that every state maintains a template. That could be a template to construct the text that should be sent to the user or the template that is used to construct the text that is sent to the LLM.
The flow is as follows:
When the state machine traverses from one state to the next, Genie Flow takes the template that
is attached to the target state, renders the template with the data model object and provides
it to the actor that needs it. If the event triggering the transition is a user_input
event,
then the next actor is an LLM. In case the event for the transition is ai_extraction
, then the
next actor is the user.
For templating, Genie Flow uses Jinja2, a powerful templating engine.
When rendering the template, all attributes of the data model are available. Additional attributes that are available are:
parsed_actor_input
- If the
actor_input
field of theGenieModel
happens to be JSON, then this will contain a JSON-parsed version of theactor_input
. state_id
- The id of the state. This is the class property name that is given to the state.
current_date_time
- The current date and time, in iso 8601 format, in the UTC time zone
chat_history
- This is a serialisation of the complete dialogue history, formatted using
the
DialogueFormat.YAML
format.
conclusion
In the previous examples, we have seen how, through states
, transitions
, events
, conditions
,
actions
and templates
, the programmer has complete control over how a dialogue flows and how
data is captured and presented along the way.
In the next chapter, we will go through these same three examples, but then with the actual Genie Flow code.