クリスマスを救う Microsoft のエージェント フレームワーク

· 8分で読める

はじめに

完璧なクリスマスプレゼントを見つけるのはストレスになるかもしれません。ギフトのアイデアをブレインストーミングしたり、店舗間の価格を比較したり、すべてが予定通りに届くようにしたりする間に、ホリデー ショッピングはすぐに大変になってしまいます。これらのタスクを、連携して動作する専門の AI エージェントに委任できたらどうなるでしょうか?この投稿では、Microsoft のエージェント フレームワークを使用して、ギフトのアイデアの生成から価格の比較まで、ワークフローを通じてすべてが調整される特定のタスクを各エージェントが専門とするマルチエージェント システムを構築する方法を説明します。

フェスティブ テック カレンダー 2025

このプロジェクトは、ホリデー シーズン中のテクノロジーを祝う素晴らしいコミュニティ イベントである Festive Tech Calendar 2025 での私のセッションの一部です。イベントの詳細については、Sessionize をご覧ください。

Microsoft のエージェント フレームワークとは何ですか?

Agent Framework は、AI エージェントおよびマルチエージェント システムを構築、オーケストレーション、展開するための Microsoft のソリューションです。これは、順次、同時、またはハンドオフ パターンを通じて動作できるエージェントを作成するための柔軟な基盤を提供します。このフレームワークは OpenAI、Azure OpenAI、Microsoft Foundry モデルをサポートしており、非常に多用途です。

主な機能は次のとおりです。

  • マルチエージェント オーケストレーション: グループ チャット、順次、同時、ハンドオフ パターン
  • プラグイン エコシステム: ネイティブ関数、OpenAPI、およびモデル コンテキスト プロトコル (MCP) で拡張します。
  • ワークフロー サポート: エグゼキュータとエッジを使用して複雑なエージェント パイプラインを構築

前提条件

コードに入る前に、以下が揃っていることを確認してください。

  • .NET9
  • Azure OpenAI アクセス (または OpenAI API キー)
  • Visual Studio または Visual Studio Code

必要なパッケージをインストールします (プレビュー中は --prerelease フラグが必要であることに注意してください)。

dotnet add package Microsoft.Agents.AI.OpenAI --prerelease
dotnet add package Microsoft.Agents.AI.Workflows --prerelease
dotnet add package Azure.AI.OpenAI
dotnet add package Azure.Identity
dotnet add package Azure.AI.Agents.Persistent --prerelease

ギフト ショッピング エージェント

当社のクリスマス プレゼント ファインダーは、連携する 3 人の専門エージェントで構成されます。

  1. ギフト アイデア エージェント - 受取人のプロフィールに基づいてクリエイティブなギフトの提案を生成します
  2. 価格比較エージェント - さまざまな店舗間で最高の価格を見つけます
  3. サマリー エージェント - 最終的な推奨事項をまとめます

モデル

まず、エージェント パイプラインを流れるデータ モデルを定義します。

public class GiftRecipient
{
    public string Name { get; set; } = string.Empty;
    public int Age { get; set; }
    public List<string> Interests { get; set; } = [];
    public decimal Budget { get; set; }
}

