😽

Azure Agent Service で Bing 検索結果を取得できるか試してみた

2025/03/14に公開

はじめに

現在、AI で Bing の検索結果をグラウンディングを行うためには Grounding with Bing Search を Azure Agent Service で使う必要があります。

公式ドキュメント: Bing 検索を使用したグラウンディング

Azure Agent Service で Bing の検索を行うように設定をすると Bing の検索結果をもとに答えてくれるようになります。

例えば以下のようにナレッジに何も追加していないときに「.NET の最新のプレビューバージョンはいくつですか?」と質問をしても具体的には答えてくれません。
しかし、Azure Agent Service で Bing の検索を行うように設定をすると Bing の検索結果をもとに答えてくれるようになります。ちゃんと .NET 10 Preview 1 と答えてくれました。

こうすることでインターネット上の最新情報を持ったエージェントを作成することができます。

自分の AI アプリへの組み込み

さて、ここで気になるのが自分の AI アプリに組み込むことができるのかということです。最初から Azure Agent Service 前提で作っていればいいですが、それとは別に独自の RAG を組み込んだものもあって、そこに Bing も混ぜ込みたいみたいな感じですね。

例えば以下のように Semantic Kernel (C#) を使って猫っぽく振舞うアシスタントを既に作っていたとします。

// 一部 Semantic Kernel のプレビュー機能を使うため、その警告の抑止を行っています。
#pragma warning disable SKEXP0001

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

var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .AddJsonFile("appsettings.Development.json", true)
    .Build();

var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        configuration["AOAI:ModelDeploymentName"]!,
        configuration["AOAI:Endpoint"]!,
        new AzureCliCredential())
    .Build();

var catAgent = new ChatCompletionAgent
{
    Name = "Cat",
    Instructions = """
        あなたは猫型アシスタントです。猫っぽい言葉遣いで話してください。
        """,
    Kernel = kernel
};

ChatHistory history = [
    new ChatMessageContent(AuthorRole.User, "最新の .NET のプレビューバージョンについて教えて"),
];
await foreach (var message in catAgent.InvokeAsync(history))
{
    Console.WriteLine($"{message.AuthorName}: {message.Content}");
}

これを実行すると以下のようになります。Bing の検索結果にアクセスできないので具体的な答えが返ってきません。

Cat: ごめんね~、今のところ最新の .NET のプレビューバージョンについて詳しくは分からないにゃ。でも、マイクロソフトの公式サイトをチェックすると最新情報が手に入るはずだにゃ!また、GitHubのリポジトリでもプレビューバージョンの情報が公開されてることが多いから、そちらも見てみるといいにゃ。お役に立てなくて申し訳ないにゃん!

これに Bing の検索結果を組み込見たいと思います。アプローチとしては以下のような方法が考えられます。

  1. Bing を使う Agent を猫型エージェントの Tool として設定する
  2. Azure Agent Service で猫型エージェントを最初から作って Bing を使うように設定する
  3. Azure Agent Service を使った Bing 検索を行う Agent と猫型エージェントを組み合わせて最終的に猫型エージェントに回答を生成させるワークフローやマルチエージェントシステムを作成する

個人的には既存の AI が作りこまれていれば作りこまれているほど 2 に行きづらいと思います。3 も既存のアプリに結構手を入れることになると思います。今回のサンプルの場合は 2 にするのも簡単なので 2 に仕立てて Bing 検索の仕方を勉強して 1 に持っていくことにします。

やり方としては、公式の SDK を使うか Semantic Kernel の方にも Azure Agent Service に対応した機能があるので、そちらを使うかになると思います。今回は個人的な好みで Semantic Kernel を使ってみたいと思います。

因みに公式の場合は こちら に例が載っているので参考になると思います。

Semantic Kernel で Azure Agent Service に接続する方法は以下のようになります。

// 一部 Semantic Kernel のプレビュー機能を使うため、その警告の抑止を行っています。
#pragma warning disable SKEXP0001
#pragma warning disable SKEXP0110

using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.AzureAI;
using Microsoft.SemanticKernel.ChatCompletion;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;

var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .AddJsonFile("appsettings.Development.json", true)
    .Build();

var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        configuration["AOAI:ModelDeploymentName"]!,
        configuration["AOAI:Endpoint"]!,
        new AzureCliCredential())
    .Build();

// Bing を使う Agent を作成開始!
// AI Foundary のプロジェクトを取得
var projectClient = AzureAIAgent.CreateAzureAIClient(
    configuration["AgentService:ConnectionString"]!,
    new AzureCliCredential());
// AgentClient を取得
var agentsClient = projectClient.GetAgentsClient();

// Bing に接続するための Connection を取得
var bingConnection = await projectClient.GetConnectionsClient()
    .GetConnectionAsync(configuration["AgentService:BingConnectionId"]!);
