C# でも動作するマルチエージェント フレームワーク AutoGen 動的グループチャット編

2025/02/08
★★★

複数のエージェントを動的に連携させて問題解決を行うグループチャット

前回は、複数のエージェントをシーケンシャルに実行してタスクを実行するラウンドロビン グループチャットについて説明しました。
今回は、タスク解決のために動的にエージェント選択して実行する GroupChat について説明します。

エージェントの種類としては以下を実装していきます。

  • coderAgent: タスク解決のためのコードを記述するエージェント。
  • runnerAgent: coderAgent が記述したコードを実行するエージェント。
  • summarizerAgent: コード実行結果を要約するエージェント。
  • userProxyAgent: ユーザーの入力を受け取るエージェント。
  • groupAdminAgent: 各エージェントの実行順を制御するエージェント。

これらのエージェントを使用して、与えられた問題をコード記述及び実行によって解決します。

coderAgent

Azure OpenAI を使用して C# コードを記述する coderAgent を実装します。
システム メッセージには、コーディングのルールを宣言します。

string azureOpenAIEndpoint = "<Azure OpenAI Service エンドポイント>";
string azureOpenAIKey = "<Azure OpenAI Service キー>";
string azureOpneAIModelDeployName = "<Azure OpenAI Service 配置モデル名>";

// Azure OpenAI チャット クライアント
AzureOpenAIClient azureOpenAIClient = new(new Uri(azureOpenAIEndpoint), new AzureKeyCredential(azureOpenAIKey));
var azOpenAIChatClient = azureOpenAIClient.GetChatClient(azureOpneAIModelDeployName);

// コーダー エージェント: C# コードの記述
var coderAgent = new OpenAIChatAgent(
	name: "coderAgent",
	chatClient: azOpenAIChatClient,
	systemMessage: """
		あなたは C# プログラマーです。あなたは、タスクを解決するために C# コードを記述します。
		あなたが記述したコードは、runnerAgent によって実行されます。

		C# コードを記述する際のルールは次のとおりです。
		- ```csharp と ``` の間にコードを記述してください。				
		- 明示的な型指定を使用してください。var を使用しないでください。				
		- 外部ライブラリの使用を避け、代わりに .NET Core ライブラリを使用するようにしてください。				
		- トップレベルのステートメントを使用してください。Main メソッドを使用してコードを書かないでください。
		- コードの実行結果が分かるようにコンソール出力するコードを記述してください。
		- .NET Interactive で実行可能なコードを記述してください。
		- インターネットへの接続は HttpClient クラスを使用してください。
		""")
	.RegisterMessageConnector()
	.RegisterPrintMessage();

「```csharp と ``` の間にコードを記述してください。」とすることで、runnerAgent でのコード実行時に、コード部分を抽出できるようにしています。
runnerAgent では、.NET Interactive によるコード実行を行うので、「トップレベルのステートメントを使用してください。Main メソッドを使用してコードを書かないでください。」、「.NET Interactive で実行可能なコードを記述してください。」としています。
また、.NET Interactive での実行結果を取得できるように、「コードの実行結果が分かるようにコンソール出力するコードを記述してください。」としています。
その他のルールについては、タスクに対して正しくコードが生成されない場合に、正しくコードが生成されるようなルールを追加していってください。

runnerAgent

.NET Interactive を使用して C# コードを実行する runnerAgent を実装します。
まず、DotnetInteractiveKernelBuilder で .NET Interactive カーネルを初期化します。

// .NET Interactive カーネル
var dotnetInteractiveKernel = DotnetInteractiveKernelBuilder
	.CreateDefaultInProcessKernelBuilder()
	// .AddPythonKernel("python3")  // Python カーネルを追加
	.Build();

.AddPythonKernel("python3") とすることで、Python コードを実行させることもできます。

次に、AssistantAgent でコード実行エージェントを実装します。

// コード実行エージェント
var runnerAgent = new AssistantAgent(
	name: "runnerAgent",
	systemMessage: "あなたは与えられた C# コードを実行します。",
	defaultReply: "実行コードが見つかりません。coderAgent さんコードを記述してください。")
	.RegisterMiddleware(async (msgs, option, agent, ct) =>
	{
		var mostRecentCoderMessage = msgs.LastOrDefault(x => x.From == "coderAgent");		

		if (mostRecentCoderMessage?.ExtractCodeBlock("```csharp", "```") is string code)
		{
			// コードブロックの実行
			var result = await dotnetInteractiveKernel.RunSubmitCodeCommandAsync(code, "csharp");

			result = $"""
            # コードの実行結果
            {result}
            """;					

			return new TextMessage(Role.Assistant, result, from: agent.Name);
		}
		else
		{
			// defaultReply を返信
			return await agent.GenerateReplyAsync(msgs, option, ct);
		}
	})
	.RegisterPrintMessage();

