🔥

Semantic Kernel のマルチエージェントを試してみよう!

2024/08/16に公開

Semantic Kernel でマルチエージェントをサポートする機能が実験的機能として追加されました。
ちょっと面白そうなので、試してみようと思います。

はじめかた

マルチエージェント機能は Microsoft.SemanticKernel.Agents.Core パッケージとして提供されています。
ということでコンソールアプリのプロジェクトを作成して Agents を使うために以下のパッケージの参照を追加します。

  • Microsoft.SemanticKernel
  • Microsoft.SemanticKernel.Connectors.AzureOpenAI
  • Microsoft.SemanticKernel.Agents.Core

さらに、構成情報をユーザーシークレットから扱うのと、Managed ID 認証を使って Azure OpenAI Service に繋ぐために以下のパッケージも追加します。

  • Microsoft.Extensions.Configuration.UserSecrets
  • Azure.Identity

執筆時点での各パッケージのバージョンは以下のようになっています。

MultiAgentTest.csproj
<PackageReference Include="Azure.Identity" Version="1.12.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.SemanticKernel" Version="1.18.0-rc" />
<PackageReference Include="Microsoft.SemanticKernel.Agents.Core" Version="1.18.0-alpha" />

Semantic Kernel を使うための下準備

ここは Semantic Kernel の普通の使い方なので詳細は割愛しますが、以下のような感じで下準備をします。
ユーザーシークレットに以下のように呼び出す先の Azure OpenAI Service のエンドポイントとモデルのデプロイ名を記載します。
Managed ID を使うので Azure CLI でログインしているアカウントに対して該当の Azure OpenAI Service のリソースに対して Cognitive Servce OpenAI User のロールを付与しておきます。

secrets.json
{
  "OpenAI": {
    "Endpoint": "https://リソース名.openai.azure.com",
    "DeploymentName": "gpt-4oのモデルのデプロイ名"
  }
}

そして以下のコードを書いて Chat Completion API が呼び出せるか確認します。

Program.cs
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;

// 構成の読み込み
var configuration = new ConfigurationBuilder()
    .AddUserSecrets<Program>()
    .Build();

var endpoint = configuration["OpenAI:Endpoint"];
var deploymentName = configuration["OpenAI:DeploymentName"];

// 手抜き null チェック
ArgumentNullException.ThrowIfNull(endpoint, nameof(endpoint));
ArgumentNullException.ThrowIfNull(deploymentName, nameof(deploymentName));

// Kernel を作成
var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        deploymentName,
        endpoint,
        new AzureCliCredential())
    .Build();

// 疎通確認の ChatCompletion API の呼び出し
var chatHistory = new ChatHistory();
chatHistory.AddSystemMessage("""
    あなたは優秀な AI アシスタントです。
    """);
chatHistory.AddUserMessage("こんにちは!");

var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
var result = await chatCompletionService.GetChatMessageContentAsync(chatHistory, kernel: kernel);
Console.WriteLine(result.Items.OfType<TextContent>().First().Text);

動かすと、以下のような結果が表示されます。

こんにちは!今日はどんなお手伝いができますか?

これで Azure OpenAI Service との疎通が確認できたので、次にマルチエージェントを試してみます。

エージェントの定義

エージェントは以下のように ChatCompletionAgent クラスを使って定義できます。

var gamblerAgent = new ChatCompletionAgent
{
    Name = "GamblerAgent",
    Instructions = """
    あなたはギャンブラーです。
    数字が50より大きいか小さいかを予想してください。
    """,
    Description = "ギャンブラー",
    Kernel = kernel,
};

var diceAgent = new ChatCompletionAgent
{
    Name = "DiceAgent",
    Instructions = """
    あなたは乱数を生成するエージェントです。
    何かを言われたら、0100の間の乱数を生成して数字を返します。
    数字以外は回答しないでください。
    """,
    Description = "Dice agent は乱数を生成するエージェントです。",
    Kernel = kernel,
};

他にも沢山のプロパティがありますが、Name で名前を定義して、Instructions でエージェントへの指示を定義して、Description でエージェントの説明を定義します。
そして、Kernel には Semantic Kernel の Kernel クラスのインスタンスを設定します。今回は同じ Kernel を渡していますが、試していないのですが異なる Kernel を渡すことで違うモデルを使うようにすることが出来ると思います。

エージェントの実行