// Bing を使う Agent を作成
var bingAgentDefinition = await agentsClient.CreateAgentAsync(
    configuration["AOAI:ModelDeploymentName"]!,
    name: "Bing",
    description: "Bing で検索するエージェント",
    instructions: """
        あなたは猫型エージェントです。猫型っぽい言葉遣いで話してください。
        """,
    tools: [
        new BingGroundingToolDefinition(new()
        {
            ConnectionList = { new(bingConnection.Value.Id) },
        })
    ]);
// Semantic Kernel の Agent でラップ
AzureAIAgent bingAgent = new(bingAgentDefinition, agentsClient);

// Bing で検索を考慮した回答を取得する
var thread = await agentsClient.CreateThreadAsync();
await bingAgent.AddChatMessageAsync(
    thread.Value.Id, 
    new ChatMessageContent(AuthorRole.User, "最新の .NET のプレビューバージョンについて教えて"));
await foreach (var message in bingAgent.InvokeAsync(thread.Value.Id))
{
    foreach (var item in message.Items)
    {
        Console.WriteLine(JsonSerializer.Serialize(item, new JsonSerializerOptions { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }));
    }
}

Semantic Kernel の Agent 機能を使うと Azure Agent Service の生の API を使わないで済むかと思ったらそんなことありませんでした。バリバリの生の API を使っています。しかも現時点では実行結果が以下のようになるので参考にした URL も表示されません…!

{"$type":"TextContent","Text":"にゃんと!最新の .NET プレビューバージョンは .NET 10 のプレビュー1にゃんだよ【3†source】。たくさんの新機能と改善が含まれていて、ライブラリ、C#、ASP.NET Core、Blazor、.NET MAUI などに大きな強化がされているにゃん。詳しくはリリースノートをチェックしてみてね。【3†source】"}
{"$type":"AnnotationContent","Quote":"【3†source】","StartIndex":0,"EndIndex":0}
{"$type":"AnnotationContent","Quote":"【3†source】","StartIndex":0,"EndIndex":0}

ということで以下のようにして素の SDK を使うことにして以下のようにコードを書き換えました。

// 一部 Semantic Kernel のプレビュー機能を使うため、その警告の抑止を行っています。
#pragma warning disable SKEXP0001
#pragma warning disable SKEXP0110

using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;

var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .AddJsonFile("appsettings.Development.json", true)
    .Build();

var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        configuration["AOAI:ModelDeploymentName"]!,
        configuration["AOAI:Endpoint"]!,
        new AzureCliCredential())
    .Build();

// Bing を使う Agent を作成開始!
// AI Foundary のプロジェクトを取得
var projectClient = new AIProjectClient(
    configuration["AgentService:ConnectionString"]!,
    new AzureCliCredential());
// AgentClient を取得
var agentsClient = projectClient.GetAgentsClient();

// Bing に接続するための Connection を取得
var bingConnection = await projectClient.GetConnectionsClient()
    .GetConnectionAsync(configuration["AgentService:BingConnectionId"]!);
// Bing を使う Agent を作成
var bingAgentDefinition = await agentsClient.CreateAgentAsync(
    configuration["AOAI:ModelDeploymentName"]!,
    name: "Bing",
    description: "Bing で検索するエージェント",
    instructions: """
        あなたは猫型エージェントです。猫型っぽい言葉遣いで話してください。
        """,
    tools: [
        new BingGroundingToolDefinition(new()
        {
            ConnectionList = { new(bingConnection.Value.Id) },
        })
    ]);

// Bing で検索を考慮した回答を取得する
var threadResponse = await agentsClient.CreateThreadAsync();
var thread = threadResponse.Value;

var messageResponse = await agentsClient.CreateMessageAsync(
    thread.Id,
    MessageRole.User,
    "最新の .NET のプレビューバージョンについて教えて");

var runResponse = await agentsClient.CreateRunAsync(thread, bingAgentDefinition.Value);
do
{
    await Task.Delay(500);
    runResponse = await agentsClient.GetRunAsync(thread.Id, runResponse.Value.Id);
}
while (runResponse.Value.Status == RunStatus.Queued || runResponse.Value.Status == RunStatus.InProgress);

var afterRunMessagesResponse = await agentsClient.GetMessagesAsync(thread.Id);
var messages = afterRunMessagesResponse.Value.Data;

// Note: messages iterate from newest to oldest, with the messages[0] being the most recent
var threadMessage = messages[0];
Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
foreach (var contentItem in threadMessage.ContentItems)
{
    if (contentItem is MessageTextContent textItem)
    {
        Console.Write(textItem.Text);
    }
    else if (contentItem is MessageImageFileContent imageFileItem)
    {
        Console.Write($"<image from ID: {imageFileItem.FileId}");
    }
    Console.WriteLine();
}

これで以下のようになりました。

2025-03-14 09:51:26 -  assistant: 最新の .NET のプレビューバージョンは、.NET 10のプレビュー1です。2025年2月にリリースされており、.NET ランタイム、SDK、ライブラリ、C#、ASP.NET Core、Blazor、.NET MAUIなど、多くの分野での主要な改善が含まれているんだにゃあ【1†source】。

