Hi, and welcome to AI in C#,
where we go into the Microsoft Agent Framework,
and more specifically in this video, looking at
workflows.
This is my first video in the workflows,
so I will give you a short introduction,
and then we're going to go in and
see a sample of an agent-assisted workflow,
where we will be making some pizzas.
So, but let's learn a bit about the
concepts, and they have two main concepts in
all of this, and that is executors and
edges.
So a workflow are simply just pieces of
code that you want to run, stringed together
in a flow.
So the flow could be just from one
box to another, it could be one box,
but if something else, something go out, you
can fan out, and then aggregate, fan in,
and there's multiple different concepts in such workflows.
If you know workflows in general, you know
that there's a lot of different cases and
stuff you can do.
But what we are going to do is,
we are going to build the executors, and
we are going to define the edges on
how these executors talk to each other.
And in order to better understand it, I
made a small sample, which we'll go to.
And the smaller workflows are in general, the
more contrived they are, but I've tried to
make something that could be sort of realistic,
and for that reason, it's also a little
bigger to see in code, but it's more
realistic compared to just having a workflow that
went from one box to another, and then
ended.
So the idea is that we have a
user that are hungry, and they, via a
chatbot or some kind of input field, just
ask for a pizza.
And then we will put that order into
a workflow, and the first thing we'll do
is we'll have an order parser that will
turn out what have the customer actually asked
for, and turn that into something that are
more structured.
Then we will parse that structure that the
AI will build here into a stock checker,
which right now don't have any AI, but
it could have, but it could also have
tool calling to a real inventory management system,
and what we have.
But essentially, it's just a hard-coded thing
that some stocks can be missing, and if
it's missing, it should go and make a
warning where we use an AI to generate
some kind of message back to the user,
and if they want to have some replacement
of some of the stock.
And if all stock is there, we of
course have some success, and we can make
the pizza.
So this would be a fairly simple workflow,
but it will also be an AI-assisted
workflow, in that these two executors have some
AI injected.
But beyond that, we could have made this
with any other workflow, NuGet package, if we
wanted to, but given that it is here,
we will of course use them together.
So let's go into code and see all
this in action, and we'll just simply take
it line by line here, and then we
will see it up and running.
So first up, we have our configuration, just
our setup of our agents, and we have
made a small agent factory here, just to
fill it out here, because we need two
agents.
One that is an order taker, and in
this case, we're using structured output to get
a pizza order, and the pizza order is
just something that have a size, what toppings
they want, and at some point, what kind
of warnings are there to this order.
So the agent here is just take whatever
the customer asks for, and turn it into
a pizza order, and that's the only thing
it can do.
The other one is, if there's any problems
with the pizza, it should explain to the
customer or to the user that their pizza
order can't be met, so we don't give
a stock answer, but a real answer from
an agent.
So nothing new here, we have done this
all the other places we have gone through
this code.
I've just put it out into a separate
class, so it doesn't fill up, and we
don't get the full meaning of this.
Next up, we have four executors, because as
we saw in our thing here, we have
one, two, three, four, so we need four
of these.
Could some of them have been together?
Of course they could.
Could everything have just been in one class
with if statements?
Yes, it could, but the idea is, of
course, that you can make these executors.
You can perhaps even make executors that can
be reused across multiple workflows, and in general
just that you don't need to string all
this up.
Beyond that, the workflows can be resumed and
suspended, and have human in the loop, and
be long running, and all kinds of things,
but that will be for another video.
For this one, we're just going to make
a simple one.
So let's go back to code, and see
our first executor.
So our first executor is an order parser,
and as we can see, it gets the
order taker agent in.
And if we look at an executor, the
way they work is you make a class,
and you make it extend the reflection executor.
They are all called reflection executor, so that's
the only one at the moment.
And it's of type this, and we give
it a name.
And then we need to tell what kind
this executor, what should it do.
Well, in our case, we wanted to handle
a message, and that message would be that
it has an input of string, meaning that
the order from the user as a string,
and the output should be the pizza order.
And when we have this interface, we need
to implement a handle async here.
We could have this interface multiple times, so
it could have been that it was in
text, but it could also be an MP4
file with the voice of the user, and
we could parse that, and so on.
So an executor can have multiple handles if
need be, but in the above case, we'll
just keep one.
And here we just do normal agent work.
We're going to use structured output to get
a pizza order back, so we're essentially just
taking the string and turning it into an
object.
We'll set a breakpoint here, so we can
see that in a little while.
Next up, we have our stock checker, so
our stock checker is fairly simple.
It's just going to take a pizza order
in and return a pizza order out.
And if we do that, we just get
our order.
We loop to all the different toppings that
the previous step extracted from us, and then
we have hard-coded here right now that
if it's mushrooms, one of the toppings, we
simulate that that's out of stock.
In real life, we will, of course, have
had some code here, or maybe even an
agent that could figure out if it said
the word mushroom or mushrooms or some kind
of shiitake mushroom or whatever, so an AI
could help us find the right topping for
just based on text.
But in this case, we're just simulating that
if it's mushrooms we are out of, we
will add a warning to our pizza order,
or else we'll just write out that we
are able to add that topping onto the
pizza and reduce the stock.
So we'll also set a breakpoint here when
we get to that.
Next we have the two end results, either
a success, which is fairly simple, we'll just
say pizza okay, or a warning where we
give an agent, because we want to check
the warning, get all the warning text out,
and pass that into the agent that it
needs to explain what we are out of
and what they can do.
And here we will probably send an email
or an SMS message or whatever our system
would do.
So these are the four we have, but
now we need to explain the edges.
This one here is of course starting the
workflow, but one, two, three edges.
And the first edge is just passing along,
so we are simply going in and building
a workflow builder.
And what we are telling the workflow builder
is that the first executor should be the
order parser, which we saw here.
So that would always be the start of
this workflow.
And then we are telling it make an
edge, meaning draw an arrow between these two
boxes, from the order parser to the stock
checker.
And there, it is very important that we
then see that this one had an output
of a pizza order, while this had an
input of a pizza order.
If these don't fit, the workflow will not
run.
It will simply just stop at the first
step.
But again, a simple case here, we're just
parsing along after one have finished at work,
the second one will take over.
And then, once we have the output of
our stock checker that enhanced the order with
warnings or not, we go in and add
a switch edge, meaning we are going to
branch into two different things.
And the way we branch is we build
an add switch, and we do a switch
builder, where we add a case that says,
if the warning is zero, then it's a
success.
If the warning is different from zero, then
it's a warning.
And that is actually everything to define our
workflow.
But you can imagine you could make a
much more advanced workflow if you wanted to.
And again, there is multiple steps here for
various fan out, meaning take this one step
and start these two steps, or fan in,
take these two, three, four steps, and aggregate
them into one step, and so on.
But more on that in future videos.
But for now, we're just going to build
our workflow, and now we have the option
to run it.
And in order to run it, we, of
course, need some input.
And in our case, we've just hard-coded
here, make a big pepperoni pizza with mushrooms
and onions.
And then we are going to use this
in-process execution, which will go in and
stream our workflow, meaning we can, on the
fly, see that something happens, because workflows could
be several hours, perhaps.
So we can see live what is going
on.
We could also just have run it, and
then we will first see the results at
the very end of the workflow.
But we're giving our in-process executor our
workflow and our input, and we get a
streaming run, which we can then watch here.
And watch means we can get the various
workflow events.
So there's various events you can get.
So, for example, this is every time an
executor is done, but it could also be
every time an executor starts, every time the
workflow starts.
And there can be groups inside workflows called
super steps.
But for now, we are just going to
check every time something ends.
We will just write it out as completed.
The executors, by themselves, take care of the
rest.
So now I think we should just set
a breakpoint and walk through it while we
see the output.
So I am running the wrong one.
Let me quickly do this.
And let's see it in action then.
So we're getting our configuration, and we are
building our executors.
So they don't do anything at build time.
They are just being marked up.
Then we're going to make our edges and
our switches, and we can also set some
breakpoints in our switch so we can see
that a little while.
So now I'm going to start to run.
And as we saw, we set a breakpoint
in the pass order.
So we set a breakpoint in the executor
here, and we're going to set one in
success.
Not that we're going to read it because
we are asking for mushrooms.
And we're going to set one here.
So whenever I start this, it now begins.
And the first thing that happens is we
hit the order parser, and the input was
a string that just was make a big
pepperoni pizza with mushroom and onions.
And we are going to take that message
and just use our agent in order to
get our response back, which will be some
JSON, in this case a large pizza with
toppings pepperoni, mushrooms, and onions, and no warnings,
of course, because we don't have any.
And that is what this deserialize will do,
so we actually have a real object.
And that is the one we are going
to give back to the system.
So pressing F5, we now ended up in
the second step, the check, the stock checker,
and we have our object so we can
work with that.
And our first topping is pepperoni, which is
just okay.
So we'll write out that it's okay.
The second topping is our mushrooms, which we'll
simulate as out of stock.
And then we had the onions, which was
also okay.
And then we're just going to return the
message back because we manipulated it using the
warnings And now we hit our switch with
our case, and it will ask us, or
it will check, hey, how many warnings are
there?
There's one.
So we're not going to hit this step.
Is this one?
Yeah.
Okay.
So now we are actually going to hit
our events if we press along, and we'll
get our order, our message here, with our
warning of out of ingredients mushrooms.
We're going to make that into a string,
so just out of ingredient mushrooms, and we
will give that to the AI in order
for it to make a nice response back.
I'm sorry, but we can't fulfil the order,
and so on.
And we end up having the full workflow.
We passed the order.
It completed.
We added pepperoni.
We were out of stock with mushrooms, but
we added the onions.
We did stop.
It completed.
We couldn't fulfil the order, and we used
an AI to write out our apology to
the user.
So that is actually everything there is to
workflows.
This is a workflow that are fairly small.
I can imagine myself building lots of bigger
and bigger stuff, and I can imagine myself
building some generic executors that I can use
across multiple workflows, like sending emails, checking values
in various systems, and so on, because you
can just put agents in them.
So it's just a way of having a
nice structure compared to trying a lot of
if and else statements in there.
I will, of course, make multiple other videos
on fan in, fan out, and so on,
but now you have the general idea on
what a workflow is and how you build
it up.
So hopefully, it was beneficial, and see you
in the next one.