エージェントを作成したら AgentChatGroup クラスを作ってエージェントをグルーピングします。
その際に ExecutionSettings プロパティの SelectionStrategyTerminationStrategy を設定することでエージェントの実行方法を設定できます。
SelectionStrategy は次にどのエージェントが話すべきかを決める方法を定義します。TerminationStrategy はエージェントの会話を終了する条件を定義します。

SelectionStrategy にも TerminationStrategy にも Semantic Kernel の KernelFunction を指定することも出来ますし、自分で C# でクラスを実装して指定することも出来ます。
今回は、SekectionStrategy には単純に順番にエージェントを呼び出す SequentialSelectionStrategy を使い、TerminationStrategy には独自実装をしてエージェントのやりとりが3ターンで終了するようにしてみます。

// 実験的機能を使うエラーを消す
#pragma warning disable SKEXP0110
#pragma warning disable SKEXP0001

using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.Chat;

// 構成の読み込み
var configuration = new ConfigurationBuilder()
    .AddUserSecrets<Program>()
    .Build();

var endpoint = configuration["OpenAI:Endpoint"];
var deploymentName = configuration["OpenAI:DeploymentName"];

// 手抜き null チェック
ArgumentNullException.ThrowIfNull(endpoint, nameof(endpoint));
ArgumentNullException.ThrowIfNull(deploymentName, nameof(deploymentName));

// Kernel を作成
var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        deploymentName,
        endpoint,
        new AzureCliCredential())
    .Build();

// ギャンブラー
var gamblerAgent = new ChatCompletionAgent
{
    Name = "GamblerAgent",
    Instructions = """
    あなたはギャンブラーです。
    数字が50より大きいか小さいかを予想してください。
    """,
    Description = "ギャンブラー",
    Kernel = kernel,
};

// サイコロ
var diceAgent = new ChatCompletionAgent
{
    Name = "DiceAgent",
    Instructions = """
    あなたは乱数を生成するエージェントです。
    何かを言われたら、0100の間の乱数を生成して数字を返します。
    数字以外は回答しないでください。
    """,
    Description = "DiceAgent は乱数を生成するエージェントです。",
    Kernel = kernel,
};

// エージェントのグループチャット
var agentChat = new AgentGroupChat(gamblerAgent, diceAgent)
{
    ExecutionSettings = new AgentGroupChatSettings
    {
        // 順番にエージェントが発言する
        SelectionStrategy = new SequentialSelectionStrategy(),
        // 3 ターンで終了する
        TerminationStrategy = new ThreeTurnTerminationStrategy(),
    },
};

// 実行してエージェントの発言を表示
await foreach (var message in agentChat.InvokeAsync())
{
    Console.WriteLine($"{message.AuthorName}({message.Role}): {message.Content}");
}


// 3 ターンで終了するストラテジー
class ThreeTurnTerminationStrategy : TerminationStrategy
{
    protected override Task<bool> ShouldAgentTerminateAsync(Agent agent, IReadOnlyList<ChatMessageContent> history, CancellationToken cancellationToken) => 
        Task.FromResult(history.Count >= 6);
}

これを実行すると、以下のような結果が表示されます。

GamblerAgent(Assistant): 私は50より小さいと予想します。
DiceAgent(Assistant): 31
GamblerAgent(Assistant): 正解でした!次の数字を予想します。
DiceAgent(Assistant): 75
GamblerAgent(Assistant): 今回は50より大きかったですね。次に行きましょう。

次の数字が50より大きいか小さいか予想してください。
DiceAgent(Assistant): 57

Gambler と Dice が交互に会話して 3 ターンで終わっています。エージェントは順番に呼び出されているので会話として成り立ってませんが…。
なので、次は SelectionStrategy を変えて AI に次に選択するエージェントを選択してもらうようにします。KernelFunctionSelectionStrategy を使うと KernelFunction を使って次に選択するエージェントを選択できます。KernelFunction には _agents_ 変数にエージェント名のカンマ区切りの文字列と _history_ にチャットの履歴を表す JSON が入っています。

これを使って、ちゃんと数字の大小を当てているような動きになるようにしてみます。AgentGroupChat の定義を以下のように変更します。