RegisterMiddleware 拡張メソッドでチャット履歴を取得し、coderAgent が送信したメッセージを取得します。
coderAgent が送信したメッセージから、コード部分を取得して、Microsoft.DotNet.Interactive.Kernel.RunSubmitCodeCommandAsync でコードを実行します。 コード実行時のコンソール出力が戻り値として取得できるので、このエージェントの出力としています。

summarizerAgent

コード実行結果を要約する summarizerAgent を実装します。

// コード実行結果の要約エージェント
var summarizerAgent = new OpenAIChatAgent(
	name: "summarizerAgent",
	chatClient: azOpenAIChatClient,
	systemMessage: """
		あなたは runnerAgent が実行したコードの実行結果を要約して説明します。
		""")
	.RegisterMessageConnector()
	.RegisterPrintMessage();

userProxyAgent

ユーザーの入力を受け取る userProxyAgent を実装します。前々回の記事で説明しています。

var userProxyAgent = new UserProxyAgent(
	name: "user",
	humanInputMode: HumanInputMode.ALWAYS)
	.RegisterPrintMessage();

groupAdminAgent

各エージェントの実行順を制御する groupAdminAgent を実装します。
システム メッセージで各エージェントの実行ルールを宣言しています。

// グループ チャット管理者エージェント
var groupAdminAgent = new OpenAIChatAgent(
	chatClient: azOpenAIChatClient,
	name: "groupAdminAgent",
	systemMessage: """
		あなたはグループチャットの管理者です。
		user から受け取った問題を coderAgent によるコーディング、runnerAgent によるコード実行で解決を行います。
		コード実行が成功したら user から受け取った問題とコードの実行結果から summarizerAgent で回答を生成してください。
		""")
	.RegisterMessageConnector()
	.RegisterPrintMessage();

グループチャット

各エージェントをグループチャットに登録します。

// グループチャット
var groupChat = new GroupChat(
	admin: groupAdminAgent,	
	members:
	[		
		coderAgent,
		runnerAgent,
		summarizerAgent,
		userProxyAgent,
	]);

GroupChat コンストラクタの admin を指定することで、各エージェントの実行順を制御するエージェントを明示的に登録できます。
members には、グループチャットのメンバーとする各エージェントを設定します。

グループチャットへのメッセージの送信

最後に、グループチャットへの最初のメッセージ送信部分を実装します。

// プロンプトからユーザーのメッセージを取得
Console.Write("input message: ");
string userMessage = Console.ReadLine() ?? string.Empty;

var groupChatManager = new GroupChatManager(groupChat);

// 取得したメッセージで会話を開始
await userProxyAgent.InitiateChatAsync(	
	message: userMessage,
	receiver: groupChatManager,	
	maxRound: 30);

Console.ReadLine で取得したメッセージでグループチャットを開始します。

マルチエージェントで与えられた問題をコード記述及び実行によって解決

これまで説明したコードをコンソール アプリケーションで実装した例は以下となります。

using AutoGen;
using AutoGen.Core;
using AutoGen.DotnetInteractive;
using AutoGen.DotnetInteractive.Extension;
using AutoGen.OpenAI;
using AutoGen.OpenAI.Extension;
using AutoGen.SemanticKernel.Extension;
using Azure;
using Azure.AI.OpenAI;

namespace AutoGenConsoleAppDemo003;

internal class Program
{
	private static readonly string _azureOpenAIEndpoint = "https://<Azure OpenAI Service エンドポイント ドメイン名>.openai.azure.com/";
	private static readonly string _azureOpenAIKey = "<Azure OpenAI Service エンドポイント キー>";
	private static readonly string _azureOpenAIModelDeployName = "gpt-4o";

