2024-10-02 11:42:27 -07:00
// Copyright (c) Microsoft Corporation. All rights reserved.
2024-04-26 09:21:46 -07:00
// Example04_Dynamic_GroupChat_Coding_Task.cs
2025-01-28 17:13:36 -05:00
using AutoGen.Basic.Sample ;
2024-05-09 15:36:20 -07:00
using AutoGen.Core ;
2024-04-26 09:21:46 -07:00
using AutoGen.DotnetInteractive ;
2024-08-08 10:49:58 -07:00
using AutoGen.DotnetInteractive.Extension ;
2024-08-27 14:37:47 -07:00
using AutoGen.OpenAI ;
using AutoGen.OpenAI.Extension ;
2024-04-26 09:21:46 -07:00
using FluentAssertions ;
public partial class Example04_Dynamic_GroupChat_Coding_Task
{
public static async Task RunAsync ( )
{
var instance = new Example04_Dynamic_GroupChat_Coding_Task ( ) ;
2024-08-08 10:49:58 -07:00
var kernel = DotnetInteractiveKernelBuilder
2024-08-09 18:53:48 -07:00
. CreateDefaultInProcessKernelBuilder ( )
2024-08-08 10:49:58 -07:00
. AddPythonKernel ( "python3" )
. Build ( ) ;
2024-04-26 09:21:46 -07:00
2024-08-27 14:37:47 -07:00
var gpt4o = LLMConfiguration . GetOpenAIGPT4o_mini ( ) ;
2024-04-26 09:21:46 -07:00
2024-08-27 14:37:47 -07:00
var groupAdmin = new OpenAIChatAgent (
chatClient : gpt4o ,
2024-04-26 09:21:46 -07:00
name : "groupAdmin" ,
2024-08-27 14:37:47 -07:00
systemMessage : "You are the admin of the group chat" )
. RegisterMessageConnector ( )
2024-05-20 22:48:19 -07:00
. RegisterPrintMessage ( ) ;
2024-04-26 09:21:46 -07:00
2024-08-27 14:37:47 -07:00
var userProxy = new DefaultReplyAgent ( name : "user" , defaultReply : GroupChatExtension . TERMINATE )
2024-04-26 09:21:46 -07:00
. RegisterPrintMessage ( ) ;
// Create admin agent
2024-08-27 14:37:47 -07:00
var admin = new OpenAIChatAgent (
chatClient : gpt4o ,
2024-04-26 09:21:46 -07:00
name : "admin" ,
systemMessage : "" "
You are a manager who takes coding problem from user and resolve problem by splitting them into small tasks and assign each task to the most appropriate agent .
Here ' s available agents who you can assign task to :
2024-08-08 10:49:58 -07:00
- coder : write python code to resolve task
- runner : run python code from coder
2024-04-26 09:21:46 -07:00
The workflow is as follows :
- You take the coding problem from user
- You break the problem into small tasks . For each tasks you first ask coder to write code to resolve the task . Once the code is written , you ask runner to run the code .
- Once a small task is resolved , you summarize the completed steps and create the next step .
- You repeat the above steps until the coding problem is resolved .
You can use the following json format to assign task to agents :
` ` ` task
{
"to" : "{agent_name}" ,
"task" : "{a short description of the task}" ,
"context" : "{previous context from scratchpad}"
}
` ` `
If you need to ask user for extra information , you can use the following format :
` ` ` ask
{
"question" : "{question}"
}
` ` `
Once the coding problem is resolved , summarize each steps and results and send the summary to the user using the following format :
` ` ` summary
2024-08-08 10:49:58 -07:00
@user , < summary of the task >
2024-04-26 09:21:46 -07:00
` ` `
Your reply must contain one of [ task | ask | summary ] to indicate the type of your message .
2024-08-27 14:37:47 -07:00
"" ")
. RegisterMessageConnector ( )
2024-04-26 09:21:46 -07:00
. RegisterPrintMessage ( ) ;
// create coder agent
// The coder agent is a composite agent that contains dotnet coder, code reviewer and nuget agent.
// The dotnet coder write dotnet code to resolve the task.
// The code reviewer review the code block from coder's reply.
// The nuget agent install nuget packages if there's any.
2024-08-27 14:37:47 -07:00
var coderAgent = new OpenAIChatAgent (
2024-04-26 09:21:46 -07:00
name : "coder" ,
2024-08-27 14:37:47 -07:00
chatClient : gpt4o ,
2024-08-08 10:49:58 -07:00
systemMessage : @ "You act as python coder, you write python code to resolve task. Once you finish writing code, ask runner to run the code for you.
2024-04-26 09:21:46 -07:00
Here ' re some rules to follow on writing dotnet code :
2024-08-08 10:49:58 -07:00
- put code between ` ` ` python and ` ` `
- Try avoid using external library
2024-04-26 09:21:46 -07:00
- Always print out the result to console . Don ' t write code that doesn ' t print out anything .
2024-08-08 10:49:58 -07:00
Use the following format to install pip package :
` ` ` python
% pip install < package_name >
2024-04-26 09:21:46 -07:00
` ` `
If your code is incorrect , Fix the error and send the code again .
Here ' s some externel information
- The link to mlnet repo is : https : //github.com/dotnet/machinelearning. you don't need a token to use github pr api. Make sure to include a User-Agent header, otherwise github will reject it.
2024-08-27 14:37:47 -07:00
")
. RegisterMessageConnector ( )
2024-04-26 09:21:46 -07:00
. RegisterPrintMessage ( ) ;
// code reviewer agent will review if code block from coder's reply satisfy the following conditions:
// - There's only one code block
// - The code block is csharp code block
// - The code block is top level statement
// - The code block is not using declaration
2024-08-27 14:37:47 -07:00
var codeReviewAgent = new OpenAIChatAgent (
chatClient : gpt4o ,
2024-04-26 09:21:46 -07:00
name : "reviewer" ,
systemMessage : "" "
You are a code reviewer who reviews code from coder . You need to check if the code satisfy the following conditions :
2024-08-08 10:49:58 -07:00
- The reply from coder contains at least one code block , e . g ` ` ` python and ` ` `
- There ' s only one code block and it ' s python code block
2024-04-26 09:21:46 -07:00
You don ' t check the code style , only check if the code satisfy the above conditions .
Put your comment between ` ` ` review and ` ` ` , if the code satisfies all conditions , put APPROVED in review . result field . Otherwise , put REJECTED along with comments . make sure your comment is clear and easy to understand .
# # Example 1 # #
` ` ` review
comment : The code satisfies all conditions .
result : APPROVED
` ` `
# # Example 2 # #
` ` ` review
comment : The code is inside main function . Please rewrite the code in top level statement .
result : REJECTED
` ` `
2024-08-27 14:37:47 -07:00
"" ")
. RegisterMessageConnector ( )
2024-04-26 09:21:46 -07:00
. RegisterPrintMessage ( ) ;
// create runner agent
// The runner agent will run the code block from coder's reply.
// It runs dotnet code using dotnet interactive service hook.
// It also truncate the output if the output is too long.
2024-08-08 10:49:58 -07:00
var runner = new DefaultReplyAgent (
2024-04-26 09:21:46 -07:00
name : "runner" ,
defaultReply : "No code available, coder, write code please" )
. RegisterMiddleware ( async ( msgs , option , agent , ct ) = >
{
var mostRecentCoderMessage = msgs . LastOrDefault ( x = > x . From = = "coder" ) ? ? throw new Exception ( "No coder message found" ) ;
2024-08-08 10:49:58 -07:00
if ( mostRecentCoderMessage . ExtractCodeBlock ( "```python" , "```" ) is string code )
{
var result = await kernel . RunSubmitCodeCommandAsync ( code , "python" ) ;
// only keep the first 500 characters
if ( result . Length > 500 )
{
result = result . Substring ( 0 , 500 ) ;
}
result = $"" "
# [ CODE_BLOCK_EXECUTION_RESULT ]
{ result }
"" ";
return new TextMessage ( Role . Assistant , result , from : agent . Name ) ;
}
else
{
return await agent . GenerateReplyAsync ( msgs , option , ct ) ;
}
2024-04-26 09:21:46 -07:00
} )
. RegisterPrintMessage ( ) ;
var adminToCoderTransition = Transition . Create ( admin , coderAgent , async ( from , to , messages ) = >
{
// the last message should be from admin
var lastMessage = messages . Last ( ) ;
if ( lastMessage . From ! = admin . Name )
{
return false ;
}
return true ;
} ) ;
var coderToReviewerTransition = Transition . Create ( coderAgent , codeReviewAgent ) ;
var adminToRunnerTransition = Transition . Create ( admin , runner , async ( from , to , messages ) = >
{
// the last message should be from admin
var lastMessage = messages . Last ( ) ;
if ( lastMessage . From ! = admin . Name )
{
return false ;
}
// the previous messages should contain a message from coder
var coderMessage = messages . FirstOrDefault ( x = > x . From = = coderAgent . Name ) ;
if ( coderMessage is null )
{
return false ;
}
return true ;
} ) ;
var runnerToAdminTransition = Transition . Create ( runner , admin ) ;
var reviewerToAdminTransition = Transition . Create ( codeReviewAgent , admin ) ;
var adminToUserTransition = Transition . Create ( admin , userProxy , async ( from , to , messages ) = >
{
// the last message should be from admin
var lastMessage = messages . Last ( ) ;
if ( lastMessage . From ! = admin . Name )
{
return false ;
}
return true ;
} ) ;
var userToAdminTransition = Transition . Create ( userProxy , admin ) ;
var workflow = new Graph (
[
adminToCoderTransition ,
coderToReviewerTransition ,
reviewerToAdminTransition ,
adminToRunnerTransition ,
runnerToAdminTransition ,
adminToUserTransition ,
userToAdminTransition ,
] ) ;
// create group chat
var groupChat = new GroupChat (
admin : groupAdmin ,
members : [ admin , coderAgent , runner , codeReviewAgent , userProxy ] ,
workflow : workflow ) ;
// task 1: retrieve the most recent pr from mlnet and save it in result.txt
2024-08-08 10:49:58 -07:00
var task = "" "
retrieve the most recent pr from mlnet and save it in result . txt
"" ";
var chatHistory = new List < IMessage >
{
new TextMessage ( Role . Assistant , task )
{
From = userProxy . Name
}
} ;
await foreach ( var message in groupChat . SendAsync ( chatHistory , maxRound : 10 ) )
{
if ( message . From = = admin . Name & & message . GetContent ( ) . Contains ( "```summary" ) )
{
// Task complete!
break ;
}
}
2024-04-26 09:21:46 -07:00
2024-08-08 10:49:58 -07:00
// check if the result file is created
var result = "result.txt" ;
2024-04-26 09:21:46 -07:00
File . Exists ( result ) . Should ( ) . BeTrue ( ) ;
}
}