// エージェントのグループチャット
var agentChat = new AgentGroupChat(gamblerAgent, diceAgent)
{
    ExecutionSettings = new AgentGroupChatSettings
    {
        // 会話の流れを考慮するように SelectionStrategy を変更
        SelectionStrategy = new KernelFunctionSelectionStrategy(
            kernel.CreateFunctionFromPrompt("""
                会話履歴から次に会話すべきエージェントを選択してください。
                回答にはエージェント名だけを回答して他のことは絶対に話さないでください。
                - 会話履歴がない場合は GamblerAgent が先に話します。
                - 会話履歴があって GamblerAgent が次に出る数字を予想している場合は DiceAgent が話します。
                - それ以外の場合は GamblerAgent が話します。

                ## 会話履歴
                {{$_history_}}

                ## エージェント名
                {{$_agents_}}
                """),
            kernel),
        // 3 ターンで終了する
        TerminationStrategy = new ThreeTurnTerminationStrategy(),
    },
};

実行すると以下のような結果になりました。ちゃんと数字を予想しているようになっていますね。

GamblerAgent(Assistant): おそらく数字は50より大きいでしょう。
DiceAgent(Assistant): 88
GamblerAgent(Assistant): 私の予想が当たりましたね。他にも何か予想してみたいですか?
GamblerAgent(Assistant): では、同じようにもう一度やってみましょう。私は数字が50より小さいと予想します。
DiceAgent(Assistant): 13
GamblerAgent(Assistant): 私の予想が当たりましたね!また何か予想してみますか?

AgentGroupChat には任意のメッセージを追加することも出来ます。AddChatMessage メソッドを使って、エージェントの発言の前にメッセージを追加することが出来ます。InvokeAsync を呼び出している前に以下のように追加してみました。

// こんな風にチャットメッセージをプログラムからも追加できる
agentChat.AddChatMessage(new(AuthorRole.User, "さぁ、賽は投げられた。"));
// 実行してエージェントの発言を表示
await foreach (var message in agentChat.InvokeAsync())
{
    Console.WriteLine($"{message.AuthorName}({message.Role}): {message.Content}");
}

実行すると以下のような結果になりました。出だしがそれっぽい感じになってますね。

GamblerAgent(Assistant): 賭け金と運が試される瞬間ですね。それでは、数字が50より大きいか小さいかを予想してください。
DiceAgent(Assistant): 76
GamblerAgent(Assistant): 予想が完了しました。あなたの予想は「50より大きい」です。結果を待ちましょう。
GamblerAgent(Assistant): 現時点で結果を確認する手段がありません。再度予想を立ててみますか、それとも他に試したいことがあ りますか?
GamblerAgent(Assistant): さぁ、次の賭けを始めましょう。数字が50より大きいか小さいかを再度予想してください。

終了条件も変えてみましょう。KernelFunctionSelectionStrategy を使うことで KernelFunction を使って終了条件を判定することが出来ます。KernelFunctionTerminationStrategy を使うことで KernelFunction を使って終了条件を判定することが出来ます。KernelFunction には _history_ 変数にチャットの履歴を表す JSON が入っています。_agent_ には会話したエージェントの名前が入っているので、これを使って終了判定を定義します。最終的に KernelFunction が返した結果を ResultParser でパースして true を返したら終了という判定になります。

これを使って、GamblerAgent が 2 回連続で数字の予想を当てたら終了するようにしてみます。

// 実験的機能を使うエラーを消す
#pragma warning disable SKEXP0110
#pragma warning disable SKEXP0001

using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.Chat;
using Microsoft.SemanticKernel.ChatCompletion;

// 構成の読み込み
var configuration = new ConfigurationBuilder()
    .AddUserSecrets<Program>()
    .Build();

var endpoint = configuration["OpenAI:Endpoint"];
var deploymentName = configuration["OpenAI:DeploymentName"];

// 手抜き null チェック
ArgumentNullException.ThrowIfNull(endpoint, nameof(endpoint));
ArgumentNullException.ThrowIfNull(deploymentName, nameof(deploymentName));

// Kernel を作成
var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        deploymentName,
        endpoint,
        new AzureCliCredential())
    .Build();

// ギャンブラー
var gamblerAgent = new ChatCompletionAgent
{
    Name = "GamblerAgent",
    Instructions = """
    あなたはギャンブラーです。
    数字が50より大きいか小さいかを予想してください。
    """,
    Description = "ギャンブラー",
    Kernel = kernel,
};

// サイコロ
var diceAgent = new ChatCompletionAgent
{
    Name = "DiceAgent",
    Instructions = """
    あなたは乱数を生成するエージェントです。
    何かを言われたら、0100の間の乱数を生成して数字を返します。
    数字以外は回答しないでください。
    """,
    Description = "DiceAgent は乱数を生成するエージェントです。",
    Kernel = kernel,
};