あれ?URL が取れない…。ということで threadMessage 変数を見てみたら URL が入っていない…。ContentItemsAnnotations にそれらしきものは入っていますが、肝心の URL が入っていませんでした。多分まだ preview 版の SDK なので仕方ないのでしょう…。正式対応を待ちます。

ということで、どちらを使っても URL が取れないのであれば Semantic Kernel の方が API が簡単なので Semantic Kernel を使うことにします。

ツール化しよう

最後にこれをツール化しておきます。
ツールとして呼び出せるように Semantic Kernel のプラグインにします。プラグインは KernelFunctionDescription 等の属性のついたメソッドを持つクラスです。以下のようにしました。

// Bing 検索をプラグイン化
class BingPlugin(IConfiguration configuration)
{
    [KernelFunction, Description("Bing 検索を使用した回答を返します。")]
    public async Task<ChatMessageContent> BingSearchAsync(
        [Description("知りたいこと")] string question,
        [Description("検索結果の最大数")] int maxResults = 10)
    {
        // Bing を使う Agent を作成開始!
        // AI Foundary のプロジェクトを取得
        var projectClient = AzureAIAgent.CreateAzureAIClient(
            configuration["AgentService:ConnectionString"]!,
            new AzureCliCredential());
        // AgentClient を取得
        var agentsClient = projectClient.GetAgentsClient();

        // Bing に接続するための Connection を取得
        var bingConnection = await projectClient.GetConnectionsClient()
            .GetConnectionAsync(configuration["AgentService:BingConnectionId"]!);
        // Bing を使う Agent を作成
        var bingAgentDefinition = await agentsClient.CreateAgentAsync(
            configuration["AOAI:ModelDeploymentName"]!,
            name: "Bing",
            description: "Bing で検索するエージェント",
            instructions: """
                あなたは Bing で検索を行い回答をするエージェントです。
                """,
            tools: [
                new BingGroundingToolDefinition(new()
                {
                    ConnectionList = { new(bingConnection.Value.Id) },
                })
            ]);
        // Semantic Kernel の Agent でラップ
        AzureAIAgent bingAgent = new(bingAgentDefinition, agentsClient);

        // Bing で検索を考慮した回答を取得する
        var thread = await agentsClient.CreateThreadAsync();
        await bingAgent.AddChatMessageAsync(
            thread.Value.Id,
            new ChatMessageContent(AuthorRole.User, $"{question}。回答の後に {maxResults} 件の引用元タイトルとURLの一覧を出してください。"));
        var result = await bingAgent.InvokeAsync(thread.Value.Id).FirstOrDefaultAsync();
        return result ?? new ChatMessageContent(AuthorRole.Assistant, "見つかりませんでした。");
    }
}

そして、猫型アシスタントが、これを自動的に呼び出すように設定します。

// 一部 Semantic Kernel のプレビュー機能を使うため、その警告の抑止を行っています。
#pragma warning disable SKEXP0001
#pragma warning disable SKEXP0110

using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.AzureAI;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using System.ComponentModel;

var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .AddJsonFile("appsettings.Development.json", true)
    .Build();

var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        configuration["AOAI:ModelDeploymentName"]!,
        configuration["AOAI:Endpoint"]!,
        new AzureCliCredential())
    .Build();

// プラグインを登録
kernel.Plugins.AddFromObject(new BingPlugin(configuration));

var catAgent = new ChatCompletionAgent
{
    Name = "Cat",
    Instructions = """
        あなたは猫型アシスタントです。猫っぽい言葉遣いで話してください。
        """,
    Kernel = kernel,
    // 自動的にプラグインを呼び出すように設定
    Arguments = new(new AzureOpenAIPromptExecutionSettings
    {
        ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,
    }),
};

ChatHistory history = [
    new ChatMessageContent(AuthorRole.User, "最新の .NET のプレビューバージョンについて教えて"),
];
await foreach (var message in catAgent.InvokeAsync(history))
{
    Console.WriteLine($"{message.AuthorName}: {message.Content}");
}

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

Cat: 今のところ、最新の .NET プレビュー版は **.NET 10 Preview 1** だにゃ!これについてもっと詳しく知りたい場合は、以下のリンクをチェックしてみてにゃ:

1. [.NET 10 Preview 1 is now available! - .NET Blog](https://devblogs.microsoft.com/dotnet/announcing-net-10-preview-1/)
2. [次期LTS版となる.NET 10 Preview 1が公開。.NETランタイムのオーバーヘッド削減など](https://www.publickey1.jp/blog/25/net_10_preview_1.html)
3. [What's New in .NET 10 (Preview) - Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10-preview)

一応ちゃんと回答してくれますね。

まとめ

もっといい方法がありそう…。

今回の最終版のコード全体は以下のリポジトリに置いています。

https://github.com/runceel/BingAgentLab

Microsoft (有志)

Discussion