Master Sequential Agents: Build Complex AI Apps with Flowise

15.97k views5530 WordsCopy TextShare
Leon van Zyl
In this Flowise Sequential Agents tutorial you will learn how to create advanced AI agent, and multi...
Video Transcript:
<b>Flowise 2. 0 introduces one of the most</b> <b>powerful features to date,</b> <b>sequential agentic flows. </b> <b>With sequential agents, you can control</b> <b>your multi-agent</b> <b>applications like never before.
</b> <b>You can call agents conditionally, run</b> <b>them in parallel, add</b> <b>human in the loop, and much,</b> <b>much more. Unlike multi-agent flows,</b> <b>where the supervisor controls</b> <b>the flow of the application,</b> <b>sequential agents give you full control. </b> <b>In this video, we will look at the most</b> <b>important concepts</b> <b>related to sequential agents.
</b> <b>If you're new to Flowwise, then you're</b> <b>definitely in for a treat. </b> <b>Flowwise is a low-code platform</b> <b>for building advanced AI applications</b> <b>using a simple drag-and-drop interface. </b> <b>It's open source and completely free to</b> <b>use.
I have plenty of</b> <b>videos on my channel showing you</b> <b>how to install, update, and self-host</b> <b>Flowwise. But in order to</b> <b>make this a complete tutorial</b> <b>for beginners, we will set up Flowwise on</b> <b>our local machines. If</b> <b>you already have Flowwise</b> <b>installed, then please ensure to update</b> <b>your instance to version 2.
0 or greater. </b> <b>In order to install Flowwise on your</b> <b>machine, you need to</b> <b>install Node. js from this website,</b> <b>and after installing Node, open the</b> <b>command prompt or terminal</b> <b>on your machine, and enter</b> <b>"npx flowwise start".
The first time you</b> <b>execute this, you will be</b> <b>asked whether you want to</b> <b>install Flowwise and its dependencies. </b> <b>Simply enter "y" and press</b> <b>enter. And once installation</b> <b>is complete, you can access Flowwise in</b> <b>your browser by going to</b> <b>localhost 3000, and you</b> <b>should be presented with a dashboard like</b> <b>this.
Now, in order to</b> <b>build multi-agent flows, we need</b> <b>to go to "agent flows", and let's add a</b> <b>new flow. I'll start by giving</b> <b>my flow a name, and I'll call</b> <b>this "sequential agent basics". Within</b> <b>Flowwise, we can build all sorts of</b> <b>multi-agent solutions.
</b> <b>If we go to "add nodes", we can of course</b> <b>use multi-agent teams,</b> <b>where a supervisor will control</b> <b>the logic flow between the different</b> <b>worker nodes. But with</b> <b>the release of Flowwise 2. 0,</b> <b>we now have sequential agents, which</b> <b>gives us way more control</b> <b>over the flow of our application.
</b> <b>And at first, this might seem extremely</b> <b>intimidating, as we have several nodes to</b> <b>choose from. But in this video, we will</b> <b>go through each of these nodes, and by</b> <b>the end of the video,</b> <b>you will be able to create extremely cool</b> <b>and complex multi-agent applications. </b> <b>Let's start by looking at the start node.
</b> <b>The start node does</b> <b>exactly what you would think. </b> <b>This represents the start of our</b> <b>sequential flow. The start node takes in</b> <b>a chat model as input,</b> <b>as well as memory, state, and input</b> <b>moderation.
Let's start by adding our</b> <b>LLM. Let's go to "add</b> <b>nodes", and under "chat models", we can</b> <b>add any of the available</b> <b>chat models to our application. </b> <b>If you want to follow along without</b> <b>incurring any costs, then I</b> <b>highly recommend using the</b> <b>"grok chat model".
It's free to use and</b> <b>blazingly fast, and I</b> <b>already created a separate video</b> <b>showing you how to integrate grok chat</b> <b>with Flow-wise. But for</b> <b>this demo, I'll actually use</b> <b>the "chat OpenAI" node. So I'll start by</b> <b>adding this node to the</b> <b>canvas.
Let's start by setting</b> <b>our OpenAI credentials by clicking on the</b> <b>drop-down, and let's click on "create</b> <b>new". Let's give our</b> <b>credential a name, like "OpenAI API". Now</b> <b>we just have to fit our</b> <b>OpenAI API key, which we can get</b> <b>from "platform.
openai. com slash API</b> <b>keys". Let's create a new key by clicking</b> <b>on "create new secret</b> <b>key".
Let's give it a name, and let's</b> <b>click on "create secret key". Then copy</b> <b>this key, and paste it into</b> <b>this field in Flow-wise. Let's click on</b> <b>"add", and then let's select our model</b> <b>name.
For this demo,</b> <b>I'm going to select the "gpt40" model,</b> <b>and I'm going to change the</b> <b>temperature to 0. 2. And let's</b> <b>attach our chat model to the start node.
</b> <b>Great! Now let's add our</b> <b>first agent to this Flow. Let's</b> <b>go to "add nodes", and then under</b> <b>"sequential agents", add the agent node.
</b> <b>Let's connect our start node</b> <b>to our agent node. Now let's have a</b> <b>closer look at the agent node. For agent</b> <b>nodes, we can assign</b> <b>tools.
We can also add a preceding node,</b> <b>which could either be a</b> <b>start node, another agent,</b> <b>an LLM node, or a tool node. We can also</b> <b>assign a chat model, but this is</b> <b>optional. If we don't</b> <b>assign a chat model, the agent node will</b> <b>simply inherit the model</b> <b>assigned to the starting node,</b> <b>which in our example is this chat openai</b> <b>node.
We also need to</b> <b>provide our agent with a name,</b> <b>and take note this name needs to be</b> <b>unique. Let's call this agent</b> <b>"assistant". We can also enter</b> <b>a "system" prompt, which we can use to</b> <b>prime the model, provide context, set a</b> <b>role, etc.
Let's clear</b> <b>these values, and let's set our prompt as</b> <b>"You are a friendly</b> <b>assistant called John". Let's have a</b> <b>look at the rest of this node. "Require</b> <b>approval" can be used to enable human in</b> <b>the loop functionality,</b> <b>which we will look at later on in this</b> <b>video.
You can use "format</b> <b>prompt values" to set the values</b> <b>of variables, which we will look at as</b> <b>well, and if we click on additional</b> <b>parameters, we can use</b> <b>the "approval prompt approve button text</b> <b>and reject button"</b> <b>when the human in the loop</b> <b>functionality is enabled. We also have</b> <b>the capability of updating</b> <b>state, which we will look</b> <b>at later as well. But for now, we are</b> <b>done with the agent.
Now before we can</b> <b>run this, we do need</b> <b>to set some sort of end state. Let's go</b> <b>to add nodes, and with "in sequential</b> <b>agents", we can either</b> <b>add an end node or a loop node. Both of</b> <b>these nodes represent an end</b> <b>state in our flow.
For now,</b> <b>let's simply add an end node, and let's</b> <b>attach our agent to this end</b> <b>node like so. We can now save</b> <b>our flow, and in the chat, let's test</b> <b>this out. Let's say "hello".
And of</b> <b>course, we can see that</b> <b>the assistant node was executed as well</b> <b>as the response from that node. </b> <b>Fantastic! We now have an</b> <b>agent that we can talk to.
Now let's</b> <b>discuss agent memory. During the</b> <b>execution of the flow,</b> <b>the conversation history is added to a</b> <b>global state, and after</b> <b>execution, all those messages</b> <b>are deleted. So when we execute the flow</b> <b>again, our agent will not</b> <b>be able to recall information</b> <b>from past messages, and I can prove that</b> <b>to you.
Let's enter</b> <b>something like "my name is Leon". Our</b> <b>agent will greet us with my name, but if</b> <b>I then ask it "what is my</b> <b>name? ", the agent can't recall</b> <b>that information.
So to add memory to our</b> <b>flow, we can simply go to add</b> <b>nodes, then within "memory",</b> <b>we can add the agent memory node. I'm</b> <b>actually going to move these</b> <b>nodes around just to make a</b> <b>bit of space. And now let's attach our</b> <b>agent memory node to the agent memory</b> <b>input on the start node.
</b> <b>With this node, the conversation history</b> <b>will actually be stored</b> <b>in a SQLite database. Let's</b> <b>go ahead and test this out by saving the</b> <b>flow, and in the chat, let's try</b> <b>providing my name again</b> <b>by entering "my name is Leon", and now</b> <b>let's ask it "what is my</b> <b>name? ", and this time our assistant</b> <b>was able to recall my name.
Great! Now</b> <b>let's discuss the state node. </b> <b>By default, the conversation</b> <b>history is added to state and shared</b> <b>amongst all the agents in the</b> <b>flow, but we can also use the</b> <b>state structure to store our very own</b> <b>values, and these values will be shared</b> <b>amongst all the agents</b> <b>in the state as well.
Let's have a look</b> <b>at an example. Let's go to</b> <b>add nodes under sequential</b> <b>agents. Let's add the state node.
Let's</b> <b>attach this node to our</b> <b>starting node, and now we can</b> <b>click on additional parameters to add our</b> <b>very own state properties. </b> <b>Now take note we do not have to</b> <b>add a property for the conversation</b> <b>history as that is handled for us by</b> <b>default, so all we have to do</b> <b>is add any additional values to the state</b> <b>which we want to make available to our</b> <b>agents. As an example,</b> <b>let's set a value for my name in state.
</b> <b>Let's click on add item. Let's enter a</b> <b>key value as a name,</b> <b>and for the operation let's simply select</b> <b>replace. You can use the</b> <b>append operation to simply add</b> <b>values to an array or a list of values.
</b> <b>For now we simply want to</b> <b>replace the value with my name. </b> <b>Great! This value can be retrieved by the</b> <b>agents from the state</b> <b>structure.
Let me show you how that</b> <b>works. Let's make the name available to</b> <b>this agent by changing the</b> <b>system prompt slightly. Let's add</b> <b>something like you are talking to, and</b> <b>instead of hardcoding my name</b> <b>like this, we want to retrieve</b> <b>the value from the state.
So let's add a</b> <b>variable value using</b> <b>opening and closing curly braces,</b> <b>and within the braces let's give this</b> <b>placeholder a name. I'll just call mine</b> <b>name. Let's click on save.
</b> <b>Now we just have to link up this</b> <b>placeholder with the value from state,</b> <b>and we can do that by</b> <b>clicking on format prompt values. At the</b> <b>moment the name variable</b> <b>is simply an empty string,</b> <b>and we can change that by clicking on the</b> <b>edit button, and if we</b> <b>click on this field we can now</b> <b>select from a range of values. The</b> <b>question is simply the</b> <b>message from the chat window.
We can</b> <b>also access the chat history, or we can</b> <b>fetch the first message from</b> <b>the content, or the last message</b> <b>from our list of messages. What we are</b> <b>interested is this option over here. </b> <b>Let's then edit this</b> <b>value, and let's replace this with our</b> <b>name value in state.
So this</b> <b>is referring to this key value</b> <b>in the state structure, which has a value</b> <b>of Leon. So let's test this</b> <b>out by going through the chat,</b> <b>and it say hello, and as you can see the</b> <b>agent now knows our name. So</b> <b>let's say we wanted to change</b> <b>this value from Leon to John.
Let's save</b> <b>this flow, let's test it again, and let's</b> <b>ask what is my name,</b> <b>and as you can see it is pulling the name</b> <b>from the state. Great! </b> <b>Now let's have a look at the</b> <b>condition node.
We can use the condition</b> <b>node to conditionally call</b> <b>agents. Let's assume that the</b> <b>first time you execute this flow the</b> <b>user's name in the state</b> <b>property will actually be blank. </b> <b>If the value is blank then we want to</b> <b>call an agent which will be</b> <b>responsible for collecting</b> <b>the user's name and then storing that</b> <b>name in the state property.
So</b> <b>the second time we execute the</b> <b>flow and then if the name is populated</b> <b>this assistant over here</b> <b>will be triggered. So let's</b> <b>set that up. First I'm just going to make</b> <b>some space by moving this</b> <b>agent over.
Let's break this</b> <b>connection and let's add a second agent</b> <b>which will be responsible for collecting</b> <b>the user's information. </b> <b>So let's add that agent, let's give it a</b> <b>name like name collection</b> <b>agent, and in the system</b> <b>prompt let's enter something like you are</b> <b>responsible for</b> <b>collecting the user's name. </b> <b>Let's also provide the steps that the</b> <b>agent can follow like if the</b> <b>user has not provided their</b> <b>name then politely ask for their name and</b> <b>if the user provided their</b> <b>name then ask them how can I</b> <b>assist you.
Let's save this agent and</b> <b>let's attach our agent to the</b> <b>end node. So now we have two</b> <b>agents which we want to call</b> <b>conditionally based on whether the name</b> <b>is set in state. Let's go to</b> <b>add nodes and with the sequential agents</b> <b>let's add the condition node.
Let's</b> <b>attach the start node</b> <b>to this condition node and now let's have</b> <b>a look at the details of</b> <b>this condition node. First let's</b> <b>give our condition node a name. This can</b> <b>be anything you want like check if the</b> <b>user has a name.
Then</b> <b>let's click on this condition button. </b> <b>Here we can set up the different</b> <b>conditions for this node. </b> <b>We can either use this condition table or</b> <b>we can handle this</b> <b>programmatically using code.
Let's</b> <b>stick with the condition table. Let's add</b> <b>a new item and for the</b> <b>variable we can access the name</b> <b>property in state. So we can say that if</b> <b>the name is not empty we</b> <b>then want to simply continue to</b> <b>our agent node.
You can call this output</b> <b>whatever you want. I'll</b> <b>simply call it continue but if we</b> <b>save this you will now notice this</b> <b>continue output over here. If none of the</b> <b>conditions in this list</b> <b>is met the end output will be executed.
</b> <b>We can also have additional</b> <b>outputs so for example let's</b> <b>say we had a specific output for a name</b> <b>that is Leon. We could add</b> <b>a Leon output as well and if</b> <b>I save this you will now notice this</b> <b>connection over here for Leon but of</b> <b>course we don't need</b> <b>that for now. But if the name is empty</b> <b>this end output will be</b> <b>triggered which we can hook up</b> <b>to our name collection agent.
Like so</b> <b>let's test this out by saving the flow</b> <b>and just as a reminder</b> <b>if we go to state the name is currently</b> <b>John. So we would expect this flow to</b> <b>trigger the continue</b> <b>output over here. In the chat let's say</b> <b>hello and indeed this triggered our</b> <b>assistant node.
So let's</b> <b>test this end scenario by going to state</b> <b>let's clear the name then</b> <b>let's save the flow and in the</b> <b>chat let's say hello and this time it is</b> <b>triggering the name</b> <b>collection agent. Great it's very</b> <b>important to note that although we</b> <b>provided our name this name was not</b> <b>stored in the global state</b> <b>so in order to grab this name from the</b> <b>user's message and then</b> <b>store it in state we will be</b> <b>using another node called the LLM node. </b> <b>We can use the LLM node to</b> <b>process unstructured data</b> <b>like a user's message to extract</b> <b>information from the message and then</b> <b>output the data in a</b> <b>structured format.
Let me give you an</b> <b>example of why you would</b> <b>want to parse unstructured data. </b> <b>In this example I simply parsed my name</b> <b>so you might think that</b> <b>it's simple enough to simply</b> <b>grab the value that the user provided and</b> <b>install that value in state</b> <b>but what if the user entered</b> <b>something like my name is Leon that would</b> <b>mean that the agent would</b> <b>store this entire string</b> <b>as the name in the state which is not</b> <b>correct. We want to use the intelligence</b> <b>of an LLM to identify</b> <b>a human name from the string and then</b> <b>output only that name and store it in</b> <b>state.
All right enough</b> <b>talk let's actually implement this. Let's</b> <b>go to add nodes and with in</b> <b>sequential agents let's add</b> <b>the LLM node. Before we attach this node</b> <b>to anything let's first</b> <b>have a look at this node</b> <b>in isolation.
This node can be triggered</b> <b>by the starting node another</b> <b>agent an LLM or a tool node</b> <b>so effectively this node will receive the</b> <b>output of one of these</b> <b>nodes and then process that data</b> <b>in any way that we want. Let's actually</b> <b>start by giving this node a</b> <b>name like name collector and</b> <b>within additional parameters we can</b> <b>provide clear instructions on what sort</b> <b>of data this node needs</b> <b>to extract. For example your job is to</b> <b>identify and extract a human</b> <b>name from the user's message</b> <b>only respond with the name.
We'll leave</b> <b>the human prompt empty and</b> <b>what we can then do is add a</b> <b>variable for the user message and again</b> <b>we can create variables</b> <b>using opening and closing curly</b> <b>braces. Let's call this question or</b> <b>whatever you want then with informat</b> <b>prompt values we can</b> <b>simply assign the question from the chat</b> <b>box like so. So this will</b> <b>now receive something like</b> <b>my name is Leon and with this prompt the</b> <b>model will try to extract</b> <b>the name.
Now one of the</b> <b>benefits of the LLM nodes is we can</b> <b>return the data in a fixed structure. In</b> <b>this example we want</b> <b>to return the name so let's add an item</b> <b>let's call this name which is of type</b> <b>string and we can add a</b> <b>description like the name of the user. So</b> <b>as this LLM node would have</b> <b>extracted the name from the</b> <b>user's message it will then assign the</b> <b>name to this key value over</b> <b>here and additionally we can</b> <b>also use this node to update state and</b> <b>that is exactly what we</b> <b>want to do.
Let's click on add</b> <b>item within the state we want to reassign</b> <b>this name property and</b> <b>for the value we want to use</b> <b>this name output over here so from this</b> <b>list of values we will</b> <b>select flow dot output and we will</b> <b>replace this key value with name. This is</b> <b>the value from this JSON output</b> <b>structure. I know there</b> <b>was a lot to process but these LLM nodes</b> <b>are extremely powerful.
</b> <b>Simply see it as a node that</b> <b>can receive output from another node,</b> <b>extract information from that output,</b> <b>store the information</b> <b>that it extracted into this structured</b> <b>output list and optionally</b> <b>we can use the values that it</b> <b>extracted to update state. Now the</b> <b>question is where do we add this node in</b> <b>this flow and for me</b> <b>it makes sense to add it after this</b> <b>condition node and during this flow where</b> <b>we collect the name. </b> <b>I'm actually going to move these nodes</b> <b>over let's break this connection let's</b> <b>bring in the LLM node</b> <b>let's attach this end connector to the</b> <b>LLM node and then let's</b> <b>connect our LLM node to our agent.
</b> <b>So let's quickly have a look at this</b> <b>again initially the name</b> <b>property in the state is blank</b> <b>so the condition node will route us down</b> <b>this path where the agent will ask the</b> <b>user to provide their</b> <b>name. If the user simply enters something</b> <b>like hello this LLM node</b> <b>will not be able to extract</b> <b>the name from that text and therefore</b> <b>also not set the name in</b> <b>state then the name collection</b> <b>agent will be called to ask the user for</b> <b>their name. The user will</b> <b>then enter their name which</b> <b>will again trigger this path this time</b> <b>the name collector will be able to</b> <b>extract their name and</b> <b>update state and the name collection</b> <b>agent will then determine</b> <b>that the name was provided</b> <b>and then ask the user how the agent can</b> <b>help them.
In fact in the</b> <b>scenario where the user enters</b> <b>something that's not a name we simply</b> <b>want the LLM node to return a</b> <b>blank value so let's announce</b> <b>this slightly to add something like if</b> <b>you are unable to determine a</b> <b>name like if the user entered</b> <b>hello or hi then return a blank value for</b> <b>the name. Let's save our</b> <b>flow let's enter hello and</b> <b>in the name collector we can actually see</b> <b>the changes that were</b> <b>made to the state which was</b> <b>simply a blank value because we were</b> <b>unable to determine a name. </b> <b>Also the name collection agent</b> <b>responded with what is your name now</b> <b>let's see what happens if we actually</b> <b>enter a name.
My name</b> <b>is Leon this time the name collector was</b> <b>able to extract the name</b> <b>and store that value in state. </b> <b>Now the name collection agent is asking</b> <b>us how they can help if we</b> <b>send another message we will</b> <b>expect the flow to take us to the</b> <b>assistant agent so let's enter something</b> <b>like why is the sky blue</b> <b>so we can now see that the condition node</b> <b>called the assistant</b> <b>agent and it was the assistant</b> <b>agent that responded so I hope you will</b> <b>agree that the LLM nodes are extremely</b> <b>powerful for parsing</b> <b>unstructured data and for setting state.
Copyright © 2024. Made with ♥ in London by YTScribe.com