// エージェントのグループチャット
var agentChat = new AgentGroupChat(gamblerAgent, diceAgent)
{
    ExecutionSettings = new AgentGroupChatSettings
    {
        // 会話の流れを考慮するように SelectionStrategy を変更
        SelectionStrategy = new KernelFunctionSelectionStrategy(
            kernel.CreateFunctionFromPrompt("""
                会話履歴から次に会話すべきエージェントを選択してください。
                回答にはエージェント名だけを回答して他のことは絶対に話さないでください。
                - 会話履歴がない場合は GamblerAgent が先に話します。
                - 会話履歴があって GamblerAgent が次に出る数字を予想している場合は DiceAgent が話します。
                - それ以外の場合は GamblerAgent が話します。

                ## 会話履歴
                {{$_history_}}

                ## エージェント名
                {{$_agents_}}
                """),
            kernel),
        TerminationStrategy = new KernelFunctionTerminationStrategy(
            kernel.CreateFunctionFromPrompt("""
                会話履歴を確認して以下の条件を満たすかどうか判定してください。
                条件を満たす場合には Done と回答してください。

                ## 満たすべき条件
                GamblerAgent が 2 回連続で数字の予想を当てている。

                ## 会話履歴
                {{$_history_}}
                """),
            kernel)
        {
            ResultParser = result => result.GetValue<string>() == "Done",
        }
    },
};

// 実行してエージェントの発言を表示
await foreach (var message in agentChat.InvokeAsync())
{
    Console.WriteLine($"{message.AuthorName}({message.Role}): {message.Content}");
}

実行すると以下のような結果になりました。GamblerAgent さん優秀…!

GamblerAgent(Assistant): このゲームでは、ランダムに選ばれる数字が50より大きいか小さいかを予想します。予想を始めます。数 字が50より大きいと予想します。
DiceAgent(Assistant): 74
GamblerAgent(Assistant): 数字は74でした。私の予想は正しかったですね。次もこの調子で続けてみます。
GamblerAgent(Assistant): 次の数字が50より大きいか小さいかを予想します。数字が50より小さいと予想します。
DiceAgent(Assistant): 47

実用的な例

では、遊びは終わりにして実用的な例を考えてみます。お題としては、以下の記事の要件をお借りしました。

https://zenn.dev/microsoft/articles/aoai-architecture-01

この記事の要件を Azure のアーキテクチャを考える専門家と、レビューワー2人とう体制で対応するようにしてみました。コードは以下のようになります。

// 実験的機能を使うエラーを消す
#pragma warning disable SKEXP0110
#pragma warning disable SKEXP0001

using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.Chat;
using Microsoft.SemanticKernel.ChatCompletion;

// 構成の読み込み
var configuration = new ConfigurationBuilder()
    .AddUserSecrets<Program>()
    .Build();

var endpoint = configuration["OpenAI:Endpoint"];
var deploymentName = configuration["OpenAI:DeploymentName"];

// 手抜き null チェック
ArgumentNullException.ThrowIfNull(endpoint, nameof(endpoint));
ArgumentNullException.ThrowIfNull(deploymentName, nameof(deploymentName));

// Kernel を作成
var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        deploymentName,
        endpoint,
        new AzureCliCredential())
    .Build();

var architectAgent = new ChatCompletionAgent
{
    Name = "AzureArchitectAgent",
    Instructions = """
    あなたは Microsoft Azure のアーキテクチャを考える専門家です。
    お客様の要望にあわせて最適なアーキテクチャを提案します。
    """,
    Description = "Microsoft Azure のアーキテクチャを考える専門家です。",
    Kernel = kernel,
};

var reviewerAgent = new ChatCompletionAgent
{
    Name = "ReviewerAgent",
    Instructions = """
    あなたは Microsoft Azure の専門家です。
    非機能要求(性能・ログ・運用・バックアップなど)の観点からアーキテクチャーのレビューを行ってください。
    もし、提示されたアーキテクチャーに考慮漏れが有る場合には指摘を行ってください。
    回答には指摘事項以外を含めないでください。指摘事項はわかりやすく箇条書きで記載してください。
    指摘事項が無い場合は「指摘事項はありません」と回答してください。
    """,
    Description = "非機能要求の観点からレビューを行うレビューワーです。",
    Kernel = kernel,
};

var securityAgent = new ChatCompletionAgent
{
    Name = "SecurityAgent",
    Instructions = """
    あなたはセキュリティの専門家です。
    提示されたアーキテクチャーにセキュリティ上の問題点がある場合は指摘を行ってください。    
    回答には指摘事項以外を含めないでください。指摘事項はわかりやすく箇条書きで記載してください。
    指摘事項が無い場合は「指摘事項はありません」と回答してください。
    """,
    Description = "セキュリティの専門家の観点でレビューを行うレビューワーです。",
    Kernel = kernel,
};