public class GiftIdea
{
    public string Name { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public string Category { get; set; } = string.Empty;
    public decimal EstimatedPrice { get; set; }
}

public class PriceResult
{
    public string GiftName { get; set; } = string.Empty;
    public string Store { get; set; } = string.Empty;
    public decimal Price { get; set; }
    public string Url { get; set; } = string.Empty;
}

public class GiftRecommendation
{
    public GiftIdea Gift { get; set; } = new();
    public List<PriceResult> Prices { get; set; } = [];
    public PriceResult? BestDeal { get; set; }
}

エージェントの構築

エージェント フレームワークの利点は、特化したエージェントを簡単に作成できることです。各エージェントは、その専門知識を定義する特定のシステム プロンプトを備えた単純な ChatClientAgent です。

using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;

public static class ChristmasAgentFactory
{
    public static AIAgent CreateGiftIdeaAgent(IChatClient chatClient)
    {
        return new ChatClientAgent(
            chatClient,
            @"You are a creative Christmas gift advisor. When given information about a person 
            (age, interests, budget), you suggest thoughtful and personalized gift ideas.
            
            For each suggestion, provide:
            - Gift name
            - Brief description of why it's a good fit
            - Category (Electronics, Books, Fashion, Home, Experience, etc.)
            - Estimated price range
            
            Always suggest 3-5 gift options within the specified budget.
            Format your response as a structured list.");
    }

    public static AIAgent CreatePriceComparisonAgent(IChatClient chatClient)
    {
        return new ChatClientAgent(
            chatClient,
            @"You are a price comparison specialist. Given a list of gift ideas, 
            you research and compare prices from different online retailers.
            
            For each gift, provide:
            - Store name (Amazon, Best Buy, Target, Walmart, etc.)
            - Current price
            - Any available discounts or deals
            
            Always highlight the best deal for each item.
            Consider shipping costs and delivery times for Christmas.");
    }

    public static AIAgent CreateSummaryAgent(IChatClient chatClient)
    {
        return new ChatClientAgent(
            chatClient,
            @"You are a gift recommendation summarizer. Take the gift ideas and price 
            comparisons and create a final recommendation report.
            
            Your summary should:
            - Rank gifts by value (quality vs price)
            - Highlight the top pick with reasoning
            - Include total cost estimate
            - Add any tips for holiday shopping
            
            Make the summary cheerful and festive!");
    }
}

ワークフローの作成

ここからが楽しい部分で、エージェントを一連のワークフローに接続します。エージェント フレームワークは、エージェントをさまざまなパターンに構成するための WorkflowBuilder および AgentWorkflowBuilder を提供します。

public static class ChristmasGiftWorkflow
{
    public static async Task RunAsync()
    {
        // Set up the Azure OpenAI client
        var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") 
            ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
        var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") 
            ?? "gpt-4o-mini";
        
        var chatClient = new AzureOpenAIClient(
            new Uri(endpoint), 
            new DefaultAzureCredential())
            .GetChatClient(deploymentName)
            .AsIChatClient();

        // Create our specialized agents
        AIAgent giftIdeaAgent = ChristmasAgentFactory.CreateGiftIdeaAgent(chatClient);
        AIAgent priceAgent = ChristmasAgentFactory.CreatePriceComparisonAgent(chatClient);
        AIAgent summaryAgent = ChristmasAgentFactory.CreateSummaryAgent(chatClient);

        // Build a sequential workflow: Ideas -> Prices -> Summary
        var workflow = AgentWorkflowBuilder.BuildSequential(
            "ChristmasGiftFinder",
            [giftIdeaAgent, priceAgent, summaryAgent]);

        // Define our gift recipient
        var recipient = new GiftRecipient
        {
            Name = "Mom",
            Age = 55,
            Interests = ["gardening", "cooking", "reading", "yoga"],
            Budget = 100
        };

        var prompt = $@"Find Christmas gifts for {recipient.Name}, 
            age {recipient.Age}, who enjoys {string.Join(", ", recipient.Interests)}. 
            Budget: ${recipient.Budget}";

        // Execute the workflow with streaming
        await using StreamingRun run = await InProcessExecution.StreamAsync(
            workflow, 
            new ChatMessage(ChatRole.User, prompt));

        // Send the turn token to start processing
        await run.TrySendMessageAsync(new TurnToken(emitEvents: true));

        // Watch for workflow events
        await foreach (WorkflowEvent evt in run.WatchStreamAsync())
        {
            if (evt is AgentRunUpdateEvent agentUpdate)
            {
                Console.Write(agentUpdate.Data);
            }
            else if (evt is WorkflowOutputEvent outputEvent)
            {
                Console.WriteLine("\n🎄 Final Recommendations:");
                Console.WriteLine(outputEvent.Data);
            }
        }
    }
}

エージェントの同時実行

複数の人へのギフトを一度に検索したい場合はどうすればよいですか?エージェント フレームワークは同時実行をサポートしているため、次のシナリオに最適です。

public static async Task FindGiftsForEveryoneAsync(IChatClient chatClient, List<GiftRecipient> recipients)
{
    // Create an agent for each recipient
    var agents = recipients.Select(r => new ChatClientAgent(
        chatClient,
        $@"Find the perfect Christmas gift for {r.Name} (age {r.Age}), 
           who loves {string.Join(", ", r.Interests)}. Budget: ${r.Budget}.
           Provide one well-researched recommendation with price."
    )).ToList();

    // Build a concurrent workflow - all agents run in parallel
    var workflow = AgentWorkflowBuilder.BuildConcurrent(
        "FamilyGiftFinder",
        agents);

    await using StreamingRun run = await InProcessExecution.StreamAsync(
        workflow, 
        new ChatMessage(ChatRole.User, "Find gifts now!"));

    await run.TrySendMessageAsync(new TurnToken(emitEvents: true));

    await foreach (WorkflowEvent evt in run.WatchStreamAsync())
    {
        if (evt is WorkflowOutputEvent output)
        {
            Console.WriteLine("🎁 All gift recommendations ready!");
            Console.WriteLine(output.Data);
        }
    }
}

より詳細な制御のためのカスタム エグゼキュータより複雑なシナリオの場合は、ワークフローをきめ細かく制御できるカスタム エグゼキューターを作成できます。

public sealed class GiftValidatorExecutor : Executor<List<ChatMessage>, List<ChatMessage>>
{
    public GiftValidatorExecutor() : base("GiftValidator") { }

    public override async ValueTask<List<ChatMessage>> HandleAsync(
        List<ChatMessage> messages,
        IWorkflowContext context,
        CancellationToken cancellationToken = default)
    {
        var lastMessage = messages.LastOrDefault()?.Text ?? "";
        
        // Validate that suggestions are within budget
        if (lastMessage.Contains("over budget", StringComparison.OrdinalIgnoreCase))
        {
            Console.WriteLine("⚠️ Some suggestions exceeded budget, filtering...");
            // Add validation logic here
        }

        Console.WriteLine("✅ Gift suggestions validated!");
        return messages;
    }
}

次に、このエグゼキューターをワークフローに挿入できます。

var validator = new GiftValidatorExecutor();

var workflow = new WorkflowBuilder(giftIdeaAgent)
    .AddEdge(giftIdeaAgent, validator)
    .AddEdge(validator, priceAgent)
    .AddEdge(priceAgent, summaryAgent)
    .WithOutputFrom(summaryAgent)
    .Build();

Bing Grounding を使用して実際の Web 検索を追加する

これまでのところ、エージェントは AI モデルの知識に基づいて応答を生成しています。しかし、Web で実際の製品の価格と在庫状況を検索したい場合はどうすればよいでしょうか?ここで、Bing Search によるグラウンディング が登場します。これは、Microsoft Foundry (旧称 Azure AI Foundry) で利用可能なツールで、エージェントが応答を生成するときにリアルタイムの公開 Web データを組み込むことができます。

まず、Azure PortalGrounding with Bing Search リソースを作成する必要があります。必ず AI プロジェクトと同じリソース グループに作成してください。

Bing Search を使用したグラウンディングの設定

Grounding with Bing Search の利点は、Azure AI エージェントと直接統合できることです。エージェントは、ユーザーのクエリに基づいて検索ツールをいつ使用するかを決定し、Web を検索し、その結果を使用して根拠のある応答を生成します。

using Azure.AI.Agents.Persistent;
using Azure.Identity;

public static class BingGroundingSetup
{
    public static async Task<PersistentAgent> CreateAgentWithBingGroundingAsync(
        string projectEndpoint,
        string modelDeploymentName,
        string bingConnectionId)
    {
        // Create the Persistent Agents client
        var agentClient = new PersistentAgentsClient(projectEndpoint, new DefaultAzureCredential());

        // Configure the Bing Grounding tool
        var bingGroundingTool = new BingGroundingToolDefinition(
            new BingGroundingSearchToolParameters(
                [new BingGroundingSearchConfiguration(bingConnectionId)]
            )
        );

        // Create the agent with Bing Grounding enabled
        var agent = await agentClient.Administration.CreateAgentAsync(
            model: modelDeploymentName,
            name: "ChristmasPriceHunter",
            instructions: @"You are a Christmas gift price comparison specialist.
                
                When given gift ideas, use Bing to search for:
                - Current prices at major retailers (Amazon, Best Buy, Target, Walmart)
                - Available discounts and holiday deals
                - Shipping times to ensure delivery before Christmas
                
                Always provide URLs to the products you find.
                Highlight the best deals and recommend where to buy.",
            tools: [bingGroundingTool]
        );

        return agent;
    }
}

Real Search を使用した価格比較エージェントの作成

次に、Web で実際の製品情報を検索する完全な価格比較エージェントを作成しましょう。

using Azure.AI.Agents.Persistent;
using Azure.Identity;

public class ChristmasPriceAgent
{
    private readonly PersistentAgentsClient _client;
    private readonly string _modelDeployment;
    private readonly string _bingConnectionId;

    public ChristmasPriceAgent(string projectEndpoint, string modelDeployment, string bingConnectionId)
    {
        _client = new PersistentAgentsClient(projectEndpoint, new DefaultAzureCredential());
        _modelDeployment = modelDeployment;
        _bingConnectionId = bingConnectionId;
    }

    public async Task<string> FindGiftPricesAsync(string giftIdeas)
    {
        // Create agent with Bing Grounding
        var bingTool = new BingGroundingToolDefinition(
            new BingGroundingSearchToolParameters(
                [new BingGroundingSearchConfiguration(_bingConnectionId)]
            )
        );

        var agent = await _client.Administration.CreateAgentAsync(
            model: _modelDeployment,
            name: "PriceHunter",
            instructions: @"Search the web for current prices on the given gift ideas. 
                For each gift, find prices from at least 2-3 different stores.
                Include direct links to the products.
                Note any Christmas sales or discounts available.",
            tools: [bingTool]
        );

        try
        {
            // Create a thread for the conversation
            var thread = await _client.Threads.CreateThreadAsync();

            // Add the gift ideas as a message
            await _client.Messages.CreateMessageAsync(
                thread.Id,
                MessageRole.User,
                $"Find current prices for these gift ideas: {giftIdeas}"
            );

            // Run the agent
            var run = await _client.Runs.CreateRunAsync(thread.Id, agent.Id);

            // Wait for completion
            do
            {
                await Task.Delay(500);
                run = await _client.Runs.GetRunAsync(thread.Id, run.Id);
            }
            while (run.Status == RunStatus.Queued || run.Status == RunStatus.InProgress);

            // Get the response
            var messages = _client.Messages.GetMessages(thread.Id);
            var response = messages
                .Where(m => m.Role == MessageRole.Agent)
                .SelectMany(m => m.ContentItems)
                .OfType<MessageTextContent>()
                .FirstOrDefault();

            // Clean up
            await _client.Threads.DeleteThreadAsync(thread.Id);

            return response?.Text ?? "No results found.";
        }
        finally
        {
            // Clean up the agent
            await _client.Administration.DeleteAgentAsync(agent.Id);
        }
    }
}

Bing Grounding をワークフローに統合する

完全なギフト検索ワークフローで Bing を利用した価格エージェントを使用する方法は次のとおりです。

public static async Task RunWithBingGroundingAsync()
{
    var projectEndpoint = Environment.GetEnvironmentVariable("PROJECT_ENDPOINT")!;
    var modelDeployment = Environment.GetEnvironmentVariable("MODEL_DEPLOYMENT_NAME") ?? "gpt-4o";
    var bingConnectionId = Environment.GetEnvironmentVariable("BING_CONNECTION_ID")!;
    // Connection ID format: /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.CognitiveServices/accounts/{account}/projects/{project}/connections/{connection}

    Console.WriteLine("🔍 Searching the web for real prices with Bing Grounding...\n");

    var priceAgent = new ChristmasPriceAgent(projectEndpoint, modelDeployment, bingConnectionId);

    // First, generate gift ideas (could come from another agent)
    var giftIdeas = "1. Milwaukee cordless drill set, 2. Weber portable grill, 3. Carhartt beanie";

    // Search for real prices
    var priceResults = await priceAgent.FindGiftPricesAsync(giftIdeas);

    Console.WriteLine("📊 Price Comparison Results:");
    Console.WriteLine(priceResults);
    Console.WriteLine("\n🎁 Happy Shopping! 🎁");
}

Bing 結果からの引用を処理する

Bing Search を使用したグラウンディングの重要な側面の 1 つは、応答にソース Web サイトへのリンクを含む引用が含まれていることです。それらを抽出して表示する方法は次のとおりです。

public static void ProcessBingGroundingResponse(IEnumerable<PersistentThreadMessage> messages)
{
    foreach (var message in messages.Where(m => m.Role == MessageRole.Agent))
    {
        foreach (var content in message.ContentItems)
        {
            if (content is MessageTextContent textContent)
            {
                var response = textContent.Text;

                // Process URL citations
                if (textContent.Annotations != null)
                {
                    foreach (var annotation in textContent.Annotations)
                    {
                        if (annotation is MessageTextUriCitationAnnotation uriAnnotation)
                        {
                            // Replace citation placeholder with markdown link
                            response = response.Replace(
                                uriAnnotation.Text,
                                $" [{uriAnnotation.UriCitation.Title}]({uriAnnotation.UriCitation.Uri})"
                            );
                        }
                    }
                }

                Console.WriteLine(response);
            }
        }
    }
}

価格比較エージェントが実行されると、Bing Search によるグラウンディング を使用して、実際の製品リスト、現在の価格、利用可能なセールをライブ Web から検索します。エージェントはクエリに基づいていつ検索するかを自動的に決定し、適切な引用を含む根拠のある応答を返します。

完全なプログラム

Program.cs ではすべてがどのようにまとめられているかを次に示します。

using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;

Console.WriteLine("🎄 Christmas Gift Finder - Powered by AI Agents 🎄\n");

var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!;
var deployment = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";

var chatClient = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
    .GetChatClient(deployment)
    .AsIChatClient();

// Create the agent team
var ideaAgent = ChristmasAgentFactory.CreateGiftIdeaAgent(chatClient);
var priceAgent = ChristmasAgentFactory.CreatePriceComparisonAgent(chatClient);
var summaryAgent = ChristmasAgentFactory.CreateSummaryAgent(chatClient);

// Build the workflow
var workflow = AgentWorkflowBuilder.BuildSequential(
    "ChristmasGiftWorkflow",
    [ideaAgent, priceAgent, summaryAgent]);

Console.Write("Who are you shopping for? ");
var recipientName = Console.ReadLine() ?? "Someone special";

Console.Write("What are their interests? ");
var interests = Console.ReadLine() ?? "general";

Console.Write("What's your budget? $");
var budget = Console.ReadLine() ?? "50";

var prompt = $"Find Christmas gifts for {recipientName} who enjoys {interests}. Budget: ${budget}";

Console.WriteLine("\n🔍 Searching for the perfect gifts...\n");

await using var run = await InProcessExecution.StreamAsync(
    workflow,
    new ChatMessage(ChatRole.User, prompt));

await run.TrySendMessageAsync(new TurnToken(emitEvents: true));

await foreach (var evt in run.WatchStreamAsync())
{
    switch (evt)
    {
        case AgentRunUpdateEvent update:
            Console.Write(update.Update.Text);
            break;
        case WorkflowOutputEvent output:
            Console.WriteLine("\n\n🎁 Happy Shopping! 🎁");
            break;
    }
}

結論

Microsoft のエージェント フレームワークを使用すると、各エージェントが特定のタスクに特化したマルチエージェント システムを驚くほど簡単に構築できます。ワークフローを通じてこれらのエージェントを調整することで、クリスマスの買い物などの複雑な問題に体系的かつ効率的な方法で取り組むことができます。

これをさらに強力にするのは、ツールを通じて現実世界の機能を追加できることです。 Microsoft Foundry の Grounding と Bing Search を統合することにより、当社の価格比較エージェントは実際に Web で現在の価格、取引、在庫状況を検索できるようになり、単純な AI チャットボットが適切な引用を備えた真に役立つショッピング アシスタントに変わります。

フレームワークは逐次パターン、同時パターン、およびハンドオフ パターンをサポートしているため、ニーズに正確に一致するエージェント システムを設計できます。ギフトを探す場合でも、旅行を計画する場合でも、その他の複数ステップのタスクでも、エージェント フレームワークはそれを実現するための構成要素を提供します。このホリデー シーズンは、AI エージェントにリサーチを任せて、あなたはプレゼントのラッピングや家族との時間を楽しむことに集中しましょう。

ソースコード

この投稿で示されている概念は、公式のエージェント フレームワーク サンプルに基づいています。 Microsoft Agent Framework GitHub リポジトリ でさらに多くの例を調べることができます。

楽しい休暇とコーディングを楽しんでください! 🎄