	static async Task Main(string[] args)
	{
		string azureOpenAIEndpoint = _azureOpenAIEndpoint;
		string azureOpenAIKey = _azureOpenAIKey;
		string azureOpneAIModelDeployName = _azureOpenAIModelDeployName;

		// Azure OpenAI チャット クライアント
		AzureOpenAIClient azureOpenAIClient = new(new Uri(azureOpenAIEndpoint), new AzureKeyCredential(azureOpenAIKey));
		var azOpenAIChatClient = azureOpenAIClient.GetChatClient(azureOpneAIModelDeployName);

		// コーダー エージェント: C# コードの記述
		var coderAgent = new OpenAIChatAgent(
			name: "coderAgent",
			chatClient: azOpenAIChatClient,
			systemMessage: """
				あなたは C# プログラマーです。あなたは、タスクを解決するために C# コードを記述します。
				あなたが記述したコードは、runnerAgent によって実行されます。

				C# コードを記述する際のルールは次のとおりです。
				- ```csharp と ``` の間にコードを記述してください。				
				- 明示的な型指定を使用してください。var を使用しないでください。				
				- 外部ライブラリの使用を避け、代わりに .NET Core ライブラリを使用するようにしてください。				
				- トップレベルのステートメントを使用してください。Main メソッドを使用してコードを書かないでください。
				- コードの実行結果が分かるようにコンソール出力するコードを記述してください。
				- .NET Interactive で実行可能なコードを記述してください。
				- インターネットへの接続は HttpClient クラスを使用してください。
				""")
			.RegisterMessageConnector()
			.RegisterPrintMessage();

		// .NET Interactive カーネル
		var dotnetInteractiveKernel = DotnetInteractiveKernelBuilder
			.CreateDefaultInProcessKernelBuilder()
			// .AddPythonKernel("python3")  // Python カーネルを追加
			.Build();

		// コード実行エージェント
		var runnerAgent = new AssistantAgent(
			name: "runnerAgent",
			systemMessage: "あなたは与えられた C# をコードを実行します。",
			defaultReply: "実行コードが見つかりません。coderAgent さんコードを記述してください。")
			.RegisterMiddleware(async (msgs, option, agent, ct) =>
			{
				var mostRecentCoderMessage = msgs.LastOrDefault(x => x.From == "coderAgent");

				if (mostRecentCoderMessage?.ExtractCodeBlock("```csharp", "```") is string code)
				{
					// コードブロックの実行
					var result = await dotnetInteractiveKernel.RunSubmitCodeCommandAsync(code, "csharp");

					result = $"""
                    # コードの実行結果
                    {result}
                    """;					

					return new TextMessage(Role.Assistant, result, from: agent.Name);
				}
				else
				{
					// defaultReply を返信
					return await agent.GenerateReplyAsync(msgs, option, ct);
				}
			})
			.RegisterPrintMessage();

		// コード実行結果の要約エージェント
		var summarizerAgent = new OpenAIChatAgent(
			name: "summarizerAgent",
			chatClient: azOpenAIChatClient,
			systemMessage: """
				あなたは runnerAgent が実行したコードの実行結果を要約して説明します。
				""")
			.RegisterMessageConnector()
			.RegisterPrintMessage();

		// ユーザー プロキシ エージェント
		var userProxyAgent = new UserProxyAgent(
			name: "user",
			humanInputMode: HumanInputMode.ALWAYS)
			.RegisterPrintMessage();

		// グループ チャット管理者エージェント
		var groupAdminAgent = new OpenAIChatAgent(
			chatClient: azOpenAIChatClient,
			name: "groupAdminAgent",
			systemMessage: """
				あなたはグループチャットの管理者です。
				user から受け取った問題を coderAgent によるコーディング、runnerAgent によるコード実行で解決を行います。
				コード実行が成功したら user から受け取った問題とコードの実行結果から summarizerAgent で回答を生成してください。
				""")
			.RegisterMessageConnector()
			.RegisterPrintMessage();

		// グループチャット
		var groupChat = new GroupChat(
			admin: groupAdminAgent,			
			members:
			[				
				coderAgent,
				runnerAgent,
				summarizerAgent,
				userProxyAgent,
			]);

		// プロンプトからユーザーのメッセージを取得
		Console.Write("input message: ");
		string userMessage = Console.ReadLine() ?? string.Empty;

		var groupChatManager = new GroupChatManager(groupChat);

		// プロンプト取得したメッセージで会話を開始
		await userProxyAgent.InitiateChatAsync(			
			message: userMessage,
			receiver: groupChatManager,			
			maxRound: 30);
	}
}

このコードの実行後、Console.ReadLineでユーザーの入力を待機します。 コード実行後

フェボナッチ数の問題を質問してみます。 ユーザー質問の入力

問題を解決するために coderAgent がコードを記述していることが確認できます。 coderAgent のコード記述

続いて runnerAgent によりコードが実行されています。 runnerAgent によるコード実行

最後に、summarizerAgent により結果が要約されています。 summarizerAgent による要約

前回の記事では、AutoGen for .NET ラウンドロビン グループチャットを使用したシーケンシャルにエージェントを実行するグループチャットの方法について説明しました。 今回は、与えられた問題に対してダイナミックにエージェント実行させてタスクを解決する方法を説明しました。

ラウンドロビン グループチャットを使用した場合ではエージェントの実行順序を明示的に指定できます。今回使用した GroupChat では、admin に指定した groupAdminAgent で実行順を制御できます。 今回の例であるコード記述、コード実行、結果の要約というタスクの実行順は固定的なためラウンドロビン グループチャットでの実装の方が適切かもしれません。

動的なグループチャットを使用するシーンとしては、問題を解決するために複数のタスクの実行が必要で、問題の内容によって実行順序や必要なエージェントが異なる場合ではないかと考えています。

動的なグループチャットでは各エージェントの制御が難しくなるため解決したい問題に応じて各エージェントの制御方法を選択することが重要であると考えます。

コメント (0)

コメントの投稿