var agentChat = new AgentGroupChat(architectAgent, reviewerAgent, securityAgent)
{
    ExecutionSettings = new()
    {
        SelectionStrategy = new KernelFunctionSelectionStrategy(
            kernel.CreateFunctionFromPrompt($$$"""
                会話履歴を確認して次に話すエージェントを選択してください。
                ユーザーが解決したい問題吟味して以下のエージェント一覧の中から次に発言をするべき適切なエージェントを選択してください。
                回答にはエージェント名だけを回答して他のことは絶対に話さないでください。
                - {{{architectAgent.Name}}} が最初にアーキテクチャを考えるエージェントです
                - {{{architectAgent.Name}}} の回答には {{{reviewerAgent.Name}}} によるレビューの指摘が必要です
                - {{{architectAgent.Name}}} の回答には {{{securityAgent.Name}}} によるセキュリティの指摘が必要です
                - レビューで指摘を受けた場合には {{{architectAgent.Name}}} が指摘を反映して回答を修正する必要があります
                - 修正した回答には {{{reviewerAgent.Name}}}{{{securityAgent.Name}}} が再度レビューを行います

                ## エージェント一覧
                - {{{architectAgent.Name}}}: {{{architectAgent.Description}}}
                - {{{reviewerAgent.Name}}}: {{{reviewerAgent.Description}}}
                - {{{securityAgent.Name}}}: {{{securityAgent.Description}}}

                ## 会話履歴
                {{$_history_}}
                """),
            kernel),
        TerminationStrategy = new KernelFunctionTerminationStrategy(
            kernel.CreateFunctionFromPrompt($$$"""
                会話履歴を以下の観点で確認をして、全ての条件を満たしている場合には Done と回答して、そうじゃない場合には Continue と回答してください。
                回答には Done と Continue 意外を含めないでください。

                ## 確認の観点
                - {{{architectAgent.Name}}}の回答には {{{reviewerAgent.Name}}} によるレビューの指摘が全て反映されている
                - {{{architectAgent.Name}}}の回答には {{{securityAgent.Name}}} によるセキュリティの指摘が全て反映されている
                - {{{reviewerAgent.Name}}} がレビューの反映結果に対して問題ないという見解を示している。
                - {{{securityAgent.Name}}} がレビューの反映結果に対して問題ないという見解を示している。
                - ユーザーの要望が全て満たされている
                
                ## 会話履歴
                {{$_history_}}
                """),
            kernel)
        {
            ResultParser = result => result.GetValue<string>() == "Done",
        }
    }
};

agentChat.AddChatMessage(new(AuthorRole.User, """
    以下のシステムアーキテクチャ設計を Microsoft Azure を利用して行ってください。アーキテクチャー図は mermaid 形式で記述してください。
    =============== business requirements ========================
    - 1982 年に設立された Contoso 社は、世界規模でゴルフ製品(ゴルフクラブ、ゴルフボール、ゴルフ向けの付属製品)を販売する巨大メーカーに成長した
    - リ○マ○ショックの景気後退により、東京近辺にデータセンタを集約したものの、500万円/月程度のデータセンタ費用が発生している
    - 更に、日本各所でいくつかのサーバを利用しており、コストがかかっている
    =============== current system issues  ========================
    - データセンタ内に数十の Web サーバが存在し、顧客データが複数の場所に分散して存在している
    -ハードウェア更新時期を迎え、今後のハードウェア更新リスクを回避するめに Contoso 社はシステムをどこかに移行したい
    =============== system requirements ========================
    - 「製品カタログ」「在庫」「部門」の三つの WEB アプリケーションが存在する
    - Web アプリは C# で開発されている
    - 全世界に販売しているため、日本以外の国からのレスポンスも改善が必要
    - 製品カタログのデータは SQL Server に格納されており、製品カタログはアジア(日本含む)、アメリカ東海岸、西ヨーロッパからアクセスされる
    - 製品カタログのデータは日本でしか更新されず、夜間にデータバックアップが取得されている
    - 製品カタログのデータベースサイズは 50GB で、年間 5% 程度データ量が増加する
    - 製品カタログは主に読み込み用途で利用される
    - 外部アクセスからのセキュリティ担保は SSL 暗号化通信を想定しているが、内部ユーザ(カタログ更新)は SSL に加えたオンプレミスからの閉域ネットワークを想定```
    """));
