Sometimes, you may want to add more control on how the next agent is selected in a GroupChat based on the task you want to resolve. For example, in the previous code writing example, the original code interpreter workflow can be improved by the following diagram because it's not necessary for admin to directly talk to reviewer, nor it's necessary for coder to talk to runner.
flowchart TD
A[Admin] -->|Ask coder to write code| B[Coder]
B -->|Ask Reviewer to review code| C[Reviewer]
C -->|Ask Runner to run code| D[Runner]
D -->|Send result if succeed| A[Admin]
D -->|Ask coder to fix if failed| B[Coder]
C -->|Ask coder to fix if not approved| B[Coder]
By having GroupChat to follow a specific graph flow, we can bring prior knowledge to group chat and make the conversation more efficient and robust. This is where Graph comes in.
Create a graph
The following code shows how to create a graph that represents the diagram above. The graph doesn't need to be a finite state machine where each state can only have one legitimate next state. Instead, it can be a directed graph where each state can have multiple legitimate next states. And if there are multiple legitimate next states, the admin agent of GroupChat will decide which one to go based on the conversation context.
Tip
Graph supports conditional transitions. To create a conditional transition, you can pass a lambda function to canTransitionAsync when creating a Transition. The lambda function should return a boolean value indicating if the transition can be taken.
var reviewer = await CreateReviewerAgentAsync(gpt4o);
var coder = await CreateCoderAgentAsync(gpt4o);
var runner = await CreateRunnerAgentAsync(kernel);
var admin = await CreateAdminAsync(gpt4o);
var admin2CoderTransition = Transition.Create(admin, coder);
var coder2ReviewerTransition = Transition.Create(coder, reviewer);
var reviewer2RunnerTransition = Transition.Create(
from: reviewer,
to: runner,
canTransitionAsync: async (from, to, messages) =>
{
var lastMessage = messages.Last();
if (lastMessage is TextMessage textMessage && textMessage.Content.ToLower().Contains("the code looks good, please ask runner to run the code for you.") is true)
{
// ask runner to run the code
return true;
}
return false;
});
var reviewer2CoderTransition = Transition.Create(
from: reviewer,
to: coder,
canTransitionAsync: async (from, to, messages) =>
{
var lastMessage = messages.Last();
if (lastMessage is TextMessage textMessage && textMessage.Content.ToLower().Contains("there're some comments from code reviewer, please fix these comments") is true)
{
// ask coder to fix the code based on reviewer's comments
return true;
}
return false;
});
var runner2CoderTransition = Transition.Create(
from: runner,
to: coder,
canTransitionAsync: async (from, to, messages) =>
{
var lastMessage = messages.Last();
if (lastMessage is TextMessage textMessage && textMessage.Content.ToLower().Contains("error") is true)
{
// ask coder to fix the error
return true;
}
return false;
});
var runner2AdminTransition = Transition.Create(runner, admin);
var workflow = new Graph(
[
admin2CoderTransition,
coder2ReviewerTransition,
reviewer2RunnerTransition,
reviewer2CoderTransition,
runner2CoderTransition,
runner2AdminTransition,
]);
Once the graph is created, you can pass it to the group chat. The group chat will then use the graph along with admin agent to orchestrate the conversation flow.
var groupChat = new GroupChat(
admin: admin,
workflow: workflow,
members:
[
admin,
coder,
runner,
reviewer,
]);