await foreach (var message in agentChat.InvokeAsync())
{
    Console.WriteLine($"{message.AuthorName}({message.Role}): {message.Content}");
    await Task.Delay(1000);
}

実行結果は以下のようになりました。

AzureArchitectAgent(Assistant): ### Azure アーキテクチャ提案

以下に、上記の要件に応じた Azure ベースのアーキテクチャを mermaid 形式で提案します。このアーキテクチャでは、Azure の管理されたサービスを活用してコストを削減しながら、セキュリティ、スケーラビリティ、および高可用性を確保することを目指します。
    ```mermaid
    graph TD;
        subgraph On-Premises
            UserInternal[内部ユーザ]
        end
    
        subgraph Azure["Microsoft Azure"]
            subgraph AppServices["Web アプリケーションサービス"]
                CatalogApp[製品カタログ Web アプリ]
                InventoryApp[在庫 Web アプリ]
                DepartmentApp[部門 Web アプリ]
            end
    
            subgraph SQLServer["Azure SQL Database"]
                CatalogDB[(製品カタログDB)]
            end
    
            subgraph Storage["Blob Storage"]
                BackupData[バックアップデータ]
            end
    
            subgraph Networking["Virtual Network (VNet)"]
                Subnet["Subnet"]
                VPNGateway[VPN Gateway]
            end
        end
    
        subgraph "Regions"
            AsiaDC["Asia Data Center"]
            USDC["US East Data Center"]
            EUDC["Europe West Data Center"]
        end
    
        InternalUser --> VPNGateway --> CatalogApp
        UserInternal -.-> VPNGateway
    
        CatalogApp --> CatalogDB
        InventoryApp --> InventoryDB[(在庫DB)]
        DepartmentApp --> DepartmentDB[(部門DB)]
    
        CatalogApp & CatalogDB <--> AsiaDC
        CatalogApp & CatalogDB <--> USDC
        CatalogApp & CatalogDB <--> EUDC
    
        CatalogDB --> BackupData
    
        subgraph ExternalAccess[外部アクセス]
            UserExternal[外部ユーザ]
        end
    
        UserExternal --> LB[ロードバランサ]
        LB --> CatalogApp
        LB --> InventoryApp
        LB --> DepartmentApp
    
        VPNGateway --> VNet
        VNet --> Subnet
    
        CatalogDB -. Backup (夜間) .-> BackupData
    ```

### アーキテクチャ概要

1. **App Services (Web アプリケーションサービス)**
   - 製品カタログ、在庫、および部門の Web アプリケーションを Azure App Services にホスト。これにより、管理の手間を大幅に軽減し、スケーラビリティと高可用性を実現します。

2. **Azure SQL Database**
   - 製品カタログデータを Azure SQL Database に保存。アメリカ東、ヨーロッパ西、およびアジア(日本含む)のリージョンでレ プリケーションを行い、グローバルなアクセスのスピードを向上させます。データのバックアップは夜間に Blob Storage に保存。

3. **Networking**
   - 内部ユーザがオンプレミスから安全にアクセスできるように、Azure Virtual Network (VNet) と VPN Gateway を使ってオンプ レミスとの閉域ネットワークを構築。

4. **Blob Storage**
   - バックアップデータを保存するために利用。

5. **ロードバランサ**
   - 外部ユーザからの Web アプリケーションへのアクセスを分散するため、ロードバランサを使用。

### 推奨される Azure サービス

- **Azure App Service**: マネージドされた Web ホスティングプラットフォーム。
- **Azure SQL Database**: マネージドされたデータベースサービスで、高可用性、スケーラビリティ、セキュリティを提供。
- **Azure Blob Storage**: スケーラブルなオブジェクトストレージ。
- **Azure Virtual Network (VNet)**: 仮想ネットワークを作成し、セキュアな接続を実現。
- **Azure VPN Gateway**: オンプレミスとの安全な接続を確保。
- **Azure Load Balancer**: トラフィックを複数のサービスに分散。

このアーキテクチャはコスト効率、スケーラビリティ、セキュリティに優れたクラウドベースのソリューションを提供し、Contoso 社の現在のシステムの課題を解消することを目指しています。
ReviewerAgent(Assistant): ### 非機能要求の観点でのアーキテクチャレビュー

提案されたアーキテクチャを非機能要求の観点からレビューし、考慮漏れがあるかを確認します。

#### 性能
- グローバルアクセス向けに複数地域でレプリケーションを行っている点が評価できます。
- 各Webアプリに対してロードバランサが設定されており、スケーラビリティの観点でも十分です。

#### ログ
- ログ管理に関する記述がありません。Azure MonitorやAzure Log Analyticsを使ってログを一元管理することを推奨します。
  - **指摘事項**: Azure Monitor や Log Analytics を使ってアプリケーションおよびデータベースのログを収集し、監視を行うこ とを考慮してください。

#### 運用
- セキュリティについてはVPN Gatewayを使って閉域ネットワークを構成しており、内部ユーザのアクセスのセキュリティが保たれて います。
- しかし、運用監視に関する具体的な内容が不足しています。
  - **指摘事項**: Azure Monitorを利用したリソースの監視とアラート設定を追加することを検討してください。
  - **指摘事項**: Azure Managed Disksの使用を検討し、ディスクレベルでのスナップショットバックアップも考慮してください。

#### バックアップ
- SQLデータベースのバックアップ方法については記述がありますが、他のデータ(Webアプリケーションの設定やVNet設定など)のバックアップについては明確にされていません。
  - **指摘事項**: Azure Backupを使って、Webアプリケーションやインフラ全体のバックアップソリューションを追加してください 。

### 指摘事項のサマリー
- **ログ管理**: Azure Monitor や Log Analytics を使ってログの収集と監視を行うことを考慮してください。
- **運用監視**: Azure Monitorを利用したリソースの監視とアラート設定を追加することを検討してください。
- **ディスクバックアップ**: Azure Managed Disksの使用とスナップショットバックアップを考慮してください。
- **全体バックアップ**: Azure Backupを使って、Webアプリケーションやインフラ全体のバックアップソリューションを追加してく ださい。
SecurityAgent(Assistant): ### セキュリティ上の問題点

提案された Azure アーキテクチャには以下のセキュリティ上の問題点があります:

- **監視とログ管理の不足**:
  - ログと監視に関する記述がないため、攻撃検知やインシデント対応が困難となる可能性があります。

- **データのバックアップ戦略の不足**:
  - データベース以外のデータ(Webアプリケーションの設定やVNet設定など)のバックアップが考慮されていません。

- **内部ユーザの認証方法不明**:
  - 内部ユーザの認証とアクセス制御についての具体的な方法が記載されていません。

### 指摘事項

- **監視とログ管理**:
  - Azure Monitor や Log Analytics を使ってアプリケーションおよびデータベースのログを収集し、監視を行うことを考慮してく ださい。

- **データのバックアップ**:
  - Azure Backupを利用して、Webアプリケーションやインフラ全体のバックアップソリューションを追加してください。
  - Azure Managed Disksの使用を検討し、ディスクレベルでのスナップショットバックアップも考慮してください。

- **内部ユーザの認証**:
  - Azure Active Directory (AAD) を利用し、内部ユーザの認証とアクセス制御を行うことを検討してください。
  - 多要素認証(MFA)を導入し、セキュリティを強化してください。
AzureArchitectAgent(Assistant): 以下の追加要件を反映した改善策を含む修正版のシステムアーキテクチャを提示します。

### 改善点
1. **ログ管理**: Azure Monitor および Azure Log Analytics を使用して、すべてのリソースのログを収集および監視します。
2. **運用監視**: Azure Monitor を利用して、リソースの監視とアラート設定を行います。
3. **ディスクバックアップ**: Azure Managed Disks のスナップショットバックアップを導入します。
4. **全体バックアップ**: Azure Backup を使用して、Web アプリケーションやインフラ全体のバックアップソリューションを追加します。
5. **内部ユーザの認証**: Azure Active Directory (AAD) を使用し、多要素認証 (MFA) を導入します。

### 修正版アーキテクチャ図

    ```mermaid
    graph TD;
        subgraph On-Premises
            InternalUser[内部ユーザ]
        end
    
        subgraph Azure["Microsoft Azure"]
            subgraph AppServices["Web アプリケーションサービス"]
                CatalogApp[製品カタログ Web アプリ]
                InventoryApp[在庫 Web アプリ]
                DepartmentApp[部門 Web アプリ]
            end
    
            subgraph SQLServer["Azure SQL Database"]
                CatalogDB[(製品カタログDB)]
            end
    
            subgraph Storage["Blob Storage"]
                BackupData[バックアップデータ]
            end
    
            subgraph Networking["Virtual Network (VNet)"]
                Subnet["Subnet"]
                VPNGateway[VPN Gateway]
            end
    
            subgraph Monitoring["Monitor & Log Analytics"]
                MonitorSystem[Azure Monitor]
                LogAnalytics[Log Analytics]
            end
    
        end
    
        subgraph "Regions"
            AsiaDC["Asia Data Center"]
            USDC["US East Data Center"]
            EUDC["Europe West Data Center"]
        end
    
        InternalUser --> VPNGateway --> CatalogApp
        InternalUser -.-> VPNGateway
    
        CatalogApp --> CatalogDB
        InventoryApp --> InventoryDB[(在庫DB)]
        DepartmentApp --> DepartmentDB[(部門DB)]
    
        CatalogApp & CatalogDB <--> AsiaDC
        CatalogApp & CatalogDB <--> USDC
        CatalogApp & CatalogDB <--> EUDC
    
        CatalogDB --> BackupData
        Subnet --> MonitorSystem
        Subnet --> LogAnalytics
    
        subgraph ExternalAccess[外部アクセス]
            UserExternal[外部ユーザ]
        end
    
        UserExternal --> LB[ロードバランサ]
        LB --> CatalogApp
        LB --> InventoryApp
        LB --> DepartmentApp
    
        VPNGateway --> VNet
        VNet --> Subnet
    
        CatalogDB -. Backup (夜間) .-> BackupData
    
        subgraph AAD["Azure Active Directory"]
            AADUser[内部ユーザ認証 (MFA)]
        end
    
        InternalUser --> AADUser -.-> VPNGateway
    ```
    
### ポイントのまとめ

1. **Azure Monitor & Log Analytics**:
   - Azure Monitor および Log Analytics を使用してリソースのログを収集し、監視。
2. **Azure Managed Disks**:
   - ディスクレベルでのスナップショットバックアップを導入。
3. **Azure Backup**:
   - 包括的なバックアップソリューションを提供。
4. **Azure Active Directory (AAD)**:
   - 内部ユーザの認証に Azure Active Directory を使用し、MFA (多要素認証) を導入。

この改良版アーキテクチャにより、Contoso 社の現行システムの問題点を解決し、コスト効率、可用性、セキュリティ、運用管理を大幅に向上させることができます。
ReviewerAgent(Assistant): ### 非機能要求の再レビュー

提案された修正版のAzureアーキテクチャに対する非機能要求の観点からの指摘を確認します。

#### 性能
- グローバルアクセス向けの複数地域でのレプリケーションを行っているため、優れたパフォーマンスが期待できます。

#### ログ
- Azure MonitorやLog Analyticsの利用が考慮され、ログ管理が強化されています。

#### 運用
- Azure Monitorを使用したリソースの監視とアラート設定が行われています。

#### バックアップ
- ディスクのスナップショットバックアップおよびAzure Backupを使用した包括的なバックアップが考慮されています。

### セキュリティの再レビュー

#### 認証とアクセス制御
- Azure Active Directory (AAD)と多要素認証(MFA)の利用により、セキュリティが強化されています。

### 指摘事項のサマリー
「指摘事項はありません」

### 総合評価

再レビューの結果、提案された修正版のアーキテクチャは非機能要求の観点から十分な考慮がされており、特にログ管理、運用監視、バックアップおよびセキュリティ面での強化が行われています。このアーキテクチャはContoso社のニーズに応じた適切なソリューシ ョンを提供しています。

全てのやりとりが出ているので長いですが、二人のレビューワーが指摘を行っている様子が見て取れると思います。
因みに最終的に出力された mermaid には文法エラーがあったので GitHub Copilot とかを駆使してエラーだけとったバージョンを以下に示します。なんか、Azure のサービス名を正確に書いて欲しい感じもありますね…。

アーキテクチャー図を mermaid で詳細に書くこと自体が若干無理がありそうな気もする (アイコンとか使いたい…) ので、完璧を求めるのは難しいかもしれませんが、まぁとりあえずやってみた感じとしてはこんなものでしょうか…。

gpt-4o の知識のカットオフの時期以降の変化は考慮されていないため実際に使う場合は RAG をするか、Azure 特化カスタムモデルでも作るかといった手を入れる必要がありますが、なんとなく動いてくれました。まぁまぁいけそうですかね?

まとめ

ということで Semantic Kernel のマルチエージェントの機能を使って遊んでみました。昔はハンドメイドで AI 同士を会話させるといったことをやって遊んでましたが、これを使うと簡略化して実装出来て良い感じですね。リリースに期待…!

Microsoft (有志)

Discussion